<commit_before>const settingsCache = require('../settings/cache');
const MembersApi = require('@tryghost/members-api');
const logging = require('../../../shared/logging');
const mail = require('../mail');
const models = require('../../models');
const signinEmail = require('./emails/signin');
const signupEmail = require('./emails/signup');
const subscribeEmail = require('./emails/subscribe');
const updateEmail = require('./emails/updateEmail');
const SingleUseTokenProvider = require('./SingleUseTokenProvider');
const urlUtils = require('../../../shared/url-utils');

const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;

const ghostMailer = new mail.GhostMailer();

module.exports = createApiInstance;

function createApiInstance(config) {
    const membersApiInstance = MembersApi({
        tokenConfig: config.getTokenConfig(),
        auth: {
            getSigninURL: config.getSigninURL.bind(config),
            allowSelfSignup: config.getAllowSelfSignup(),
            tokenProvider: new SingleUseTokenProvider(models.SingleUseToken, MAGIC_LINK_TOKEN_VALIDITY)
        },
        mail: {
            transporter: {
                sendMail(message) {
                    if (process.env.NODE_ENV !== 'production') {
                        logging.warn(message.text);
                    }
                    let msg = Object.assign({
                        from: config.getAuthEmailFromAddress(),
                        subject: 'Signin',
                        forceTextContent: true
                    }, message);

                    return ghostMailer.send(msg);
                }
            },
            getSubject(type) {
                const siteTitle = settingsCache.get('title');
                switch (type) {
                case 'subscribe':
                    return `📫 Confirm your subscription to ${siteTitle}`;
                case 'signup':
                    return `🙌 Complete your sign up to ${siteTitle}!`;
                case 'updateEmail':
                    return `📫 Confirm your email update for ${siteTitle}!`;
                case 'signin':
                default:
                    return `🔑 Secure sign in link for ${siteTitle}`;
                }
            },
            getText(url, type, email) {
                const siteTitle = settingsCache.get('title');
                switch (type) {
                case 'subscribe':
                    return `
                        Hey there,

                        You're one tap away from subscribing to ${siteTitle} — please confirm your email address with this link:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        All the best!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can simply delete this message. You will not be subscribed.
                        `;
                case 'signup':
                    return `
                        Hey there!

                        Thanks for signing up for ${siteTitle} — use this link to complete the sign up process and be automatically signed in:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        See you soon!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can simply delete this message. You will not be signed up, and no account will be created for you.
                        `;
                case 'updateEmail':
                    return `
                            Hey there,

                            Please confirm your email address with this link:

                            ${url}

                            For your security, the link will expire in 24 hours time.

                            ---

                            Sent to ${email}
                            If you did not make this request, you can simply delete this message. This email address will not be used.
                            `;
                case 'signin':
                default:
                    return `
                        Hey there,

                        Welcome back! Use this link to securely sign in to your ${siteTitle} account:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        See you soon!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can safely ignore this email.
                        `;
                }
            },
            getHTML(url, type, email) {
                const siteTitle = settingsCache.get('title');
                const siteUrl = urlUtils.urlFor('home', true);
                const domain = urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
                const siteDomain = (domain && domain[1]);
                const accentColor = settingsCache.get('accent_color');
                switch (type) {
                case 'subscribe':
                    return subscribeEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'signup':
                    return signupEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'updateEmail':
                    return updateEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'signin':
                default:
                    return signinEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                }
            }
        },
        paymentConfig: {
            stripe: config.getStripePaymentConfig()
        },
        models: {
            /**
             * Settings do not have their own models, so we wrap the webhook in a "fake" model
             */
            StripeWebhook: {
                async upsert(data, options) {
                    const settings = [{
                        key: 'members_stripe_webhook_id',
                        value: data.webhook_id
                    }, {
                        key: 'members_stripe_webhook_secret',
                        value: data.secret
                    }];
                    await models.Settings.edit(settings, options);
                }
            },
            StripeCustomer: models.MemberStripeCustomer,
            StripeCustomerSubscription: models.StripeCustomerSubscription,
            Member: models.Member,
            MemberSubscribeEvent: models.MemberSubscribeEvent,
            MemberPaidSubscriptionEvent: models.MemberPaidSubscriptionEvent,
            MemberLoginEvent: models.MemberLoginEvent,
            MemberEmailChangeEvent: models.MemberEmailChangeEvent,
            MemberPaymentEvent: models.MemberPaymentEvent,
            MemberStatusEvent: models.MemberStatusEvent,
            StripeProduct: models.StripeProduct,
            StripePrice: models.StripePrice,
            Product: models.Product
        },
        logger: logging
    });

    return membersApiInstance;
}
<commit_msg>Passed Settings model to @tryghost/members-api<commit_after>const settingsCache = require('../settings/cache');
const MembersApi = require('@tryghost/members-api');
const logging = require('../../../shared/logging');
const mail = require('../mail');
const models = require('../../models');
const signinEmail = require('./emails/signin');
const signupEmail = require('./emails/signup');
const subscribeEmail = require('./emails/subscribe');
const updateEmail = require('./emails/updateEmail');
const SingleUseTokenProvider = require('./SingleUseTokenProvider');
const urlUtils = require('../../../shared/url-utils');

const MAGIC_LINK_TOKEN_VALIDITY = 24 * 60 * 60 * 1000;

const ghostMailer = new mail.GhostMailer();

module.exports = createApiInstance;

function createApiInstance(config) {
    const membersApiInstance = MembersApi({
        tokenConfig: config.getTokenConfig(),
        auth: {
            getSigninURL: config.getSigninURL.bind(config),
            allowSelfSignup: config.getAllowSelfSignup(),
            tokenProvider: new SingleUseTokenProvider(models.SingleUseToken, MAGIC_LINK_TOKEN_VALIDITY)
        },
        mail: {
            transporter: {
                sendMail(message) {
                    if (process.env.NODE_ENV !== 'production') {
                        logging.warn(message.text);
                    }
                    let msg = Object.assign({
                        from: config.getAuthEmailFromAddress(),
                        subject: 'Signin',
                        forceTextContent: true
                    }, message);

                    return ghostMailer.send(msg);
                }
            },
            getSubject(type) {
                const siteTitle = settingsCache.get('title');
                switch (type) {
                case 'subscribe':
                    return `📫 Confirm your subscription to ${siteTitle}`;
                case 'signup':
                    return `🙌 Complete your sign up to ${siteTitle}!`;
                case 'updateEmail':
                    return `📫 Confirm your email update for ${siteTitle}!`;
                case 'signin':
                default:
                    return `🔑 Secure sign in link for ${siteTitle}`;
                }
            },
            getText(url, type, email) {
                const siteTitle = settingsCache.get('title');
                switch (type) {
                case 'subscribe':
                    return `
                        Hey there,

                        You're one tap away from subscribing to ${siteTitle} — please confirm your email address with this link:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        All the best!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can simply delete this message. You will not be subscribed.
                        `;
                case 'signup':
                    return `
                        Hey there!

                        Thanks for signing up for ${siteTitle} — use this link to complete the sign up process and be automatically signed in:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        See you soon!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can simply delete this message. You will not be signed up, and no account will be created for you.
                        `;
                case 'updateEmail':
                    return `
                            Hey there,

                            Please confirm your email address with this link:

                            ${url}

                            For your security, the link will expire in 24 hours time.

                            ---

                            Sent to ${email}
                            If you did not make this request, you can simply delete this message. This email address will not be used.
                            `;
                case 'signin':
                default:
                    return `
                        Hey there,

                        Welcome back! Use this link to securely sign in to your ${siteTitle} account:

                        ${url}

                        For your security, the link will expire in 24 hours time.

                        See you soon!
                        The team at ${siteTitle}

                        ---

                        Sent to ${email}
                        If you did not make this request, you can safely ignore this email.
                        `;
                }
            },
            getHTML(url, type, email) {
                const siteTitle = settingsCache.get('title');
                const siteUrl = urlUtils.urlFor('home', true);
                const domain = urlUtils.urlFor('home', true).match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i'));
                const siteDomain = (domain && domain[1]);
                const accentColor = settingsCache.get('accent_color');
                switch (type) {
                case 'subscribe':
                    return subscribeEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'signup':
                    return signupEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'updateEmail':
                    return updateEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                case 'signin':
                default:
                    return signinEmail({url, email, siteTitle, accentColor, siteDomain, siteUrl});
                }
            }
        },
        paymentConfig: {
            stripe: config.getStripePaymentConfig()
        },
        models: {
            /**
             * Settings do not have their own models, so we wrap the webhook in a "fake" model
             */
            StripeWebhook: {
                async upsert(data, options) {
                    const settings = [{
                        key: 'members_stripe_webhook_id',
                        value: data.webhook_id
                    }, {
                        key: 'members_stripe_webhook_secret',
                        value: data.secret
                    }];
                    await models.Settings.edit(settings, options);
                }
            },
            StripeCustomer: models.MemberStripeCustomer,
            StripeCustomerSubscription: models.StripeCustomerSubscription,
            Member: models.Member,
            MemberSubscribeEvent: models.MemberSubscribeEvent,
            MemberPaidSubscriptionEvent: models.MemberPaidSubscriptionEvent,
            MemberLoginEvent: models.MemberLoginEvent,
            MemberEmailChangeEvent: models.MemberEmailChangeEvent,
            MemberPaymentEvent: models.MemberPaymentEvent,
            MemberStatusEvent: models.MemberStatusEvent,
            StripeProduct: models.StripeProduct,
            StripePrice: models.StripePrice,
            Product: models.Product,
            Settings: models.Settings
        },
        logger: logging
    });

    return membersApiInstance;
}
<|endoftext|>


<commit_before>/**
 * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You
 * may not use this file except in compliance with the License. A copy of
 * the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 * ANY KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */

module.exports = function () {

  this.World = require("./world.js").World;

  /* Global error code steps */

  this.Then(/^the error code should be "([^"]*)"$/, function(code, callback) {
    this.assert.ok(this.error, 'Response does not contain an error');
    this.assert.equal(this.error.code, code);
    callback();
  });

  this.Then(/^the error message should be:$/, function(message, callback) {
    this.assert.ok(this.error, 'Response does not contain an error');
    this.assert.equal(this.error.message, message);
    callback();
  });

  this.Then(/^the status code should be (\d+)$/, function(status, callback) {
    this.assert.equal(this.response.httpResponse.statusCode, parseInt(status));
    callback();
  });

  this.Then(/^I should get the error:$/, function(table, callback) {
    var err = table.hashes()[0]
    this.assert.equal(this.error.code, err.code);
    this.assert.equal(this.error.message, err.message);
    callback();
  });
};
<commit_msg>Add support for different error message matchers<commit_after>/**
 * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You
 * may not use this file except in compliance with the License. A copy of
 * the License is located at
 *
 *     http://aws.amazon.com/apache2.0/
 *
 * or in the "license" file accompanying this file. This file is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
 * ANY KIND, either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 */

module.exports = function () {

  this.World = require("./world.js").World;

  /* Global error code steps */

  this.Then(/^the error code should be "([^"]*)"$/, function(code, callback) {
    this.assert.ok(this.error, 'Response does not contain an error');
    this.assert.equal(this.error.code, code);
    callback();
  });

  this.Then(/^the error message should (be|equal|match|contain):$/, function(matcher, message, callback) {
    if (matcher === 'be') matcher = 'equal';
    if (matcher === 'contain') matcher = 'match';
    this.assert.ok(this.error, 'Response does not contain an error');
    this.assert[matcher](this.error.message, message);
    callback();
  });

  this.Then(/^the status code should be (\d+)$/, function(status, callback) {
    this.assert.equal(this.response.httpResponse.statusCode, parseInt(status));
    callback();
  });

  this.Then(/^I should get the error:$/, function(table, callback) {
    var err = table.hashes()[0]
    this.assert.equal(this.error.code, err.code);
    this.assert.equal(this.error.message, err.message);
    callback();
  });
};
<|endoftext|>


<commit_before>/*globals $, requestAnimationFrame*/
$(function() {
  'use strict';

  var selection = [];
  var offsets = [];
  var stage = [];

  var PI2 = 2 * Math.PI;
  var DEG_TO_RAD = Math.PI / 180,
      RAD_TO_DEG = 180 / Math.PI;

  var mouse = {
    x: null,
    y: null
  };

  var config = {
    handler: {
      radius: 10
    }
  };

  var backgroundColor = '#888';

  var snapping = true;

  function Handler( x, y, radius ) {
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.radius = radius || 0.0;
  }

  Handler.prototype.draw = function( ctx ) {
    // Draw outer circle.
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius, 0, PI2 );
    ctx.fillStyle = 'white';
    ctx.fill();

    // Draw inner circle.
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius - 3, 0, PI2 );
    ctx.fillStyle = '#222'; //'rgba(255, 0, 0, 1.0)';
    ctx.fill();
  };

  Handler.prototype.contains = function( x, y ) {
    var dx = x - this.x,
        dy = y - this.y;

    return Math.sqrt( dx * dx + dy * dy ) < this.radius;
  };

  function Sphere( x, y, radius ) {
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.radius = radius || 0.0;

    this.handlers = [
      // Start angle.
      new Handler(
        this.x + Math.cos( 315 * DEG_TO_RAD ) * this.radius,
        this.y + Math.sin( 315 * DEG_TO_RAD ) * this.radius,
        config.handler.radius
      ),
      // End angle.
      new Handler(
        this.x + Math.cos( 90 * DEG_TO_RAD ) * this.radius,
        this.y + Math.sin( 90 * DEG_TO_RAD ) * this.radius,
        config.handler.radius
      ),
      new Handler(
        this.x + 0.5 * this.radius,
        this.y + 0.5 * this.radius,
        config.handler.radius
      )
    ];
  }

  Sphere.prototype.draw = function( ctx ) {
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius, 0, PI2 );
    ctx.fillStyle = 'rgba( 250, 120, 80, 1.0)';
    ctx.fill();

    var h0 = this.handlers[0],
        h1 = this.handlers[1],
        h2 = this.handlers[2];

    if ( snapping ) {
      var cp = closestPointOnCircle( h0.x, h0.y, this.x, this.y, this.radius );
      h0.x = cp.x;
      h0.y = cp.y;

      cp = closestPointOnCircle( h1.x, h1.y, this.x, this.y, this.radius );
      h1.x = cp.x;
      h1.y = cp.y;

      var perp = perpendicular( h0.x, h0.y, h1.x, h1.y );

      ctx.beginPath();
      ctx.moveTo( perp.x0, perp.y0 );
      ctx.lineTo( perp.x1, perp.y1 );

      ctx.lineWidth = 2;
      ctx.strokeStyle = 'white';
      ctx.stroke();

      cp = closestPointOnLine( h2.x, h2.y, perp.x0, perp.y0, perp.x1, perp.y1 );
      h2.x = cp.x;
      h2.y = cp.y;
    }

    var circle = circleFromPoints(
      h0.x, h0.y,
      h1.x, h1.y,
      h2.x, h2.y
    );

    if ( circle ) {
      ctx.beginPath();
      ctx.arc( circle.x, circle.y, circle.radius, 0, PI2 );
      ctx.lineWidth = 2;
      ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
      ctx.stroke();

      ctx.beginPath();
      var startAngle = angleFrom( this.x, this.y, h0.x, h0.y ),
          endAngle   = angleFrom( this.x, this.y, h1.x, h1.y );
      ctx.arc( this.x, this.y, this.radius, startAngle, endAngle );

      startAngle = angleFrom( circle.x, circle.y, h0.x, h0.y );
      endAngle   = angleFrom( circle.x, circle.y, h1.x, h1.y );
      ctx.arc( circle.x, circle.y, circle.radius, endAngle, startAngle, true );

      ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
      ctx.fill();

      ctx.font = '12px Monaco';
      var startText = 'start: ' + ( startAngle * RAD_TO_DEG ).toFixed(1),
          endText   = 'end: '   + ( endAngle   * RAD_TO_DEG ).toFixed(1);

      ctx.fillStyle = 'white';
      ctx.fillText( startText, h0.x + 20, h0.y );
      ctx.fillStyle = 'rgba(0, 255, 0, 1.0)';
      ctx.fillText( endText,   h1.x + 20, h1.y );
    }
  };

  Sphere.prototype.contains = function() {
    return false;
  };

  function tick() {
    draw( context );
    requestAnimationFrame( tick );
  }

  function draw( ctx ) {
    ctx.fillStyle = backgroundColor;
    ctx.fillRect( 0, 0, canvas.width, canvas.height );

    stage.forEach(function( object ) {
      object.draw( ctx );
    });
  }

  function mousePosition( event ) {
    return {
      x: event.pageX - canvas.offsetLeft,
      y: event.pageY - canvas.offsetTop
    };
  }

  function onMouseDown( event ) {
    mouse = mousePosition( event );

    var object;
    for ( var i = 0, il = stage.length; i < il; i++ ) {
      object = stage[i];
      if ( object.contains( mouse.x, mouse.y ) ) {
        selection.push( object );
        offsets.push({
          x: object.x,
          y: object.y
        });
      }
    }
  }

  function onMouseMove( event ) {
    var point = mousePosition( event );

    var dx = point.x - mouse.x,
        dy = point.y - mouse.y;

    selection.forEach(function( object, index ) {
      object.x = offsets[ index ].x + dx;
      object.y = offsets[ index ].y + dy;
    });
  }

  function onMouseUp() {
    selection = [];
    offsets = [];
  }

  function circleFromPoints( x0, y0, x1, y1, x2, y2 ) {
    /**
     * Given the equation of a circle located at (xc, yc) and with a radius r:
     *
     * (x - xc)^2 + (y - yc)^2 = r^2
     *
     * We substitute the coords of the three points, and get three equations
     * for three unknowns:
     *
     * (x0 - xc)^2 + (y0 - yc)^2 = r^2
     * (x1 - xc)^2 + (y1 - yc)^2 = r^2
     * (x2 - xc)^2 + (y2 - yc)^2 = r^2
     *
     * By subtracting the first from the second and third equations and
     * constructing a system of linear equations. With  some algebraic
     * manipulation, we eventually get:
     *
     * xc = [(x1^2 - y0^2) + (y1^2 - y0^2)] - (y1 - y0) /(y2 - y0) * [(x2^2 - x0^2) + (y2^2 - y0^2)]
     *      ----------------------------------------------------------------------------------------
     *                           2[x1 - x0 - (y1 - y0) / (y2 - y0) * (x2 - x0)]
     *
     * yc = [(x1^2 - y0^2) + (y1^2 - y0^2)] - (x1 - x0) /(x2 - x0) * [(x2^2 - x0^2) + (y2^2 - y0^2)]
     *      ----------------------------------------------------------------------------------------
     *                           2[y1 - y0 - (x1 - x0) / (x2 - x0) * (y2 - y0)]
     *
     * Plug xc and yc back in to solve for the radius.
     */

    var dx10 = x1 - x0,
        dy10 = y1 - y0,
        dx20 = x2 - x0,
        dy20 = y2 - y0;

    if ( !dx20 || !dy20 ) {
      if ( !dx10 || !dy10 ) {
        // Don't do anything if degenerate.
        return;
      }

      // Swap vars so it can work.
      var temp = x1;
      x1 = x2;
      x2 = temp;

      temp = y1;
      y1 = y2;
      y2 = temp;

      dx10 = x1 - x0;
      dy10 = y1 - y0;
      dx20 = x2 - x0;
      dy20 = y2 - y0;
    }

    // Differences of squares. e.g. x1^2 - x0^2.
    var dsqx10 = dx10 * ( x1 + x0 ),
        dsqy10 = dy10 * ( y1 + y0 ),
        dsqx20 = dx20 * ( x2 + x0 ),
        dsqy20 = dy20 * ( y2 + y0 );

    // Ratios, e.g. rdx10dx20 = (x1 - x0) / (x2 - x0).
    var rdx10dx20 = dx10 / dx20,
        rdy10dy20 = dy10 / dy20;

    // Denominators.
    var xden = 2 * ( dx10 - rdy10dy20 * dx20 ),
        yden = 2 * ( dy10 - rdx10dx20 * dy20 );

    if ( !xden || !yden ) {
      return;
    }

    var x = ( ( dsqx10 + dsqy10 ) - rdy10dy20 * ( dsqx20 + dsqy20 ) ) / xden,
        y = ( ( dsqx10 + dsqy10 ) - rdx10dx20 * ( dsqx20 + dsqy20 ) ) / yden;

    // Now solve for the radius.
    var r = Math.sqrt( ( x0 - x ) * ( x0 - x ) + ( y0 - y ) * ( y0 - y ) );

    return {
      x: x,
      y: y,
      radius: r
    };
  }

  /**
   * Determines the angle from x0, y0 to x1, y1.
   * Returns the angle in radians.
   */
  function angleFrom( x0, y0, x1, y1 ) {
    return Math.atan2( y1 - y0, x1 - x0 );
  }

  /**
   * Given the point (x, y), find the closest point on the line given by
   * (x0, y0) -> (x1, y1).
   */
  function closestPointOnLine( x, y, x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    // Check for line degeneracy.
    if ( !dx && !dy ) {
      return;
    }

    var lengthSquared = dx * dx + dy * dy;

    // Calculate parameter of closest point on line segment.
    var t = ( ( x - x0 ) * ( x1 - x0 ) + ( y - y0 ) * ( y1 - y0 ) ) / lengthSquared;

    return {
      x: x0 + t * ( x1 - x0 ),
      y: y0 + t * ( y1 - y0 )
    };
  }

  /**
   * Given the point (x, y), find the closest point on the circle defined by a
   * center point (cx, cy) and radius.
   */
  function closestPointOnCircle( x, y, cx, cy, radius ) {
    var angle = angleFrom( cx, cy, x, y );
    return {
      x: cx + Math.cos( angle ) * radius,
      y: cy + Math.sin( angle ) * radius
    };
  }

  /**
   * Returns the perpendicular line segment to the line segment given by
   * (x0, y0) and (x1, y1), The two line segments form the diagonals of a
   * square.
   *
   * The (x0, y0) of the resulting line segment will be counterclockwise from
   * the (x0, y0) of the original line segment.
   */
  function perpendicular( x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    // Midpoint.
    var mx = x0 + 0.5 * dx,
        my = y0 + 0.5 * dy;

    return {
      x0: mx - 0.5 * dy,
      y0: my + 0.5 * dx,
      x1: mx + 0.5 * dy,
      y1: my - 0.5 * dx
    };
  }

  var $canvas = $( '#canvas' );
  var canvas = $canvas[0];
  var context = canvas.getContext( '2d' );

  $canvas.on({
    mousedown: onMouseDown,
    mousemove: onMouseMove,
    mouseup: onMouseUp
  });

  $canvas.css( 'position', 'absolute' );
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;

  stage.push(
    new Sphere( 300, 0.5 * canvas.height, 100 ),
    new Sphere( 700, 0.5 * canvas.height, 100 ),
    new Sphere( 1100, 0.5 * canvas.height, 100 ),
    new Sphere( 1500, 0.5 * canvas.height, 100 )
  );

  var sph0 = stage[0],
      sph1 = stage[1],
      sph2 = stage[2];

  // First sphere.
  sph0.handlers[2].x = sph0.x - 0.25 * sph0.radius;
  sph0.handlers[2].y = sph0.y - 0.25 * sph0.radius;

  // Second sphere.
  sph1.handlers[1].x = sph1.x + Math.cos( 225 * DEG_TO_RAD ) * sph1.radius;
  sph1.handlers[1].y = sph1.y + Math.sin( 225 * DEG_TO_RAD ) * sph1.radius;
  sph1.handlers[2].x = sph1.x;
  sph1.handlers[2].y = sph1.y - 1.25 * sph1.radius;

  // Third sphere.
  sph2.handlers[1].x = sph2.x + Math.cos( 225 * DEG_TO_RAD ) * sph2.radius;
  sph2.handlers[1].y = sph2.y + Math.sin( 225 * DEG_TO_RAD ) * sph2.radius;
  sph2.handlers[2].x = sph2.x;
  sph2.handlers[2].y = sph2.y;

  stage.forEach(function( object ) {
    if ( object instanceof Sphere ) {
      stage.push.apply( stage, object.handlers );
    }
  });

  tick();
});
<commit_msg>Tinker around with Sphere code, not working completely.<commit_after>/*globals $, requestAnimationFrame*/
$(function() {
  'use strict';

  var selection = [];
  var offsets = [];
  var stage = [];

  var PI2 = 2 * Math.PI;
  var DEG_TO_RAD = Math.PI / 180,
      RAD_TO_DEG = 180 / Math.PI;

  var mouse = {
    x: null,
    y: null
  };

  var config = {
    handler: {
      radius: 10
    }
  };

  var backgroundColor = '#888';

  var snapping = true;

  function Handler( x, y, radius ) {
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.radius = radius || 0.0;
  }

  Handler.prototype.draw = function( ctx ) {
    // Draw outer circle.
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius, 0, PI2 );
    ctx.fillStyle = 'white';
    ctx.fill();

    // Draw inner circle.
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius - 3, 0, PI2 );
    ctx.fillStyle = '#222'; //'rgba(255, 0, 0, 1.0)';
    ctx.fill();
  };

  Handler.prototype.contains = function( x, y ) {
    var dx = x - this.x,
        dy = y - this.y;

    return Math.sqrt( dx * dx + dy * dy ) < this.radius;
  };

  function Sphere( x, y, radius ) {
    this.x = x || 0.0;
    this.y = y || 0.0;
    this.radius = radius || 0.0;

    this.handlers = [
      // Start angle.
      new Handler(
        this.x + Math.cos( 315 * DEG_TO_RAD ) * this.radius,
        this.y + Math.sin( 315 * DEG_TO_RAD ) * this.radius,
        config.handler.radius
      ),
      // End angle.
      new Handler(
        this.x + Math.cos( 90 * DEG_TO_RAD ) * this.radius,
        this.y + Math.sin( 90 * DEG_TO_RAD ) * this.radius,
        config.handler.radius
      ),
      new Handler(
        this.x + 0.5 * this.radius,
        this.y + 0.5 * this.radius,
        config.handler.radius
      )
    ];
  }

  Sphere.prototype.draw = function( ctx ) {
    ctx.beginPath();
    ctx.arc( this.x, this.y, this.radius, 0, PI2 );
    ctx.fillStyle = 'rgba( 250, 120, 80, 1.0)';
    ctx.fill();

    var h0 = this.handlers[0],
        h1 = this.handlers[1],
        h2 = this.handlers[2];

    if ( snapping ) {
      var cp = closestPointOnCircle( h0.x, h0.y, this.x, this.y, this.radius );
      h0.x = cp.x;
      h0.y = cp.y;

      cp = closestPointOnCircle( h1.x, h1.y, this.x, this.y, this.radius );
      h1.x = cp.x;
      h1.y = cp.y;

      var perp = perpendicular( h0.x, h0.y, h1.x, h1.y );

      ctx.beginPath();
      ctx.moveTo( perp.x0, perp.y0 );
      ctx.lineTo( perp.x1, perp.y1 );

      ctx.lineWidth = 2;
      ctx.strokeStyle = 'white';
      ctx.stroke();

      cp = closestPointOnLine( h2.x, h2.y, perp.x0, perp.y0, perp.x1, perp.y1 );
      h2.x = cp.x;
      h2.y = cp.y;
    }

    var circle = circleFromPoints(
      h0.x, h0.y,
      h1.x, h1.y,
      h2.x, h2.y
    );

    if ( circle ) {
      ctx.beginPath();
      ctx.arc( circle.x, circle.y, circle.radius, 0, PI2 );
      ctx.lineWidth = 2;
      ctx.strokeStyle = 'rgba(255, 0, 0, 0.5)';
      ctx.stroke();

      ctx.beginPath();
      var startAngle = angleFrom( this.x, this.y, h0.x, h0.y ),
          endAngle   = angleFrom( this.x, this.y, h1.x, h1.y );
      ctx.arc( this.x, this.y, this.radius, startAngle, endAngle );

      startAngle = angleFrom( circle.x, circle.y, h0.x, h0.y );
      endAngle   = angleFrom( circle.x, circle.y, h1.x, h1.y );
      var midAngle = angleFrom( circle.x, circle.y, h2.x, h2.y );
      ctx.arc( circle.x, circle.y, circle.radius, endAngle, startAngle, startAngle < midAngle );

      ctx.fillStyle = 'rgba(0, 0, 0, 0.3)';
      ctx.fill();

      ctx.font = '12px Monaco';
      var startText = 'start: ' + ( startAngle * RAD_TO_DEG ).toFixed(1) + ', ' + ( startAngle < endAngle ),
          endText   = 'end: '   + ( endAngle   * RAD_TO_DEG ).toFixed(1);

      var midText = 'mid: ' + ( midAngle * RAD_TO_DEG ).toFixed(1);

      ctx.fillStyle = 'white';
      ctx.fillText( startText, h0.x + 20, h0.y );
      ctx.fillStyle = 'rgba(0, 255, 0, 1.0)';
      ctx.fillText( endText,   h1.x + 20, h1.y );
      ctx.fillStyle = 'rgba(255, 255, 0, 1.0)';
      ctx.fillText( midText, h2.x + 20, h2.y );
    }
  };

  Sphere.prototype.contains = function() {
    return false;
  };

  function tick() {
    draw( context );
    requestAnimationFrame( tick );
  }

  function draw( ctx ) {
    ctx.fillStyle = backgroundColor;
    ctx.fillRect( 0, 0, canvas.width, canvas.height );

    stage.forEach(function( object ) {
      object.draw( ctx );
    });
  }

  function mousePosition( event ) {
    return {
      x: event.pageX - canvas.offsetLeft,
      y: event.pageY - canvas.offsetTop
    };
  }

  function onMouseDown( event ) {
    mouse = mousePosition( event );

    var object;
    for ( var i = 0, il = stage.length; i < il; i++ ) {
      object = stage[i];
      if ( object.contains( mouse.x, mouse.y ) ) {
        selection.push( object );
        offsets.push({
          x: object.x,
          y: object.y
        });
      }
    }
  }

  function onMouseMove( event ) {
    var point = mousePosition( event );

    var dx = point.x - mouse.x,
        dy = point.y - mouse.y;

    selection.forEach(function( object, index ) {
      object.x = offsets[ index ].x + dx;
      object.y = offsets[ index ].y + dy;
    });
  }

  function onMouseUp() {
    selection = [];
    offsets = [];
  }

  function circleFromPoints( x0, y0, x1, y1, x2, y2 ) {
    /**
     * Given the equation of a circle located at (xc, yc) and with a radius r:
     *
     * (x - xc)^2 + (y - yc)^2 = r^2
     *
     * We substitute the coords of the three points, and get three equations
     * for three unknowns:
     *
     * (x0 - xc)^2 + (y0 - yc)^2 = r^2
     * (x1 - xc)^2 + (y1 - yc)^2 = r^2
     * (x2 - xc)^2 + (y2 - yc)^2 = r^2
     *
     * By subtracting the first from the second and third equations and
     * constructing a system of linear equations. With  some algebraic
     * manipulation, we eventually get:
     *
     * xc = [(x1^2 - y0^2) + (y1^2 - y0^2)] - (y1 - y0) /(y2 - y0) * [(x2^2 - x0^2) + (y2^2 - y0^2)]
     *      ----------------------------------------------------------------------------------------
     *                           2[x1 - x0 - (y1 - y0) / (y2 - y0) * (x2 - x0)]
     *
     * yc = [(x1^2 - y0^2) + (y1^2 - y0^2)] - (x1 - x0) /(x2 - x0) * [(x2^2 - x0^2) + (y2^2 - y0^2)]
     *      ----------------------------------------------------------------------------------------
     *                           2[y1 - y0 - (x1 - x0) / (x2 - x0) * (y2 - y0)]
     *
     * Plug xc and yc back in to solve for the radius.
     */

    var dx10 = x1 - x0,
        dy10 = y1 - y0,
        dx20 = x2 - x0,
        dy20 = y2 - y0;

    if ( !dx20 || !dy20 ) {
      if ( !dx10 || !dy10 ) {
        // Don't do anything if degenerate.
        return;
      }

      // Swap vars so it can work.
      var temp = x1;
      x1 = x2;
      x2 = temp;

      temp = y1;
      y1 = y2;
      y2 = temp;

      dx10 = x1 - x0;
      dy10 = y1 - y0;
      dx20 = x2 - x0;
      dy20 = y2 - y0;
    }

    // Differences of squares. e.g. x1^2 - x0^2.
    var dsqx10 = dx10 * ( x1 + x0 ),
        dsqy10 = dy10 * ( y1 + y0 ),
        dsqx20 = dx20 * ( x2 + x0 ),
        dsqy20 = dy20 * ( y2 + y0 );

    // Ratios, e.g. rdx10dx20 = (x1 - x0) / (x2 - x0).
    var rdx10dx20 = dx10 / dx20,
        rdy10dy20 = dy10 / dy20;

    // Denominators.
    var xden = 2 * ( dx10 - rdy10dy20 * dx20 ),
        yden = 2 * ( dy10 - rdx10dx20 * dy20 );

    if ( !xden || !yden ) {
      return;
    }

    var x = ( ( dsqx10 + dsqy10 ) - rdy10dy20 * ( dsqx20 + dsqy20 ) ) / xden,
        y = ( ( dsqx10 + dsqy10 ) - rdx10dx20 * ( dsqx20 + dsqy20 ) ) / yden;

    // Now solve for the radius.
    var r = Math.sqrt( ( x0 - x ) * ( x0 - x ) + ( y0 - y ) * ( y0 - y ) );

    return {
      x: x,
      y: y,
      radius: r
    };
  }

  /**
   * Determines the angle from x0, y0 to x1, y1.
   * Returns the angle in radians.
   */
  function angleFrom( x0, y0, x1, y1 ) {
    return Math.atan2( y1 - y0, x1 - x0 );
  }

  /**
   * Given the point (x, y), find the closest point on the line given by
   * (x0, y0) -> (x1, y1).
   */
  function closestPointOnLine( x, y, x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    // Check for line degeneracy.
    if ( !dx && !dy ) {
      return;
    }

    var lengthSquared = dx * dx + dy * dy;

    // Calculate parameter of closest point on line segment.
    var t = ( ( x - x0 ) * ( x1 - x0 ) + ( y - y0 ) * ( y1 - y0 ) ) / lengthSquared;

    return {
      x: x0 + t * ( x1 - x0 ),
      y: y0 + t * ( y1 - y0 )
    };
  }

  /**
   * Given the point (x, y), find the closest point on the circle defined by a
   * center point (cx, cy) and radius.
   */
  function closestPointOnCircle( x, y, cx, cy, radius ) {
    var angle = angleFrom( cx, cy, x, y );
    return {
      x: cx + Math.cos( angle ) * radius,
      y: cy + Math.sin( angle ) * radius
    };
  }

  /**
   * Returns the perpendicular line segment to the line segment given by
   * (x0, y0) and (x1, y1), The two line segments form the diagonals of a
   * square.
   *
   * The (x0, y0) of the resulting line segment will be counterclockwise from
   * the (x0, y0) of the original line segment.
   */
  function perpendicular( x0, y0, x1, y1 ) {
    var dx = x1 - x0,
        dy = y1 - y0;

    // Midpoint.
    var mx = x0 + 0.5 * dx,
        my = y0 + 0.5 * dy;

    return {
      x0: mx - 0.5 * dy,
      y0: my + 0.5 * dx,
      x1: mx + 0.5 * dy,
      y1: my - 0.5 * dx
    };
  }

  var $canvas = $( '#canvas' );
  var canvas = $canvas[0];
  var context = canvas.getContext( '2d' );

  $canvas.on({
    mousedown: onMouseDown,
    mousemove: onMouseMove,
    mouseup: onMouseUp
  });

  $canvas.css( 'position', 'absolute' );
  canvas.width = 1920; // window.innerWidth;
  canvas.height = window.innerHeight;

  stage.push(
    new Sphere( 300, 0.5 * canvas.height, 100 ),
    new Sphere( 700, 0.5 * canvas.height, 100 ),
    new Sphere( 1100, 0.5 * canvas.height, 100 ),
    new Sphere( 1500, 0.5 * canvas.height, 100 )
  );

  var sph0 = stage[0],
      sph1 = stage[1],
      sph2 = stage[2];

  // First sphere.
  sph0.handlers[2].x = sph0.x - 0.25 * sph0.radius;
  sph0.handlers[2].y = sph0.y - 0.25 * sph0.radius;

  // Second sphere.
  sph1.handlers[1].x = sph1.x + Math.cos( 225 * DEG_TO_RAD ) * sph1.radius;
  sph1.handlers[1].y = sph1.y + Math.sin( 225 * DEG_TO_RAD ) * sph1.radius;
  sph1.handlers[2].x = sph1.x;
  sph1.handlers[2].y = sph1.y - 1.25 * sph1.radius;

  // Third sphere.
  sph2.handlers[1].x = sph2.x + Math.cos( 225 * DEG_TO_RAD ) * sph2.radius;
  sph2.handlers[1].y = sph2.y + Math.sin( 225 * DEG_TO_RAD ) * sph2.radius;
  sph2.handlers[2].x = sph2.x;
  sph2.handlers[2].y = sph2.y;

  stage.forEach(function( object ) {
    if ( object instanceof Sphere ) {
      stage.push.apply( stage, object.handlers );
    }
  });

  tick();
});
<|endoftext|>


<commit_before>Package.describe({
	name: "meteortypescript:compiler",
	summary: "TypeScript is a staticaly typed superset of JavaScript",
	version: "1.0.2",
	git: "https://github.com/meteor-typescript/meteor-typescript-compiler.git",
	version: "2.0.0"
});

Package._transitional_registerBuildPlugin({
	name: "compileTypescript",
	use: ['meteor'],
	sources: [
		'plugin/compile-tsc.js'
	],
	npmDependencies: {
		"typescript": "1.4.1",
		"node-persist": "0.0.2",
		"temp": "0.8.1",
		"glob": "4.3.5",
		"rimraf": "2.2.8"
	}
});
<commit_msg>fix version<commit_after>Package.describe({
	name: "meteortypescript:compiler",
	summary: "TypeScript is a staticaly typed superset of JavaScript",
	git: "https://github.com/meteor-typescript/meteor-typescript-compiler.git",
	version: "2.0.0"
});

Package._transitional_registerBuildPlugin({
	name: "compileTypescript",
	use: ['meteor'],
	sources: [
		'plugin/compile-tsc.js'
	],
	npmDependencies: {
		"typescript": "1.4.1",
		"node-persist": "0.0.2",
		"temp": "0.8.1",
		"glob": "4.3.5",
		"rimraf": "2.2.8"
	}
});
<|endoftext|>


<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Custom-styled native input/buttons
//>>label: Buttons: Input or button-based
//>>group: Forms
//>>css.structure: ../css/structure/jquery.mobile.forms.button.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../../jquery.mobile.widget" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$.widget( "mobile.button", {

	initSelector: "input[type='button'], input[type='submit'], input[type='reset']",

	options: {
		theme: null,
		icon: null,
		iconpos: "left",
		iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */
		corners: true,
		shadow: true,
		inline: null,
		mini: null,
		wrapperClass: null,
		enhanced: false
	},

	_create: function() {

		if ( this.element.is( ":disabled" ) ) {
			this.options.disabled = true;
		}

		if ( !this.options.enhanced ) {
			this._enhance();
		}

		$.extend( this, {
			wrapper: this.element.parent()
		});

		this._on( {
			focus: function() {
				this.widget().addClass( $.mobile.focusClass );
			},

			blur: function() {
				this.widget().removeClass( $.mobile.focusClass );
			}
		});

		this.refresh( true );
	},

	_enhance: function() {
		this.element.wrap( this._button() );
	},

	_button: function() {
		return $("<div class='ui-btn ui-input-btn" +
			( this.options.wrapperClass ? " " + this.options.wrapperClass : "" ) +
			( this.options.theme ? " ui-btn-" + this.options.theme : "" ) +
			( this.options.corners ? " ui-corner-all" : "" ) +
			( this.options.shadow ? " ui-shadow" : "" ) +
			( this.options.inline ? " ui-btn-inline" : "" ) +
			( this.options.mini ? " ui-mini" : "" ) +
			( this.options.disabled ? " ui-disabled" : "" ) +
			( ( this.options.iconpos && this.options.icon ) ? " ui-btn-icon-" + this.options.iconpos : ( this.options.icon ? " ui-btn-icon-left" : "" ) ) +
			( this.options.icon ? " ui-icon-" + this.options.icon : "" ) +
			"' >" + this.element.val() + "
");
	},

	widget: function() {
		return this.wrapper;
	},

	_destroy: function() {
			this.element.insertBefore( this.button );
			this.button.remove();
	},

	_setOptions: function( options ) {
		if ( options.theme !== undefined ) {
			this.widget().removeClass( this.options.theme ).addClass( "ui-btn-" + options.theme );
		}
		if ( options.corners !== undefined ) {
			this.widget().toggleClass( "ui-corner-all", options.corners );
		}
		if ( options.shadow !== undefined ) {
			this.widget().toggleClass( "ui-shadow", options.shadow );
		}
		if ( options.inline !== undefined ) {
			this.widget().toggleClass( "ui-btn-inline", options.inline );
		}
		if ( options.mini !== undefined ) {
			this.widget().toggleClass( "ui-mini", options.mini );
		}
		if ( options.iconpos !== undefined ) {
			this.widget().removeClass( "ui-btn-icon-" + options.iconpos );
		}
		if ( options.icon !== undefined ) {
			if ( !this.options.iconpos && !options.iconpos ) {
				this.widget.toggleClass( "ui-btn-icon-left", options.icon );
			}
			this.widget().removeClass( "ui-icon-" + this.options.icon ).toggleClass( "ui-icon-" + options.icon, options.icon );
		}
		if( options.disabled !== undefined ) {
			this.element.prop( "disabled", options.disabled );
		}

		this._super( options );
	},

	refresh: function( create ) {
		if ( this.options.icon && this.options.iconpos === "notext" && this.element.attr( "title" ) ) {
			this.element.attr( "title", this.element.val() );
		}
		if ( !create ) {
			var originalElement = this.element.detach();
			$( this.wrapper ).text( this.element.val() ).append( originalElement );
		}
	}
});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>Button: Toggle class ui-state-disabled in response to the value of the "disabled" option, and add ui-state-disabled during init instead of ui-disabled.<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Custom-styled native input/buttons
//>>label: Buttons: Input or button-based
//>>group: Forms
//>>css.structure: ../css/structure/jquery.mobile.forms.button.css
//>>css.theme: ../css/themes/default/jquery.mobile.theme.css

define( [ "jquery", "../../jquery.mobile.widget" ], function( jQuery ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, undefined ) {

$.widget( "mobile.button", {

	initSelector: "input[type='button'], input[type='submit'], input[type='reset']",

	options: {
		theme: null,
		icon: null,
		iconpos: "left",
		iconshadow: false, /* TODO: Deprecated in 1.4, remove in 1.5. */
		corners: true,
		shadow: true,
		inline: null,
		mini: null,
		wrapperClass: null,
		enhanced: false
	},

	_create: function() {

		if ( this.element.is( ":disabled" ) ) {
			this.options.disabled = true;
		}

		if ( !this.options.enhanced ) {
			this._enhance();
		}

		$.extend( this, {
			wrapper: this.element.parent()
		});

		this._on( {
			focus: function() {
				this.widget().addClass( $.mobile.focusClass );
			},

			blur: function() {
				this.widget().removeClass( $.mobile.focusClass );
			}
		});

		this.refresh( true );
	},

	_enhance: function() {
		this.element.wrap( this._button() );
	},

	_button: function() {
		var options = this.options;

		return $("<div class='ui-btn ui-input-btn" +
			( options.wrapperClass ? " " + options.wrapperClass : "" ) +
			( options.theme ? " ui-btn-" + options.theme : "" ) +
			( options.corners ? " ui-corner-all" : "" ) +
			( options.shadow ? " ui-shadow" : "" ) +
			( options.inline ? " ui-btn-inline" : "" ) +
			( options.mini ? " ui-mini" : "" ) +
			( options.disabled ? " ui-state-disabled" : "" ) +
			( ( options.iconpos && options.icon ) ?
				" ui-btn-icon-" + options.iconpos :
				( options.icon ? " ui-btn-icon-left" : "" ) ) +
			( options.icon ? " ui-icon-" + options.icon : "" ) +
			"' >" + this.element.val() + "
");
	},

	widget: function() {
		return this.wrapper;
	},

	_destroy: function() {
			this.element.insertBefore( this.button );
			this.button.remove();
	},

	_setOptions: function( options ) {
		var outer = this.widget();

		if ( options.theme !== undefined ) {
			outer
				.removeClass( this.options.theme )
				.addClass( "ui-btn-" + options.theme );
		}
		if ( options.corners !== undefined ) {
			outer.toggleClass( "ui-corner-all", options.corners );
		}
		if ( options.shadow !== undefined ) {
			outer.toggleClass( "ui-shadow", options.shadow );
		}
		if ( options.inline !== undefined ) {
			outer.toggleClass( "ui-btn-inline", options.inline );
		}
		if ( options.mini !== undefined ) {
			outer.toggleClass( "ui-mini", options.mini );
		}
		if ( options.iconpos !== undefined ) {
			outer.removeClass( "ui-btn-icon-" + options.iconpos );
		}
		if ( options.icon !== undefined ) {
			if ( !this.options.iconpos && !options.iconpos ) {
				outer.toggleClass( "ui-btn-icon-left", options.icon );
			}
			outer
				.removeClass( "ui-icon-" + this.options.icon )
				.toggleClass( "ui-icon-" + options.icon, options.icon );
		}
		if( options.disabled !== undefined ) {
			this.element.prop( "disabled", options.disabled );
			outer.toggleClass( "ui-state-disabled", options.disabled );
		}

		this._super( options );
	},

	refresh: function( create ) {
		if ( this.options.icon && this.options.iconpos === "notext" && this.element.attr( "title" ) ) {
			this.element.attr( "title", this.element.val() );
		}
		if ( !create ) {
			var originalElement = this.element.detach();
			$( this.wrapper ).text( this.element.val() ).append( originalElement );
		}
	}
});

})( jQuery );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>const anonFuncHandler = require('./../../lib/anonFuncHandler.js')
const expect = require('expect');
const test = require('tape');

test('type of Anonymous Function Handler', t => {
  t.plan(1);
  t.equal(typeof anonFuncHandler, 'function')
});

test('output when passed anonymous function', t => {
  t.plan(2);
  const anonFunc = function(){};
  const anonFunc2 = function     () {};
  const output = anonFuncHandler(anonFunc.toString());
  const output2 = anonFuncHandler(anonFunc2.toString());
  t.equal(output, 'function anonymousFunc(){}');
  t.equal(output2, 'function anonymousFunc() {}');
});
<commit_msg>testing named functions output<commit_after>const anonFuncHandler = require('./../../lib/anonFuncHandler.js')
const expect = require('expect');
const test = require('tape');

test('type of Anonymous Function Handler', t => {
  t.plan(1);
  t.equal(typeof anonFuncHandler, 'function')
});

test('output when passed anonymous function', t => {
  t.plan(2);
  const anonFunc = function(){};
  const anonFunc2 = function     () {};
  const output = anonFuncHandler(anonFunc.toString());
  const output2 = anonFuncHandler(anonFunc2.toString());
  t.equal(output, 'function anonymousFunc(){}');
  t.equal(output2, 'function anonymousFunc() {}');
});

test('output when passed named function', t => {
  t.plan(2);
  const namedFunc = function fn(){};
  const namedFunc2 = function     fn2() {};
  const output = anonFuncHandler(namedFunc.toString());
  const output2 = anonFuncHandler(namedFunc2.toString());
  t.equal(output, 'function fn(){}');
  t.equal(output2, 'function fn2() {}');
});
<|endoftext|>


<commit_before>import Controller from '@ember/controller';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import moment from 'moment';
import {A} from '@ember/array';
import {action} from '@ember/object';
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const PAID_PARAMS = [{
    name: 'All members',
    value: null
}, {
    name: 'Free members',
    value: 'false'
}, {
    name: 'Paid members',
    value: 'true'
}];

export default class MembersController extends Controller {
    @service ajax;
    @service config;
    @service ellaSparse;
    @service feature;
    @service ghostPaths;
    @service membersStats;
    @service store;

    queryParams = [
        'label',
        {paidParam: 'paid'},
        {searchParam: 'search'}
    ];

    @tracked members = A([]);
    @tracked searchText = '';
    @tracked searchParam = '';
    @tracked paidParam = null;
    @tracked label = null;
    @tracked modalLabel = null;
    @tracked showLabelModal = false;
    @tracked showDeleteMembersModal = false;

    @tracked _availableLabels = A([]);

    paidParams = PAID_PARAMS;

    constructor() {
        super(...arguments);
        this._availableLabels = this.store.peekAll('label');
    }

    // Computed properties -----------------------------------------------------

    get listHeader() {
        let {searchText, selectedLabel, members} = this;

        if (members.loading) {
            return 'Loading...';
        }

        if (searchText) {
            return 'Search result';
        }

        let count = ghPluralize(members.length, 'member');

        if (selectedLabel && selectedLabel.slug) {
            if (members.length > 1) {
                return `${count} match current filter`;
            } else {
                return `${count} matches current filter`;
            }
        }

        return count;
    }

    get showingAll() {
        return !this.searchParam && !this.paidParam && !this.label;
    }

    get availableLabels() {
        let labels = this._availableLabels
            .filter(label => !label.isNew)
            .filter(label => label.id !== null)
            .sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
        let options = labels.toArray();

        options.unshiftObject({name: 'All labels', slug: null});

        return options;
    }

    get selectedLabel() {
        let {label, availableLabels} = this;
        return availableLabels.findBy('slug', label);
    }

    get labelModalData() {
        let label = this.modalLabel;
        let labels = this.availableLabels;

        return {
            label,
            labels
        };
    }

    get selectedPaidParam() {
        return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
    }

    // Actions -----------------------------------------------------------------

    @action
    refreshData() {
        this.fetchMembersTask.perform();
        this.fetchLabelsTask.perform();
        this.membersStats.invalidate();
        this.membersStats.fetch();
    }

    @action
    search(e) {
        this.searchTask.perform(e.target.value);
    }

    @action
    exportData() {
        let exportUrl = ghostPaths().url.api('members/upload');
        let downloadParams = new URLSearchParams();
        downloadParams.set('limit', 'all');
        if (this.paidParam !== null) {
            downloadParams.set('paid', this.paidParam);
        }
        if (this.label !== null) {
            downloadParams.set('filter', `label:${this.label}`);
        }
        if (this.searchText !== '') {
            downloadParams.set('search', this.searchText);
        }
        let iframe = document.getElementById('iframeDownload');

        if (!iframe) {
            iframe = document.createElement('iframe');
            iframe.id = 'iframeDownload';
            iframe.style.display = 'none';
            document.body.append(iframe);
        }
        iframe.setAttribute('src', `${exportUrl}?${downloadParams.toString()}`);
    }

    @action
    changeLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        this.label = label.slug;
    }

    @action
    addLabel(e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        const newLabel = this.store.createRecord('label');
        this.modalLabel = newLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    editLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        let modalLabel = this.availableLabels.findBy('slug', label);
        this.modalLabel = modalLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    toggleLabelModal() {
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    changePaidParam(paid) {
        this.paidParam = paid.value;
    }

    @action
    toggleDeleteMembersModal() {
        this.showDeleteMembersModal = !this.showDeleteMembersModal;
    }

    @action
    deleteMembers() {
        return this.deleteMembersTask.perform();
    }

    // Tasks -------------------------------------------------------------------

    @task({restartable: true})
    *searchTask(query) {
        yield timeout(250); // debounce
        this.searchParam = query;
    }

    @task
    *fetchLabelsTask() {
        yield this.store.query('label', {limit: 'all'});
    }

    @task({restartable: true})
    *fetchMembersTask(params) {
        // params is undefined when called as a "refresh" of the model
        let {label, paidParam, searchParam} = typeof params === 'undefined' ? this : params;

        if (!searchParam) {
            this.resetSearch();
        }

        // use a fixed created_at date so that subsequent pages have a consistent index
        let startDate = new Date();

        // bypass the stale data shortcut if params change
        let forceReload = !params
            || label !== this._lastLabel
            || paidParam !== this._lastPaidParam
            || searchParam !== this._lastSearchParam;
        this._lastLabel = label;
        this._lastPaidParam = paidParam;
        this._lastSearchParam = searchParam;

        // unless we have a forced reload, do not re-fetch the members list unless it's more than a minute old
        // keeps navigation between list->details->list snappy
        if (!forceReload && this._startDate && !(this._startDate - startDate > 1 * 60 * 1000)) {
            return this.members;
        }

        this._startDate = startDate;

        this.members = yield this.ellaSparse.array((range = {}, query = {}) => {
            const labelFilter = label ? `label:'${label}'+` : '';
            const paidQuery = paidParam ? {paid: paidParam} : {};
            const searchQuery = searchParam ? {search: searchParam} : {};

            query = Object.assign({
                limit: range.length,
                page: range.start / range.length,
                order: 'created_at desc',
                filter: `${labelFilter}created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`
            }, paidQuery, searchQuery, query);

            return this.store.query('member', query).then((result) => {
                return {
                    data: result,
                    total: result.meta.pagination.total
                };
            });
        }, {
            limit: 50
        });
    }

    @task({drop: true})
    *deleteMembersTask() {
        let {label, paidParam, searchParam} = this;

        let filter = label ? `label:${label}` : '';
        let paidQuery = paidParam ? {paid: paidParam} : {};
        let searchQuery = searchParam ? {search: searchParam} : {};
        let allQuery = !label && !paidParam && !searchParam ? {all: true} : {};

        let query = new URLSearchParams(Object.assign({}, {filter}, paidQuery, searchQuery, allQuery));
        let url = `${this.ghostPaths.url.api('members')}?${query}`;

        // response contains details of which members failed to be deleted
        let response = yield this.ajax.del(url);

        // reset and reload
        this.store.unloadAll('member');
        this.reload();

        return response.meta.stats;
    }

    // Internal ----------------------------------------------------------------

    resetSearch() {
        this.searchText = '';
    }

    reload() {
        this.membersStats.invalidate();
        this.membersStats.fetch();
        this.fetchMembersTask.perform();
    }
}
<commit_msg>🐛 Fixed first set of members appearing twice in members list<commit_after>import Controller from '@ember/controller';
import ghostPaths from 'ghost-admin/utils/ghost-paths';
import moment from 'moment';
import {A} from '@ember/array';
import {action} from '@ember/object';
import {ghPluralize} from 'ghost-admin/helpers/gh-pluralize';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency-decorators';
import {timeout} from 'ember-concurrency';
import {tracked} from '@glimmer/tracking';

const PAID_PARAMS = [{
    name: 'All members',
    value: null
}, {
    name: 'Free members',
    value: 'false'
}, {
    name: 'Paid members',
    value: 'true'
}];

export default class MembersController extends Controller {
    @service ajax;
    @service config;
    @service ellaSparse;
    @service feature;
    @service ghostPaths;
    @service membersStats;
    @service store;

    queryParams = [
        'label',
        {paidParam: 'paid'},
        {searchParam: 'search'}
    ];

    @tracked members = A([]);
    @tracked searchText = '';
    @tracked searchParam = '';
    @tracked paidParam = null;
    @tracked label = null;
    @tracked modalLabel = null;
    @tracked showLabelModal = false;
    @tracked showDeleteMembersModal = false;

    @tracked _availableLabels = A([]);

    paidParams = PAID_PARAMS;

    constructor() {
        super(...arguments);
        this._availableLabels = this.store.peekAll('label');
    }

    // Computed properties -----------------------------------------------------

    get listHeader() {
        let {searchText, selectedLabel, members} = this;

        if (members.loading) {
            return 'Loading...';
        }

        if (searchText) {
            return 'Search result';
        }

        let count = ghPluralize(members.length, 'member');

        if (selectedLabel && selectedLabel.slug) {
            if (members.length > 1) {
                return `${count} match current filter`;
            } else {
                return `${count} matches current filter`;
            }
        }

        return count;
    }

    get showingAll() {
        return !this.searchParam && !this.paidParam && !this.label;
    }

    get availableLabels() {
        let labels = this._availableLabels
            .filter(label => !label.isNew)
            .filter(label => label.id !== null)
            .sort((labelA, labelB) => labelA.name.localeCompare(labelB.name, undefined, {ignorePunctuation: true}));
        let options = labels.toArray();

        options.unshiftObject({name: 'All labels', slug: null});

        return options;
    }

    get selectedLabel() {
        let {label, availableLabels} = this;
        return availableLabels.findBy('slug', label);
    }

    get labelModalData() {
        let label = this.modalLabel;
        let labels = this.availableLabels;

        return {
            label,
            labels
        };
    }

    get selectedPaidParam() {
        return this.paidParams.findBy('value', this.paidParam) || {value: '!unknown'};
    }

    // Actions -----------------------------------------------------------------

    @action
    refreshData() {
        this.fetchMembersTask.perform();
        this.fetchLabelsTask.perform();
        this.membersStats.invalidate();
        this.membersStats.fetch();
    }

    @action
    search(e) {
        this.searchTask.perform(e.target.value);
    }

    @action
    exportData() {
        let exportUrl = ghostPaths().url.api('members/upload');
        let downloadParams = new URLSearchParams();
        downloadParams.set('limit', 'all');
        if (this.paidParam !== null) {
            downloadParams.set('paid', this.paidParam);
        }
        if (this.label !== null) {
            downloadParams.set('filter', `label:${this.label}`);
        }
        if (this.searchText !== '') {
            downloadParams.set('search', this.searchText);
        }
        let iframe = document.getElementById('iframeDownload');

        if (!iframe) {
            iframe = document.createElement('iframe');
            iframe.id = 'iframeDownload';
            iframe.style.display = 'none';
            document.body.append(iframe);
        }
        iframe.setAttribute('src', `${exportUrl}?${downloadParams.toString()}`);
    }

    @action
    changeLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        this.label = label.slug;
    }

    @action
    addLabel(e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        const newLabel = this.store.createRecord('label');
        this.modalLabel = newLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    editLabel(label, e) {
        if (e) {
            e.preventDefault();
            e.stopPropagation();
        }
        let modalLabel = this.availableLabels.findBy('slug', label);
        this.modalLabel = modalLabel;
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    toggleLabelModal() {
        this.showLabelModal = !this.showLabelModal;
    }

    @action
    changePaidParam(paid) {
        this.paidParam = paid.value;
    }

    @action
    toggleDeleteMembersModal() {
        this.showDeleteMembersModal = !this.showDeleteMembersModal;
    }

    @action
    deleteMembers() {
        return this.deleteMembersTask.perform();
    }

    // Tasks -------------------------------------------------------------------

    @task({restartable: true})
    *searchTask(query) {
        yield timeout(250); // debounce
        this.searchParam = query;
    }

    @task
    *fetchLabelsTask() {
        yield this.store.query('label', {limit: 'all'});
    }

    @task({restartable: true})
    *fetchMembersTask(params) {
        // params is undefined when called as a "refresh" of the model
        let {label, paidParam, searchParam} = typeof params === 'undefined' ? this : params;

        if (!searchParam) {
            this.resetSearch();
        }

        // use a fixed created_at date so that subsequent pages have a consistent index
        let startDate = new Date();

        // bypass the stale data shortcut if params change
        let forceReload = !params
            || label !== this._lastLabel
            || paidParam !== this._lastPaidParam
            || searchParam !== this._lastSearchParam;
        this._lastLabel = label;
        this._lastPaidParam = paidParam;
        this._lastSearchParam = searchParam;

        // unless we have a forced reload, do not re-fetch the members list unless it's more than a minute old
        // keeps navigation between list->details->list snappy
        if (!forceReload && this._startDate && !(this._startDate - startDate > 1 * 60 * 1000)) {
            return this.members;
        }

        this._startDate = startDate;

        this.members = yield this.ellaSparse.array((range = {}, query = {}) => {
            const labelFilter = label ? `label:'${label}'+` : '';
            const paidQuery = paidParam ? {paid: paidParam} : {};
            const searchQuery = searchParam ? {search: searchParam} : {};

            query = Object.assign({
                limit: range.length,
                page: range.page,
                order: 'created_at desc',
                filter: `${labelFilter}created_at:<='${moment.utc(this._startDate).format('YYYY-MM-DD HH:mm:ss')}'`
            }, paidQuery, searchQuery, query);

            return this.store.query('member', query).then((result) => {
                return {
                    data: result,
                    total: result.meta.pagination.total
                };
            });
        }, {
            limit: 50
        });
    }

    @task({drop: true})
    *deleteMembersTask() {
        let {label, paidParam, searchParam} = this;

        let filter = label ? `label:${label}` : '';
        let paidQuery = paidParam ? {paid: paidParam} : {};
        let searchQuery = searchParam ? {search: searchParam} : {};
        let allQuery = !label && !paidParam && !searchParam ? {all: true} : {};

        let query = new URLSearchParams(Object.assign({}, {filter}, paidQuery, searchQuery, allQuery));
        let url = `${this.ghostPaths.url.api('members')}?${query}`;

        // response contains details of which members failed to be deleted
        let response = yield this.ajax.del(url);

        // reset and reload
        this.store.unloadAll('member');
        this.reload();

        return response.meta.stats;
    }

    // Internal ----------------------------------------------------------------

    resetSearch() {
        this.searchText = '';
    }

    reload() {
        this.membersStats.invalidate();
        this.membersStats.fetch();
        this.fetchMembersTask.perform();
    }
}
<|endoftext|>


<commit_before>import { debouncedResize } from 'common/utils';

const google = window.google;

export const createGoogleMaps = () => {
  const map = new google.maps.Map(document.getElementById('footer-map'), {
    center: new google.maps.LatLng(63.41819751959266, 10.40592152481463),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    disableDefaultUI: true,
    streetViewControl: false,
    styles: [
      {
        featureType: 'all',
        stylers: [
          { saturation: -100 },
        ],
      },
      {
        stylers: [
          { hue: '#666666' },
          { lightness: -30 },
        ],
      },
      {
        featureType: 'poi.school',
        stylers: [
          { color: '#b3b3b3' },
        ],
      },
      {
        featureType: 'poi.park',
        stylers: [
          { color: '#d2e4c4' },
        ],

      },
      {
        featureType: 'road.local',
        elementType: 'all',
        stylers: [
          { hue: '#f4f4f4' },
          { lightness: 52 },
        ],
      },
      {
        featureType: 'poi.sports_complex',
        stylers: [
          { saturation: 4 },
          { weight: 0.5 },
          { color: '#bad5aa' },
        ],
      },
    ],
  });

  // Applying the marker to the map, but only if the map has been created
  // eslint-disable-next-line no-new
  new google.maps.Marker({
    map,
    position: new google.maps.LatLng(63.41816871425781, 10.405924207023645),
    icon: '/static/img/map-marker.png',
    visible: true,
  });
  return map;
};

export const initGoogleMaps = () => {
  // Init the footer-map, but don't crash if google is not found
  let map;
  try {
    map = createGoogleMaps();
  } catch (err) {
    console.warn('Could not load Google Maps:', err); // eslint-disable-line no-console
  }

  // Reset center of the map on window-resize
  debouncedResize(() => {
    if (map) {
      map.panTo(new google.maps.LatLng(63.41819751959266, 10.40592152481463));
    }
  });
};
<commit_msg>update map position<commit_after>import { debouncedResize } from 'common/utils';

const google = window.google;

export const createGoogleMaps = () => {
  const map = new google.maps.Map(document.getElementById('footer-map'), {
    center: new google.maps.LatLng(63.415510, 10.404668),
    zoom: 15,
    mapTypeId: google.maps.MapTypeId.ROADMAP,
    disableDefaultUI: true,
    streetViewControl: false,
    styles: [
      {
        featureType: 'all',
        stylers: [
          { saturation: -100 },
        ],
      },
      {
        stylers: [
          { hue: '#666666' },
          { lightness: -30 },
        ],
      },
      {
        featureType: 'poi.school',
        stylers: [
          { color: '#b3b3b3' },
        ],
      },
      {
        featureType: 'poi.park',
        stylers: [
          { color: '#d2e4c4' },
        ],

      },
      {
        featureType: 'road.local',
        elementType: 'all',
        stylers: [
          { hue: '#f4f4f4' },
          { lightness: 52 },
        ],
      },
      {
        featureType: 'poi.sports_complex',
        stylers: [
          { saturation: 4 },
          { weight: 0.5 },
          { color: '#bad5aa' },
        ],
      },
    ],
  });

  // Applying the marker to the map, but only if the map has been created
  // eslint-disable-next-line no-new
  new google.maps.Marker({
    map,
    position: new google.maps.LatLng(63.415510, 10.404668),
    icon: '/static/img/map-marker.png',
    visible: true,
  });
  return map;
};

export const initGoogleMaps = () => {
  // Init the footer-map, but don't crash if google is not found
  let map;
  try {
    map = createGoogleMaps();
  } catch (err) {
    console.warn('Could not load Google Maps:', err); // eslint-disable-line no-console
  }

  // Reset center of the map on window-resize
  debouncedResize(() => {
    if (map) {
      map.panTo(new google.maps.LatLng(63.415510, 10.404668));
    }
  });
};
<|endoftext|>


<commit_before>let globals = require('globals')

let jest = { }
for (let i in globals.jest) {
  if (i !== 'test' && i !== 'xtest') jest[i] = globals.jest[i]
}

module.exports = {
  extends: 'standard',
  plugins: [
    'jest',
    'security',
    'node',
    'unicorn'
  ],
  rules: {
    'standard/no-callback-literal': 'off',

    'security/detect-possible-timing-attacks': 'error',
    'security/detect-eval-with-expression': 'error',
    'security/detect-non-literal-require': 'error',
    'security/detect-non-literal-regexp': 'error',
    'security/detect-pseudoRandomBytes': 'error',
    'security/detect-buffer-noassert': 'error',
    'security/detect-unsafe-regex': 'error',
    'security/detect-new-buffer': 'error',

    'node/no-unpublished-require': 'error',
    'node/no-extraneous-require': 'error',
    'node/no-missing-require': 'error',

    'unicorn/custom-error-definition': 'error',
    'unicorn/no-unused-properties': 'error',
    'unicorn/no-array-instanceof': 'error',
    'unicorn/prefer-event-key': 'error',
    'unicorn/regex-shorthand': 'error',

    'import/order': ['error', {
      'groups': [
        ['builtin', 'external'],
        ['internal', 'parent', 'sibling', 'index']
      ],
      'newlines-between': 'always'
    }],

    'nonblock-statement-body-position': 'error',
    'no-misleading-character-class': 'error',
    'computed-property-spacing': ['error', 'never'],
    'array-bracket-spacing': ['error', 'never'],
    'func-name-matching': 'error',
    'no-useless-return': 'error',
    'consistent-return': 'error',
    'multiline-ternary': ['error', 'never'],
    'no-nested-ternary': 'error',
    'block-scoped-var': 'error',
    'no-invalid-this': 'error',
    'global-require': 'error',
    'no-unused-vars': ['error', {
      args: 'after-used',
      vars: 'all'
    }],
    'no-new-symbol': 'error',
    'getter-return': 'error',
    'for-direction': 'error',
    'no-lonely-if': 'error',
    'prefer-const': 'off',
    'quote-props': ['error', 'consistent-as-needed'],
    'func-style': ['error', 'declaration'],
    'no-shadow': 'error',
    'max-len': ['error', 80],
    'no-new': 'off'
  },
  env: {
    browser: true,
    node: true
  },
  overrides: [
    {
      files: ['test/*', 'test/**/*', '*.test.js'],
      rules: {
        'node/no-unpublished-require': 'off',
        'node/no-missing-require': ['error', {
          allowModules: ['worker_threads']
        }]
      },
      globals: jest
    },
    {
      files: ['*.test.js'],
      rules: {
        'jest/require-to-throw-message': 'error',
        'jest/prefer-to-be-undefined': 'error',
        'jest/no-commented-out-tests': 'error',
        'jest/prefer-to-have-length': 'error',
        'jest/no-standalone-expect': 'error',
        'jest/prefer-hooks-on-top': 'error',
        'jest/no-jasmine-globals': 'error',
        'jest/no-identical-title': 'error',
        'jest/no-duplicate-hooks': 'error',
        'jest/consistent-test-it': ['error', { fn: 'it' }],
        'jest/prefer-called-with': 'error',
        'jest/no-disabled-tests': 'error',
        'jest/prefer-to-be-null': 'error',
        'jest/prefer-to-contain': 'error',
        'jest/no-test-prefixes': 'error',
        'jest/no-test-callback': 'error',
        'jest/no-focused-tests': 'error',
        'jest/no-truthy-falsy': 'error',
        'jest/lowercase-name': 'error',
        'jest/valid-describe': 'error',
        'jest/no-try-expect': 'error',
        'jest/prefer-spy-on': 'error',
        'jest/valid-expect': 'error',
        'jest/valid-title': 'error',
        'jest/prefer-todo': 'error',
        'global-require': 'off'
      },
      globals: jest
    }
  ]
}
<commit_msg>Use jest extendable configs<commit_after>let globals = require('globals')

let jest = { }
for (let i in globals.jest) {
  if (i !== 'test' && i !== 'xtest') jest[i] = globals.jest[i]
}

module.exports = {
  extends: 'standard',
  plugins: [
    'jest',
    'security',
    'node',
    'unicorn'
  ],
  rules: {
    'standard/no-callback-literal': 'off',

    'security/detect-possible-timing-attacks': 'error',
    'security/detect-eval-with-expression': 'error',
    'security/detect-non-literal-require': 'error',
    'security/detect-non-literal-regexp': 'error',
    'security/detect-pseudoRandomBytes': 'error',
    'security/detect-buffer-noassert': 'error',
    'security/detect-unsafe-regex': 'error',
    'security/detect-new-buffer': 'error',

    'node/no-unpublished-require': 'error',
    'node/no-extraneous-require': 'error',
    'node/no-missing-require': 'error',

    'unicorn/custom-error-definition': 'error',
    'unicorn/no-unused-properties': 'error',
    'unicorn/no-array-instanceof': 'error',
    'unicorn/prefer-event-key': 'error',
    'unicorn/regex-shorthand': 'error',

    'import/order': ['error', {
      'groups': [
        ['builtin', 'external'],
        ['internal', 'parent', 'sibling', 'index']
      ],
      'newlines-between': 'always'
    }],

    'nonblock-statement-body-position': 'error',
    'no-misleading-character-class': 'error',
    'computed-property-spacing': ['error', 'never'],
    'array-bracket-spacing': ['error', 'never'],
    'func-name-matching': 'error',
    'no-useless-return': 'error',
    'consistent-return': 'error',
    'multiline-ternary': ['error', 'never'],
    'no-nested-ternary': 'error',
    'block-scoped-var': 'error',
    'no-invalid-this': 'error',
    'global-require': 'error',
    'no-unused-vars': ['error', {
      args: 'after-used',
      vars: 'all'
    }],
    'no-new-symbol': 'error',
    'getter-return': 'error',
    'for-direction': 'error',
    'no-lonely-if': 'error',
    'prefer-const': 'off',
    'quote-props': ['error', 'consistent-as-needed'],
    'func-style': ['error', 'declaration'],
    'no-shadow': 'error',
    'max-len': ['error', 80],
    'no-new': 'off'
  },
  env: {
    browser: true,
    node: true
  },
  overrides: [
    {
      files: ['test/*', 'test/**/*', '*.test.js'],
      rules: {
        'node/no-unpublished-require': 'off',
        'node/no-missing-require': ['error', {
          allowModules: ['worker_threads']
        }]
      },
      globals: jest
    },
    {
      files: ['*.test.js'],
      extends: [
        'plugin:jest/recommended',
        'plugin:jest/style'
      ],
      rules: {
        'jest/require-to-throw-message': 'error',
        'jest/valid-expect-in-promise': 'off',
        'jest/prefer-hooks-on-top': 'error',
        'jest/no-duplicate-hooks': 'error',
        'jest/consistent-test-it': ['error', { fn: 'it' }],
        'jest/prefer-called-with': 'error',
        'jest/no-truthy-falsy': 'error',
        'jest/lowercase-name': 'error',
        'jest/prefer-spy-on': 'error',
        'jest/expect-expect': 'off',
        'jest/valid-title': 'error',
        'jest/prefer-todo': 'error',

        'global-require': 'off'
      },
      globals: jest
    }
  ]
}
<|endoftext|>