<commit_before>/* Core rebalance algorithms, no UI. */

function rebalance(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextRebalanceMap,
             actualizeNextMap);
}
function failOver(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextFailOverMap,
             actualizeNextMap);
}
function restoreBack(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextRestoreBackMap,
             actualizeNextMap);
}
function cancelRebalance(ctx, req) {
  return run(ctx, req,
             cancelTakeOverSteps,
             takeCurrentMapAsNextMap,
             actualizeNextMap);
}
function actualizeNextMap(ctx, req) {
  return run(ctx, req,
             validateNextMap,
             scheduleSteps,
             executeSteps,
             checkHealth);
}

// --------------------------------------------------------

function validatePartitionSettings(ctx, req) {
  req.nextBucketEvents = _.clone(req.prevBucketEvents);
  req.nextBucketEvents.events = sortDesc(req.nextBucketEvents.events || [],
                                         "when");

  req.lastPartitionMap = _.findWhere(req.nextBucketEvents.events,
                                     { class: "partitionMap" });
  if (req.lastPartitionMap) {
    req.lastPartitions = partitionsWithNodeNames(req.lastPartitionMap.partitions,
                                                 req.lastPartitionMap.nodes);
  }

  req.arrNodes = { want: req.wantPartitionParams.nodes };
  req.mapNodes = {};

  req.lastPartitionParams = _.findWhere(req.nextBucketEvents.events,
                                        { class: "partitionParams" });
  if (req.lastPartitionParams) {
    req.err =
      _.reduce(["keyFunc", "model", "numPartitions"], function(r, k) {
          if (req.lastPartitionParams[k] != req.wantPartitionParams[k]) {
            return "partitionParams." + k + " not equal: " +
              req.lastPartitionParams[k] + " vs " + req.wantPartitionParams[k];
          }
          return r;
        }, null);
    if (req.err) {
      return;
    }

    req.arrNodes.added =
      _.difference(req.wantPartitionParams.nodes, req.lastPartitionParams.nodes);
    req.arrNodes.removed =
      _.difference(req.lastPartitionParams.nodes, req.wantPartitionParams.nodes);
    req.arrNodes.same =
      _.intersection(req.lastPartitionParams.nodes, req.wantPartitionParams.nodes);
  } else {
    req.arrNodes.added   = req.wantPartitionParams.nodes;
    req.arrNodes.removed = [];
    req.arrNodes.same    = [];
  }
  _.each(req.arrNodes, function(a, k) { req.mapNodes[k] = arrToMap(a); });
  function arrToMap(a) {
    return _.reduce(a, function(m, k) { m[k] = {}; return m; }, {});
  }

  req.partitionModel =
    ctx.getObj("partitionModel-" + req.wantPartitionParams.model).result;
  if (!req.partitionModel) {
    req.err = "error: missing partitionModel-" + req.wantPartitionParams.model;
    return;
  }
  req.partitionModelStates =
    sortDesc(_.reduce(req.partitionModel.states,
                      function(a, v, k) {
                        a.push(_.defaults(_.clone(v), { name: k }));
                        return a;
                      }, []),
             "priority");
  req.mapStatePriority = // Key is state name (e.g., "master"), val is priority int.
    _.reduce(req.partitionModel.states,
             function(m, partitionModel, name) {
               m[name] = partitionModel.priority;
               return m;
             }, {});
}

function allocNextMap(ctx, req) {
  req.nextPartitionMap =
    ctx.newObj("partitionMap", _.omit(req.wantPartitionParams, "class")).result;
  req.nextPartitionMap.partitions =
    keyFunc[req.wantPartitionParams.keyFunc].allocPartitions(req);
  req.nextPartitionMapNumPartitions =
    _.size(req.nextPartitionMap.partitions);
}

function planNextRebalanceMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextFailOverMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextRestoreBackMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextMap(ctx, req) {
  // TODO: maintenance mode & swap rebalance detected as part of rebalance.

  // Remove nodes that user wants to be removed.
  var lastPartitions = req.lastPartitions || {};
  var nextPartitions =
    _.object(_.map(req.nextPartitionMap.partitions,
                   function(partition, partitionId) {
                     var lastPartition = lastPartitions[partitionId] || {};
                     var nextPartition = removeNodesFromPartition(lastPartition,
                                                                  req.arrNodes.removed);
                     return [partitionId, nextPartition];
                   }));

  req.stateNodeCountsBeg = countStateNodes(nextPartitions);
  req.stateNodeCountsCur = countStateNodes(nextPartitions);

  // Run through the sorted partition states (master, slave, etc) that
  // have constraints and invoke assignStateToPartitions().
  _.each(req.partitionModelStates,
         function(s) {
           var constraints =
             parseInt((req.wantPartitionParams.constraints || {})[s.name]) ||
             parseInt(s.constraints);
           if (constraints >= 0) {
             assignStateToPartitions(s.name, constraints);
           }
         });

  // Given a state and its constraints, for every partition, find and
  // assign the best nodes to that state.
  function assignStateToPartitions(state, constraints) {
    var partitionIds = _.keys(nextPartitions);
    nextPartitions =
      _.object(_.map(partitionIds,
                     function(partitionId) {
                       var partition = nextPartitions[partitionId];
                       var nodesToAssign =
                         findBestNodes(partitionId, partition, state, constraints);
                       partition = removeNodesFromPartition(partition,
                                                            partition[state],
                                                            decStateNodeCountsCur);
                       partition = removeNodesFromPartition(partition,
                                                            nodesToAssign,
                                                            decStateNodeCountsCur);
                       partition[state] = nodesToAssign;
                       incStateNodeCountsCur(state, nodesToAssign);
                       return [partitionId, partition];
                     }));
  }

  function findBestNodes(partitionId, partition, state, constraints) {
    var stateNodeCounts =
      req.stateNodeCountsCur[state] =
      req.stateNodeCountsCur[state] || {};
    var statePriority = req.mapStatePriority[state];
    var candidateNodes = req.nextPartitionMap.nodes;
    _.each(partition,
           function(sNodes, s) {
             // Filter out or don't touch nodes at a higher priority state.
             // E.g., if we're assigning slaves, leave the masters untouched.
             if (req.mapStatePriority[s] > statePriority) {
               candidateNodes = _.difference(candidateNodes, sNodes);
             }
           });
    candidateNodes = _.sortBy(candidateNodes, scoreNode);
    return candidateNodes.slice(0, constraints);

    function scoreNode(node) {
      var isCurrent = _.contains(partition[state] || [], node);
      var currentFactor = isCurrent ? -1 : 0;
      var r = stateNodeCounts[node] || 0;
      r = r + currentFactor;
      return r;
    }
  }

  function incStateNodeCountsCur(state, nodes) {
    adjustStateNodeCounts(req.stateNodeCountsCur, state, nodes, 1);
  }
  function decStateNodeCountsCur(state, nodes) {
    adjustStateNodeCounts(req.stateNodeCountsCur, state, nodes, -1);
  }

  function adjustStateNodeCounts(stateNodeCounts, state, nodes, amt) {
    _.each(nodes, function(n) {
        var s = stateNodeCounts[state] = stateNodeCounts[state] || {};
        s[n] = (s[n] || 0) + amt;
        if (s[n] < 0) {
          console.log("ERROR: adjustStateNodeCounts < 0" +
                      ", state: " + state + " node: " + n + " s[n]: " + s[n]);
        }
        if (s[n] > req.nextPartitionMapNumPartitions) {
          console.log("ERROR: adjustStateNodeCounts < numPartitions" +
                      ", state: " + state + " node: " + n + " s[n]: " + s[n]);
        }
      });
  }

  req.stateNodeCountsCur = countStateNodes(nextPartitions);

  req.nextPartitionMap.partitions = nextPartitions;
  req.nextPartitionMap.partitions =
    partitionsWithNodeIndexes(req.nextPartitionMap.partitions,
                              req.nextPartitionMap.nodes);

  // TODO: mark partitions on removed node as dead.
}

function validateNextMap(ctx, req) {
  // TODO: do real validation here.
  req.nextBucketEvents.events.unshift(req.wantPartitionParams);
  req.nextBucketEvents.events.unshift(req.nextPartitionMap);
}

function scheduleSteps(ctx, req) {
}

function executeSteps(ctx, req) {
}

function cancelTakeOverSteps(ctx, req) {
}

function takeCurrentMapAsNextMap(ctx, req) {
}

function checkHealth(ctx, req) {
}

// --------------------------------------------------------

// Returns partition with nodes removed.  Example, when removeNodes == ["a"],
//   before - partition: {"0": { "master": ["a"], "slave": ["b"] } }
//   after  - partition: {"0": { "master": [], "slave": ["b"] } }
function removeNodesFromPartition(partition, removeNodes, cb) {
  return _.object(_.map(partition,
                        function(partitionNodes, state) {
                          if (cb) {
                            cb(state, _.intersection(partitionNodes, removeNodes));
                          }
                          return [state, _.difference(partitionNodes, removeNodes)];
                        }));
}

// Converts node indexes to node names.  Example, with "nodes": ["a", "b"]:
//   before - "partitions": { "0": { "master": [0], "slave": [1] }, ... }
//   after  - "partitions": { "0": { "master": ["a"], "slave": ["b"] }, ... }
// Reverse of partitionsWithNodeIndexes().
function partitionsWithNodeNames(partitions, nodes) {
  return partitionsMap(partitions,
                       function(nodeIdx) { return nodes[nodeIdx]; });
}

// Converts node names to indexes.  Example, with node" == ["a", "b"]:
//   before - partitions: { "0": { "master": ["a"], "slave": ["b"] }, ... }
//   after  - partitions: { "0": { "master": [0], "slave": [1] }, ... }
// Reverse of partitionsWithNodeNames().
function partitionsWithNodeIndexes(partitions, nodes) {
  return partitionsMap(partitions,
                       function(nodeName) { return _.indexOf(nodes, nodeName); });
}

// Like map(), but runs f() on every nodes array in the partition.
// Example, with partitions == { "0": { "master": ["a"], "slave": ["b", "c"] } }
// then you'll see f(["a"]) and f(["b", "c"]).
function partitionsMap(partitions, f) {
  return _.object(_.map(partitions,
                        function(partition, partitionId) {
                          return [partitionId,
                                  _.object(_.map(partition,
                                                 function(arr, state) {
                                                   return [state, _.map(arr, f)];
                                                 }))];
                        }));
}

// Example, with partitions of...
//   { "0": { "master": ["a"], "slave": ["b", "c"] } },
//   { "1": { "master": ["b"], "slave": ["c"] } }
// then return value will be...
//   { "master": { "a": 1, "b": 1 } },
//   { "slave": { "b": 1, "c": 2 } }
function countStateNodes(partitions) {
  return _.reduce(partitions, function(r, partition, partitionId) {
      return _.reduce(partition, function(r, nodes, state) {
          _.each(nodes, function(node) {
              var s = r[state] = r[state] || {};
              s[node] = (s[node] || 0) + 1;
            });
          return r;
        }, r);
    }, {});
}

// --------------------------------------------------------

function run(ctx, req) { // Varargs are steps to apply to req as long as no req.err.
  return _.reduce(_.rest(arguments, 2),
                  function(req, step) {
                    return req.err ? req : step(ctx, req) || req;
                  }, req);
}

function sortDesc(a, field) { return _.sortBy(a, field).reverse(); }
<commit_msg>Sort partitions to support swap rebalance.<commit_after>/* Core rebalance algorithms, no UI. */

function rebalance(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextRebalanceMap,
             actualizeNextMap);
}
function failOver(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextFailOverMap,
             actualizeNextMap);
}
function restoreBack(ctx, req) {
  return run(ctx, req,
             validatePartitionSettings,
             allocNextMap,
             planNextRestoreBackMap,
             actualizeNextMap);
}
function cancelRebalance(ctx, req) {
  return run(ctx, req,
             cancelTakeOverSteps,
             takeCurrentMapAsNextMap,
             actualizeNextMap);
}
function actualizeNextMap(ctx, req) {
  return run(ctx, req,
             validateNextMap,
             scheduleSteps,
             executeSteps,
             checkHealth);
}

// --------------------------------------------------------

function validatePartitionSettings(ctx, req) {
  req.nextBucketEvents = _.clone(req.prevBucketEvents);
  req.nextBucketEvents.events = sortDesc(req.nextBucketEvents.events || [],
                                         "when");

  req.lastPartitionMap = _.findWhere(req.nextBucketEvents.events,
                                     { class: "partitionMap" });
  if (req.lastPartitionMap) {
    req.lastPartitions = partitionsWithNodeNames(req.lastPartitionMap.partitions,
                                                 req.lastPartitionMap.nodes);
  }

  req.arrNodes = { want: req.wantPartitionParams.nodes };
  req.mapNodes = {};

  req.lastPartitionParams = _.findWhere(req.nextBucketEvents.events,
                                        { class: "partitionParams" });
  if (req.lastPartitionParams) {
    req.err =
      _.reduce(["keyFunc", "model", "numPartitions"], function(r, k) {
          if (req.lastPartitionParams[k] != req.wantPartitionParams[k]) {
            return "partitionParams." + k + " not equal: " +
              req.lastPartitionParams[k] + " vs " + req.wantPartitionParams[k];
          }
          return r;
        }, null);
    if (req.err) {
      return;
    }

    req.arrNodes.added =
      _.difference(req.wantPartitionParams.nodes, req.lastPartitionParams.nodes);
    req.arrNodes.removed =
      _.difference(req.lastPartitionParams.nodes, req.wantPartitionParams.nodes);
    req.arrNodes.same =
      _.intersection(req.lastPartitionParams.nodes, req.wantPartitionParams.nodes);
  } else {
    req.arrNodes.added   = req.wantPartitionParams.nodes;
    req.arrNodes.removed = [];
    req.arrNodes.same    = [];
  }
  _.each(req.arrNodes, function(a, k) { req.mapNodes[k] = arrToMap(a); });
  function arrToMap(a) {
    return _.reduce(a, function(m, k) { m[k] = {}; return m; }, {});
  }

  req.partitionModel =
    ctx.getObj("partitionModel-" + req.wantPartitionParams.model).result;
  if (!req.partitionModel) {
    req.err = "error: missing partitionModel-" + req.wantPartitionParams.model;
    return;
  }
  req.partitionModelStates =
    sortDesc(_.reduce(req.partitionModel.states,
                      function(a, v, k) {
                        a.push(_.defaults(_.clone(v), { name: k }));
                        return a;
                      }, []),
             "priority");
  req.mapStatePriority = // Key is state name (e.g., "master"), val is priority int.
    _.reduce(req.partitionModel.states,
             function(m, partitionModel, name) {
               m[name] = partitionModel.priority;
               return m;
             }, {});
}

function allocNextMap(ctx, req) {
  req.nextPartitionMap =
    ctx.newObj("partitionMap", _.omit(req.wantPartitionParams, "class")).result;
  req.nextPartitionMap.partitions =
    keyFunc[req.wantPartitionParams.keyFunc].allocPartitions(req);
  req.nextPartitionMapNumPartitions =
    _.size(req.nextPartitionMap.partitions);
}

function planNextRebalanceMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextFailOverMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextRestoreBackMap(ctx, req) {
  return planNextMap(ctx, req);
}

function planNextMap(ctx, req) {
  // TODO: maintenance mode & swap rebalance detected as part of rebalance.

  // Remove nodes that user wants to be removed.
  var lastPartitions = req.lastPartitions || {};
  var nextPartitions =
    _.object(_.map(req.nextPartitionMap.partitions,
                   function(partition, partitionId) {
                     var lastPartition = lastPartitions[partitionId] || {};
                     var nextPartition = removeNodesFromPartition(lastPartition,
                                                                  req.arrNodes.removed);
                     return [partitionId, nextPartition];
                   }));

  req.stateNodeCountsBeg = countStateNodes(nextPartitions);
  req.stateNodeCountsCur = countStateNodes(nextPartitions);

  // Run through the sorted partition states (master, slave, etc) that
  // have constraints and invoke assignStateToPartitions().
  _.each(req.partitionModelStates,
         function(s) {
           var constraints =
             parseInt((req.wantPartitionParams.constraints || {})[s.name]) ||
             parseInt(s.constraints);
           if (constraints >= 0) {
             assignStateToPartitions(s.name, constraints);
           }
         });

  // Given a state and its constraints, for every partition, find and
  // assign the best nodes to that state.
  function assignStateToPartitions(state, constraints) {
    // The order that we visit the partitions can help us reach a
    // better assignment.
    var partitionIds = _.keys(nextPartitions).sort();
    partitionIds = _.sortBy(partitionIds, function(partitionId) {
        var lastPartition = lastPartitions[partitionId] || {};
        // First, visit partitions assigned to nodes that are
        // scheduled to be removed.
        if (!_.isEmpty(_.intersection(lastPartition[state],
                                      req.arrNodes.removed))) {
          return -1;
        }
        return 0;
      });

    nextPartitions =
      _.object(_.map(partitionIds,
                     function(partitionId) {
                       var partition = nextPartitions[partitionId];
                       var nodesToAssign =
                         findBestNodes(partitionId, partition, state, constraints);
                       partition = removeNodesFromPartition(partition,
                                                            partition[state],
                                                            decStateNodeCountsCur);
                       partition = removeNodesFromPartition(partition,
                                                            nodesToAssign,
                                                            decStateNodeCountsCur);
                       partition[state] = nodesToAssign;
                       incStateNodeCountsCur(state, nodesToAssign);
                       return [partitionId, partition];
                     }));
  }

  function findBestNodes(partitionId, partition, state, constraints) {
    var stateNodeCounts =
      req.stateNodeCountsCur[state] =
      req.stateNodeCountsCur[state] || {};
    var statePriority = req.mapStatePriority[state];
    var candidateNodes = req.nextPartitionMap.nodes;
    _.each(partition,
           function(sNodes, s) {
             // Filter out or don't touch nodes at a higher priority state.
             // E.g., if we're assigning slaves, leave the masters untouched.
             if (req.mapStatePriority[s] > statePriority) {
               candidateNodes = _.difference(candidateNodes, sNodes);
             }
           });
    candidateNodes = _.sortBy(candidateNodes, scoreNode);
    return candidateNodes.slice(0, constraints);

    function scoreNode(node) {
      var isCurrent = _.contains(partition[state], node);
      var currentFactor = isCurrent ? -1 : 0;
      var r = stateNodeCounts[node] || 0;
      r = r + currentFactor;
      return r;
    }
  }

  function incStateNodeCountsCur(state, nodes) {
    adjustStateNodeCounts(req.stateNodeCountsCur, state, nodes, 1);
  }
  function decStateNodeCountsCur(state, nodes) {
    adjustStateNodeCounts(req.stateNodeCountsCur, state, nodes, -1);
  }

  function adjustStateNodeCounts(stateNodeCounts, state, nodes, amt) {
    _.each(nodes, function(n) {
        var s = stateNodeCounts[state] = stateNodeCounts[state] || {};
        s[n] = (s[n] || 0) + amt;
        if (s[n] < 0) {
          console.log("ERROR: adjustStateNodeCounts < 0" +
                      ", state: " + state + " node: " + n + " s[n]: " + s[n]);
        }
        if (s[n] > req.nextPartitionMapNumPartitions) {
          console.log("ERROR: adjustStateNodeCounts < numPartitions" +
                      ", state: " + state + " node: " + n + " s[n]: " + s[n]);
        }
      });
  }

  req.stateNodeCountsCur = countStateNodes(nextPartitions);

  req.nextPartitionMap.partitions = nextPartitions;
  req.nextPartitionMap.partitions =
    partitionsWithNodeIndexes(req.nextPartitionMap.partitions,
                              req.nextPartitionMap.nodes);

  // TODO: mark partitions on removed node as dead.
}

function validateNextMap(ctx, req) {
  // TODO: do real validation here.
  req.nextBucketEvents.events.unshift(req.wantPartitionParams);
  req.nextBucketEvents.events.unshift(req.nextPartitionMap);
}

function scheduleSteps(ctx, req) {
}

function executeSteps(ctx, req) {
}

function cancelTakeOverSteps(ctx, req) {
}

function takeCurrentMapAsNextMap(ctx, req) {
}

function checkHealth(ctx, req) {
}

// --------------------------------------------------------

// Returns partition with nodes removed.  Example, when removeNodes == ["a"],
//   before - partition: {"0": { "master": ["a"], "slave": ["b"] } }
//   after  - partition: {"0": { "master": [], "slave": ["b"] } }
function removeNodesFromPartition(partition, removeNodes, cb) {
  return _.object(_.map(partition,
                        function(partitionNodes, state) {
                          if (cb) {
                            cb(state, _.intersection(partitionNodes, removeNodes));
                          }
                          return [state, _.difference(partitionNodes, removeNodes)];
                        }));
}

// Converts node indexes to node names.  Example, with "nodes": ["a", "b"]:
//   before - "partitions": { "0": { "master": [0], "slave": [1] }, ... }
//   after  - "partitions": { "0": { "master": ["a"], "slave": ["b"] }, ... }
// Reverse of partitionsWithNodeIndexes().
function partitionsWithNodeNames(partitions, nodes) {
  return partitionsMap(partitions,
                       function(nodeIdx) { return nodes[nodeIdx]; });
}

// Converts node names to indexes.  Example, with node" == ["a", "b"]:
//   before - partitions: { "0": { "master": ["a"], "slave": ["b"] }, ... }
//   after  - partitions: { "0": { "master": [0], "slave": [1] }, ... }
// Reverse of partitionsWithNodeNames().
function partitionsWithNodeIndexes(partitions, nodes) {
  return partitionsMap(partitions,
                       function(nodeName) { return _.indexOf(nodes, nodeName); });
}

// Like map(), but runs f() on every nodes array in the partition.
// Example, with partitions == { "0": { "master": ["a"], "slave": ["b", "c"] } }
// then you'll see f(["a"]) and f(["b", "c"]).
function partitionsMap(partitions, f) {
  return _.object(_.map(partitions,
                        function(partition, partitionId) {
                          return [partitionId,
                                  _.object(_.map(partition,
                                                 function(arr, state) {
                                                   return [state, _.map(arr, f)];
                                                 }))];
                        }));
}

// Example, with partitions of...
//   { "0": { "master": ["a"], "slave": ["b", "c"] } },
//   { "1": { "master": ["b"], "slave": ["c"] } }
// then return value will be...
//   { "master": { "a": 1, "b": 1 } },
//   { "slave": { "b": 1, "c": 2 } }
function countStateNodes(partitions) {
  return _.reduce(partitions, function(r, partition, partitionId) {
      return _.reduce(partition, function(r, nodes, state) {
          _.each(nodes, function(node) {
              var s = r[state] = r[state] || {};
              s[node] = (s[node] || 0) + 1;
            });
          return r;
        }, r);
    }, {});
}

// --------------------------------------------------------

function run(ctx, req) { // Varargs are steps to apply to req as long as no req.err.
  return _.reduce(_.rest(arguments, 2),
                  function(req, step) {
                    return req.err ? req : step(ctx, req) || req;
                  }, req);
}

function sortDesc(a, field) { return _.sortBy(a, field).reverse(); }
<|endoftext|>


<commit_before>YUI.add("board-model", function (Y) {

    var Board = Y.Base.create("board", Y.Model, [], {

        initializer: function () {
            this._bidList = new Y.Bridge.Model.BidList();
            this._bidList.parent = this;
            this._bidList.addTarget(this);

            this._cardList = new Y.Bridge.Model.CardList();
            this._cardList.parent = this;
            this._cardList.addTarget(this);

            this.after("bidsChange", this._afterBidsChange);
            this.after("cardsChange", this._afterCardsChange);

            this._resetBids(this.get("bids"));
            this._resetCards(this.get("cards"));
        },

        createBid: function (model, object, callback) {
            this._bidList.create(model, object, callback);
        },

        createCard: function (model, object, callback) {
            this._cardList.create(model, object, callback);
        },

        generateAuction: function () {
            return {
                bids: this._bidList.bids(),
                vulnerable: this.get("vulnerable"),
                dealer: this.get("dealer"),
                visible: true
            };
        },

        generateCards: function (direction) {
            var cards = this._cardList.cards(),
                result = this.get("deal")[direction];

            result = Y.Array.filter(result, function (card) {
                return Y.Array.indexOf(cards, card) === -1;
            });

            return result;
        },

        activeDirection: function () {
            var dealerPosition = Y.Bridge.directionPosition(this.get("dealer"));

            switch (this.get("state")) {
            case "auction":
                return Y.Bridge.DIRECTIONS[dealerPosition + this._bidList.size()];
                break;
            case "playing":
                return "N"; // TODO
                break;
            }
            return undefined;
        },

        _afterBidsChange: function (event) {
            this._resetBids(event.newVal);
        },

        _afterCardsChange: function (event) {
            this._resetCards(event.newVal);
        },

        _resetBids: function (bids) {
            this._bidList.reset(bids);
        },

        _resetCards: function (cards) {
            this._cardList.reset(cards);
        },

        state: function () {
            if (this._cardList.isCompleted()) {
                return "completed";
            } else if (this._bidList.isCompleted()) {
                return "playing";
            } else {
                return "auction";
            }
        },

        hand: function (direction) {
            var cards,
                result = this.get("deal")[direction] || ["", "", "", "", "", "", "", "", "", "", "", "", ""];

            if (result[0] !== "") {
                cards = this._cardList.cards();

                return Y.Array.filter(result, function (card) {
                    return Y.Array.indexOf(cards, card) === -1;
                });
            } else {
                return result.slice(0, cardsLeft(direction)); // TODO: cardsLeft
            }
        }

    }, {

        ATTRS: {

            deal: {

            },

            dealer: {

            },

            vulnerable: {

            },

            // writeonly
            bids: {
                value: []
            },

            // writeonly
            cards: {
                value: []
            }

        }

    });

    Y.namespace("Bridge.Model").Board = Board;

}, "", { requires: ["model", "bid-model-list", "card-model-list"] });
<commit_msg>fixed activedirection<commit_after>YUI.add("board-model", function (Y) {

    var Board = Y.Base.create("board", Y.Model, [], {

        initializer: function () {
            this._bidList = new Y.Bridge.Model.BidList();
            this._bidList.parent = this;
            this._bidList.addTarget(this);

            this._cardList = new Y.Bridge.Model.CardList();
            this._cardList.parent = this;
            this._cardList.addTarget(this);

            this.after("bidsChange", this._afterBidsChange);
            this.after("cardsChange", this._afterCardsChange);

            this._resetBids(this.get("bids"));
            this._resetCards(this.get("cards"));
        },

        createBid: function (model, object, callback) {
            this._bidList.create(model, object, callback);
        },

        createCard: function (model, object, callback) {
            this._cardList.create(model, object, callback);
        },

        generateAuction: function () {
            return {
                bids: this._bidList.bids(),
                vulnerable: this.get("vulnerable"),
                dealer: this.get("dealer"),
                visible: true
            };
        },

        generateCards: function (direction) {
            var cards = this._cardList.cards(),
                result = this.get("deal")[direction];

            result = Y.Array.filter(result, function (card) {
                return Y.Array.indexOf(cards, card) === -1;
            });

            return result;
        },

        activeDirection: function () {
            var dealerPosition = Y.Bridge.directionPosition(this.get("dealer"));

            switch (this.get("state")) {
            case "auction":
                return Y.Bridge.DIRECTIONS[(dealerPosition + this._bidList.size()) % 4];
                break;
            case "playing":
                return "N"; // TODO
                break;
            }
            return undefined;
        },

        _afterBidsChange: function (event) {
            this._resetBids(event.newVal);
        },

        _afterCardsChange: function (event) {
            this._resetCards(event.newVal);
        },

        _resetBids: function (bids) {
            this._bidList.reset(bids);
        },

        _resetCards: function (cards) {
            this._cardList.reset(cards);
        },

        state: function () {
            if (this._cardList.isCompleted()) {
                return "completed";
            } else if (this._bidList.isCompleted()) {
                return "playing";
            } else {
                return "auction";
            }
        },

        hand: function (direction) {
            var cards,
                result = this.get("deal")[direction] || ["", "", "", "", "", "", "", "", "", "", "", "", ""];

            if (result[0] !== "") {
                cards = this._cardList.cards();

                return Y.Array.filter(result, function (card) {
                    return Y.Array.indexOf(cards, card) === -1;
                });
            } else {
                return result.slice(0, cardsLeft(direction)); // TODO: cardsLeft
            }
        }

    }, {

        ATTRS: {

            deal: {

            },

            dealer: {

            },

            vulnerable: {

            },

            // writeonly
            bids: {
                value: []
            },

            // writeonly
            cards: {
                value: []
            }

        }

    });

    Y.namespace("Bridge.Model").Board = Board;

}, "", { requires: ["model", "bid-model-list", "card-model-list"] });
<|endoftext|>


<commit_before>/**
 * Topic Service
 * @author Lawrence Daniels <lawrence.daniels@gmail.com>
 */
(function () {
    angular.module('trifecta')
        .factory('TopicSvc', function ($http, $log, $q, $timeout) {
            var service = {
                topics: []
            };

            service.getFilteredTopics = function(hideEmptyTopics) {
                return service.topics.filter(function(topic) {
                    return !hideEmptyTopics || topic.totalMessages > 0;
                });
            };

            /**
             * Filters out topics without messages; returning only the topics containing messages
             * @param topics the given array of topic summaries
             * @returns Array of topics containing messages
             */
            service.filterEmptyTopics = function (topics) {
                var filteredTopics = [];
                for (var n = 0; n < topics.length; n++) {
                    var ts = topics[n];
                    if (ts.totalMessages > 0) {
                        filteredTopics.push(ts);
                    }
                }
                return filteredTopics;
            };

            /**
             * Attempts to find and return the first non-empty topic; however, if none are found, it returns the
             * first topic in the array
             * @returns the first non-empty topic
             */
            service.findNonEmptyTopic = function() {
                for(var n = 0; n < service.topics.length; n++) {
                    var topic = service.topics[n];
                    if(topic.totalMessages > 0) {
                        return topic;
                    }
                }
                return service.topics.length ? service.topics[0] : null;
            };

            service.getReplicas = function (topic) {
                return $http.get("/rest/getLeaderAndReplicas/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopics = function () {
                return $http.get("/rest/getTopicSummaries")
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopicsByName = function (topic) {
                return $http.get("/rest/getTopicsByName/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopicDetails = function (topic) {
                return $http.get("/rest/getTopicDetails/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            /**
             * {"consumerId":"dev","topic":"shocktrade.keystats.avro","partition":1,"offset":4910,"topicOffset":8388,"lastModified":1419391037744,"messagesLeft":3478}
             * @param updatedConsumers
             */
            service.updateConsumers = function (updatedConsumers) {
                angular.forEach(updatedConsumers, function(u) {
                    var t = service.findTopicByName(u.topic);
                    var consumers = t ? t.consumers : null;
                    if(consumers) {
                        var detail = service.findConsumerDetail(consumers, u);
                        if(detail) {
                            detail.delta = u.offset - detail.offset;
                            detail.messagesLeft = u.messagesLeft;
                            detail.lastModified = u.lastModified;
                            detail.offset = u.offset;
                            detail.topicOffset = u.topicOffset;
                            detail.messagesLeft = u.messagesLeft;
                            detail.lastModified = u.lastModified;

                            // deltas should exist for 60 seconds (since the last update)
                            (function() {
                                var lastDelta = detail.delta;
                                $timeout(function () {
                                    if(detail.delta == lastDelta) detail.delta = null;
                                }, 60000);
                            })();
                        }
                    }
                });
            };

            service.updateTopic = function(updatedTopic) {
                angular.forEach(service.topics, function(t) {
                    if(t.topic == updatedTopic.topic) {
                        if(!t.updatingTopics) t.updatingTopics = 0;

                        // setup the topic update indicator
                        (function() {
                            t.updatingTopics++;
                            $timeout(function () {
                                t.updatingTopics--;
                            }, 7500);
                        })();

                        // update the topic with the delta information
                        t.totalMessages = updatedTopic.totalMessages;
                        var p = service.findPartition(t, updatedTopic.partition);
                        if(p) {
                            p.delta = updatedTopic.endOffset - p.endOffset;
                            p.startOffset = updatedTopic.startOffset;
                            p.endOffset = updatedTopic.endOffset;
                            p.messages = updatedTopic.messages;

                            // deltas should exist for 60 seconds (since the last update)
                            (function() {
                                var lastDelta = p.delta;
                                $timeout(function () {
                                    if(p.delta == lastDelta) p.delta = null;
                                }, 60000);
                            })();
                        }
                    }
                });
            };

            service.updateTopics = function(updatedTopics) {
                angular.forEach(updatedTopics, function (updatedTopic) {
                    service.updateTopic(updatedTopic);
                });
            };

            service.findConsumerDetail = function(consumers, consumerDelta) {
                for(var n = 0; n < consumers.length; n++) {
                    if(consumers[n].consumerId == consumerDelta.consumerId) {
                        var details = consumers[n].details;
                        for(var m = 0; m < details.length; m++) {
                            if(details[m].partition == consumerDelta.partition) return details[m];
                        }
                    }
                }
                return null;
            };

            service.findTopicByName = function(topicName) {
                for(var n = 0; n < service.topics.length; n++) {
                    var t = service.topics[n];
                    if(t.topic == topicName) return t;
                }
                return null;
            };

            service.findPartition = function(topic, partitionId) {
                var partitions = topic.partitions;
                for(var n = 0; n < partitions.length; n++) {
                    var p = partitions[n];
                    if (p.partition == partitionId) return p;
                }
                return null;
            };

            // pre-load the topics
            service.getTopics().then(function(topics) {
                $log.info("Retrieved " + topics.length + " topic(s)...");
                var sortedTopics = topics.sort(function(a, b) {
                    var ta = a.topic.toLowerCase();
                    var tb = b.topic.toLowerCase();
                    return ta > tb ? 1 : ta < tb ? -1 : 0;
                });

                angular.forEach(sortedTopics, function(t) {
                    t.consumers = [];
                    service.topics.push(t);
                });
            });

            return service;
        })
})();<commit_msg>fixed issue with negative deltas<commit_after>/**
 * Topic Service
 * @author Lawrence Daniels <lawrence.daniels@gmail.com>
 */
(function () {
    angular.module('trifecta')
        .factory('TopicSvc', function ($http, $log, $q, $timeout) {
            var service = {
                topics: []
            };

            service.getFilteredTopics = function(hideEmptyTopics) {
                return service.topics.filter(function(topic) {
                    return !hideEmptyTopics || topic.totalMessages > 0;
                });
            };

            /**
             * Filters out topics without messages; returning only the topics containing messages
             * @param topics the given array of topic summaries
             * @returns Array of topics containing messages
             */
            service.filterEmptyTopics = function (topics) {
                var filteredTopics = [];
                for (var n = 0; n < topics.length; n++) {
                    var ts = topics[n];
                    if (ts.totalMessages > 0) {
                        filteredTopics.push(ts);
                    }
                }
                return filteredTopics;
            };

            /**
             * Attempts to find and return the first non-empty topic; however, if none are found, it returns the
             * first topic in the array
             * @returns the first non-empty topic
             */
            service.findNonEmptyTopic = function() {
                for(var n = 0; n < service.topics.length; n++) {
                    var topic = service.topics[n];
                    if(topic.totalMessages > 0) {
                        return topic;
                    }
                }
                return service.topics.length ? service.topics[0] : null;
            };

            service.getReplicas = function (topic) {
                return $http.get("/rest/getLeaderAndReplicas/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopics = function () {
                return $http.get("/rest/getTopicSummaries")
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopicsByName = function (topic) {
                return $http.get("/rest/getTopicsByName/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            service.getTopicDetails = function (topic) {
                return $http.get("/rest/getTopicDetails/" + topic)
                    .then(function (response) {
                        return response.data;
                    });
            };

            /**
             * {"consumerId":"dev","topic":"shocktrade.keystats.avro","partition":1,"offset":4910,"topicOffset":8388,"lastModified":1419391037744,"messagesLeft":3478}
             * @param updatedConsumers
             */
            service.updateConsumers = function (updatedConsumers) {
                angular.forEach(updatedConsumers, function(u) {
                    var t = service.findTopicByName(u.topic);
                    var consumers = t ? t.consumers : null;
                    if(consumers) {
                        var detail = service.findConsumerDetail(consumers, u);
                        if(detail) {
                            detail.delta = Math.max(0, u.offset - detail.offset);
                            detail.messagesLeft = u.messagesLeft;
                            detail.lastModified = u.lastModified;
                            detail.offset = u.offset;
                            detail.topicOffset = u.topicOffset;
                            detail.messagesLeft = u.messagesLeft;
                            detail.lastModified = u.lastModified;

                            // deltas should exist for 60 seconds (since the last update)
                            (function() {
                                var lastDelta = detail.delta;
                                $timeout(function () {
                                    if(detail.delta == lastDelta) detail.delta = null;
                                }, 60000);
                            })();
                        }
                    }
                });
            };

            service.updateTopic = function(updatedTopic) {
                angular.forEach(service.topics, function(t) {
                    if(t.topic == updatedTopic.topic) {
                        if(!t.updatingTopics) t.updatingTopics = 0;

                        // setup the topic update indicator
                        (function() {
                            t.updatingTopics++;
                            $timeout(function () {
                                t.updatingTopics--;
                            }, 7500);
                        })();

                        // update the topic with the delta information
                        t.totalMessages = updatedTopic.totalMessages;
                        var p = service.findPartition(t, updatedTopic.partition);
                        if(p) {
                            p.delta = updatedTopic.endOffset - p.endOffset;
                            p.startOffset = updatedTopic.startOffset;
                            p.endOffset = updatedTopic.endOffset;
                            p.messages = updatedTopic.messages;

                            // deltas should exist for 60 seconds (since the last update)
                            (function() {
                                var lastDelta = p.delta;
                                $timeout(function () {
                                    if(p.delta == lastDelta) p.delta = null;
                                }, 60000);
                            })();
                        }
                    }
                });
            };

            service.updateTopics = function(updatedTopics) {
                angular.forEach(updatedTopics, function (updatedTopic) {
                    service.updateTopic(updatedTopic);
                });
            };

            service.findConsumerDetail = function(consumers, consumerDelta) {
                for(var n = 0; n < consumers.length; n++) {
                    if(consumers[n].consumerId == consumerDelta.consumerId) {
                        var details = consumers[n].details;
                        for(var m = 0; m < details.length; m++) {
                            if(details[m].partition == consumerDelta.partition) return details[m];
                        }
                    }
                }
                return null;
            };

            service.findTopicByName = function(topicName) {
                for(var n = 0; n < service.topics.length; n++) {
                    var t = service.topics[n];
                    if(t.topic == topicName) return t;
                }
                return null;
            };

            service.findPartition = function(topic, partitionId) {
                var partitions = topic.partitions;
                for(var n = 0; n < partitions.length; n++) {
                    var p = partitions[n];
                    if (p.partition == partitionId) return p;
                }
                return null;
            };

            // pre-load the topics
            service.getTopics().then(function(topics) {
                $log.info("Retrieved " + topics.length + " topic(s)...");
                var sortedTopics = topics.sort(function(a, b) {
                    var ta = a.topic.toLowerCase();
                    var tb = b.topic.toLowerCase();
                    return ta > tb ? 1 : ta < tb ? -1 : 0;
                });

                angular.forEach(sortedTopics, function(t) {
                    t.consumers = [];
                    service.topics.push(t);
                });
            });

            return service;
        })
})();<|endoftext|>


<commit_before>module.exports = function () {
    var creeps = Game.creeps;
    var spawn = Game.spawns.Spawn1;
    var minions = {
        total: 0,
        harvest: 0,
        build: 0,
        guard: 0,
        medic: 0
    };

    if (spawn.spawning) return; // no action when already spawning

    for (var i in creeps) {
        var creep = creeps[i];

        minions.total++;
        minions[creep.memory.module]++;
    }

    var spawnCreep = function (modules, type) {
        if (spawn.createCreep(modules, undefined, {module: type})) {
            console.log('created ' + type);
        }
    };

    if (minions.harvest < 2) {
        spawnCreep([WORK, CARRY, CARRY, MOVE], 'harvest');
    }

    else if (minions.medic < minions.guard / 2) {
        spawnCreep([TOUGH, MOVE, HEAL], 'medic');
    }

    else if (minions.harvest > 0) {
        spawnCreep([TOUGH, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK], 'guard');
    }
};
<commit_msg>Debugging spawning errors.<commit_after>module.exports = function () {
    var creeps = Game.creeps;
    var spawn = Game.spawns.Spawn1;
    var minions = {
        total: 0,
        harvest: 0,
        build: 0,
        guard: 0,
        medic: 0
    };

    if (spawn.spawning) return; // no action when already spawning

    for (var i in creeps) {
        var creep = creeps[i];

        minions.total++;
        minions[creep.memory.module]++;
    }

    var spawnCreep = function (modules, type) {
        console.log(spawn.createCreep(modules, undefined, {module: type}));

        if (spawn.createCreep(modules, undefined, {module: type})) {
            console.log('created ' + type);
        }
    };

    if (minions.harvest < 2) {
        spawnCreep([WORK, CARRY, CARRY, MOVE], 'harvest');
    }

    else if (minions.medic < minions.guard / 2) {
        spawnCreep([TOUGH, MOVE, HEAL], 'medic');
    }

    else if (minions.harvest > 0) {
        spawnCreep([TOUGH, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK, RANGED_ATTACK], 'guard');
    }
};
<|endoftext|>


<commit_before>/*
 * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
 *
 * 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.
 */

// JavaScript to dynamically change form action on the UserData upload page according to the option selected (or radio buttons checked) and do validation on the form

Ensembl.Panel.UserData = Ensembl.Panel.extend({
  formats: {
    'bam'  : 'BAM',
    'bb'   : 'BIGBED',
    'bed'  : 'BED',
    'bgr'  : 'BEDGRAPH',
    'bw'   : 'BIGWIG',
    'gff'  : 'GFF',
    'gff3' : 'GFF',
    'gtf'  : 'GTF',
    'psl'  : 'PSL',
    'vcf'  : 'VCF',
    'vep'  : 'VEP',
    'wig'  : 'WIG'
  },

  init: function () {
    var panel = this;

    this.base.apply(this, arguments);

    this.elLk.activeLink    = this.el.parents('.modal_wrapper').siblings('.modal_nav').find('ul.local_context li.active');
    this.elLk.form          = this.el.find('form').off('.UserData').on('submit.UserData', function (e) { e.preventDefault(); panel.formSubmit(); });

    this.elLk.formatDropdown = this.elLk.form.find('select[name=format]').on('change', function () {
      panel.showError();
    });

    var dataInputEvent = function(e) { // add some delay to make sure the blur event actually gets fired after making sure some other event hasn't removed the input string
      panel.showError();
      var element = $(this).off('blur change paste'); // prevent all events to fire at once
      setTimeout(function() {
        element.trigger('finish').trigger('blur').on('blur change paste', dataInputEvent);
        element = null;
      }, 100);
    };

    this.elLk.dataFieldText = this.elLk.form.find('._userdata_add textarea').on({
      'focus': function(e) {
        if (!this.value || this.value === this.defaultValue) {
          $(this).val('').removeClass('inactive');
        }
      },
      'finish': function() {
        this.value = this.value.trim();
        if (this.value && this.value !== this.defaultValue) {
          panel.updateFormatDropdown(this.value, false);
        } else {
          $(this).val(this.defaultValue).addClass('inactive');
          panel.resetFormatDropdown();
        }
      },
      'blur change paste': dataInputEvent
    });

    this.elLk.dataFieldFile = this.elLk.form.find('._userdata_add input[type=file]').on('change', function () {
      panel.updateFormatDropdown(this.value, true);
    });
  },

  updateFormatDropdown: function (data, isUpload) {

    var format;

    this.elLk[isUpload ? 'dataFieldText' : 'dataFieldFile'].val('').trigger('finish');

    if (!isUpload) {
      if (data.match(/^(ht|f)tp(s?)\:\/\//)) {
        if (data.match(/\/hub\.txt$/)) { // this is most likely a track hub (although there are more cases when it could be a trackhub)
          format = 'TRACKHUB'
        }
      } else {
        isUpload  = true;      // data being uploaded from the text area
        format    = 'unknown'; // could be anything, unless we have some code that guess the format
      }
    }

    if (!format) {
      format = this.formats[data.replace(/\.gz$/, '').split('.').pop().toLowerCase()];
    }

    this.elLk.formatDropdown.find('option').prop('disabled', function () {
      return !(isUpload ? !this.className.match('_format_remote') : !this.className.match('_format_upload'));
    }).filter(':first, [value=' + format + ']:enabled').last().prop('selected', true);
    // Finally, reveal the dropdown
    this.elLk.formatDropdown.removeClass('hide');
  },

  resetFormatDropdown: function () {
    if (!this.elLk.dataFieldFile.val() && (!this.elLk.dataFieldText.val() || this.elLk.dataFieldText.val() === this.elLk.dataFieldText.prop('defaultValue'))) {
      this.elLk.formatDropdown.find('option').prop('disabled', false).first().prop('selected', true);
    }
  },

  showError: function (message, el) {
    if (message) {
      if (!this.elLk.errorMessage) {
        this.elLk.errorMessage  = $('<label>').addClass('invalid');
      }
      this.elLk.errorMessage.html(' ' + message).insertAfter(el);
    } else {
      $(this.elLk.errorMessage).empty().remove();
    }
  },

  formSubmit: function () {
    if (!this.elLk.dataFieldFile.val() && (!this.elLk.dataFieldText.val() || this.elLk.dataFieldText.val() === this.elLk.dataFieldText.prop('defaultValue'))) {
      this.showError('Please provide some data', this.elLk.dataFieldText);
      return false;
    }

    if (!this.elLk.formatDropdown.val()) {
      this.showError('Please select a format', this.elLk.formatDropdown);
      return false;
    }

    this.showError();
    this.elLk.activeLink.removeClass('active');

    return Ensembl.EventManager.trigger('modalFormSubmit', this.elLk.form);
  }
});
<commit_msg>Map gff3 extension to correct format<commit_after>/*
 * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
 *
 * 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.
 */

// JavaScript to dynamically change form action on the UserData upload page according to the option selected (or radio buttons checked) and do validation on the form

Ensembl.Panel.UserData = Ensembl.Panel.extend({
  formats: {
    'bam'  : 'BAM',
    'bb'   : 'BIGBED',
    'bed'  : 'BED',
    'bgr'  : 'BEDGRAPH',
    'bw'   : 'BIGWIG',
    'gff'  : 'GTF',
    'gff3' : 'GFF3',
    'gtf'  : 'GTF',
    'psl'  : 'PSL',
    'vcf'  : 'VCF',
    'vep'  : 'VEP',
    'wig'  : 'WIG'
  },

  init: function () {
    var panel = this;

    this.base.apply(this, arguments);

    this.elLk.activeLink    = this.el.parents('.modal_wrapper').siblings('.modal_nav').find('ul.local_context li.active');
    this.elLk.form          = this.el.find('form').off('.UserData').on('submit.UserData', function (e) { e.preventDefault(); panel.formSubmit(); });

    this.elLk.formatDropdown = this.elLk.form.find('select[name=format]').on('change', function () {
      panel.showError();
    });

    var dataInputEvent = function(e) { // add some delay to make sure the blur event actually gets fired after making sure some other event hasn't removed the input string
      panel.showError();
      var element = $(this).off('blur change paste'); // prevent all events to fire at once
      setTimeout(function() {
        element.trigger('finish').trigger('blur').on('blur change paste', dataInputEvent);
        element = null;
      }, 100);
    };

    this.elLk.dataFieldText = this.elLk.form.find('._userdata_add textarea').on({
      'focus': function(e) {
        if (!this.value || this.value === this.defaultValue) {
          $(this).val('').removeClass('inactive');
        }
      },
      'finish': function() {
        this.value = this.value.trim();
        if (this.value && this.value !== this.defaultValue) {
          panel.updateFormatDropdown(this.value, false);
        } else {
          $(this).val(this.defaultValue).addClass('inactive');
          panel.resetFormatDropdown();
        }
      },
      'blur change paste': dataInputEvent
    });

    this.elLk.dataFieldFile = this.elLk.form.find('._userdata_add input[type=file]').on('change', function () {
      panel.updateFormatDropdown(this.value, true);
    });
  },

  updateFormatDropdown: function (data, isUpload) {

    var format;

    this.elLk[isUpload ? 'dataFieldText' : 'dataFieldFile'].val('').trigger('finish');

    if (!isUpload) {
      if (data.match(/^(ht|f)tp(s?)\:\/\//)) {
        if (data.match(/\/hub\.txt$/)) { // this is most likely a track hub (although there are more cases when it could be a trackhub)
          format = 'TRACKHUB'
        }
      } else {
        isUpload  = true;      // data being uploaded from the text area
        format    = 'unknown'; // could be anything, unless we have some code that guess the format
      }
    }

    if (!format) {
      format = this.formats[data.replace(/\.gz$/, '').split('.').pop().toLowerCase()];
    }

    this.elLk.formatDropdown.find('option').prop('disabled', function () {
      return !(isUpload ? !this.className.match('_format_remote') : !this.className.match('_format_upload'));
    }).filter(':first, [value=' + format + ']:enabled').last().prop('selected', true);
    // Finally, reveal the dropdown
    this.elLk.formatDropdown.removeClass('hide');
  },

  resetFormatDropdown: function () {
    if (!this.elLk.dataFieldFile.val() && (!this.elLk.dataFieldText.val() || this.elLk.dataFieldText.val() === this.elLk.dataFieldText.prop('defaultValue'))) {
      this.elLk.formatDropdown.find('option').prop('disabled', false).first().prop('selected', true);
    }
  },

  showError: function (message, el) {
    if (message) {
      if (!this.elLk.errorMessage) {
        this.elLk.errorMessage  = $('<label>').addClass('invalid');
      }
      this.elLk.errorMessage.html(' ' + message).insertAfter(el);
    } else {
      $(this.elLk.errorMessage).empty().remove();
    }
  },

  formSubmit: function () {
    if (!this.elLk.dataFieldFile.val() && (!this.elLk.dataFieldText.val() || this.elLk.dataFieldText.val() === this.elLk.dataFieldText.prop('defaultValue'))) {
      this.showError('Please provide some data', this.elLk.dataFieldText);
      return false;
    }

    if (!this.elLk.formatDropdown.val()) {
      this.showError('Please select a format', this.elLk.formatDropdown);
      return false;
    }

    this.showError();
    this.elLk.activeLink.removeClass('active');

    return Ensembl.EventManager.trigger('modalFormSubmit', this.elLk.form);
  }
});
<|endoftext|>


<commit_before>/* @flow */

import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

import ProjectList from '../components/ProjectList';

const MyAppsQuery = gql`
  query Home_MyApps($limit: Int!, $offset: Int!) {
    me {
      id
      appCount
      email
      firstName
      id
      isLegacy
      lastName
      profilePhoto
      username
      apps(limit: $limit, offset: $offset) {
        id
        description
        fullName
        iconUrl
        lastPublishedTime
        likeCount
        name
        packageName
        privacy
      }
      likes(limit: 15, offset: 0) {
        id
      }
    }
  }
`;

export default graphql(MyAppsQuery, {
  props: props => {
    let { data } = props;

    return {
      ...props,
      data: {
        ...data,
        appCount: data.me.appCount,
        apps: data.me.apps,
      },
      loadMoreAsync() {
        return data.fetchMore({
          variables: {
            offset: (data.me && data.me.apps && data.me.apps.length) || 0,
          },
          updateQuery: (previousData, { fetchMoreResult }) => {
            if (!fetchMoreResult || !fetchMoreResult.me) {
              return previousData;
            }

            let combinedData = {
              me: {
                ...previousData.me,
                ...fetchMoreResult.me,
                apps: [...previousData.me.apps, ...fetchMoreResult.me.apps],
              },
            };

            return {
              ...combinedData,
              appCount: combinedData.me.appCount,
              apps: combinedData.me.apps,
            };
          },
        });
      },
    };
  },
  options: {
    variables: {
      limit: 15,
      offset: 0,
    },
    fetchPolicy: 'cache-and-network',
  },
})(ProjectList);
<commit_msg>Clean up pulling some values of own profile data<commit_after>/* @flow */

import gql from 'graphql-tag';
import { graphql } from 'react-apollo';

import ProjectList from '../components/ProjectList';

const MyAppsQuery = gql`
  query Home_MyApps($limit: Int!, $offset: Int!) {
    me {
      id
      appCount
      email
      firstName
      id
      isLegacy
      lastName
      profilePhoto
      username
      apps(limit: $limit, offset: $offset) {
        id
        description
        fullName
        iconUrl
        lastPublishedTime
        likeCount
        name
        packageName
        privacy
      }
      likes(limit: 15, offset: 0) {
        id
      }
    }
  }
`;

export default graphql(MyAppsQuery, {
  props: props => {
    let { data } = props;

    let apps;
    let appCount;
    if (data.me) {
      apps = data.me.apps;
      appCount = data.me.appCount;
    }

    return {
      ...props,
      data: {
        ...data,
        apps,
        appCount,
      },
      loadMoreAsync() {
        return data.fetchMore({
          variables: {
            offset: (apps && apps.length) || 0,
          },
          updateQuery: (previousData, { fetchMoreResult }) => {
            if (!fetchMoreResult || !fetchMoreResult.me) {
              return previousData;
            }

            let combinedData = {
              me: {
                ...previousData.me,
                ...fetchMoreResult.me,
                apps: [...previousData.me.apps, ...fetchMoreResult.me.apps],
              },
            };

            return {
              ...combinedData,
              appCount: combinedData.me.appCount,
              apps: combinedData.me.apps,
            };
          },
        });
      },
    };
  },
  options: {
    variables: {
      limit: 15,
      offset: 0,
    },
    fetchPolicy: 'cache-and-network',
  },
})(ProjectList);
<|endoftext|>


<commit_before>'use strict';

songsAdminApp.directive("lyrics", function() {
    return {
        replace : true,
        restrict: 'E',
        scope: {
            lyricsComponent:'=',
            originalText:'=',
        },
        templateUrl:'/admin/js/templates/lyrics.html',
        controller:function($scope){
            $scope.lyricsTextList = [];

            $scope.addLyricsText = function(){
                if($scope.lyricsText != ""){
                    $scope.lyricsTextList.push($scope.lyricsText);
                }
                $scope.lyricsText = "";
            }

            Array.prototype.insert = function (index, item) {
                this.splice(index, 0, item);
            };

            $scope.moveItemUp = function(){
                var selectedSongIndex = $scope.lyricsTextList.indexOf($scope.selectedLyricsText);
                var songToBeMovedUp = $scope.lyricsTextList.splice(selectedSongIndex, 1);

                $scope.lyricsTextList.insert(selectedSongIndex - 1, songToBeMovedUp[0].englishTransliteration);
            }

        }
    }
});<commit_msg>Array move implemented<commit_after>'use strict';

songsAdminApp.directive("lyrics", function() {
    return {
        replace : true,
        restrict: 'E',
        scope: {
            lyricsComponent:'=',
            originalText:'=',
        },
        templateUrl:'/admin/js/templates/lyrics.html',
        controller:function($scope){
            $scope.lyricsTextList = [];

            $scope.addLyricsText = function(){
                if($scope.lyricsText != ""){
                    $scope.lyricsTextList.push($scope.lyricsText);
                }
                $scope.lyricsText = "";
            }

            Array.prototype.insert = function (index, item) {
                this.splice(index, 0, item);
            };

            Array.prototype.move = function (old_index, new_index) {
                if (new_index >= this.length) {
                    var k = new_index - this.length;
                    while ((k--) + 1) {
                        this.push(undefined);
                    }
                }
                this.splice(new_index, 0, this.splice(old_index, 1)[0]);
                return this; // for testing purposes
            };

            $scope.moveItemUp = function(){
                var selectedSongIndex = $scope.lyricsTextList.indexOf($scope.selectedLyricsText);
                if(selectedSongIndex<0)
                    return;

                $scope.lyricsTextList.move(selectedSongIndex, selectedSongIndex-1);
            }

        }
    }
});<|endoftext|>


<commit_before>import React from 'react';
import {Button, ButtonGroup, ButtonToolbar, FormControls, Input, Modal,
        Accordion, Panel, ListGroup, ListGroupItem, Glyphicon, Tabs, Tab,
        FormGroup, Row, Col} from 'react-bootstrap';
import SVG from 'react-inlinesvg';

import ElementActions from './actions/ElementActions';
import ElementStore from './stores/ElementStore';

import UIStore from './stores/UIStore';
import UIActions from './actions/UIActions';

import NumeralInputWithUnitsCompo from './NumeralInputWithUnitsCompo';
import ElementCollectionLabels from './ElementCollectionLabels';
import ElementAnalysesLabels from './ElementAnalysesLabels';
import SampleDetailsAnalyses from './SampleDetailsAnalyses';
import extra from "./extra/SampleDetailsExtra"
import Select from 'react-select';
import StickyDiv from 'react-stickydiv'

import StructureEditorModal from './structure_editor/StructureEditorModal';

import Aviator from 'aviator';

import {solventOptions} from './staticDropdownOptions/options';
import Sample from './models/Sample';
import PolymerSection from './PolymerSection';
import ElementalCompositionGroup from './ElementalCompositionGroup';
import ToggleSection from './common/ToggleSection'
import SampleName from './common/SampleName'
import SampleForm from './SampleForm'

const MWPrecision = 6;

export default class SampleDetails extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      sample: props.sample,
      reaction: null,
      materialGroup: null,
      showStructureEditor: false,
      loadingMolecule: false,
      showElementalComposition: false
    }
  }

  componentDidMount() {
    ElementStore.listen(this.onChange.bind(this));
  }

  componentWillUnmount() {
    ElementStore.unlisten(this.onChange.bind(this));
  }

  onChange(state) {
    if(!state.currentElement || state.currentElement.type == 'sample') {
      this.setState({
        sample: state.currentElement,
        reaction: state.currentReaction,
        materialGroup: state.currentMaterialGroup,
        samplePanelFixed: false,
        loadingMolecule: false
      });
    }
  }

  handleSampleChanged(sample) {
    this.setState({
      sample
    });
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.setAmountAndNormalizeToGram(amount);
    this.setState({
      sample: sample
    });
  }

  handleImportedReadoutChanged(e) {
    let sample = this.state.sample;
    sample.imported_readout = this.refs.importedReadoutInput.getValue();
    this.setState({
      sample: sample
    });
  }

  showStructureEditor() {
    this.setState({
      showStructureEditor: true
    })
  }

  hideStructureEditor() {
    this.setState({
      showStructureEditor: false
    })
  }

  updateMolecule(molfile, svg_file = null) {
    ElementActions.fetchMoleculeByMolfile(molfile, svg_file);
  }

  handleStructureEditorSave(molfile, svg_file = null) {
    let {sample} = this.state;

    if(sample) {
      sample.molfile = molfile

      sample.contains_residues = molfile.indexOf(' R# ') > -1;

      if(sample.contains_residues)
        sample.sample_svg_file = svg_file;

      sample.formulaChanged = true;
    }
    this.setState({sample: sample, loadingMolecule: true});

    if(sample.contains_residues > -1){
      this.updateMolecule(molfile, svg_file);
    } else {
      this.updateMolecule(molfile);
    }

    this.hideStructureEditor()
  }

  handleStructureEditorCancel() {
    this.hideStructureEditor()
  }

  closeDetails() {
    let { currentReaction } = ElementStore.getState();
    let {sample} = this.state;

    if(currentReaction) {
      if(sample.isNew)
        ElementActions.openReactionDetails(currentReaction);
      else
        ElementActions.fetchReactionById(currentReaction)
    } else {
      UIActions.deselectAllElements();

      let uiState = UIStore.getState();
      Aviator.navigate(`/collection/${uiState.currentCollectionId}`);
    }
  }

  structureEditorButton(isDisabled) {
    return (
      <Button onClick={this.showStructureEditor.bind(this)} disabled={isDisabled}>
        <Glyphicon glyph='pencil'/>
      </Button>
    )
  }

  svgOrLoading(sample) {
    let svgPath = "";
    if (this.state.loadingMolecule) {
      svgPath = "/images/loading-bubbles.svg";
    } else {
      svgPath = sample.svgPath;
    }
    let className = svgPath ? 'svg-container' : 'svg-container-empty'
    return (

        <Glyphicon className="pull-right" glyph='pencil'/>
        <SVG key={svgPath} src={svgPath} className="molecule-mid"/>

    );
  }

  sampleAverageMW(sample) {
    let mw = sample.molecule_molecular_weight;
    if(mw)
      return `${mw.toFixed(MWPrecision)} g/mol`;
    else
      return '';
  }

  sampleExactMW(sample) {
    let mw = sample.molecule_exact_molecular_weight
    if(mw)
      return `Exact mass: ${mw.toFixed(MWPrecision)} g/mol`;
    else
      return '';
  }

  sampleHeader(sample) {
    const style = {height: '200px'};
    return (
      <Row style={style}>
        <Col md={4}>

{sample.title()}

<SampleName sample={sample}/>

{this.sampleAverageMW(sample)}

{this.sampleExactMW(sample)}

          <ElementCollectionLabels element={sample} key={sample.id}/>
          <ElementAnalysesLabels element={sample} key={sample.id+"_analyses"}/>
          {this.extraLabels().map((Lab,i)=><Lab key={i} element={sample}/>)}
        </Col>
        <Col md={8}>
          {this.svgOrLoading(sample)}
        </Col>
      </Row>
    )
  }

  moleculeInchi(sample) {
    return (
      <Input type="text" label="InChI"
             key={sample.id}
             defaultValue={sample.molecule_inchistring}
             disabled
             readOnly
      />
    )
  }

  handleSectionToggle() {
    this.setState({showElementalComposition: !this.state.showElementalComposition});
  }

  elementalPropertiesItemHeader(sample) {
    let label
    if(sample.contains_residues) {
      label = 'Polymer section'
      if(!this.state.showElementalComposition)
        label += ' / Elemental composition';
    }
    else
      label = 'Elemental composition';

    return (
      <ListGroupItem onClick={() => this.handleSectionToggle()}>
        <Col className="padding-right elem-composition-header" md={6}>
          {label}
        </Col>

          <ToggleSection show={this.state.showElementalComposition}/>

      </ListGroupItem>
    )
  }

  elementalPropertiesItemContent(sample, materialGroup, show) {
    if(!show)
      return false;

    if(sample.contains_residues)
      return (
        <ListGroupItem className="ea-section">
          <PolymerSection sample={sample}
                          parent={this}
                          show={sample.contains_residues}
                          materialGroup={materialGroup}/>
        </ListGroupItem>
      )
    else
      return (
        <ListGroupItem className="ea-section">
          <Row>
            <Col md={6}>
              <ElementalCompositionGroup sample={sample}/>
            </Col>
          </Row>
        </ListGroupItem>
      )
  }

  elementalPropertiesItem(sample) {
    if(!sample.molecule.sum_formular) { // avoid empty ListGroupItem
      return false;
    } else {
      let show = this.state.showElementalComposition;
      let materialGroup = this.state.materialGroup;

      return(

          {this.elementalPropertiesItemHeader(sample)}

          {this.elementalPropertiesItemContent(sample, materialGroup, show)}

      )
    }
  }

  samplePropertiesTab(ind){
    let sample = this.state.sample || {};

    return(
      <Tab eventKey={ind} title={'Properties'} key={'Props' + sample.id.toString()}>
        <ListGroupItem>
          <SampleForm sample={sample}
                      parent={this}/>
        </ListGroupItem>
        {this.elementalPropertiesItem(sample)}
        <ListGroupItem>
          {this.moleculeInchi(sample)}
        </ListGroupItem>
      </Tab>
    )
  }

  sampleAnalysesTab(ind){
    let sample = this.state.sample || {}
    return(
      <Tab eventKey={ind} title={'Analyses'} key={'Analyses' + sample.id.toString()}>
        <ListGroupItem style={{paddingBottom: 20}}>
          <SampleDetailsAnalyses
            sample={sample}
            parent={this}
            />
        </ListGroupItem>
      </Tab>
    )
  }

  sampleImportReadoutTab(ind){
    let sample = this.state.sample || {}
    return(
      <Tab eventKey={ind} title={'Results'} key={'Results' + sample.id.toString()}>
        <ListGroupItem style={{paddingBottom: 20}}>
        <Input type="text" label="Imported Readout"
           ref="importedReadoutInput"
           value={sample.imported_readout || ''}
           disabled
           readOnly
        />
        </ListGroupItem>
      </Tab>
    )
  }

  extraTab(ind){
    let sample = this.state.sample || {}
    let num = ind - 3 ;
    let NoName =  extra["Tab"+num];
    let TabName = extra["TabName"+num];
    return(
       <Tab eventKey={ind} key={ind} title={TabName} >
         <ListGroupItem style={{paddingBottom: 20}}>
           <NoName  sample={sample}/>
         </ListGroupItem>
       </Tab>
      )
  }
  extraLabels(){
    let labels = [];
    for (let j=0;j < extra.LabelsCount;j++){
      labels.push(extra["Labels"+j])
    }
    return labels;
  }

  render() {
    let sample = this.state.sample || {}
    let molfile = sample.molfile;
    let tabContents = [
                       (i)=>(this.samplePropertiesTab(i)),
                       (i)=>(this.sampleAnalysesTab(i)),
                       (i)=>(this.sampleImportReadoutTab(i))
                      ];
    for (let j=0;j < extra.TabCount;j++){
      tabContents.push((i)=>this.extraTab(i))
    }

    return (

        <StructureEditorModal
          key={sample.id}
          showModal={this.state.showStructureEditor}
          onSave={this.handleStructureEditorSave.bind(this)}
          onCancel={this.handleStructureEditorCancel.bind(this)}
          molfile={molfile}
          />
        <StickyDiv zIndex={2}>
          <Panel className="panel-fixed"
                 header="Sample Details"
                 bsStyle={sample.isEdited ? 'info' : 'primary'}>
            <Button bsStyle="danger" bsSize="xsmall" className="button-right" onClick={this.closeDetails.bind(this)}>

            </Button>
            {this.sampleHeader(sample)}
            <ListGroup>
            <Tabs defaultActiveKey={0}>
              {tabContents.map((e,i)=>e(i))}
            </Tabs>
            </ListGroup>
          </Panel>
        </StickyDiv>

    )
  }
}
<commit_msg>Restore missing form submit method resolve #235<commit_after>import React from 'react';
import {Button, ButtonGroup, ButtonToolbar, FormControls, Input, Modal,
        Accordion, Panel, ListGroup, ListGroupItem, Glyphicon, Tabs, Tab,
        FormGroup, Row, Col} from 'react-bootstrap';
import SVG from 'react-inlinesvg';

import ElementActions from './actions/ElementActions';
import ElementStore from './stores/ElementStore';

import UIStore from './stores/UIStore';
import UIActions from './actions/UIActions';

import NumeralInputWithUnitsCompo from './NumeralInputWithUnitsCompo';
import ElementCollectionLabels from './ElementCollectionLabels';
import ElementAnalysesLabels from './ElementAnalysesLabels';
import SampleDetailsAnalyses from './SampleDetailsAnalyses';
import extra from "./extra/SampleDetailsExtra"
import Select from 'react-select';
import StickyDiv from 'react-stickydiv'

import StructureEditorModal from './structure_editor/StructureEditorModal';

import Aviator from 'aviator';

import {solventOptions} from './staticDropdownOptions/options';
import Sample from './models/Sample';
import PolymerSection from './PolymerSection';
import ElementalCompositionGroup from './ElementalCompositionGroup';
import ToggleSection from './common/ToggleSection'
import SampleName from './common/SampleName'
import SampleForm from './SampleForm'

const MWPrecision = 6;

export default class SampleDetails extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      sample: props.sample,
      reaction: null,
      materialGroup: null,
      showStructureEditor: false,
      loadingMolecule: false,
      showElementalComposition: false
    }
  }

  componentDidMount() {
    ElementStore.listen(this.onChange.bind(this));
  }

  componentWillUnmount() {
    ElementStore.unlisten(this.onChange.bind(this));
  }

  onChange(state) {
    if(!state.currentElement || state.currentElement.type == 'sample') {
      this.setState({
        sample: state.currentElement,
        reaction: state.currentReaction,
        materialGroup: state.currentMaterialGroup,
        samplePanelFixed: false,
        loadingMolecule: false
      });
    }
  }

  handleSampleChanged(sample) {
    this.setState({
      sample
    });
  }

  handleAmountChanged(amount) {
    let sample = this.state.sample;
    sample.setAmountAndNormalizeToGram(amount);
    this.setState({
      sample: sample
    });
  }

  handleImportedReadoutChanged(e) {
    let sample = this.state.sample;
    sample.imported_readout = this.refs.importedReadoutInput.getValue();
    this.setState({
      sample: sample
    });
  }

  showStructureEditor() {
    this.setState({
      showStructureEditor: true
    })
  }

  hideStructureEditor() {
    this.setState({
      showStructureEditor: false
    })
  }

  updateMolecule(molfile, svg_file = null) {
    ElementActions.fetchMoleculeByMolfile(molfile, svg_file);
  }

  handleStructureEditorSave(molfile, svg_file = null) {
    let {sample} = this.state;

    if(sample) {
      sample.molfile = molfile

      sample.contains_residues = molfile.indexOf(' R# ') > -1;

      if(sample.contains_residues)
        sample.sample_svg_file = svg_file;

      sample.formulaChanged = true;
    }
    this.setState({sample: sample, loadingMolecule: true});

    if(sample.contains_residues > -1){
      this.updateMolecule(molfile, svg_file);
    } else {
      this.updateMolecule(molfile);
    }

    this.hideStructureEditor()
  }

  handleStructureEditorCancel() {
    this.hideStructureEditor()
  }

  _submitFunction() {
  let {sample, reaction, materialGroup} = this.state;

  if(reaction) {
    ElementActions.createSampleForReaction(sample);
    } else {
      if(sample.isNew) {
        ElementActions.createSample(sample);
      } else {
        ElementActions.updateSample(new Sample(sample));
      }
    }
  }

  closeDetails() {
    let { currentReaction } = ElementStore.getState();
    let {sample} = this.state;

    if(currentReaction) {
      if(sample.isNew)
        ElementActions.openReactionDetails(currentReaction);
      else
        ElementActions.fetchReactionById(currentReaction)
    } else {
      UIActions.deselectAllElements();

      let uiState = UIStore.getState();
      Aviator.navigate(`/collection/${uiState.currentCollectionId}`);
    }
  }

  structureEditorButton(isDisabled) {
    return (
      <Button onClick={this.showStructureEditor.bind(this)} disabled={isDisabled}>
        <Glyphicon glyph='pencil'/>
      </Button>
    )
  }

  svgOrLoading(sample) {
    let svgPath = "";
    if (this.state.loadingMolecule) {
      svgPath = "/images/loading-bubbles.svg";
    } else {
      svgPath = sample.svgPath;
    }
    let className = svgPath ? 'svg-container' : 'svg-container-empty'
    return (

        <Glyphicon className="pull-right" glyph='pencil'/>
        <SVG key={svgPath} src={svgPath} className="molecule-mid"/>

    );
  }

  sampleAverageMW(sample) {
    let mw = sample.molecule_molecular_weight;
    if(mw)
      return `${mw.toFixed(MWPrecision)} g/mol`;
    else
      return '';
  }

  sampleExactMW(sample) {
    let mw = sample.molecule_exact_molecular_weight
    if(mw)
      return `Exact mass: ${mw.toFixed(MWPrecision)} g/mol`;
    else
      return '';
  }

  sampleHeader(sample) {
    const style = {height: '200px'};
    return (
      <Row style={style}>
        <Col md={4}>

{sample.title()}

<SampleName sample={sample}/>

{this.sampleAverageMW(sample)}

{this.sampleExactMW(sample)}

          <ElementCollectionLabels element={sample} key={sample.id}/>
          <ElementAnalysesLabels element={sample} key={sample.id+"_analyses"}/>
          {this.extraLabels().map((Lab,i)=><Lab key={i} element={sample}/>)}
        </Col>
        <Col md={8}>
          {this.svgOrLoading(sample)}
        </Col>
      </Row>
    )
  }

  moleculeInchi(sample) {
    return (
      <Input type="text" label="InChI"
             key={sample.id}
             defaultValue={sample.molecule_inchistring}
             disabled
             readOnly
      />
    )
  }

  handleSectionToggle() {
    this.setState({showElementalComposition: !this.state.showElementalComposition});
  }

  elementalPropertiesItemHeader(sample) {
    let label
    if(sample.contains_residues) {
      label = 'Polymer section'
      if(!this.state.showElementalComposition)
        label += ' / Elemental composition';
    }
    else
      label = 'Elemental composition';

    return (
      <ListGroupItem onClick={() => this.handleSectionToggle()}>
        <Col className="padding-right elem-composition-header" md={6}>
          {label}
        </Col>

          <ToggleSection show={this.state.showElementalComposition}/>

      </ListGroupItem>
    )
  }

  elementalPropertiesItemContent(sample, materialGroup, show) {
    if(!show)
      return false;

    if(sample.contains_residues)
      return (
        <ListGroupItem className="ea-section">
          <PolymerSection sample={sample}
                          parent={this}
                          show={sample.contains_residues}
                          materialGroup={materialGroup}/>
        </ListGroupItem>
      )
    else
      return (
        <ListGroupItem className="ea-section">
          <Row>
            <Col md={6}>
              <ElementalCompositionGroup sample={sample}/>
            </Col>
          </Row>
        </ListGroupItem>
      )
  }

  elementalPropertiesItem(sample) {
    if(!sample.molecule.sum_formular) { // avoid empty ListGroupItem
      return false;
    } else {
      let show = this.state.showElementalComposition;
      let materialGroup = this.state.materialGroup;

      return(

          {this.elementalPropertiesItemHeader(sample)}

          {this.elementalPropertiesItemContent(sample, materialGroup, show)}

      )
    }
  }

  samplePropertiesTab(ind){
    let sample = this.state.sample || {};

    return(
      <Tab eventKey={ind} title={'Properties'} key={'Props' + sample.id.toString()}>
        <ListGroupItem>
          <SampleForm sample={sample}
                      parent={this}/>
        </ListGroupItem>
        {this.elementalPropertiesItem(sample)}
        <ListGroupItem>
          {this.moleculeInchi(sample)}
        </ListGroupItem>
      </Tab>
    )
  }

  sampleAnalysesTab(ind){
    let sample = this.state.sample || {}
    return(
      <Tab eventKey={ind} title={'Analyses'} key={'Analyses' + sample.id.toString()}>
        <ListGroupItem style={{paddingBottom: 20}}>
          <SampleDetailsAnalyses
            sample={sample}
            parent={this}
            />
        </ListGroupItem>
      </Tab>
    )
  }

  sampleImportReadoutTab(ind){
    let sample = this.state.sample || {}
    return(
      <Tab eventKey={ind} title={'Results'} key={'Results' + sample.id.toString()}>
        <ListGroupItem style={{paddingBottom: 20}}>
        <Input type="text" label="Imported Readout"
           ref="importedReadoutInput"
           value={sample.imported_readout || ''}
           disabled
           readOnly
        />
        </ListGroupItem>
      </Tab>
    )
  }

  extraTab(ind){
    let sample = this.state.sample || {}
    let num = ind - 3 ;
    let NoName =  extra["Tab"+num];
    let TabName = extra["TabName"+num];
    return(
       <Tab eventKey={ind} key={ind} title={TabName} >
         <ListGroupItem style={{paddingBottom: 20}}>
           <NoName  sample={sample}/>
         </ListGroupItem>
       </Tab>
      )
  }
  extraLabels(){
    let labels = [];
    for (let j=0;j < extra.LabelsCount;j++){
      labels.push(extra["Labels"+j])
    }
    return labels;
  }

  render() {
    let sample = this.state.sample || {}
    let molfile = sample.molfile;
    let tabContents = [
                       (i)=>(this.samplePropertiesTab(i)),
                       (i)=>(this.sampleAnalysesTab(i)),
                       (i)=>(this.sampleImportReadoutTab(i))
                      ];
    for (let j=0;j < extra.TabCount;j++){
      tabContents.push((i)=>this.extraTab(i))
    }

    return (

        <StructureEditorModal
          key={sample.id}
          showModal={this.state.showStructureEditor}
          onSave={this.handleStructureEditorSave.bind(this)}
          onCancel={this.handleStructureEditorCancel.bind(this)}
          molfile={molfile}
          />
        <StickyDiv zIndex={2}>
          <Panel className="panel-fixed"
                 header="Sample Details"
                 bsStyle={sample.isEdited ? 'info' : 'primary'}>
            <Button bsStyle="danger" bsSize="xsmall" className="button-right" onClick={this.closeDetails.bind(this)}>

            </Button>
            {this.sampleHeader(sample)}
            <ListGroup>
            <Tabs defaultActiveKey={0}>
              {tabContents.map((e,i)=>e(i))}
            </Tabs>
            </ListGroup>
          </Panel>
        </StickyDiv>

    )
  }
}
<|endoftext|>


<commit_before>module.exports = function (creep) {
    var spawn = Game.spawns.Spawn1;

    var target = creep.pos.findClosest(FIND_MY_CREEPS, {
        filter: function (object) {
            console.log(object.memory.module);

            return object.hits < object.hitsMax;
        }
    });

    if (target) {
        creep.moveTo(target);
        creep.heal(target);
    }
};
<commit_msg>Send medics back towards guards.<commit_after>module.exports = function (creep) {
    var spawn = Game.spawns.Spawn1;

    var target = creep.pos.findClosest(FIND_MY_CREEPS, {
        filter: function (creep) {
            return creep.hits < creep.hitsMax;
        }
    });

    if (target) {
        creep.moveTo(target);
        creep.heal(target);
    }

    target = creep.pos.findClosest(FIND_MY_CREEPS, {
       filter: function (creep) {
           return creep.memory.module == 'guard';
       }
    });

    if (target) {
        creep.moveTo(target);
    }
};
<|endoftext|>