<commit_before>import React, { Component } from 'react'; //eslint-disable-line
import { EditorState } from 'draft-js';
import shortid from 'shortid';
import styles from './styles.scss';

import Basic from './Basic';
import Media from './Media';
import Details from './Details';
import Uploads from './Uploads';

let _invalidFields = {};
let focusElement = null;

class CreateGame extends Component {
  state = {
    availableMac: false,
    availableWin: true,
    coverImage: '',
    editorState: EditorState.createEmpty(),
    fileId: shortid.generate(),
    genre: 'Action',
    invalidFields: {},
    macBuild: '',
    releaseStatus: 'Released',
    screenshots: [],
    shortDescription: '',
    tags: [],
    title: '',
    thumbnail: '',
    uploadError: '',
    uploadingMacBuild: false,
    uploadingWindowsBuild: false,
    windowsBuild: ''
  }

  submit = () => {
    this.validate();
  }

  validate = () => {
    _invalidFields = {};
    focusElement = null;

    this.checkRequiredFields();
    this.checkBuilds();

    this.setState({ invalidFields: _invalidFields });
    this.focusOnInvalidField();

    return focusElement === null;
  }

  checkRequiredFields = () => {
    const { coverImage, shortDescription, title, thumbnail, editorState } = this.state;

    if (!title) this.markError('title');
    if (!shortDescription) this.markError('shortDescription');
    if (!coverImage) this.markError('coverImage');
    if (!thumbnail) this.markError('thumbnail');
    if (!editorState.getCurrentContent().hasText()) this.markError('editorContainer');
  }

  checkBuilds = () => {
    const { availableMac, availableWin, macBuild, windowsBuild } = this.state;

    if (availableWin && !windowsBuild) this.markError('windowsBuild');
    if (availableMac && !macBuild) this.markError('macBuild');
    if (!availableMac && !availableWin) this.markError('platforms');
  }

  markError = (fieldId) => {
    _invalidFields[fieldId] = true;
    const element = document.getElementById(fieldId);
    if (!focusElement) focusElement = element;
  }

  focusOnInvalidField = () => {
    if (!focusElement) return;

    window.scroll(0, this.findPosition(focusElement));
    focusElement.focus();
  }

  findPosition = (element) => {
    let pos = 0;
    if (element.offsetParent) {
      do {
        pos += element.offsetTop;
      } while (element === element.offsetParent);
      return [pos];
    }
  }

  validatedInputClass = (classList, fieldId) => {
    const { invalidFields } = this.state;
    return invalidFields[fieldId] ? `${classList} ${styles.Invalid}` : classList;
  }

  handleChange = ({ target }) => {
    const { name, value } = target;
    this.setState({ [name]: value });
  }

  handleBuildChange = (name, value, uploadingName) => {
    this.setState({
      [name]: value,
      [uploadingName]: false
    });
  }

  renderSubmitButton = () => {
    const { uploadingMacBuild, uploadingWindowsBuild } = this.state;
    return uploadingMacBuild || uploadingWindowsBuild
      ? (
        <button className={styles.FormButtonDisabled} disabled onClick={this.submit}>
          Create Game
        </button>
      )
      : <button className={styles.FormButton} onClick={this.submit}>Create Game</button>;
  }

  render() {
    const {
      availableMac,
      availableWin,
      editorState,
      coverImage,
      fileId,
      genre,
      macBuild,
      releaseStatus,
      screenshots,
      shortDescription,
      tags,
      title,
      thumbnail,
      uploadError,
      uploadingMacBuild,
      uploadingWindowsBuild,
      windowsBuild
    } = this.state;

    const platforms = { availableWin, availableMac };

    return (

        <Basic
          title={title}
          shortDescription={shortDescription}
          releaseStatus={releaseStatus}
          platforms={platforms}
          handleChange={this.handleChange}
          validatedInputClass={this.validatedInputClass}
        />

        <Media
          coverImage={coverImage}
          thumbnail={thumbnail}
          screenshots={screenshots}
          handleChange={this.handleChange}
          validatedInputClass={this.validatedInputClass}
        />

        <Uploads
          platforms={platforms}
          handleChange={this.handleChange}
          handleBuildChange={this.handleBuildChange}
          macBuild={macBuild}
          uploadingMacBuild={uploadingMacBuild}
          uploadingWindowsBuild={uploadingWindowsBuild}
          windowsBuild={windowsBuild}
          fileId={fileId}
          uploadError={uploadError}
          validatedInputClass={this.validatedInputClass}
        />

          <button className={styles.CancelButton}>Cancel</button>
          {this.renderSubmitButton()}

    );
  }
}

export default CreateGame;
<commit_msg>Add basic submit function<commit_after>import React, { Component } from 'react'; //eslint-disable-line
import { EditorState, convertToRaw } from 'draft-js';
import shortid from 'shortid';
import PropTypes from 'prop-types';
import styles from './styles.scss';

import Basic from './Basic';
import Media from './Media';
import Details from './Details';
import Uploads from './Uploads';

let _invalidFields = {};
let focusElement = null;

class CreateGame extends Component {
  state = {
    availableMac: false,
    availableWin: true,
    coverImage: '',
    editorState: EditorState.createEmpty(),
    fileId: shortid.generate(),
    genre: 'Action',
    invalidFields: {},
    macBuild: '',
    releaseStatus: 'Released',
    screenshots: [],
    shortDescription: '',
    tags: [],
    title: '',
    thumbnail: '',
    trailer: '',
    uploadError: '',
    uploadingMacBuild: false,
    uploadingWindowsBuild: false,
    windowsBuild: ''
  }

  submit = () => {
    const { submitGame, user } = this.props;
    const {
      coverImage,
      editorState,
      fieldId,
      genre,
      macBuild,
      releaseStatus,
      screenshots,
      shortDescription,
      tags,
      title,
      thumbnail,
      trailer,
      windowsBuild
    } = this.state;

    const game = {
      buildsId: fieldId,
      coverImage,
      descriptionState: convertToRaw(editorState),
      developerIds: [user._id],
      genre,
      macBuild,
      releaseStatus,
      screenshots,
      shortDescription,
      tags,
      title,
      thumbnail,
      trailer,
      windowsBuild
    };

    if (this.validate()) return;

    submitGame(game);
  }

  validate = () => {
    _invalidFields = {};
    focusElement = null;

    this.checkRequiredFields();
    this.checkBuilds();

    this.setState({ invalidFields: _invalidFields });
    this.focusOnInvalidField();

    return focusElement === null;
  }

  checkRequiredFields = () => {
    const { coverImage, shortDescription, title, thumbnail, editorState } = this.state;

    if (!title) this.markError('title');
    if (!shortDescription) this.markError('shortDescription');
    if (!coverImage) this.markError('coverImage');
    if (!thumbnail) this.markError('thumbnail');
    if (!editorState.getCurrentContent().hasText()) this.markError('editorContainer');
  }

  checkBuilds = () => {
    const { availableMac, availableWin, macBuild, windowsBuild } = this.state;

    if (availableWin && !windowsBuild) this.markError('windowsBuild');
    if (availableMac && !macBuild) this.markError('macBuild');
    if (!availableMac && !availableWin) this.markError('platforms');
  }

  markError = (fieldId) => {
    _invalidFields[fieldId] = true;
    const element = document.getElementById(fieldId);
    if (!focusElement) focusElement = element;
  }

  focusOnInvalidField = () => {
    if (!focusElement) return;

    window.scroll(0, this.findPosition(focusElement));
    focusElement.focus();
  }

  findPosition = (element) => {
    let pos = 0;
    if (element.offsetParent) {
      do {
        pos += element.offsetTop;
      } while (element === element.offsetParent);
      return [pos];
    }
  }

  validatedInputClass = (classList, fieldId) => {
    const { invalidFields } = this.state;
    return invalidFields[fieldId] ? `${classList} ${styles.Invalid}` : classList;
  }

  handleChange = ({ target }) => {
    const { name, value } = target;
    this.setState({ [name]: value });
  }

  handleBuildChange = (name, value, uploadingName) => {
    this.setState({
      [name]: value,
      [uploadingName]: false
    });
  }

  renderSubmitButton = () => {
    const { uploadingMacBuild, uploadingWindowsBuild } = this.state;
    return uploadingMacBuild || uploadingWindowsBuild
      ? (
        <button className={styles.FormButtonDisabled} disabled onClick={this.submit}>
          Create Game
        </button>
      )
      : <button className={styles.FormButton} onClick={this.submit}>Create Game</button>;
  }

  render() {
    const {
      availableMac,
      availableWin,
      editorState,
      coverImage,
      fileId,
      genre,
      macBuild,
      releaseStatus,
      screenshots,
      shortDescription,
      tags,
      title,
      thumbnail,
      uploadError,
      uploadingMacBuild,
      uploadingWindowsBuild,
      windowsBuild
    } = this.state;

    const platforms = { availableWin, availableMac };

    return (

        <Basic
          title={title}
          shortDescription={shortDescription}
          releaseStatus={releaseStatus}
          platforms={platforms}
          handleChange={this.handleChange}
          validatedInputClass={this.validatedInputClass}
        />

        <Media
          coverImage={coverImage}
          thumbnail={thumbnail}
          screenshots={screenshots}
          handleChange={this.handleChange}
          validatedInputClass={this.validatedInputClass}
        />

        <Uploads
          platforms={platforms}
          handleChange={this.handleChange}
          handleBuildChange={this.handleBuildChange}
          macBuild={macBuild}
          uploadingMacBuild={uploadingMacBuild}
          uploadingWindowsBuild={uploadingWindowsBuild}
          windowsBuild={windowsBuild}
          fileId={fileId}
          uploadError={uploadError}
          validatedInputClass={this.validatedInputClass}
        />

          <button className={styles.CancelButton}>Cancel</button>
          {this.renderSubmitButton()}

    );
  }
}

CreateGame.propTypes = {
  submitGame: PropTypes.func.isRequired,
  user: PropTypes.object.isRequired
};

export default CreateGame;
<|endoftext|>


<commit_before>function getPublicToken(access_token){
    $.post('/plaid/create_token',
    {access_token: access_token },
    function(data,status,xhr){
        return data.public_token
    },
    'json'
    );
}
function getPlaid(plaidData) {
    var url = null;
    var access_token = plaidData.data('access-token');
    var env = plaidData.data('env');
    var token
    if (typeof access_token === 'undefined') {
        url = '/plaid/authenticate';
        token = null;
    } else {
        url = '/plaid/update';
        token = getPublicToken(access_token);
    }

    var linkHandler = Plaid.create({
        env: env,
        apiVersion: 'v2',
        clientName: plaidData.data('client-name'),
        key: plaidData.data('key'),
        product: ['auth', 'transactions'],
        webhook: plaidData.data('webhook'),
        token: token,
        onLoad: function () {
            // The Link module finished loading.
        },
        onSuccess: function (public_token, metadata) {
            // Send the public_token to your app server here.
            // The metadata object contains info about the institution the
            // user selected and the account ID, if selectAccount is enabled.
            $.ajax({
                type: 'POST',
                dataType: 'script',
                url: url,
                data: {
                    public_token: public_token,
                    name: metadata.account.name,
                    type: metadata.account.type,
                    owner_type: plaidData.data('owner-type'),
                    owner_id: plaidData.data('owner-id'),
                    number: metadata.account.mask
                }
            });
        },
        onExit: function () {
            // The user exited the Link flow.
        }
    });
    return linkHandler;
}
// Trigger the authentication view
$(document).on("click", '#plaidLinkButton', function () {
    var plaidData = $(this);
    linkHandler = getPlaid(plaidData);
    var plaidType = plaidData.data('type')
    //open handler for the institution
    if (typeof plaidType === 'undefined') {
        linkHandler.open();
    } else {
        linkHandler.open(plaidType);
    }

});

<commit_msg>remove debug logging closes #790<commit_after>function getPublicToken(access_token){
    $.post('/plaid/create_token',
    {access_token: access_token },
    function(data,status,xhr){
        return data.public_token;
    },
    'json'
    );
}
function getPlaid(plaidData) {
    var url = null;
    var access_token = plaidData.data('access-token');
    var env = plaidData.data('env');
    var token
    if (typeof access_token === 'undefined') {
        url = '/plaid/authenticate';
        token = null;
    } else {
        url = '/plaid/update';
        token = getPublicToken(access_token);
    }

    var linkHandler = Plaid.create({
        env: env,
        apiVersion: 'v2',
        clientName: plaidData.data('client-name'),
        key: plaidData.data('key'),
        product: ['auth', 'transactions'],
        webhook: plaidData.data('webhook'),
        token: token,
        onLoad: function () {
            // The Link module finished loading.
        },
        onSuccess: function (public_token, metadata) {
            // Send the public_token to your app server here.
            // The metadata object contains info about the institution the
            // user selected and the account ID, if selectAccount is enabled.
            $.ajax({
                type: 'POST',
                dataType: 'script',
                url: url,
                data: {
                    public_token: public_token,
                    name: metadata.account.name,
                    type: metadata.account.type,
                    owner_type: plaidData.data('owner-type'),
                    owner_id: plaidData.data('owner-id'),
                    number: metadata.account.mask
                }
            });
        },
        onExit: function () {
            // The user exited the Link flow.
        }
    });
    return linkHandler;
}
// Trigger the authentication view
$(document).on("click", '#plaidLinkButton', function () {
    var plaidData = $(this);
    linkHandler = getPlaid(plaidData);
    var plaidType = plaidData.data('type')
    //open handler for the institution
    if (typeof plaidType === 'undefined') {
        linkHandler.open();
    } else {
        linkHandler.open(plaidType);
    }

});

<|endoftext|>


<commit_before>function objj_backtrace_format(aReceiver, aSelector)
{
    return "[<" + GETMETA(aReceiver).name + " " + (typeof sprintf == "function" ? sprintf("%#08x", aReceiver.__address) : aReceiver.__address.toString(16)) + "> " + aSelector + "]";
}

function objj_msgSend_Backtrace(/*id*/ aReceiver, /*SEL*/ aSelector)
{
    if (aReceiver == nil)
        return nil;

    objj_debug_backtrace.push(objj_backtrace_format(aReceiver, aSelector));

    try
    {
        var result = class_getMethodImplementation(aReceiver.isa, aSelector).apply(aReceiver, arguments);
    }
    catch (anException)
    {
        CPLog.error("Exception " + anException + " in " + objj_backtrace_format(aReceiver, aSelector));
        objj_debug_print_backtrace();
    }

    objj_debug_backtrace.pop();

    return result;
}

function objj_msgSendSuper_Backtrace(/*id*/ aSuper, /*SEL*/ aSelector)
{
    objj_debug_backtrace.push(objj_backtrace_format(aSuper.receiver, aSelector));
    var super_class = aSuper.super_class;

    arguments[0] = aSuper.receiver;

    try
    {
        var result = class_getMethodImplementation(super_class, aSelector).apply(aSuper.receiver, arguments);
    }
    catch (anException)
    {
        CPLog.error("Exception " + anException + " in " + objj_backtrace_format(aSuper.receiver, aSelector));
        objj_debug_print_backtrace();
    }

    objj_debug_backtrace.pop();

    return result;
}

function objj_msgSend_Profile(/*id*/ aReceiver, /*SEL*/ aSelector)
{
    if (aReceiver == nil)
        return nil;

    // profiling book keeping
    var profileRecord = {
        parent      : objj_debug_profile,
        receiver    : GETMETA(aReceiver).name,
        selector    : aSelector,
        calls       : []
    }
    objj_debug_profile.calls.push(profileRecord);
    objj_debug_profile = profileRecord;
    profileRecord.start = new Date();

    var result = class_getMethodImplementation(aReceiver.isa, aSelector).apply(aReceiver, arguments);

    profileRecord.end = new Date();
    objj_debug_profile = profileRecord.parent;

    return result;
}

function objj_msgSendSuper_Profile(/*id*/ aSuper, /*SEL*/ aSelector)
{
    // profiling book keeping
    var profileRecord = {
        parent      : objj_debug_profile,
        receiver    : GETMETA(aReceiver).name,
        selector    : aSelector,
        calls       : []
    }
    objj_debug_profile.calls.push(profileRecord);
    objj_debug_profile = profileRecord;
    profileRecord.start = new Date();

    var super_class = aSuper.super_class;

    arguments[0] = aSuper.receiver;

    var result = class_getMethodImplementation(super_class, aSelector).apply(aSuper.receiver, arguments);

    profileRecord.end = new Date();
    objj_debug_profile = profileRecord.parent;

    return result;
}

var objj_msgSend_Standard = objj_msgSend,
    objj_msgSendSuper_Standard = objj_msgSendSuper;

// FIXME: This could be much better.
var objj_debug_backtrace;

function objj_backtrace_set_enabled(enabled)
{
    if (enabled)
    {
        objj_debug_backtrace = [];
        objj_msgSend = objj_msgSend_Backtrace;
        objj_msgSendSuper = objj_msgSendSuper_Backtrace;
    }
    else
    {
        objj_msgSend = objj_msgSend_Standard;
        objj_msgSendSuper = objj_msgSendSuper_Standard;
    }
}

function objj_debug_print_backtrace()
{
    alert(objj_debug_backtrace_string());
}

function objj_debug_backtrace_string()
{
    return objj_debug_backtrace ? objj_debug_backtrace.join("\n") : "";
}

var objj_debug_profile = null,
    objj_currently_profiling = false,
    objj_profile_cleanup;

function objj_profile(title)
{
    if (objj_currently_profiling)
        return;

    var objj_msgSend_profile_saved = objj_msgSend,
        objj_msgSendSuper_profile_saved = objj_msgSendSuper;

    objj_msgSend = objj_msgSend_Profile;
    objj_msgSendSuper = objj_msgSendSuper_Profile;

    var root = { calls: [] };
    objj_debug_profile = root;

    var context = {
        start : new Date(),
        title : title,
        profile : root
    };

    objj_profile_cleanup = function() {
        objj_msgSend = objj_msgSend_profile_saved;
        objj_msgSendSuper = objj_msgSendSuper_profile_saved;
        context.end = new Date();
        return context;
    }

    objj_currently_profiling = true;
}

function objj_profileEnd()
{
    if (!objj_currently_profiling)
        return;

    objj_debug_profile = null;
    objj_currently_profiling = false;

    return objj_profile_cleanup();
}

// Type checking version of objj_msgSend:

function objj_msgSend_TypeCheck(/*id*/ aReceiver, /*SEL*/ aSelector)
{
    if (aReceiver == nil)
        return nil;

    CLASS_GET_METHOD_IMPLEMENTATION(var implementation, aReceiver.isa, aSelector);

    var types = method.types;

    for (var i = 2; i < arguments.length; i++)
    {
        try
        {
            runtimeTypeCheck(types[i-1], arguments[i]);
        }
        catch (e)
        {
            objj_fprintf(warning_stream, "Type check failure: "+
                "aReceiver="+(aReceiver.isa ? "<"+aReceiver.isa.name+">" : String(aReceiver).substring(0,100))+", "+
                "aSelector="+String(aSelector).substring(0,100)+", "+
                "argument #"+(i-2)+": " + e);
        }
    }

    var result = implementation.apply(aReceiver, arguments);

    try
    {
        runtimeTypeCheck(types[0], result);
    }
    catch (e)
    {
        objj_fprintf(warning_stream, "Type check failure: "+
            "aReceiver="+(aReceiver.isa ? "<"+aReceiver.isa.name+">" : String(aReceiver).substring(0,100))+", "+
            "aSelector="+String(aSelector).substring(0,100)+", "+
            "return value: " + e);
    }

    return result;
}

function runtimeTypeCheck(typeName, actual)
{
    var objjClass;

    if (!typeName)
    {
        return;
    }
    if (typeName === "id")
    {
        return;
    }
    else if (typeName === "void")
    {
        if (actual === undefined)
            return;
    }
    else if (objjClass = objj_getClass(typeName))
    {
        if (actual && actual.isa)
        {
            var theClass = actual.isa;
            for(; theClass; theClass = theClass.super_class)
            if (theClass === objjClass)
                return;
        }
    }
    else
    {
        return;
    }

    throw("Expected " + typeName + ", was [" + ((actual && actual.isa) ? "<"+actual.isa.name+">" : actual) +"]");
}

objj_msgSend = objj_msgSend_TypeCheck;
<commit_msg>Refactored objj_msgSend debug hooks<commit_after>// formatting helpers

function objj_debug_object_format(aReceiver)
{
    return (aReceiver && aReceiver.isa) ? sprintf("<%s %#08x>", GETMETA(aReceiver).name, aReceiver.__address) : String(aReceiver);
}

function objj_debug_message_format(aReceiver, aSelector)
{
    return sprintf("[%s %s]", objj_debug_object_format(aReceiver), aSelector);
}

// save the original msgSend implementations so we can restore them later
var objj_msgSend_original = objj_msgSend,
    objj_msgSendSuper_original = objj_msgSendSuper;

// decorator management functions

// reset to default objj_msgSend* implementations
function objj_msgSend_reset()
{
    objj_msgSend = objj_msgSend_original;
    objj_msgSendSuper = objj_msgSendSuper_original;
}

// decorate both objj_msgSend and objj_msgSendSuper
function objj_msgSend_decorate()
{
    for (var i = 0; i < arguments.length; i++)
    {
        objj_msgSend = arguments[i](objj_msgSend);
        objj_msgSendSuper = arguments[i](objj_msgSendSuper);
    }
}

// reset then decorate both objj_msgSend and objj_msgSendSuper
function objj_msgSend_set_decorators()
{
    objj_msgSend_reset();
    objj_msgSend_decorate.apply(null, arguments);
}

// backtrace decorator

var objj_backtrace = [];
function objj_backtrace_decorator(msgSend)
{
    return function(aReceiverOrSuper, aSelector)
    {
        var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper);

        // push the receiver and selector onto the backtrace stack
        objj_backtrace.push({ receiver: aReceiver, selector : aSelector });
        try
        {
            return msgSend.apply(null, arguments);
        }
        catch (anException)
        {
            // print the exception and backtrace
            objj_fprintf(warning_stream, "Exception " + anException + " in " + objj_debug_message_format(aReceiver, aSelector));
            for (var i = 0; i < objj_backtrace.length; i++)
                objj_fprintf(warning_stream, objj_debug_message_format(objj_backtrace[i].receiver, objj_backtrace[i].selector));
        }
        finally
        {
            // make sure to always pop
            objj_backtrace.pop();
        }
    }
}

// type checking decorator

var objj_typechecks_reported = {};
function objj_typecheck_decorator(msgSend)
{
    return function(aReceiverOrSuper, aSelector)
    {
        var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper);

        if (!aReceiver)
            return msgSend.apply(null, arguments);

        var types = aReceiver.isa.method_dtable[aSelector].types;
        for (var i = 2; i < arguments.length; i++)
        {
            try
            {
                objj_debug_typecheck(types[i-1], arguments[i]);
            }
            catch (e)
            {
                var key = [GETMETA(aReceiver).name, aSelector, i, e].join(";");
                if (!objj_typechecks_reported[key]) {
                    objj_typechecks_reported[key] = true;
                    objj_fprintf(warning_stream, "Type check failed on argument " + (i-2) + " of " + objj_debug_message_format(aReceiver, aSelector) + ": " + e);
                }
            }
        }

        var result = msgSend.apply(null, arguments);

        try
        {
            objj_debug_typecheck(types[0], result);
        }
        catch (e)
        {
            var key = [GETMETA(aReceiver).name, aSelector, "ret", e].join(";");
            if (!objj_typechecks_reported[key]) {
                objj_typechecks_reported[key] = true;
                objj_fprintf(warning_stream, "Type check failed on return val of " + objj_debug_message_format(aReceiver, aSelector) + ": " + e);
            }
        }

        return result;
    }
}

// type checking logic:
function objj_debug_typecheck(expectedType, object)
{
    var objjClass;

    if (!expectedType)
    {
        return;
    }
    else if (expectedType === "id")
    {
        if (object !== undefined)
            return;
    }
    else if (expectedType === "void")
    {
        if (object === undefined)
            return;
    }
    else if (objjClass = objj_getClass(expectedType))
    {
        if (object === nil)
        {
            return;
        }
        else if (object.isa)
        {
            var theClass = object.isa;
            for (; theClass; theClass = theClass.super_class)
            if (theClass === objjClass)
                return;
        }
    }
    else
    {
        return;
    }

    var actualType;
    if (object === null)
        actualType = "null";
    else if (object === undefined)
        actualType = "void";
    else if (object.isa)
        actualType = GETMETA(object).name;
    else
        actualType = typeof object;

    throw ("expected=" + expectedType + ", actual=" + actualType);
}

// profile decorator
/*
function objj_debug_profile(msgSend)
{
    return function(aReceiverOrSuper, aSelector)
    {
        var aReceiver = aReceiverOrSuper && (aReceiverOrSuper.receiver || aReceiverOrSuper);

        // profiling book keeping
        var profileRecord = {
            parent      : objj_debug_profile,
            receiver    : aReceiver && GETMETA(aReceiver).name,
            selector    : aSelector,
            calls       : []
        }
        objj_debug_profile.calls.push(profileRecord);
        objj_debug_profile = profileRecord;
        profileRecord.start = new Date();

        try
        {
            return msgSend.apply(null, arguments);
        }
        finally
        {
            profileRecord.end = new Date();
            objj_debug_profile = profileRecord.parent;
        }
    }
}

// FIXME: This could be much better.
var objj_debug_backtrace;

function objj_backtrace_set_enabled(enabled)
{
    if (enabled)
    {
        objj_debug_backtrace = [];
        objj_msgSend = objj_msgSend_Backtrace;
        objj_msgSendSuper = objj_msgSendSuper_Backtrace;
    }
    else
    {
        objj_msgSend = objj_msgSend_Standard;
        objj_msgSendSuper = objj_msgSendSuper_Standard;
    }
}

function objj_debug_print_backtrace()
{
    alert(objj_debug_backtrace_string());
}

function objj_debug_backtrace_string()
{
    return objj_debug_backtrace ? objj_debug_backtrace.join("\n") : "";
}

var objj_debug_profile = null,
    objj_currently_profiling = false,
    objj_profile_cleanup;

function objj_profile(title)
{
    if (objj_currently_profiling)
        return;

    var objj_msgSend_profile_saved = objj_msgSend,
        objj_msgSendSuper_profile_saved = objj_msgSendSuper;

    objj_msgSend = objj_msgSend_Profile;
    objj_msgSendSuper = objj_msgSendSuper_Profile;

    var root = { calls: [] };
    objj_debug_profile = root;

    var context = {
        start : new Date(),
        title : title,
        profile : root
    };

    objj_profile_cleanup = function() {
        objj_msgSend = objj_msgSend_profile_saved;
        objj_msgSendSuper = objj_msgSendSuper_profile_saved;
        context.end = new Date();
        return context;
    }

    objj_currently_profiling = true;
}

function objj_profileEnd()
{
    if (!objj_currently_profiling)
        return;

    objj_debug_profile = null;
    objj_currently_profiling = false;

    return objj_profile_cleanup();
}
*/

objj_msgSend_set_decorators(objj_typecheck_decorator, objj_backtrace_decorator);
<|endoftext|>


<commit_before>function jpok(a,b){
  var dat = {pax:urb.term.pax, act:{}}
  dat.act[a] = b
  urb.send({data:dat}, function(e,dat){
    if(a === 'line' && dat.data.err){
      hist.unshift(prom.val())
      prom.val(b)
      hind = 0
    }
  })
}

var prom, prom_size, hist, hind, size, focus, pos, pax

$(function() {
  prom = $("#prom")
  prom_size = $("#prom-size")

  hist = []
  hind = 0

  var keys = ['l', 'x', 'r']
  for (i in keys) (function(k){
    Mousetrap.bind('ctrl+' + k, function(){
      jpok('cmd', k)
    })
  })(keys[i])

  prom.keydown(function(e){
    switch(e.which){
      default: return true
        break;
      case 13:  // %retn
        if(e.shiftKey) return true
        v = prom.val().replace(/\xa0/g, ' ')
        $(cont).append($('').html(prem.innerHTML),
                       $('').text(v))

        jpok('line', v)
        hist.unshift(v)
        prom.val('')
        return false
        break;
      case 38:  // %up
        if(hind == hist.length) return true
        if(pos().top === false) return true
        prom.val([hist[hind], hist[hind] = prom.val()][0]) // swap
        size()
        hind++
        return false
        break;
      case 40:  // %down
        if(hind == 0) return true
        if(pos().bot === false) return true
        size()
        hind--
        prom.val([hist[hind], hist[hind] = prom.val()][0]) // swap
        return false
    }
  })

  focus = function() { $(prom).focus(); }
  $('body').on('click', focus)
  focus()

  size = function() {
    prom_size.html(prom.val()+"
")
  }
  size()
  prom.on('input propertychange', size)

  pos = function() {
    v = prom.val()
    _top = v.slice(0,prom[0].selectionStart).indexOf("\n") === -1
    _bot = v.slice(prom[0].selectionStart).indexOf("\n") === -1
    return {top:_top,bot:_bot}
  }

  pax = '/lines'
  if(urb.term.pax != "/") pax += urb.term.pax
  urb.subscribe({path: pax}, function(e, dat){
    if(dat.data.ok) return;
    hist = dat.data.history
    hind = 0
    cont.innerHTML = ''
    for(var i in dat.data.lines){
      var lom = dat.data.lines[i]
      if(typeof lom == 'string')
        $(cont).append($('
').text(lom))
      else {
        $(cont).append($('').text(lom.prompt + '> '),
                       $('').text(lom.task))
      }
    }
    window.scrollTo(0,document.body.scrollHeight)
    prem.textContent = dat.data.prompt + '> '
  })
});
<commit_msg>Bound alt instead of ctrl on non-mac platforms<commit_after>function jpok(a,b){
  var dat = {pax:urb.term.pax, act:{}}
  dat.act[a] = b
  urb.send({data:dat}, function(e,dat){
    if(a === 'line' && dat.data.err){
      hist.unshift(prom.val())
      prom.val(b)
      hind = 0
    }
  })
}

var prom, prom_size, hist, hind, size, focus, pos, pax

$(function() {
  prom = $("#prom")
  prom_size = $("#prom-size")

  hist = []
  hind = 0

  var keys = ['l', 'x', 'r']
  var mod = /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'ctrl' : 'alt'
  for (i in keys) (function(k){
    Mousetrap.bind(mod + '+' + k, function(){
      jpok('cmd', k)
    })
  })(keys[i])

  prom.keydown(function(e){
    switch(e.which){
      default: return true
        break;
      case 13:  // %retn
        if(e.shiftKey) return true
        v = prom.val().replace(/\xa0/g, ' ')
        $(cont).append($('').html(prem.innerHTML),
                       $('').text(v))

        jpok('line', v)
        hist.unshift(v)
        prom.val('')
        return false
        break;
      case 38:  // %up
        if(hind == hist.length) return true
        if(pos().top === false) return true
        prom.val([hist[hind], hist[hind] = prom.val()][0]) // swap
        size()
        hind++
        return false
        break;
      case 40:  // %down
        if(hind == 0) return true
        if(pos().bot === false) return true
        size()
        hind--
        prom.val([hist[hind], hist[hind] = prom.val()][0]) // swap
        return false
    }
  })

  focus = function() { $(prom).focus(); }
  $('body').on('click', focus)
  focus()

  size = function() {
    prom_size.html(prom.val()+"
")
  }
  size()
  prom.on('input propertychange', size)

  pos = function() {
    v = prom.val()
    _top = v.slice(0,prom[0].selectionStart).indexOf("\n") === -1
    _bot = v.slice(prom[0].selectionStart).indexOf("\n") === -1
    return {top:_top,bot:_bot}
  }

  pax = '/lines'
  if(urb.term.pax != "/") pax += urb.term.pax
  urb.subscribe({path: pax}, function(e, dat){
    if(dat.data.ok) return;
    hist = dat.data.history
    hind = 0
    cont.innerHTML = ''
    for(var i in dat.data.lines){
      var lom = dat.data.lines[i]
      if(typeof lom == 'string')
        $(cont).append($('
').text(lom))
      else {
        $(cont).append($('').text(lom.prompt + '> '),
                       $('').text(lom.task))
      }
    }
    window.scrollTo(0,document.body.scrollHeight)
    prem.textContent = dat.data.prompt + '> '
  })
});
<|endoftext|>


<commit_before>/* global Buildkite */

import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import Emojify from '../../shared/Emojify';
import Icon from '../../shared/Icon';

import jobCommandOneliner from '../../../lib/jobCommandOneliner';
import BootstrapTooltipMixin from '../../../lib/BootstrapTooltipMixin';

const BuildHeaderPipelineComponent = createReactClass({ // eslint-disable-line react/prefer-es6-class
  displayName: 'BuildHeaderPipelineComponent',

  mixins: [BootstrapTooltipMixin],

  getInitialState() {
    return { showBroken: false };
  },

  propTypes: {
    build: PropTypes.shape({
      id: PropTypes.string.isRequired,
      path: PropTypes.string.isRequired,
      jobs: PropTypes.arrayOf(PropTypes.shape({
        state: PropTypes.string.isRequired
      })),
      jobsCount: PropTypes.number.isRequired
    }).isRequired
  },

  render() {
    const brokenJobs = this.brokenJobCount();
    const action = this.state.showBroken ? "Hide" : "Show";
    const icon = this.state.showBroken ? "eye-strikethrough" : "eye";
    const suffix = brokenJobs > 1 ? "jobs" : "job";

    const toggleBrokenNode = brokenJobs > 0
      ? (
        <button
          className="btn dark-gray hover-black regular mt0 ml0 pt0 pl0"
          onClick={this.handleToggleBrokenStepsClick}
        >
          <div
            title={`${action} ${brokenJobs} skipped ${suffix}`}
            data-toggle="tooltip"
            data-animation="false"
          >
            <Icon icon={icon} className="relative" style={{ top: -3 }} />

        </button>
      )
      : null;

    return (

          {this.stepNodes()}

          {toggleBrokenNode}

    );
  },

  brokenJobCount() {
    return this.props.build.jobs
      .filter((job) => (
        (job.state === 'broken') && (job.type !== 'waiter')
      )).length;
  },

  stepNodes() {
    if (this.props.build.jobs.length === 0) {
      return (

          There are no build steps for this pipeline. To add a step, go to your Pipeline Settings.

      );
    }

    const jobs = this.state.showBroken
      ? this.props.build.jobs
      : this.props.build.jobs.filter(({ state }) => state !== 'broken');

    const renderedJobs = jobs.map((job) => this.pipelineStep(job));

    // If the build has hidden jobs (such as on the project show / build index
    // page, we want to add a "..." build step that links to the build so you
    // can see all the steps)
    if (this.props.build.jobs.length !== this.props.build.jobsCount) {
      renderedJobs.push(
        <a
          key={`${this.props.build.id}-more-jobs`}
          href={this.props.build.path}
          className="build-pipeline-job truncate align-middle"
        >
          ...

      );
    }

    return renderedJobs;
  },

  pipelineStep(job) {
    const stepClassName = this.stepClassName(job);

    const { BuildHeaderPipelineManualStepComponent, BuildHeaderPipelineTriggerStepComponent } = Buildkite;

    if (job.type === 'script') {
      if (job.state === 'broken') {
        return (

            {this.jobName(job)}

        );
      }

      const href = `${this.props.build.path}#${job.id}`;

      return (

          {this.jobName(job)}

      );
    } else if (job.type === 'waiter') {
      if (job.continueOnFailure) {
        return (
          <div
            key={job.id}
            className={`${stepClassName} dark-gray relative`}
            title="Wait for all previous steps to finish including failures"
          >

            <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

        );
      }

      return (
        <div
          key={job.id}
          className={`${stepClassName} dark-gray`}
          title="Wait for all previous steps to pass"
        >
          <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

      );
    } else if (job.type === 'manual') {
      return (
        <BuildHeaderPipelineManualStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    } else if (job.type === 'trigger') {
      return (
        <BuildHeaderPipelineTriggerStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    }

    return (

        {job.type}

    );
  },

  stepClassName(job) {
    const state = job.state === 'finished' && (job.type === 'script' || job.type === 'trigger')
      ? (
        job.passed
          ? 'passed'
          : 'failed'
      )
      : job.state;

    return [
      'build-pipeline-job',
      `build-pipeline-job-${job.type.replace(/_/g, '-')}`,
      `build-pipeline-state-${state}`,
      'truncate',
      'align-middle'
    ].join(' ');
  },

  handleScriptJobClick(job) {
    return function(evt) {
      // return if the user did a middle-click, right-click, or used a modifier
      // key (like ctrl-click, meta-click, shift-click, etc.)
      if ((evt.button !== 0) || evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
        return;
      }

      evt.preventDefault();

      Buildkite.dispatcher.emit("job:toggle", { job, source: "BUILD_HEADER" });
    };
  },

  jobName(job) {
    if (job.name) {
      return <Emojify text={job.name} />;
    }

    return (

        {jobCommandOneliner(job.command)}

    );
  },

  handleToggleBrokenStepsClick(evt) {
    evt.preventDefault();
    evt.target.blur();
    return this.setState({ showBroken: !this.state.showBroken });
  }
});

export default BuildHeaderPipelineComponent;

<commit_msg>Show a retried icon next to jobs that were retried<commit_after>/* global Buildkite */

import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import Emojify from '../../shared/Emojify';
import Icon from '../../shared/Icon';

import jobCommandOneliner from '../../../lib/jobCommandOneliner';
import BootstrapTooltipMixin from '../../../lib/BootstrapTooltipMixin';

const BuildHeaderPipelineComponent = createReactClass({ // eslint-disable-line react/prefer-es6-class
  displayName: 'BuildHeaderPipelineComponent',

  mixins: [BootstrapTooltipMixin],

  getInitialState() {
    return { showBroken: false };
  },

  propTypes: {
    build: PropTypes.shape({
      id: PropTypes.string.isRequired,
      path: PropTypes.string.isRequired,
      jobs: PropTypes.arrayOf(PropTypes.shape({
        state: PropTypes.string.isRequired
      })),
      jobsCount: PropTypes.number.isRequired
    }).isRequired
  },

  render() {
    const brokenJobs = this.brokenJobCount();
    const action = this.state.showBroken ? "Hide" : "Show";
    const icon = this.state.showBroken ? "eye-strikethrough" : "eye";
    const suffix = brokenJobs > 1 ? "jobs" : "job";

    const toggleBrokenNode = brokenJobs > 0
      ? (
        <button
          className="btn dark-gray hover-black regular mt0 ml0 pt0 pl0"
          onClick={this.handleToggleBrokenStepsClick}
        >
          <div
            title={`${action} ${brokenJobs} skipped ${suffix}`}
            data-toggle="tooltip"
            data-animation="false"
          >
            <Icon icon={icon} className="relative" style={{ top: -3 }} />

        </button>
      )
      : null;

    return (

          {this.stepNodes()}

          {toggleBrokenNode}

    );
  },

  brokenJobCount() {
    return this.props.build.jobs
      .filter((job) => (
        (job.state === 'broken') && (job.type !== 'waiter')
      )).length;
  },

  stepNodes() {
    if (this.props.build.jobs.length === 0) {
      return (

          There are no build steps for this pipeline. To add a step, go to your Pipeline Settings.

      );
    }

    const jobs = this.state.showBroken
      ? this.props.build.jobs
      : this.props.build.jobs.filter(({ state }) => state !== 'broken');

    const renderedJobs = jobs.map((job) => this.pipelineStep(job));

    // If the build has hidden jobs (such as on the project show / build index
    // page, we want to add a "..." build step that links to the build so you
    // can see all the steps)
    if (this.props.build.jobs.length !== this.props.build.jobsCount) {
      renderedJobs.push(
        <a
          key={`${this.props.build.id}-more-jobs`}
          href={this.props.build.path}
          className="build-pipeline-job truncate align-middle"
        >
          ...

      );
    }

    return renderedJobs;
  },

  pipelineStep(job) {
    const stepClassName = this.stepClassName(job);

    const { BuildHeaderPipelineManualStepComponent, BuildHeaderPipelineTriggerStepComponent } = Buildkite;

    if (job.type === 'script') {
      if (job.state === 'broken') {
        return (

            {this.jobName(job)}

        );
      }

      const href = `${this.props.build.path}#${job.id}`;

      let retriedIcon;
      if (job.retriedInJobUuid) {
        retriedIcon = (
          <Icon
            icon="retry"
            style={{ height: 12, width: 12, top: -2, marginRight: 5 }}
            className="relative"
          />
        )
      }

      return (

          {retriedIcon}
          {this.jobName(job)}

      );
    } else if (job.type === 'waiter') {
      if (job.continueOnFailure) {
        return (
          <div
            key={job.id}
            className={`${stepClassName} dark-gray relative`}
            title="Wait for all previous steps to finish including failures"
          >

            <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

        );
      }

      return (
        <div
          key={job.id}
          className={`${stepClassName} dark-gray`}
          title="Wait for all previous steps to pass"
        >
          <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

      );
    } else if (job.type === 'manual') {
      return (
        <BuildHeaderPipelineManualStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    } else if (job.type === 'trigger') {
      return (
        <BuildHeaderPipelineTriggerStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    }

    return (

        {job.type}

    );
  },

  stepClassName(job) {
    const state = job.state === 'finished' && (job.type === 'script' || job.type === 'trigger')
      ? (
        job.passed
          ? 'passed'
          : 'failed'
      )
      : job.state;

    return [
      'build-pipeline-job',
      `build-pipeline-job-${job.type.replace(/_/g, '-')}`,
      `build-pipeline-state-${state}`,
      'truncate',
      'align-middle'
    ].join(' ');
  },

  handleScriptJobClick(job) {
    return function(evt) {
      // return if the user did a middle-click, right-click, or used a modifier
      // key (like ctrl-click, meta-click, shift-click, etc.)
      if ((evt.button !== 0) || evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
        return;
      }

      evt.preventDefault();

      Buildkite.dispatcher.emit("job:toggle", { job, source: "BUILD_HEADER" });
    };
  },

  jobName(job) {
    if (job.name) {
      return <Emojify text={job.name} />;
    }

    return (

        {jobCommandOneliner(job.command)}

    );
  },

  handleToggleBrokenStepsClick(evt) {
    evt.preventDefault();
    evt.target.blur();
    return this.setState({ showBroken: !this.state.showBroken });
  }
});

export default BuildHeaderPipelineComponent;

<|endoftext|>


<commit_before>var EVENTS = {
  'build:queued':    { build: { id: 1, number: '11.1' }, repository: { slug: 'travis-ci/travis-ci' } },
  'build:removed':   { build: { id: 1 } },
  'build:finished':  { build: { id: 1, finished_at: '2011-11-11T01:00:00Z', status: 1 }, repository: { id: 1,  slug: 'travis-ci/travis-ci' } },
  'build:started:1': { build: { id: 11, repository_id: 1,  number: '11' }, repository: { id: 1,  slug: 'travis-ci/travis-ci' } },
  'build:started:2': { build: { id: 12, repository_id: 12, number: '12' }, repository: { id: 12, slug: 'svenfuchs/minimal'   } },
  'build:log:1':     { build: { id: 2, _log: 'Foo.' } },
}

describe('Events:', function() {
  var events = Travis.Controllers.Events.create();
  var queue;
  var receiveEvent = function(event, ix) {
    SC.run(function() { events.receive(event, EVENTS[$.compact([event, ix]).join(':')]); });
  };

  beforeEach(function() {
    queue = createView('#jobs', { jobs: Travis.Job.all(), template: 'app/templates/jobs/list' });
  });

  describe('build:queued', function() {
    it('adds the job to the queue', function() {
      receiveEvent('build:queued');
      expect(queue.$('ul').html()).toHaveText('travis-ci/travis-ci #11.1');
    });
  });

  describe('build:removed', function() {
    it('build:removed event removes the job from the queue', function() {
      receiveEvent('build:queued');
      receiveEvent('build:removed');
      expect(queue.$('ul').html()).toHaveText('No jobs');
    });
  });

  describe('build:started', function() {
    it('adds the build to an existing repository', function() {
      var repository = Test.Factory.Repository.travis();
      var count  = function() { Travis.Repository.all().get('length') };
      var before = count();

      receiveEvent('build:started', 1);

      var build = Travis.Build.find(11);
      expect(build.get('number')).toEqual('11');
      expect(build.getPath('repository.slug')).toEqual('travis-ci/travis-ci');
      expect(count()).toEqual(before);
    });

    it('adds the build to a new repository', function() {
      var count  = function() { Travis.Repository.all().get('length') };
      var before = count();

      receiveEvent('build:started', 2);

      var build = Travis.Build.find(12);
      expect(build.get('number')).toEqual('12');
      expect(build.getPath('repository.slug')).toEqual('svenfuchs/minimal');
      expect(count()).toEqual(before);
    });
  });

  describe('build:log', function() {
    it('appends to an existing build', function() {
      var build = Test.Factory.Build.passing();
      var test = build.get('matrix').objectAt(0);

      receiveEvent('build:started', 1);
      receiveEvent('build:log', 1);

      expect(test.get('log')).toEqual('Done. Build script exited with: 0\nFoo.');
    });
  });

  describe('build:finished', function() {
    it('removes the job from the queue', function() {
      Test.Factory.Build.passing();
      receiveEvent('build:queued');
      receiveEvent('build:finished');
      expect(queue.$('ul').html()).toHaveText('No jobs');
    });

    it('updates the build', function() {
      var build = Test.Factory.Build.passing();
      receiveEvent('build:finished');

      expect(build.get('result')).toEqual(1);
      expect(build.get('finishedAt')).toEqual('2011-11-11T01:00:00Z');
    });
  });
});
<commit_msg>make sure we clone event mock data<commit_after>var EVENTS = {
  'build:queued':    { build: { id: 1, number: '11.1' }, repository: { slug: 'travis-ci/travis-ci' } },
  'build:removed':   { build: { id: 1 } },
  'build:finished':  { build: { id: 1, finished_at: '2011-11-11T01:00:00Z', status: 1 }, repository: { id: 1,  slug: 'travis-ci/travis-ci' } },
  'build:started:1': { build: { id: 11, repository_id: 1,  number: '11' }, repository: { id: 1,  slug: 'travis-ci/travis-ci' } },
  'build:started:2': { build: { id: 12, repository_id: 12, number: '12' }, repository: { id: 12, slug: 'svenfuchs/minimal'   } },
  'build:log:1':     { build: { id: 2, _log: 'Foo.' } },
}

describe('Events:', function() {
  var events = Travis.Controllers.Events.create();
  var queue;

  var receiveEvent = function(event, ix) {
    var data = EVENTS[$.compact([event, ix]).join(':')];
    data = JSON.parse(JSON.stringify(data)); // make sure we pass a clone
    SC.run(function() { events.receive(event, data); });
  };

  beforeEach(function() {
    queue = createView('#jobs', { jobs: Travis.Job.all(), template: 'app/templates/jobs/list' });
  });

  describe('build:queued', function() {
    it('adds the job to the queue', function() {
      receiveEvent('build:queued');
      expect(queue.$('ul').html()).toHaveText('travis-ci/travis-ci #11.1');
    });
  });

  describe('build:removed', function() {
    it('build:removed event removes the job from the queue', function() {
      receiveEvent('build:queued');
      receiveEvent('build:removed');
      expect(queue.$('ul').html()).toHaveText('No jobs');
    });
  });

  describe('build:started', function() {
    it('adds the build to an existing repository', function() {
      var repository = Test.Factory.Repository.travis();
      var count  = function() { Travis.Repository.all().get('length') };
      var before = count();

      receiveEvent('build:started', 1);

      var build = Travis.Build.find(11);
      expect(build.get('number')).toEqual('11');
      expect(build.getPath('repository.slug')).toEqual('travis-ci/travis-ci');
      expect(count()).toEqual(before);
    });

    it('adds the build to a new repository', function() {
      var count  = function() { Travis.Repository.all().get('length') };
      var before = count();

      receiveEvent('build:started', 2);

      var build = Travis.Build.find(12);
      expect(build.get('number')).toEqual('12');
      expect(build.getPath('repository.slug')).toEqual('svenfuchs/minimal');
      expect(count()).toEqual(before);
    });
  });

  describe('build:log', function() {
    it('appends to an existing build', function() {
      var build = Test.Factory.Build.passing();
      var test = build.get('matrix').objectAt(0);

      receiveEvent('build:started', 1);
      receiveEvent('build:log', 1);

      expect(test.get('log')).toEqual('Done. Build script exited with: 0\nFoo.');
    });
  });

  describe('build:finished', function() {
    it('removes the job from the queue', function() {
      Test.Factory.Build.passing();
      receiveEvent('build:queued');
      receiveEvent('build:finished');
      expect(queue.$('ul').html()).toHaveText('No jobs');
    });

    it('updates the build', function() {
      var build = Test.Factory.Build.passing();
      receiveEvent('build:finished');

      expect(build.get('result')).toEqual(1);
      expect(build.get('finishedAt')).toEqual('2011-11-11T01:00:00Z');
    });
  });
});
<|endoftext|>


<commit_before>/* global Buildkite */

import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import Emojify from 'app/components/shared/Emojify';
import Icon from 'app/components/shared/Icon';
import Dropdown from 'app/components/shared/Dropdown';

import jobCommandOneliner from 'app/lib/jobCommandOneliner';
import BootstrapTooltipMixin from 'app/lib/BootstrapTooltipMixin';

class ParallelJobGroup {
  constructor(id) {
    this.id = id;
    this.type = "parallel_group";
    this.jobs = [];

    this.name = null;
    this.command = null;
    this.state = "scheduled";
    this.passed = null;
    this.total = null;

    this.finished = 0;
  }

  appendJob(job) {
    // Set some of the initial values based on the first job we see
    if (!this.total) {
      this.name = job.name;
      this.command = job.command;
      this.state = job.state;
      this.passed = job.passed;
      this.total = job.parallelGroupTotal;
    }

    // Keep track of jobs that haven't finished yet
    if (job.state === "finished" ||
      job.state === "waiting_failed" ||
      job.state === "blocked_failed" ||
      job.state === "canceled" ||
      job.state === "timed_out" ||
      job.state === "skipped" ||
      job.state === "broken") {
      this.finished += 1;
    }

    // If any job hasn't passed, then the entire group didn't pass
    if (!job.passed) {
      this.passed = false;
    }

    // Figure out the state of the current group
    if (this.finished === this.total) {
      this.state = "finished";
    } else if (job.state === "running" || (this.finished > 0 && (this.finished !== this.total))) {
      this.state = "running";
    }

    this.jobs.push(job);
  }
}

const BuildHeaderPipelineComponent = createReactClass({ // eslint-disable-line react/prefer-es6-class
  displayName: 'BuildHeaderPipelineComponent',

  mixins: [BootstrapTooltipMixin],

  getInitialState() {
    return { showHiddenJobs: false };
  },

  propTypes: {
    build: PropTypes.shape({
      id: PropTypes.string.isRequired,
      path: PropTypes.string.isRequired,
      jobs: PropTypes.arrayOf(PropTypes.shape({
        state: PropTypes.string.isRequired
      })),
      jobsCount: PropTypes.number.isRequired
    }).isRequired
  },

  render() {
    return (

          {this.stepNodes()}

          {this.renderHiddenJobsButton()}

    );
  },

  renderHiddenJobsButton() {
    // Figure out how many hidden jobs we have (if any?)
    const hiddenJobsCount = this.hiddenJobsCount();
    if (hiddenJobsCount === 0) {
      return null;
    }

    const action = this.state.showHiddenJobs ? "Hide" : "Show";
    const icon = this.state.showHiddenJobs ? "eye-strikethrough" : "eye";
    const suffix = hiddenJobsCount > 1 ? "jobs" : "job";

    return (
      <button
        className="btn dark-gray hover-black regular mt0 ml0 pt0 pl0"
        onClick={this.handleToggleBrokenStepsClick}
      >
        <div
          title={`${action} ${hiddenJobsCount} hidden ${suffix}`}
          data-toggle="tooltip"
          data-animation="false"
        >
          <Icon icon={icon} className="relative" style={{ top: -3 }} />

      </button>
    );
  },

  hiddenJobsCount() {
    return this.props.build.jobs
      .filter((job) => (
        ((job.state === 'broken') && (job.type !== 'waiter')) || job.retriedInJobUuid
      )).length;
  },

  stepNodes() {
    if (this.props.build.jobs.length === 0) {
      return (

          There are no build steps for this pipeline. To add a step, go to your Pipeline Settings.

      );
    }

    let jobs = this.state.showHiddenJobs
      ? this.props.build.jobs
      : this.props.build.jobs.filter(({ state, retriedInJobUuid }) => state !== 'broken' && !retriedInJobUuid);

    if (Features.ParallelJobGroups) {
      let groupedJobs = [];
      let currentParallelGroup = null;
      for (const job of jobs) {
        // Ah! We've stumbled onto a parallel job, let's try and group it.
        if (job.parallelGroupTotal) {
          // If there's no current group being tracked, create one. If there *is*
          // one, and it's different to the current group, we'll finish off that
          // one and create a new group.
          if (!currentParallelGroup) {
            currentParallelGroup = new ParallelJobGroup(job.stepUuid);
          } else if (currentParallelGroup.id !== job.stepUuid) {
            // Only commit the parallel group if we were able to collect all
            // the jobs for it (we may only be showing a partial list of jobs)
            if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
              groupedJobs.push(currentParallelGroup);
            } else {
              groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
            }

            currentParallelGroup = new ParallelJobGroup(job.stepUuid);
          }

          currentParallelGroup.appendJob(job);
        } else {
          // If this job doesn't have a `parallelGroupTotal`, and there's a
          // `currentParallelGroup` in the mix, we know we've now progressed
          // passed the group onto a new job.
          if (currentParallelGroup) {
            // Only commit the parallel group if we were able to collect all the
            // jobs for it (we may only be showing a partial list of jobs)
            if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
              groupedJobs.push(currentParallelGroup);
            } else {
              groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
            }

            // We can stop tracking it now
            currentParallelGroup = null;
          }

          groupedJobs.push(job);
        }
      }

      // If there was a dangling parallel group, add it to the list of jobs to
      // render as well.
      if (currentParallelGroup) {
        // Only commit the parallel group if we were able to collect all the
        // jobs for it (we may only be showing a partial list of jobs)
        if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
          groupedJobs.push(currentParallelGroup);
        } else {
          groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
        }

        currentParallelGroup = null;
      }

      jobs = groupedJobs;
    }

    const renderedJobs = jobs.map((job) => this.pipelineStep(job));

    // If the build has hidden jobs (such as on the project show / build index
    // page, we want to add a "..." build step that links to the build so you
    // can see all the steps)
    if (this.props.build.jobs.length !== this.props.build.jobsCount) {
      renderedJobs.push(
        <a
          key={`${this.props.build.id}-more-jobs`}
          href={this.props.build.path}
          className="build-pipeline-job truncate align-middle"
        >
          ...

      );
    }

    return renderedJobs;
  },

  pipelineStep(job, inParallelGroup = false) {
    const stepClassName = this.stepClassName(job);

    const { BuildHeaderPipelineManualStepComponent, BuildHeaderPipelineTriggerStepComponent } = Buildkite;

    if (job.type === 'script') {
      if (job.state === 'broken') {
        return (
          <div
            key={job.id}
            data-toggle="tooltip"
            title={inParallelGroup ? `${this.jobName(job)} ${job.tooltip}` : job.tooltip}
            className={stepClassName}
            style={{
              maxWidth: inParallelGroup ? null : '15em',
            }}
          >
            {inParallelGroup ? null : this.jobNameNode(job)}
            {inParallelGroup && (

                {job.parallelGroupIndex + 1}

            )}

        );
      }

      const href = `${this.props.build.path}#${job.id}`;

      let retriedIcon;

      if (job.retriedInJobUuid) {
        retriedIcon = (
          <Icon
            icon="retry"
            style={{ height: 12, width: 12, top: -2, marginRight: 5 }}
            className="relative"
          />
        );
      }

      return (

          {retriedIcon}
          {inParallelGroup ? null : this.jobNameNode(job)}
          {inParallelGroup && (

              {job.parallelGroupIndex + 1}

          )}

      );
    } else if (job.type === 'parallel_group') {
      return this.renderParallelGroup(job);
    } else if (job.type === 'waiter') {
      if (job.continueOnFailure) {
        return (
          <div
            key={job.id}
            className={`${stepClassName} dark-gray relative`}
            title="Wait for all previous steps to finish including failures"
          >

            <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

        );
      }

      return (
        <div
          key={job.id}
          className={`${stepClassName} dark-gray`}
          title="Wait for all previous steps to pass"
        >
          <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

      );
    } else if (job.type === 'manual') {
      return (
        <BuildHeaderPipelineManualStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    } else if (job.type === 'trigger') {
      return (
        <BuildHeaderPipelineTriggerStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    }

    return (

        {job.type}

    );
  },

  stepClassName(job) {
    const state = job.state === 'finished' && (job.type === 'script' || job.type === 'trigger' || job.type === "parallel_group")
      ? (
        job.passed
          ? 'passed'
          : 'failed'
      )
      : job.state;

    return [
      'build-pipeline-job',
      `build-pipeline-job-${job.type.replace(/_/g, '-')}`,
      `build-pipeline-state-${state}`,
      'truncate',
      'align-middle'
    ].join(' ');
  },

  getLabelBackgroundColor(job) {
    if (job.state === "running") {
      return "#9c7c14"; // Yellow-ish
    } else if (job.state === "finished" && job.passed) {
      return "#69A770"; // Green
    } else if (job.state === "canceled" ||
                (job.state === "finished" && !job.passed)) {
      return "#a94442"; // Red
    }

    return "#afafaf"; // Gray
  },

  getWidthForParallelJobCount(parallelGroupTotal) {
    return (60 + (parallelGroupTotal.toString(10).length - 1) * 6);
  },

  idealItemsPerRow(itemCount) {
    // If we're less than 6, just use one row
    if (itemCount < 6) {
      return itemCount;
    }

    // If we've got less than 16 items, just use two rows
    if (itemCount < 16) {
      return Math.ceil(itemCount / 2);
    }

    // If we've got less than 32 items, just use four rows
    if (itemCount < 32) {
      return Math.ceil(itemCount / 4);
    }

    // Otherwise, we compute something which looks decent
    if (itemCount < 128) {
      return Math.ceil(itemCount / Math.ceil(itemCount / 10));
    }

    return Math.ceil(itemCount / Math.ceil(itemCount / 20));
  },

  renderParallelGroup(group) {
    const widthForEachJob = this.getWidthForParallelJobCount(group.total);

    const itemsPerRow = this.idealItemsPerRow(group.total);

    return (
      <Dropdown
        key={group.id}
        width={widthForEachJob * itemsPerRow + 10 + 5 * itemsPerRow - 1}
        className={this.stepClassName(group).replace("truncate", "")}
      >

            {this.jobNameNode(group)}

            {group.finished}/{group.total}

        <div
          className="build-pipeline-job-popup mx1 my0 tabular-numerals"
          style={{
            gridTemplateColumns: `repeat(auto-fill, minmax(${Math.round(widthForEachJob).toString(10)}px, 1fr))`
          }}
        >
          {group.jobs.map((job) => this.pipelineStep(job, true))}

      </Dropdown>
    );
  },

  handleScriptJobClick(job) {
    return function(evt) {
      // return if the user did a middle-click, right-click, or used a modifier
      // key (like ctrl-click, meta-click, shift-click, etc.)
      if ((evt.button !== 0) || evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
        return;
      }

      evt.preventDefault();

      Buildkite.dispatcher.emit("job:focus", { job });
    };
  },

  jobNameNode(job) {
    if (job.name) {
      return <Emojify text={job.name} />;
    }

    return (

        {jobCommandOneliner(job.command)}

    );
  },

  jobName(job) {
    return job.name || job.command;
  },

  handleToggleBrokenStepsClick(evt) {
    evt.preventDefault();
    evt.target.blur();
    return this.setState({ showHiddenJobs: !this.state.showHiddenJobs });
  }
});

export default BuildHeaderPipelineComponent;

<commit_msg>Fix up ESLint<commit_after>/* global Buildkite */

import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';

import Emojify from 'app/components/shared/Emojify';
import Icon from 'app/components/shared/Icon';
import Dropdown from 'app/components/shared/Dropdown';

import jobCommandOneliner from 'app/lib/jobCommandOneliner';
import BootstrapTooltipMixin from 'app/lib/BootstrapTooltipMixin';

class ParallelJobGroup {
  constructor(id) {
    this.id = id;
    this.type = "parallel_group";
    this.jobs = [];

    this.name = null;
    this.command = null;
    this.state = "scheduled";
    this.passed = null;
    this.total = null;

    this.finished = 0;
  }

  appendJob(job) {
    // Set some of the initial values based on the first job we see
    if (!this.total) {
      this.name = job.name;
      this.command = job.command;
      this.state = job.state;
      this.passed = job.passed;
      this.total = job.parallelGroupTotal;
    }

    // Keep track of jobs that haven't finished yet
    if (job.state === "finished" ||
      job.state === "waiting_failed" ||
      job.state === "blocked_failed" ||
      job.state === "canceled" ||
      job.state === "timed_out" ||
      job.state === "skipped" ||
      job.state === "broken") {
      this.finished += 1;
    }

    // If any job hasn't passed, then the entire group didn't pass
    if (!job.passed) {
      this.passed = false;
    }

    // Figure out the state of the current group
    if (this.finished === this.total) {
      this.state = "finished";
    } else if (job.state === "running" || (this.finished > 0 && (this.finished !== this.total))) {
      this.state = "running";
    }

    this.jobs.push(job);
  }
}

const BuildHeaderPipelineComponent = createReactClass({ // eslint-disable-line react/prefer-es6-class
  displayName: 'BuildHeaderPipelineComponent',

  mixins: [BootstrapTooltipMixin],

  getInitialState() {
    return { showHiddenJobs: false };
  },

  propTypes: {
    build: PropTypes.shape({
      id: PropTypes.string.isRequired,
      path: PropTypes.string.isRequired,
      jobs: PropTypes.arrayOf(PropTypes.shape({
        state: PropTypes.string.isRequired
      })),
      jobsCount: PropTypes.number.isRequired
    }).isRequired
  },

  render() {
    return (

          {this.stepNodes()}

          {this.renderHiddenJobsButton()}

    );
  },

  renderHiddenJobsButton() {
    // Figure out how many hidden jobs we have (if any?)
    const hiddenJobsCount = this.hiddenJobsCount();
    if (hiddenJobsCount === 0) {
      return null;
    }

    const action = this.state.showHiddenJobs ? "Hide" : "Show";
    const icon = this.state.showHiddenJobs ? "eye-strikethrough" : "eye";
    const suffix = hiddenJobsCount > 1 ? "jobs" : "job";

    return (
      <button
        className="btn dark-gray hover-black regular mt0 ml0 pt0 pl0"
        onClick={this.handleToggleBrokenStepsClick}
      >
        <div
          title={`${action} ${hiddenJobsCount} hidden ${suffix}`}
          data-toggle="tooltip"
          data-animation="false"
        >
          <Icon icon={icon} className="relative" style={{ top: -3 }} />

      </button>
    );
  },

  hiddenJobsCount() {
    return this.props.build.jobs
      .filter((job) => (
        ((job.state === 'broken') && (job.type !== 'waiter')) || job.retriedInJobUuid
      )).length;
  },

  stepNodes() {
    if (this.props.build.jobs.length === 0) {
      return (

          There are no build steps for this pipeline. To add a step, go to your Pipeline Settings.

      );
    }

    let jobs = this.state.showHiddenJobs
      ? this.props.build.jobs
      : this.props.build.jobs.filter(({ state, retriedInJobUuid }) => state !== 'broken' && !retriedInJobUuid);

    if (Features.ParallelJobGroups) {
      let groupedJobs = [];
      let currentParallelGroup = null;
      for (const job of jobs) {
        // Ah! We've stumbled onto a parallel job, let's try and group it.
        if (job.parallelGroupTotal) {
          // If there's no current group being tracked, create one. If there *is*
          // one, and it's different to the current group, we'll finish off that
          // one and create a new group.
          if (!currentParallelGroup) {
            currentParallelGroup = new ParallelJobGroup(job.stepUuid);
          } else if (currentParallelGroup.id !== job.stepUuid) {
            // Only commit the parallel group if we were able to collect all
            // the jobs for it (we may only be showing a partial list of jobs)
            if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
              groupedJobs.push(currentParallelGroup);
            } else {
              groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
            }

            currentParallelGroup = new ParallelJobGroup(job.stepUuid);
          }

          currentParallelGroup.appendJob(job);
        } else {
          // If this job doesn't have a `parallelGroupTotal`, and there's a
          // `currentParallelGroup` in the mix, we know we've now progressed
          // passed the group onto a new job.
          if (currentParallelGroup) {
            // Only commit the parallel group if we were able to collect all the
            // jobs for it (we may only be showing a partial list of jobs)
            if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
              groupedJobs.push(currentParallelGroup);
            } else {
              groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
            }

            // We can stop tracking it now
            currentParallelGroup = null;
          }

          groupedJobs.push(job);
        }
      }

      // If there was a dangling parallel group, add it to the list of jobs to
      // render as well.
      if (currentParallelGroup) {
        // Only commit the parallel group if we were able to collect all the
        // jobs for it (we may only be showing a partial list of jobs)
        if (currentParallelGroup.jobs.length === currentParallelGroup.total) {
          groupedJobs.push(currentParallelGroup);
        } else {
          groupedJobs = groupedJobs.concat(currentParallelGroup.jobs);
        }

        currentParallelGroup = null;
      }

      jobs = groupedJobs;
    }

    const renderedJobs = jobs.map((job) => this.pipelineStep(job));

    // If the build has hidden jobs (such as on the project show / build index
    // page, we want to add a "..." build step that links to the build so you
    // can see all the steps)
    if (this.props.build.jobs.length !== this.props.build.jobsCount) {
      renderedJobs.push(
        <a
          key={`${this.props.build.id}-more-jobs`}
          href={this.props.build.path}
          className="build-pipeline-job truncate align-middle"
        >
          ...

      );
    }

    return renderedJobs;
  },

  pipelineStep(job, inParallelGroup = false) {
    const stepClassName = this.stepClassName(job);

    const { BuildHeaderPipelineManualStepComponent, BuildHeaderPipelineTriggerStepComponent } = Buildkite;

    if (job.type === 'script') {
      if (job.state === 'broken') {
        return (
          <div
            key={job.id}
            data-toggle="tooltip"
            title={inParallelGroup ? `${this.jobName(job)} ${job.tooltip}` : job.tooltip}
            className={stepClassName}
            style={{
              maxWidth: inParallelGroup ? null : '15em'
            }}
          >
            {inParallelGroup ? null : this.jobNameNode(job)}
            {inParallelGroup && (

                {job.parallelGroupIndex + 1}

            )}

        );
      }

      const href = `${this.props.build.path}#${job.id}`;

      let retriedIcon;

      if (job.retriedInJobUuid) {
        retriedIcon = (
          <Icon
            icon="retry"
            style={{ height: 12, width: 12, top: -2, marginRight: 5 }}
            className="relative"
          />
        );
      }

      return (

          {retriedIcon}
          {inParallelGroup ? null : this.jobNameNode(job)}
          {inParallelGroup && (

              {job.parallelGroupIndex + 1}

          )}

      );
    } else if (job.type === 'parallel_group') {
      return this.renderParallelGroup(job);
    } else if (job.type === 'waiter') {
      if (job.continueOnFailure) {
        return (
          <div
            key={job.id}
            className={`${stepClassName} dark-gray relative`}
            title="Wait for all previous steps to finish including failures"
          >

            <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

        );
      }

      return (
        <div
          key={job.id}
          className={`${stepClassName} dark-gray`}
          title="Wait for all previous steps to pass"
        >
          <Icon icon="chevron-right" style={{ height: 15, width: 15 }} />

      );
    } else if (job.type === 'manual') {
      return (
        <BuildHeaderPipelineManualStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    } else if (job.type === 'trigger') {
      return (
        <BuildHeaderPipelineTriggerStepComponent
          key={job.id}
          build={this.props.build}
          job={job}
          stepClassName={stepClassName}
        />
      );
    }

    return (

        {job.type}

    );
  },

  stepClassName(job) {
    const state = job.state === 'finished' && (job.type === 'script' || job.type === 'trigger' || job.type === "parallel_group")
      ? (
        job.passed
          ? 'passed'
          : 'failed'
      )
      : job.state;

    return [
      'build-pipeline-job',
      `build-pipeline-job-${job.type.replace(/_/g, '-')}`,
      `build-pipeline-state-${state}`,
      'truncate',
      'align-middle'
    ].join(' ');
  },

  getLabelBackgroundColor(job) {
    if (job.state === "running") {
      return "#9c7c14"; // Yellow-ish
    } else if (job.state === "finished" && job.passed) {
      return "#69A770"; // Green
    } else if (job.state === "canceled" ||
                (job.state === "finished" && !job.passed)) {
      return "#a94442"; // Red
    }

    return "#afafaf"; // Gray
  },

  getWidthForParallelJobCount(parallelGroupTotal) {
    return (60 + (parallelGroupTotal.toString(10).length - 1) * 6);
  },

  idealItemsPerRow(itemCount) {
    // If we're less than 6, just use one row
    if (itemCount < 6) {
      return itemCount;
    }

    // If we've got less than 16 items, just use two rows
    if (itemCount < 16) {
      return Math.ceil(itemCount / 2);
    }

    // If we've got less than 32 items, just use four rows
    if (itemCount < 32) {
      return Math.ceil(itemCount / 4);
    }

    // Otherwise, we compute something which looks decent
    if (itemCount < 128) {
      return Math.ceil(itemCount / Math.ceil(itemCount / 10));
    }

    return Math.ceil(itemCount / Math.ceil(itemCount / 20));
  },

  renderParallelGroup(group) {
    const widthForEachJob = this.getWidthForParallelJobCount(group.total);

    const itemsPerRow = this.idealItemsPerRow(group.total);

    return (
      <Dropdown
        key={group.id}
        width={widthForEachJob * itemsPerRow + 10 + 5 * itemsPerRow - 1}
        className={this.stepClassName(group).replace("truncate", "")}
      >

            {this.jobNameNode(group)}

            {group.finished}/{group.total}

        <div
          className="build-pipeline-job-popup mx1 my0 tabular-numerals"
          style={{
            gridTemplateColumns: `repeat(auto-fill, minmax(${Math.round(widthForEachJob).toString(10)}px, 1fr))`
          }}
        >
          {group.jobs.map((job) => this.pipelineStep(job, true))}

      </Dropdown>
    );
  },

  handleScriptJobClick(job) {
    return function(evt) {
      // return if the user did a middle-click, right-click, or used a modifier
      // key (like ctrl-click, meta-click, shift-click, etc.)
      if ((evt.button !== 0) || evt.altKey || evt.ctrlKey || evt.metaKey || evt.shiftKey) {
        return;
      }

      evt.preventDefault();

      Buildkite.dispatcher.emit("job:focus", { job });
    };
  },

  jobNameNode(job) {
    if (job.name) {
      return <Emojify text={job.name} />;
    }

    return (

        {jobCommandOneliner(job.command)}

    );
  },

  jobName(job) {
    return job.name || job.command;
  },

  handleToggleBrokenStepsClick(evt) {
    evt.preventDefault();
    evt.target.blur();
    return this.setState({ showHiddenJobs: !this.state.showHiddenJobs });
  }
});

export default BuildHeaderPipelineComponent;

<|endoftext|>


<commit_before>/* eslint-disable no-param-reassign */
/* global Vue */

((global) => {
  class Subscription {
    constructor(containerSelector) {
      this.containerElm = (typeof containerSelector === 'string')
        ? document.querySelector(containerSelector)
        : containerSelector;

      const subscribeButton = this.containerElm.querySelector('.js-subscribe-button');
      if (subscribeButton) {
        // remove class so we don't bind twice
        subscribeButton.classList.remove('js-subscribe-button');
        subscribeButton.addEventListener('click', this.toggleSubscription.bind(this));
      }
    }

    toggleSubscription(event) {
      const button = event.currentTarget;
      const buttonSpan = button.querySelector('span');
      if (!buttonSpan || button.classList.contains('disabled')) {
        return;
      }
      button.classList.add('disabled');

      const isSubscribed = buttonSpan.innerHTML.trim() !== 'Subscribe';
      const toggleActionUrl = this.containerElm.getAttribute('data-url');

      $.post(toggleActionUrl, () => {
        button.classList.remove('disabled');

        // hack to allow this to work with the issue boards Vue object
        if (document.querySelector('html').classList.contains('issue-boards-page')) {
          Vue.set(
            gl.issueBoards.BoardsStore.detail.issue,
            'subscribed',
            !gl.issueBoards.BoardsStore.detail.issue.subscribed
          );
        } else {
          const newToggleText = isSubscribed ? 'Subscribe' : 'Unsubscribe';
          buttonSpan.innerHTML = newToggleText;

          if (button.getAttribute('data-original-title')) {
            button.setAttribute('data-original-title', newToggleText);
            $(button).tooltip('hide').tooltip('fixTitle');
          }
        }
      });
    }

    static bindAll(selector) {
      [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm));
    }
  }

  global.Subscription = Subscription;
})(window.gl || (window.gl = {}));
<commit_msg>use less error-prone lowercase comparison for isSubscribed<commit_after>/* eslint-disable no-param-reassign */
/* global Vue */

((global) => {
  class Subscription {
    constructor(containerSelector) {
      this.containerElm = (typeof containerSelector === 'string')
        ? document.querySelector(containerSelector)
        : containerSelector;

      const subscribeButton = this.containerElm.querySelector('.js-subscribe-button');
      if (subscribeButton) {
        // remove class so we don't bind twice
        subscribeButton.classList.remove('js-subscribe-button');
        subscribeButton.addEventListener('click', this.toggleSubscription.bind(this));
      }
    }

    toggleSubscription(event) {
      const button = event.currentTarget;
      const buttonSpan = button.querySelector('span');
      if (!buttonSpan || button.classList.contains('disabled')) {
        return;
      }
      button.classList.add('disabled');

      const isSubscribed = buttonSpan.innerHTML.trim().toLowerCase() !== 'subscribe';
      const toggleActionUrl = this.containerElm.getAttribute('data-url');

      $.post(toggleActionUrl, () => {
        button.classList.remove('disabled');

        // hack to allow this to work with the issue boards Vue object
        if (document.querySelector('html').classList.contains('issue-boards-page')) {
          Vue.set(
            gl.issueBoards.BoardsStore.detail.issue,
            'subscribed',
            !gl.issueBoards.BoardsStore.detail.issue.subscribed
          );
        } else {
          const newToggleText = isSubscribed ? 'Subscribe' : 'Unsubscribe';
          buttonSpan.innerHTML = newToggleText;

          if (button.getAttribute('data-original-title')) {
            button.setAttribute('data-original-title', newToggleText);
            $(button).tooltip('hide').tooltip('fixTitle');
          }
        }
      });
    }

    static bindAll(selector) {
      [].forEach.call(document.querySelectorAll(selector), elm => new Subscription(elm));
    }
  }

  global.Subscription = Subscription;
})(window.gl || (window.gl = {}));
<|endoftext|>


<commit_before>var itemtypenames = [ "Small Caliber Main Gun",
"Medium Caliber Main Gun",
"Large Caliber Main Gun",
"Secondary Gun",
"Anti-Aircraft Gun",
"Anti-Aircraft Shell",
"Armor Piercing Shell",
"Torpedos & Submarine",
"Carrier-Based Fighter",
"Carrier-Based Torpedo Bomber",
"Carrier-Based Dive Bomber",
"Carrier-Based Reconn",
"Seaplane",
"Radar",
"Anti-submarine Warfare",
"Engine",
"Extra Armor",
"Land-Based Attack Aircraft",
"Supply Transport Container" ]

var colors = [ "#D15B5B",
"#CC3D3D",
"#C22222",
"#FFEA00",
"#66CC77",
"#60BF73",
"#D15B5B",
"#5887AB",
"#66CC77",
"#65BCFF",
"#F36D6D",
"#FFC000",
"#8FCC99",
"#E29734",
"#7ECCD8",
"#FFC44D",
"#997EAE",
"#66CC77",
"#A0A0A0" ]

// Id, Name, Rarity, Internal Rarity, Type, Break Materials[, is AA]
var items = [
	[1, "12cm単装砲", 1, 0, 0, [0, 1, 1, 0]],
	[2, "12.7cm連装砲", 1, 0, 0, [0, 1, 2, 0]],
	[3, "10cm連装高角砲", 2, 1, 0, [0, 1, 3, 0], true],
	[4, "14cm単装砲", 1, 0, 1, [0, 2, 1, 0]],
	[5, "15.5cm三連装砲", 2, 0, 1, [0, 2, 5, 0]],
	[6, "20.3cm連装砲", 2, 0, 1, [0, 3, 4, 0]],
	[65, "15.2cm連装砲", 2, 2, 1, [0, 2, 3, 0]],
	[7, "35.6cm連装砲", 1, 0, 2, [0, 10, 15, 0]],
	[8, "41cm連装砲", 2, 1, 2, [0, 12, 20, 0]],
	[9, "46cm三連装砲", 3, 2, 2, [0, 24, 25, 0]],
	[10, "12.7cm連装高角砲", 1, 0, 3, [0, 2, 2, 0], true],
	[11, "15.2cm単装砲", 1, 0, 3, [0, 2, 2, 0]],
	[12, "15.5cm三連装副砲", 2, 1, 3, [0, 2, 5, 0]],
	[66, "8cm高角砲", 3, 3, 3, [0, 1, 2, 0], true],
	[37, "7.7mm機銃", 1, 0, 4, [0, 1, 1, 0]],
	[38, "12.7mm単装機銃", 1, 0, 4, [0, 1, 1, 0]],
	[39, "25mm連装機銃", 2, 0, 4, [0, 2, 1, 0]],
	[40, "25mm三連装機銃", 2, 1, 4, [0, 3, 1, 0]],
	[49, "25mm単装機銃", 2, 1, 4, [0, 1, 1, 0]],
	[35, "三式弾", 2, 0, 5, [0, 9, 6, 3]],
	[36, "九一式徹甲弾", 3, 1, 6, [0, 3, 9, 0]],
	[13, "61cm三連装魚雷", 1, 0, 7, [1, 1, 1, 0]],
	[14, "61cm四連装魚雷", 1, 0, 7, [1, 2, 2, 0]],
	[15, "61cm四連装(酸素)魚雷", 2, 1, 7, [2, 2, 2, 0]],
	[41, "甲標的", 1, 1, 7, [0, 7, 7, 0]],
	[19, "九六式艦戦", 1, 0, 8, [1, 1, 0, 1]],
	[20, "零式艦戦21型", 1, 0, 8, [1, 1, 0, 2]],
	[21, "零式艦戦52型", 2, 1, 8, [1, 2, 0, 3]],
	[22, "烈風", 3, 3, 8, [2, 2, 0, 9]],
	[55, "紫電改二", 3, 2, 8, [2, 2, 0, 7]],
	[181, "零式艦戦32型", 2, 3, 8, [1, 2, 0, 2]],
	[16, "九七式艦攻", 1, 0, 9, [1, 1, 0, 2]],
	[17, "天山", 2, 1, 9, [2, 4, 0, 4]],
	[18, "流星", 3, 2, 9, [2, 5, 0, 10]],
	[52, "流星改", 3, 3, 9, [2, 6, 0, 10]],
	[23, "九九式艦爆", 1, 0, 10, [1, 1, 0, 2]],
	[24, "彗星", 2, 1, 10, [2, 3, 0, 3]],
	[57, "彗星一二型甲", 3, 2, 10, [2, 3, 0, 4]],
	[60, "零式艦戦62型(爆戦)", 3, 2, 10, [1, 3, 0, 3]],
	[54, "彩雲", 3, 2, 11, [2, 0, 0, 11]],
	[61, "二式艦上偵察機", 2, 1, 11, [3, 1, 0, 13]],
	[25, "零式水上偵察機", 1, 1, 12, [1, 1, 0, 2]],
	[26, "瑞雲", 2, 1, 12, [2, 3, 0, 5]],
	[59, "零式水上観測機", 2, 1, 12, [1, 1, 0, 2]],
	[163, "Ro.43水偵", 3, 3, 12, [1, 1, 0, 2]],
	[27, "13号対空電探", 2, 1, 13, [0, 0, 10, 10]],
	[28, "22号対水上電探", 2, 1, 13, [0, 0, 15, 15]],
	[29, "33号対水上電探", 3, 2, 13, [0, 0, 20, 15]],
	[30, "21号対空電探", 2, 2, 13, [0, 0, 20, 20]],
	[31, "32号対水上電探", 3, 3, 13, [0, 0, 20, 25]],
	[32, "14号対空電探", 3, 4, 13, [0, 0, 25, 25]],
	[44, "九四式爆雷投射機", 1, 0, 14, [0, 2, 1, 1]],
	[45, "三式爆雷投射機", 3, 2, 14, [0, 3, 1, 1]],
	[46, "九三式水中聴音機", 1, 0, 14, [0, 0, 1, 1]],
	[47, "三式水中探信儀", 3, 2, 14, [0, 0, 1, 2]],
	[33, "改良型艦本式タービン", 1, 0, 15, [10, 0, 10, 0]],
	[34, "強化型艦本式缶", 2, 1, 15, [10, 0, 20, 0]],
	[72, "増設バルジ(中型艦)", 2, 2, 16, [0, 0, 12, 0]],
	[73, "増設バルジ(大型艦)", 2, 2, 16, [0, 0, 30, 0]],
	[168, "九六式陸攻", 1, 1, 17, [7, 4, 0, 12]],
	[75, "ドラム缶(輸送用)", 1, 0, 18, [0, 0, 1, 0]]
]

var materialNames = ["Fuel", "Ammunition", "Steel", "Bauxite"]

function developResult(id, percentage, materials, hqlevel) {
	this.id = id
	this.percentage = percentage
	this.successful = true
	for (var i = 0; i < 4; ++i) {
		if (items[id][5][i] * 10 > materials[i]) {
			this.successful = false
			break
		}
	}
	if (items[id][3] * 10 > hqlevel) this.successful = false

	this.toTRHTML = function() {
		var rtn = "<tr class=\""
		if (this.successful) rtn += "success"
		else {
			rtn += "fail\" title=\""
			var t = false
			for (var i = 0; i < 4; ++i) {
				if (items[id][5][i] * 10 > materials[i]) {
					if (t) rtn += '\n'
					rtn += "Require " + getString(materialNames[i]) + " >= " + (items[id][5][i] * 10)
					t = true
				}
			}
			if (items[id][3] * 10 > hqlevel) {
				if (t) rtn += '\n'
				rtn += "Require " + getString("HQ Level") + " >= " + (items[id][3] * 10)
			}
		}
		return rtn + "\">" + items[id][1] + "	" + percentage + "%
"
	}

	this.toTRNode = function() {
		var node = document.createElement("tr")
		if (this.successful) node.className = "success"
		else {
			node.className = "fail"
			var title = ""
			for (var i = 0; i < 4; ++i) {
				if (items[id][5][i] * 10 > materials[i]) {
					title += "\nRequire " + getString(materialNames[i]) + " >= " + (items[id][5][i] * 10)
				}
			}
			if (items[id][3] * 10 > hqlevel) {
				rtn += "\nRequire " + getString("HQ Level") + " >= " + (items[id][3] * 10)
			}
			node.title = title.slice(1)
		}
		var tdnode = document.createElement("td")
		tdnode.style.color = items[id][6] ? colors[4] : colors[items[id][4]]
		tdnode.appendChild(document.createTextNode(items[id][1]))
		node.appendChild(tdnode)
		tdnode = document.createElement("td")
		tdnode.appendChild(document.createTextNode(percentage + "%"))
		node.appendChild(tdnode)
		return node
	}
}<commit_msg>Fixed bug that unable to display item that cannot be developed due to lack of HQ level<commit_after>var itemtypenames = [ "Small Caliber Main Gun",
"Medium Caliber Main Gun",
"Large Caliber Main Gun",
"Secondary Gun",
"Anti-Aircraft Gun",
"Anti-Aircraft Shell",
"Armor Piercing Shell",
"Torpedos & Submarine",
"Carrier-Based Fighter",
"Carrier-Based Torpedo Bomber",
"Carrier-Based Dive Bomber",
"Carrier-Based Reconn",
"Seaplane",
"Radar",
"Anti-submarine Warfare",
"Engine",
"Extra Armor",
"Land-Based Attack Aircraft",
"Supply Transport Container" ]

var colors = [ "#D15B5B",
"#CC3D3D",
"#C22222",
"#FFEA00",
"#66CC77",
"#60BF73",
"#D15B5B",
"#5887AB",
"#66CC77",
"#65BCFF",
"#F36D6D",
"#FFC000",
"#8FCC99",
"#E29734",
"#7ECCD8",
"#FFC44D",
"#997EAE",
"#66CC77",
"#A0A0A0" ]

// Id, Name, Rarity, Internal Rarity, Type, Break Materials[, is AA]
var items = [
	[1, "12cm単装砲", 1, 0, 0, [0, 1, 1, 0]],
	[2, "12.7cm連装砲", 1, 0, 0, [0, 1, 2, 0]],
	[3, "10cm連装高角砲", 2, 1, 0, [0, 1, 3, 0], true],
	[4, "14cm単装砲", 1, 0, 1, [0, 2, 1, 0]],
	[5, "15.5cm三連装砲", 2, 0, 1, [0, 2, 5, 0]],
	[6, "20.3cm連装砲", 2, 0, 1, [0, 3, 4, 0]],
	[65, "15.2cm連装砲", 2, 2, 1, [0, 2, 3, 0]],
	[7, "35.6cm連装砲", 1, 0, 2, [0, 10, 15, 0]],
	[8, "41cm連装砲", 2, 1, 2, [0, 12, 20, 0]],
	[9, "46cm三連装砲", 3, 2, 2, [0, 24, 25, 0]],
	[10, "12.7cm連装高角砲", 1, 0, 3, [0, 2, 2, 0], true],
	[11, "15.2cm単装砲", 1, 0, 3, [0, 2, 2, 0]],
	[12, "15.5cm三連装副砲", 2, 1, 3, [0, 2, 5, 0]],
	[66, "8cm高角砲", 3, 3, 3, [0, 1, 2, 0], true],
	[37, "7.7mm機銃", 1, 0, 4, [0, 1, 1, 0]],
	[38, "12.7mm単装機銃", 1, 0, 4, [0, 1, 1, 0]],
	[39, "25mm連装機銃", 2, 0, 4, [0, 2, 1, 0]],
	[40, "25mm三連装機銃", 2, 1, 4, [0, 3, 1, 0]],
	[49, "25mm単装機銃", 2, 1, 4, [0, 1, 1, 0]],
	[35, "三式弾", 2, 0, 5, [0, 9, 6, 3]],
	[36, "九一式徹甲弾", 3, 1, 6, [0, 3, 9, 0]],
	[13, "61cm三連装魚雷", 1, 0, 7, [1, 1, 1, 0]],
	[14, "61cm四連装魚雷", 1, 0, 7, [1, 2, 2, 0]],
	[15, "61cm四連装(酸素)魚雷", 2, 1, 7, [2, 2, 2, 0]],
	[41, "甲標的", 1, 1, 7, [0, 7, 7, 0]],
	[19, "九六式艦戦", 1, 0, 8, [1, 1, 0, 1]],
	[20, "零式艦戦21型", 1, 0, 8, [1, 1, 0, 2]],
	[21, "零式艦戦52型", 2, 1, 8, [1, 2, 0, 3]],
	[22, "烈風", 3, 3, 8, [2, 2, 0, 9]],
	[55, "紫電改二", 3, 2, 8, [2, 2, 0, 7]],
	[181, "零式艦戦32型", 2, 3, 8, [1, 2, 0, 2]],
	[16, "九七式艦攻", 1, 0, 9, [1, 1, 0, 2]],
	[17, "天山", 2, 1, 9, [2, 4, 0, 4]],
	[18, "流星", 3, 2, 9, [2, 5, 0, 10]],
	[52, "流星改", 3, 3, 9, [2, 6, 0, 10]],
	[23, "九九式艦爆", 1, 0, 10, [1, 1, 0, 2]],
	[24, "彗星", 2, 1, 10, [2, 3, 0, 3]],
	[57, "彗星一二型甲", 3, 2, 10, [2, 3, 0, 4]],
	[60, "零式艦戦62型(爆戦)", 3, 2, 10, [1, 3, 0, 3]],
	[54, "彩雲", 3, 2, 11, [2, 0, 0, 11]],
	[61, "二式艦上偵察機", 2, 1, 11, [3, 1, 0, 13]],
	[25, "零式水上偵察機", 1, 1, 12, [1, 1, 0, 2]],
	[26, "瑞雲", 2, 1, 12, [2, 3, 0, 5]],
	[59, "零式水上観測機", 2, 1, 12, [1, 1, 0, 2]],
	[163, "Ro.43水偵", 3, 3, 12, [1, 1, 0, 2]],
	[27, "13号対空電探", 2, 1, 13, [0, 0, 10, 10]],
	[28, "22号対水上電探", 2, 1, 13, [0, 0, 15, 15]],
	[29, "33号対水上電探", 3, 2, 13, [0, 0, 20, 15]],
	[30, "21号対空電探", 2, 2, 13, [0, 0, 20, 20]],
	[31, "32号対水上電探", 3, 3, 13, [0, 0, 20, 25]],
	[32, "14号対空電探", 3, 4, 13, [0, 0, 25, 25]],
	[44, "九四式爆雷投射機", 1, 0, 14, [0, 2, 1, 1]],
	[45, "三式爆雷投射機", 3, 2, 14, [0, 3, 1, 1]],
	[46, "九三式水中聴音機", 1, 0, 14, [0, 0, 1, 1]],
	[47, "三式水中探信儀", 3, 2, 14, [0, 0, 1, 2]],
	[33, "改良型艦本式タービン", 1, 0, 15, [10, 0, 10, 0]],
	[34, "強化型艦本式缶", 2, 1, 15, [10, 0, 20, 0]],
	[72, "増設バルジ(中型艦)", 2, 2, 16, [0, 0, 12, 0]],
	[73, "増設バルジ(大型艦)", 2, 2, 16, [0, 0, 30, 0]],
	[168, "九六式陸攻", 1, 1, 17, [7, 4, 0, 12]],
	[75, "ドラム缶(輸送用)", 1, 0, 18, [0, 0, 1, 0]]
]

var materialNames = ["Fuel", "Ammunition", "Steel", "Bauxite"]

function developResult(id, percentage, materials, hqlevel) {
	this.id = id
	this.percentage = percentage
	this.successful = true
	for (var i = 0; i < 4; ++i) {
		if (items[id][5][i] * 10 > materials[i]) {
			this.successful = false
			break
		}
	}
	if (items[id][3] * 10 > hqlevel) this.successful = false

	this.toTRHTML = function() {
		var rtn = "<tr class=\""
		if (this.successful) rtn += "success"
		else {
			rtn += "fail\" title=\""
			var t = false
			for (var i = 0; i < 4; ++i) {
				if (items[id][5][i] * 10 > materials[i]) {
					if (t) rtn += '\n'
					rtn += "Require " + getString(materialNames[i]) + " >= " + (items[id][5][i] * 10)
					t = true
				}
			}
			if (items[id][3] * 10 > hqlevel) {
				if (t) rtn += '\n'
				rtn += "Require " + getString("HQ Level") + " >= " + (items[id][3] * 10)
			}
		}
		return rtn + "\">" + items[id][1] + "	" + percentage + "%
"
	}

	this.toTRNode = function() {
		var node = document.createElement("tr")
		if (this.successful) node.className = "success"
		else {
			node.className = "fail"
			var title = ""
			for (var i = 0; i < 4; ++i) {
				if (items[id][5][i] * 10 > materials[i]) {
					title += "\nRequire " + getString(materialNames[i]) + " >= " + (items[id][5][i] * 10)
				}
			}
			if (items[id][3] * 10 > hqlevel) {
				title += "\nRequire " + getString("HQ Level") + " >= " + (items[id][3] * 10)
			}
			node.title = title.slice(1)
		}
		var tdnode = document.createElement("td")
		tdnode.style.color = items[id][6] ? colors[4] : colors[items[id][4]]
		tdnode.appendChild(document.createTextNode(items[id][1]))
		node.appendChild(tdnode)
		tdnode = document.createElement("td")
		tdnode.appendChild(document.createTextNode(percentage + "%"))
		node.appendChild(tdnode)
		return node
	}
}<|endoftext|>


<commit_before>// @flow

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import styled from 'styled-components';

import Badge from '../Badge';

import cssVariables from '../../../css';

import { formatNumber } from '../../../lib/number';

const ACTIVE_CLASS_NAME = 'active';

const TabButton = styled(Link).attrs({
  className: 'px4 pt2 border-bottom block text-decoration-none black',
  activeClassName: ACTIVE_CLASS_NAME
})`
  border-bottom-width: 3px;
  border-color: transparent;
  transition: border-color 120ms;
  margin-bottom: -1px;
  height: 44px; /* Needed because the badge causes the height to increase */

  &:hover {
    border-color: #D5EBA4;
  }

  &.${ACTIVE_CLASS_NAME} {
    border-color: #14CC80;
  }
`;

type Props = {
  badge?: number,
  children: React$Node
};

class Tab extends React.PureComponent<Props> {
  static propTypes = {
    badge: PropTypes.number,
    children: PropTypes.node.isRequired
  };

  render() {
    const { badge, children, ...props } = this.props;

    const affix = badge
      ? <Badge outline={true}>{formatNumber(badge)}</Badge>
      : null;

    return (

        <TabButton {...props}>
          {children}
          {' '}
          {affix}
        </TabButton>

    );
  }
}

export default Tab;
<commit_msg>omg eslint<commit_after>// @flow

import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import styled from 'styled-components';

import Badge from '../Badge';

import { formatNumber } from '../../../lib/number';

const ACTIVE_CLASS_NAME = 'active';

const TabButton = styled(Link).attrs({
  className: 'px4 pt2 border-bottom block text-decoration-none black',
  activeClassName: ACTIVE_CLASS_NAME
})`
  border-bottom-width: 3px;
  border-color: transparent;
  transition: border-color 120ms;
  margin-bottom: -1px;
  height: 44px; /* Needed because the badge causes the height to increase */

  &:hover {
    border-color: #D5EBA4;
  }

  &.${ACTIVE_CLASS_NAME} {
    border-color: #14CC80;
  }
`;

type Props = {
  badge?: number,
  children: React$Node
};

class Tab extends React.PureComponent<Props> {
  static propTypes = {
    badge: PropTypes.number,
    children: PropTypes.node.isRequired
  };

  render() {
    const { badge, children, ...props } = this.props;

    const affix = badge
      ? <Badge outline={true}>{formatNumber(badge)}</Badge>
      : null;

    return (

        <TabButton {...props}>
          {children}
          {' '}
          {affix}
        </TabButton>

    );
  }
}

export default Tab;
<|endoftext|>