<commit_before>import { isString } from 'substance'
import { UNKNOWN } from './CellStates'
import { transpile } from '../shared/expressionHelpers'
import { isExpression, qualifiedId } from '../shared/cellHelpers'

export default class Cell {

  constructor(doc, cellData) {
    const { id, lang, source, status, inputs, output, value, errors, hasSideEffects, next, prev } = cellData
    this.doc = doc

    // Attention: the given cell id is not necessarily globally unique
    // thus, we derive a unique id using the document id and the node id
    // localId is used later to be able to map back to the associated node
    // TODO: I would rather go for only one id, and always have a doc
    if (doc) {
      let docId = this.docId = doc.id
      // is the id already a qualified id?
      if (id.startsWith(docId)) {
        this.id = id
        this.unqualifiedId = id.slice(docId.length+1)
      } else {
        this.id = qualifiedId(doc, cellData)
        this.unqualifiedId = id
      }
    } else {
      this.docId = null
      this.id = id
      this.unqualifiedId = id
    }

    this._isSheetCell = (this.doc && this.doc.type === 'sheet')

    this.lang = lang

    // the source code is transpiled to an object with
    // - (original) source
    // - transpiledSource
    // - symbolMapping
    // - isConstant
    this._source = this._transpile(source)

    // managed by CellGraph
    this.status = status || UNKNOWN
    // a set of symbols ('x', 'A1', 'A1:B10', 'doc1!x', 'sheet1!A1', 'sheet1!A1:A10', 'sheet1!foo')
    this.inputs = new Set(inputs || [])
    // an output symbol (typically only used for document cells)
    this.output = output
    // one or many CellErrors
    this.errors = errors || []
    // the last computed value
    this.value = value
    // for cells with side effects
    this.hasSideEffects = Boolean(hasSideEffects)
    // for cells in a linear model
    // this is particularly important for cells with side effects
    this.next = next
    this.prev = prev
    // used by CellGraph
    this.level = 0
    // TODO: maybe we want to keep some stats, e.g. time of last evaluation, duration of last evaluation etc.
    this.stats = {}
  }

  clearErrors(filter) {
    if (isString(filter)) {
      const type = filter
      filter = (e) => {
        return e.type === type
      }
    }
    this.errors = this.errors.filter(e => !filter(e))
  }

  addErrors(errors) {
    this.errors = this.errors.concat(errors)
  }

  hasErrors() {
    return this.errors.length > 0
  }

  hasError(type) {
    for (let i = 0; i < this.errors.length; i++) {
      if (this.errors[i].type === type) return true
    }
    return false
  }

  get state() {
    console.warn('DEPRECATED: use cellState.status instead.')
    return this.status
  }

  hasOutput() {
    return Boolean(this.output)
  }

  hasValue() {
    return Boolean(this.value)
  }

  getValue() {
    return this.value
  }

  getLang() {
    return this.lang || (this.doc ? this.doc.lang : 'mini')
  }

  updateSource(source) {
    this._source = this._transpile(source)
  }

  get source() {
    return this._source.original
  }

  set source(source) {
    this._source = this._transpile(source)
  }

  get transpiledSource() {
    return this._source.transpiled
  }

  get symbolMapping() {
    return this._source.symbolMapping
  }

  isConstant() {
    return this._source.isConstant
  }

  isSheetCell() {
    return this._isSheetCell
  }

  toString() {
    // sheet1!A1 <- { ... source }
    let parts = []
    if (this.output) {
      parts.push(this.output)
      parts.push(' <- ')
    } else {
      parts.push(this.id)
      parts.push(': ')
    }
    parts.push(this._source.original)
    return parts.join('')
  }

  _transpile(source) {
    let original = source
    let transpiled
    let symbolMapping = {}
    let isConstant = false
    if (this._isSheetCell) {
      let m = isExpression(source)
      if (m) {
        let L = m[0].length
        let prefix = new Array(L)
        prefix.fill(' ')
        transpiled = prefix + transpile(source.slice(L), symbolMapping)
      } else {
        isConstant = true
      }
    } else {
      transpiled = transpile(source, symbolMapping)
    }
    return {
      original,
      transpiled,
      symbolMapping,
      isConstant
    }
  }
}<commit_msg>Added a comment about qualified ids.<commit_after>import { isString } from 'substance'
import { UNKNOWN } from './CellStates'
import { transpile } from '../shared/expressionHelpers'
import { isExpression, qualifiedId } from '../shared/cellHelpers'

export default class Cell {

  constructor(doc, cellData) {
    const { id, lang, source, status, inputs, output, value, errors, hasSideEffects, next, prev } = cellData
    this.doc = doc

    // Attention: the given cell id is not necessarily globally unique
    // thus, we derive a unique id using the document id and the node id
    // localId is used later to be able to map back to the associated node
    // TODO: I would rather go for only one id, and always have a doc
    if (doc) {
      let docId = this.docId = doc.id
      // is the id already a qualified id?
      if (id.startsWith(docId)) {
        this.id = id
        // ATTENTION: assuming that the qualified id is joining
        // the doc id and the node id with a single character (e.g. '!')
        this.unqualifiedId = id.slice(docId.length+1)
      } else {
        this.id = qualifiedId(doc, cellData)
        this.unqualifiedId = id
      }
    } else {
      this.docId = null
      this.id = id
      this.unqualifiedId = id
    }

    this._isSheetCell = (this.doc && this.doc.type === 'sheet')

    this.lang = lang

    // the source code is transpiled to an object with
    // - (original) source
    // - transpiledSource
    // - symbolMapping
    // - isConstant
    this._source = this._transpile(source)

    // managed by CellGraph
    this.status = status || UNKNOWN
    // a set of symbols ('x', 'A1', 'A1:B10', 'doc1!x', 'sheet1!A1', 'sheet1!A1:A10', 'sheet1!foo')
    this.inputs = new Set(inputs || [])
    // an output symbol (typically only used for document cells)
    this.output = output
    // one or many CellErrors
    this.errors = errors || []
    // the last computed value
    this.value = value
    // for cells with side effects
    this.hasSideEffects = Boolean(hasSideEffects)
    // for cells in a linear model
    // this is particularly important for cells with side effects
    this.next = next
    this.prev = prev
    // used by CellGraph
    this.level = 0
    // TODO: maybe we want to keep some stats, e.g. time of last evaluation, duration of last evaluation etc.
    this.stats = {}
  }

  clearErrors(filter) {
    if (isString(filter)) {
      const type = filter
      filter = (e) => {
        return e.type === type
      }
    }
    this.errors = this.errors.filter(e => !filter(e))
  }

  addErrors(errors) {
    this.errors = this.errors.concat(errors)
  }

  hasErrors() {
    return this.errors.length > 0
  }

  hasError(type) {
    for (let i = 0; i < this.errors.length; i++) {
      if (this.errors[i].type === type) return true
    }
    return false
  }

  get state() {
    console.warn('DEPRECATED: use cellState.status instead.')
    return this.status
  }

  hasOutput() {
    return Boolean(this.output)
  }

  hasValue() {
    return Boolean(this.value)
  }

  getValue() {
    return this.value
  }

  getLang() {
    return this.lang || (this.doc ? this.doc.lang : 'mini')
  }

  updateSource(source) {
    this._source = this._transpile(source)
  }

  get source() {
    return this._source.original
  }

  set source(source) {
    this._source = this._transpile(source)
  }

  get transpiledSource() {
    return this._source.transpiled
  }

  get symbolMapping() {
    return this._source.symbolMapping
  }

  isConstant() {
    return this._source.isConstant
  }

  isSheetCell() {
    return this._isSheetCell
  }

  toString() {
    // sheet1!A1 <- { ... source }
    let parts = []
    if (this.output) {
      parts.push(this.output)
      parts.push(' <- ')
    } else {
      parts.push(this.id)
      parts.push(': ')
    }
    parts.push(this._source.original)
    return parts.join('')
  }

  _transpile(source) {
    let original = source
    let transpiled
    let symbolMapping = {}
    let isConstant = false
    if (this._isSheetCell) {
      let m = isExpression(source)
      if (m) {
        let L = m[0].length
        let prefix = new Array(L)
        prefix.fill(' ')
        transpiled = prefix + transpile(source.slice(L), symbolMapping)
      } else {
        isConstant = true
      }
    } else {
      transpiled = transpile(source, symbolMapping)
    }
    return {
      original,
      transpiled,
      symbolMapping,
      isConstant
    }
  }
}<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * Displays the roots of a quadratic as non-interactive points with coordinate labels.
 * There may be 0, 1 or 2 roots.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const Circle = require( 'SCENERY/nodes/Circle' );
  const DerivedProperty = require( 'AXON/DerivedProperty' );
  const DerivedPropertyIO = require( 'AXON/DerivedPropertyIO' );
  const CoordinatesNode = require( 'GRAPHING_QUADRATICS/common/view/CoordinatesNode' );
  const GQColors = require( 'GRAPHING_QUADRATICS/common/GQColors' );
  const GQConstants = require( 'GRAPHING_QUADRATICS/common/GQConstants' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Node = require( 'SCENERY/nodes/Node' );
  const Property = require( 'AXON/Property' );
  const Tandem = require( 'TANDEM/Tandem' );
  const Vector2 = require( 'DOT/Vector2' );
  const Vector2IO = require( 'DOT/Vector2IO' );

  // ifphetio
  const NullableIO = require( 'ifphetio!PHET_IO/types/NullableIO' );

  // constants
  const X_SPACING = 15; // between root point and its coordinates display

  class RootsNode extends Node {

    /**
     * @param {Property.<Quadratic>} quadraticProperty - the interactive quadratic
     * @param {Graph} graph
     * @param {ModelViewTransform2} modelViewTransform
     * @param {BooleanProperty} rootsVisibleProperty
     * @param {BooleanProperty} coordinatesVisibleProperty
     * @param {Object} [options]
     */
    constructor( quadraticProperty, graph, modelViewTransform,
                 rootsVisibleProperty, coordinatesVisibleProperty, options ) {

      options = _.extend( {
        radius: 10,
        tandem: Tandem.required
      }, options );

      // points
      const pointOptions = {
        fill: GQColors.ROOTS,
        center: modelViewTransform.modelToViewXY( graph.xRange.getCenter(), graph.yRange.getCenter() )
      };
      const leftRootNode = new Circle( options.radius, pointOptions );
      const rightRootNode = new Circle( options.radius, pointOptions );

      // coordinates corresponding to the quadratic's left or single root (if it has roots)
      const leftCoordinatesProperty = new DerivedProperty( [ quadraticProperty ],
        quadratic => ( quadratic.roots && quadratic.roots.length > 0 ) ? quadratic.roots[ 0 ] : null, {
          isValidValue: value => ( value instanceof Vector2 || value === null ),
          phetioType: DerivedPropertyIO( NullableIO( Vector2IO ) ),
          phetioDocumentation: 'coordinates displayed on the left (or single) root'
        } );

      // coordinates corresponding to the quadratic's right root, if it has 2 roots
      const rightCoordinatesProperty = new DerivedProperty( [ quadraticProperty ],
        quadratic => ( quadratic.roots && quadratic.roots.length === 2 ) ? quadratic.roots[ 1 ] : null, {
          isValidValue: value => ( value instanceof Vector2 || value === null ),
          phetioType: DerivedPropertyIO( NullableIO( Vector2IO ) ),
          phetioDocumentation: 'coordinates displayed on the right root'
        } );

      // coordinate displays
      const coordinatesOptions = {
        foregroundColor: 'white',
        backgroundColor: GQColors.ROOTS,
        decimals: GQConstants.ROOTS_DECIMALS,
        maxWidth: GQConstants.COORDINATES_MAX_WIDTH
      };
      const leftCoordinatesNode = new CoordinatesNode( leftCoordinatesProperty, coordinatesOptions );
      const rightCoordinatesNode = new CoordinatesNode( rightCoordinatesProperty, coordinatesOptions );

      // decorate root with coordinates
      leftRootNode.addChild( leftCoordinatesNode );
      rightRootNode.addChild( rightCoordinatesNode );

      assert && assert( !options.children, 'RootsNode sets children' );
      options.children = [ leftRootNode, rightRootNode ];

      super( options );

      quadraticProperty.link( quadratic => {

        // start with both roots invisible, make visible the ones that are needed
        leftRootNode.visible = false;
        rightRootNode.visible = false;

        let roots = quadratic.roots;

        if ( roots && roots.length !== 0 ) {
          assert && assert( roots.length === 1 || roots.length === 2, 'unexpected number of roots: ' + roots.length );

          const leftRoot = roots[ 0 ];
          leftRootNode.translation = modelViewTransform.modelToViewPosition( leftRoot );
          leftRootNode.visible = graph.contains( leftRoot );

          if ( roots.length === 2 ) {
            const rightRoot = roots[ 1 ];
            assert && assert( leftRoot.x < rightRoot.x, 'unexpected order of roots: ' + roots );
            rightRootNode.translation = modelViewTransform.modelToViewPosition( rightRoot );
            rightRootNode.visible = graph.contains( rightRoot );
          }

          // position coordinates to left and right of roots
          leftCoordinatesNode.right = -( options.radius + X_SPACING );
          leftCoordinatesNode.centerY = 0;
          rightCoordinatesNode.left = options.radius + X_SPACING;
          rightCoordinatesNode.centerY = 0;
        }
      } );

      Property.multilink( [ rootsVisibleProperty, quadraticProperty ], ( rootsVisible, quadratic ) => {
        this.visible = !!( rootsVisible && quadratic.roots && quadratic.roots.length !== 0 );
      } );

      coordinatesVisibleProperty.link( visible => {
        leftCoordinatesNode.visible = visible;
        rightCoordinatesNode.visible = visible;
      } );
    }
  }

  return graphingQuadratics.register( 'RootsNode', RootsNode );
} );<commit_msg>Added phetioDocumentation about null values, see https://github.com/phetsims/graphing-quadratics/issues/14<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * Displays the roots of a quadratic as non-interactive points with coordinate labels.
 * There may be 0, 1 or 2 roots.
 *
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( require => {
  'use strict';

  // modules
  const Circle = require( 'SCENERY/nodes/Circle' );
  const DerivedProperty = require( 'AXON/DerivedProperty' );
  const DerivedPropertyIO = require( 'AXON/DerivedPropertyIO' );
  const CoordinatesNode = require( 'GRAPHING_QUADRATICS/common/view/CoordinatesNode' );
  const GQColors = require( 'GRAPHING_QUADRATICS/common/GQColors' );
  const GQConstants = require( 'GRAPHING_QUADRATICS/common/GQConstants' );
  const graphingQuadratics = require( 'GRAPHING_QUADRATICS/graphingQuadratics' );
  const Node = require( 'SCENERY/nodes/Node' );
  const Property = require( 'AXON/Property' );
  const Tandem = require( 'TANDEM/Tandem' );
  const Vector2 = require( 'DOT/Vector2' );
  const Vector2IO = require( 'DOT/Vector2IO' );

  // ifphetio
  const NullableIO = require( 'ifphetio!PHET_IO/types/NullableIO' );

  // constants
  const X_SPACING = 15; // between root point and its coordinates display

  class RootsNode extends Node {

    /**
     * @param {Property.<Quadratic>} quadraticProperty - the interactive quadratic
     * @param {Graph} graph
     * @param {ModelViewTransform2} modelViewTransform
     * @param {BooleanProperty} rootsVisibleProperty
     * @param {BooleanProperty} coordinatesVisibleProperty
     * @param {Object} [options]
     */
    constructor( quadraticProperty, graph, modelViewTransform,
                 rootsVisibleProperty, coordinatesVisibleProperty, options ) {

      options = _.extend( {
        radius: 10,
        tandem: Tandem.required
      }, options );

      // points
      const pointOptions = {
        fill: GQColors.ROOTS,
        center: modelViewTransform.modelToViewXY( graph.xRange.getCenter(), graph.yRange.getCenter() )
      };
      const leftRootNode = new Circle( options.radius, pointOptions );
      const rightRootNode = new Circle( options.radius, pointOptions );

      // coordinates corresponding to the quadratic's left or single root (if it has roots)
      const leftCoordinatesProperty = new DerivedProperty( [ quadraticProperty ],
        quadratic => ( quadratic.roots && quadratic.roots.length > 0 ) ? quadratic.roots[ 0 ] : null, {
          isValidValue: value => ( value instanceof Vector2 || value === null ),
          phetioType: DerivedPropertyIO( NullableIO( Vector2IO ) ),
          phetioDocumentation: 'coordinates displayed on the left (or single) root, or null if there are no roots'
        } );

      // coordinates corresponding to the quadratic's right root, if it has 2 roots
      const rightCoordinatesProperty = new DerivedProperty( [ quadraticProperty ],
        quadratic => ( quadratic.roots && quadratic.roots.length === 2 ) ? quadratic.roots[ 1 ] : null, {
          isValidValue: value => ( value instanceof Vector2 || value === null ),
          phetioType: DerivedPropertyIO( NullableIO( Vector2IO ) ),
          phetioDocumentation: 'coordinates displayed on the right root, or null if there is 1 or less root'
        } );

      // coordinate displays
      const coordinatesOptions = {
        foregroundColor: 'white',
        backgroundColor: GQColors.ROOTS,
        decimals: GQConstants.ROOTS_DECIMALS,
        maxWidth: GQConstants.COORDINATES_MAX_WIDTH
      };
      const leftCoordinatesNode = new CoordinatesNode( leftCoordinatesProperty, coordinatesOptions );
      const rightCoordinatesNode = new CoordinatesNode( rightCoordinatesProperty, coordinatesOptions );

      // decorate root with coordinates
      leftRootNode.addChild( leftCoordinatesNode );
      rightRootNode.addChild( rightCoordinatesNode );

      assert && assert( !options.children, 'RootsNode sets children' );
      options.children = [ leftRootNode, rightRootNode ];

      super( options );

      quadraticProperty.link( quadratic => {

        // start with both roots invisible, make visible the ones that are needed
        leftRootNode.visible = false;
        rightRootNode.visible = false;

        let roots = quadratic.roots;

        if ( roots && roots.length !== 0 ) {
          assert && assert( roots.length === 1 || roots.length === 2, 'unexpected number of roots: ' + roots.length );

          const leftRoot = roots[ 0 ];
          leftRootNode.translation = modelViewTransform.modelToViewPosition( leftRoot );
          leftRootNode.visible = graph.contains( leftRoot );

          if ( roots.length === 2 ) {
            const rightRoot = roots[ 1 ];
            assert && assert( leftRoot.x < rightRoot.x, 'unexpected order of roots: ' + roots );
            rightRootNode.translation = modelViewTransform.modelToViewPosition( rightRoot );
            rightRootNode.visible = graph.contains( rightRoot );
          }

          // position coordinates to left and right of roots
          leftCoordinatesNode.right = -( options.radius + X_SPACING );
          leftCoordinatesNode.centerY = 0;
          rightCoordinatesNode.left = options.radius + X_SPACING;
          rightCoordinatesNode.centerY = 0;
        }
      } );

      Property.multilink( [ rootsVisibleProperty, quadraticProperty ], ( rootsVisible, quadratic ) => {
        this.visible = !!( rootsVisible && quadratic.roots && quadratic.roots.length !== 0 );
      } );

      coordinatesVisibleProperty.link( visible => {
        leftCoordinatesNode.visible = visible;
        rightCoordinatesNode.visible = visible;
      } );
    }
  }

  return graphingQuadratics.register( 'RootsNode', RootsNode );
} );<|endoftext|>


<commit_before>/////////////////////////////////////////////////////////////
//
// Author Scott Herbert (www.scott-herbert.com)
//		  Dimitri Kourkoulis (http://dimitros.net/en/home)
//
// Version History
// 1 (10-Feb-2013) Inital release on to GitHub.
//
// 2 (12-Mar-2013) Small modification by Dimitri Kourkoulis.
//    This version of the script, keeps visitors on the web site
//    if they decline the use of cookies. However, in that event,
//    the cookie stored in their browser is named 'jsNoCookieCheck'
//    instead of 'jsCookieCheck'. This can be used by the software
//    and/or CMS of the web site to disable cookie producing content.
//    An example of how this has been used in one case on the Umbraco
//    CMS can be found here:
//    http://dimitros.net/en/blog/complyingwiththecookielaw
//
// 3 (03-April-2013) SAH - Added a variable that allows the developer to
//	  select if the script redirects (as it originally did) or carry?s
//    on (as per Dimitri?s great addition).
//
// Download from http://adf.ly/IvElY

function getCookie(c_name) {
    var i, x, y, ARRcookies = document.cookie.split(";");
    for (i = 0; i < ARRcookies.length; i++) {
        x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
        y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
        x = x.replace(/^\s+|\s+$/g, "");
        if (x == c_name) {
            return unescape(y);
        }
    }
}

function displayNotification(c_action) {

    // this sets the page background to semi-transparent black should work with all browsers
    var message = "
";

    // center vert
    message = message + "

";

    // this is the message displayed to the user.
    message = message + "This site uses heavy sarcasm, talks about imaginary friends and things that never happened or even might be physically possible.
If you are too much of a sissy to handle that, I prefer would prefer if you just go somewhere else
and click the \"I don't agree\" button.

Oh, also this site uses cookies:
In order for it to work correctly, we store a small file (called a cookie) on your computer.
 If you click \"I agree\" below we will create the cookies all is well. If you click on \"I don't agree\" we will send you on your merry way..far, VERY far away from here.
";

    // Displays the I agree/disagree buttons.
    // Feel free to change the address of the I disagree redirection to either a non-cookie site or a Google or the ICO web site
    message = message + "
<INPUT TYPE='button' VALUE='I Agree' onClick='JavaScript:doAccept();' /> <INPUT TYPE='button' VALUE=\"I don't agree\" onClick='JavaScript:doNotAccept("
	message = message + c_action;
	message = message + ");' />";

    // and this closes everything off.
    message = message + "

";

    document.writeln(message);
}

function doAccept() {
    setCookie("ILoveAr0xA", null, 365);
    location.reload(true);
}

function doNotAccept(c_action) {

	if (c_action == 1) {
        alert("You didn't want sarcasm or tracking cookies #sadpanda is sad. Hope you have more fun somewhere else.");
		window.location.replace("http://www.wikihow.com/Have-Fun-with-Grandma");
	} else {
		window.location.replace("http://www.wikihow.com/Have-Fun-with-Grandma");
	}
}

function setCookie(c_name, value, exdays) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString() + "; path=/");
    document.cookie = c_name + "=" + c_value;
}

function checkCookie(c_action) {

    var cookieName = "ILoveAr0xA";
    var cookieChk = getCookie(cookieName);
    if (cookieChk != null && cookieChk != "") {
        // the jsCookieCheck cookie exists so we can assume the person has read the notification
        // within the last year and has accepted the use of cookies
        setCookie(cookieName, cookieChk, 365); // set the cookie to expire in a year.
    }
    else {
        // No cookie exists, so display the lightbox effect notification.
        displayNotification(c_action);
    }
}

// blockOrCarryOn - 1 = Carry on, store a do not store cookies cookie and carry on
//					0 = Block, redirect the user to a different website (google for example)
var blockOrCarryOn = 1;
checkCookie(blockOrCarryOn);
<commit_msg>more js<commit_after>/////////////////////////////////////////////////////////////
//
// Author Scott Herbert (www.scott-herbert.com)
//		  Dimitri Kourkoulis (http://dimitros.net/en/home)
//
// Version History
// 1 (10-Feb-2013) Inital release on to GitHub.
//
// 2 (12-Mar-2013) Small modification by Dimitri Kourkoulis.
//    This version of the script, keeps visitors on the web site
//    if they decline the use of cookies. However, in that event,
//    the cookie stored in their browser is named 'jsNoCookieCheck'
//    instead of 'jsCookieCheck'. This can be used by the software
//    and/or CMS of the web site to disable cookie producing content.
//    An example of how this has been used in one case on the Umbraco
//    CMS can be found here:
//    http://dimitros.net/en/blog/complyingwiththecookielaw
//
// 3 (03-April-2013) SAH - Added a variable that allows the developer to
//	  select if the script redirects (as it originally did) or carry?s
//    on (as per Dimitri?s great addition).
//
// Download from http://adf.ly/IvElY

function getCookie(c_name) {
    var i, x, y, ARRcookies = document.cookie.split(";");
    for (i = 0; i < ARRcookies.length; i++) {
        x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("="));
        y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1);
        x = x.replace(/^\s+|\s+$/g, "");
        if (x == c_name) {
            return unescape(y);
        }
    }
}

function displayNotification(c_action) {

    // this sets the page background to semi-transparent black should work with all browsers
    var message = "
";

    // center vert
    message = message + "

";

    // this is the message displayed to the user.
    message = message + "This site uses heavy sarcasm, talks about imaginary friends and things that never happened or even might be physically possible.
If you are too much of a sissy to handle that, I prefer would prefer if you just go somewhere else
and click the \"I don't agree\" button.

Oh, also this site uses cookies:
In order for it to work correctly, we store a small file (called a cookie) on your computer.
 If you click \"I agree\" below we will create the cookies all is well. If you click on \"I don't agree\" we will send you far, VERY far away.
";

    // Displays the I agree/disagree buttons.
    // Feel free to change the address of the I disagree redirection to either a non-cookie site or a Google or the ICO web site
    message = message + "
<INPUT TYPE='button' VALUE='I Agree' onClick='JavaScript:doAccept();' /> <INPUT TYPE='button' VALUE=\"I don't agree\" onClick='JavaScript:doNotAccept("
	message = message + c_action;
	message = message + ");' />";

    // and this closes everything off.
    message = message + "

";

    document.writeln(message);
}

function doAccept() {
    setCookie("ILoveAr0xA", null, 365);
    location.reload(true);
}

function doNotAccept(c_action) {

	if (c_action == 1) {
        alert("You didn't want sarcasm or tracking cookies #sadpanda is sad. Hope you have more fun somewhere else.");
		window.location.replace("http://www.wikihow.com/Have-Fun-with-Grandma");
	} else {
		window.location.replace("http://www.wikihow.com/Have-Fun-with-Grandma");
	}
}

function setCookie(c_name, value, exdays) {
    var exdate = new Date();
    exdate.setDate(exdate.getDate() + exdays);
    var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString() + "; path=/");
    document.cookie = c_name + "=" + c_value;
}

function checkCookie(c_action) {

    var cookieName = "ILoveAr0xA";
    var cookieChk = getCookie(cookieName);
    if (cookieChk != null && cookieChk != "") {
        // the jsCookieCheck cookie exists so we can assume the person has read the notification
        // within the last year and has accepted the use of cookies
        setCookie(cookieName, cookieChk, 365); // set the cookie to expire in a year.
    }
    else {
        // No cookie exists, so display the lightbox effect notification.
        displayNotification(c_action);
    }
}

// blockOrCarryOn - 1 = Carry on, store a do not store cookies cookie and carry on
//					0 = Block, redirect the user to a different website (google for example)
var blockOrCarryOn = 1;
checkCookie(blockOrCarryOn);
<|endoftext|>


<commit_before>/*
 * Tangram UI
 * Copyright 2009 Baidu Inc. All rights reserved.
 *
 * path: baidu/ui/createUI.js
 * author: berg
 * version: 1.1.0
 * date: 2010/12/02
 */

///import baidu.ui;
///import baidu.ui.Base;
///import baidu.ui.create;
///import baidu.object.extend;

/**
 * 创建一个UI控件类
 *
 * @param {function} constructor ui控件构造器
 * @param {object} options 选项
 *
 * @return {object} ui控件
 */
baidu.ui.createUI = function(constructor, options) {
    options = options || {};
    var superClass = options.superClass || baidu.lang.Class,
        lastStep = superClass == baidu.lang.Class ? 1 : 0,
        i,
        n,
        ui = function(opt){// 创建新类的真构造器函数
            var me = this;
            opt = opt || {};
            // 继承父类的构造器
            superClass.call(me, !lastStep ? opt : (opt.guid || ""));

            //扩展静态配置到this上
            baidu.object.extend(me, ui.options);
            //扩展当前options中的项到this上
            baidu.object.extend(me, opt);

            me.classPrefix = me.classPrefix || "tangram-" + me.uiType.toLowerCase();

            //初始化行为
            //行为就是在控件实例上附加一些属性和方法
            for(i in baidu.ui.behavior){
                //添加行为到控件上
                if(typeof me[i] != 'undefined' && me[i]){
                    baidu.object.extend(me, baidu.ui.behavior[i]);
                    if(baidu.lang.isFunction(me[i])){
                        me.addEventListener("onload", function(){
                            baidu.ui.behavior[i].call(me[i].apply(me));
                        });
                    }else{
                        baidu.ui.behavior[i].call(me);
                    }
                }
            }

            //执行控件自己的构造器
            constructor.apply(me, arguments);

            //执行插件的构造器
            for (i=0, n=ui._addons.length; i<n; i++) {
                ui._addons[i](me);
            }
            if(opt.parent && me.setParent){
                me.setParent(opt.parent);
            }
            if(opt.autoRender){
                me.render(opt.element);
            }
        },
        C = function(){};

    C.prototype = superClass.prototype;

    //继承父类的原型链
    var proto = ui.prototype = new C();

    //继承Base中的方法到prototype中
    for (i in baidu.ui.Base) {
        proto[i] = baidu.ui.Base[i];
    }

    /**
     * 扩展控件的prototype
     *
     * @param {Object} json 要扩展进prototype的对象
     *
     * @return {Object} 扩展后的对象
     */
    ui.extend = function(json){
        for (i in json) {
            ui.prototype[i] = json[i];
        }
        return ui;  // 这个静态方法也返回类对象本身
    };

    //插件支持
    ui._addons = [];
    ui.register = function(f){
        if (typeof f == "function")
            ui._addons.push(f);
    };

    //静态配置支持
    ui.options = {};

    return ui;
};
<commit_msg>去掉继承类会重复执行render<commit_after>/*
 * Tangram UI
 * Copyright 2009 Baidu Inc. All rights reserved.
 *
 * path: baidu/ui/createUI.js
 * author: berg
 * version: 1.1.0
 * date: 2010/12/02
 */

///import baidu.ui;
///import baidu.ui.Base;
///import baidu.ui.create;
///import baidu.object.extend;

/**
 * 创建一个UI控件类
 *
 * @param {function} constructor ui控件构造器
 * @param {object} options 选项
 *
 * @return {object} ui控件
 */
baidu.ui.createUI = function(constructor, options) {
    options = options || {};
    var superClass = options.superClass || baidu.lang.Class,
        lastStep = superClass == baidu.lang.Class ? 1 : 0,
        i,
        n,
        ui = function(opt, _inherit){// 创建新类的真构造器函数
            var me = this;
            opt = opt || {};
            // 继承父类的构造器
            superClass.call(me, !lastStep ? opt : (opt.guid || ""), true);

            //扩展静态配置到this上
            baidu.object.extend(me, ui.options);
            //扩展当前options中的项到this上
            baidu.object.extend(me, opt);

            me.classPrefix = me.classPrefix || "tangram-" + me.uiType.toLowerCase();

            //初始化行为
            //行为就是在控件实例上附加一些属性和方法
            for(i in baidu.ui.behavior){
                //添加行为到控件上
                if(typeof me[i] != 'undefined' && me[i]){
                    baidu.object.extend(me, baidu.ui.behavior[i]);
                    if(baidu.lang.isFunction(me[i])){
                        me.addEventListener("onload", function(){
                            baidu.ui.behavior[i].call(me[i].apply(me));
                        });
                    }else{
                        baidu.ui.behavior[i].call(me);
                    }
                }
            }

            //执行控件自己的构造器
            constructor.apply(me, arguments);

            //执行插件的构造器
            for (i=0, n=ui._addons.length; i<n; i++) {
                ui._addons[i](me);
            }
            if(opt.parent && me.setParent){
                me.setParent(opt.parent);
            }
            if(opt.autoRender && !_inherit){
                me.render(opt.element);
            }
        },
        C = function(){};

    C.prototype = superClass.prototype;

    //继承父类的原型链
    var proto = ui.prototype = new C();

    //继承Base中的方法到prototype中
    for (i in baidu.ui.Base) {
        proto[i] = baidu.ui.Base[i];
    }

    /**
     * 扩展控件的prototype
     *
     * @param {Object} json 要扩展进prototype的对象
     *
     * @return {Object} 扩展后的对象
     */
    ui.extend = function(json){
        for (i in json) {
            ui.prototype[i] = json[i];
        }
        return ui;  // 这个静态方法也返回类对象本身
    };

    //插件支持
    ui._addons = [];
    ui.register = function(f){
        if (typeof f == "function")
            ui._addons.push(f);
    };

    //静态配置支持
    ui.options = {};

    return ui;
};
<|endoftext|>


<commit_before>Ext.namespace('Kwf.Facebook');
Kwf.Facebook.appId = '{Kwf_Util_Facebook_Assets::getAppId()}';
window.fbAsyncInit = function() {
    FB.init({
        appId      : Kwf.Facebook.appId, // App ID
        status     : true, // check login status
        cookie     : true, // enable cookies to allow the server to access the session
        oauth      : true, // enable OAuth 2.0
        xfbml      : true  // parse XFBML
    });
// Additional initialization code here
};
// Load the SDK Asynchronously
(function(d){
    var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
    js = d.createElement('script'); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/en_US/all.js";
    d.getElementsByTagName('head')[0].appendChild(js);
}(document));
<commit_msg>use of german facebook sdk version<commit_after>Ext.namespace('Kwf.Facebook');
Kwf.Facebook.appId = '{Kwf_Util_Facebook_Assets::getAppId()}';
window.fbAsyncInit = function() {
    FB.init({
        appId      : Kwf.Facebook.appId, // App ID
        status     : true, // check login status
        cookie     : true, // enable cookies to allow the server to access the session
        oauth      : true, // enable OAuth 2.0
        xfbml      : true  // parse XFBML
    });
// Additional initialization code here
};
// Load the SDK Asynchronously
(function(d){
    var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
    js = d.createElement('script'); js.id = id; js.async = true;
    js.src = "//connect.facebook.net/de_DE/all.js";
    d.getElementsByTagName('head')[0].appendChild(js);
}(document));
<|endoftext|>


<commit_before>var mongoose = require('mongoose');

var schema = new mongoose.Schema({
    model: {
        type: String,
        required: true
    },
    wheelsLevel: {
        type: Number,
        required: true
    },
    engineLevel: {
        type: Number,
        required: true
    },
    turbo: {
        type: Number,
        required: true
    },
    urlImage: {
        type: String
    },
    damage: {
        type: Number,
        required: true
    },
    dateCreated: {
        type: Date,
        required: true
    }
});

// TODO: Seed initial cars
mongoose.model('Car', schema);<commit_msg>properties: levelRequired and price added to Car model<commit_after>var mongoose = require('mongoose');

var schema = new mongoose.Schema({
    model: {
        type: String,
        required: true
    },
    wheelsLevel: {
        type: Number,
        required: true
    },
    engineLevel: {
        type: Number,
        required: true
    },
    turbo: {
        type: Number,
        required: true
    },
    urlImage: {
        type: String
    },
    damage: {
        type: Number,
        required: true
    },
    dateCreated: {
        type: Date,
        required: true
    },
    price : {
        type: Number,
        required: true
    },
    levelRequired: {
        type: Number,
        required: true
    }
});

// TODO: Seed initial cars
mongoose.model('Car', schema);<|endoftext|>


<commit_before>/*
 * This is a shim logging module for use in the browser.
 *
 * Our server-side logging library doesn't work client-side.  This just
 * implements the interface with basic output to `console.log`.
 *
 * TODO: For production use we should mimic the interface with NOOPs.
 *
 */

var common = require('./common')

var _console = ['log','error','warn','debug','info'].reduce((m,v) => {
	// IE9 doesn't even _define_ console unless the dev tools are open.
	// IE9 also needs a real function for `apply` to work.
	m[v] = (typeof console === 'undefined')
		?() => {}
		:Function.prototype.bind.call(console[v], console)
	return m;
}, {});

var lvl_map = {
	emergency : 'error',
	alert     : 'error',
	critical  : 'error',
	error     : 'error',
	warning   : 'warn',
	notice    : 'warn',
	debug     : 'debug',
	info      : 'info',
};
var clog = lvl => _console[lvl_map[lvl] || 'log'];

// IE9 also doesn't support color.
var monochrome = typeof _console.log == "object";

// We don't chain our transports in the same way as winston client-side, but
// we'll conform more-or-less to winston's interface for the `log` method for
// consistency's sake.  This means passing a function as the fourth argument.
// We'll use a noop.
var noop = () => {};

var makeLogger = function(group, opts){
	var config = common.config[group]

	var logger = {
		opts,
		name: opts.name,
		level: config.baseLevel,
		log: function(level, msg, meta){

			// We want an array of arguments to apply to
			// `console.log` so we don't trail an `undefined` when
			// `meta` isn't passed.
			var args = [msg];
			if (meta !== void 0) args.push(meta);

			this.transports.forEach(transport => {
				if (config.levels[level] > config.levels[transport.level]) return;
				setTimeout(transport.log.bind(transport, level, msg, meta, noop), 0);
			});

			if (config.levels[level] > config.levels[this.level]) return;

			clog(level).apply(
				_console,
				(monochrome?[`${level}: [${opts.name}]`]:[
					'%c'+level+'%c: [%c'+opts.name+'%c]',
					'color: '+config.colors[level],
					'color: black',
					'color: '+opts.color.client,
					'color: black',
				]).concat(args)
			);
		},
		transports: [],
		add: function(transport, opts){
			this.transports.push(new transport(opts));
		},
		stack: common.stack,
	}

	Object.keys(config.levels).forEach(level => {
		// note that this has to be an ES-5 style function and cannot be an arrow function
		// because arguments doesn't bind to the arrow function's arguments; it would bind
		// to makeLogger's arguments.
		logger[level] = function(){
			logger.log.apply(logger, [level].concat([].slice.call(arguments)));
		}
	});

	(config.extraTransports||[])
		.forEach(transport => logger.add(transport, opts));

	return logger;
}

var getLogger = common.makeGetLogger(makeLogger);

var setLevel = function(group, level){

	// Update level for any future loggers.
	common.config[group].baseLevel = level;

	common.forEachLogger((logger, loggerGroup) => {
		if (loggerGroup === group) logger.level = level;
	});
}

var addTransport = function(group, transport){

	if (!common.config[group].extraTransports){
		common.config[group].extraTransports = [];
	}

	common.config[group].extraTransports.push(transport);

	common.forEachLogger((logger, loggerGroup) => {
		if (loggerGroup === group) logger.add(transport, logger.opts);
	});
}

module.exports = { getLogger, setLevel, addTransport };
<commit_msg>Not all browsers implement all console methods<commit_after>/*
 * This is a shim logging module for use in the browser.
 *
 * Our server-side logging library doesn't work client-side.  This just
 * implements the interface with basic output to `console.log`.
 *
 * TODO: For production use we should mimic the interface with NOOPs.
 *
 */

var common = require('./common')

var _console = ['log','error','warn','debug','info'].reduce((m,v) => {
	// IE9 doesn't even _define_ console unless the dev tools are open.
	// IE9 also needs a real function for `apply` to work.
	m[v] = (typeof console === 'undefined')
		?() => {}
		:Function.prototype.bind.call(console[v]||console.log, console)
	return m;
}, {});

var lvl_map = {
	emergency : 'error',
	alert     : 'error',
	critical  : 'error',
	error     : 'error',
	warning   : 'warn',
	notice    : 'warn',
	debug     : 'debug',
	info      : 'info',
};
var clog = lvl => _console[lvl_map[lvl] || 'log'];

// IE9 also doesn't support color.
var monochrome = typeof _console.log == "object";

// We don't chain our transports in the same way as winston client-side, but
// we'll conform more-or-less to winston's interface for the `log` method for
// consistency's sake.  This means passing a function as the fourth argument.
// We'll use a noop.
var noop = () => {};

var makeLogger = function(group, opts){
	var config = common.config[group]

	var logger = {
		opts,
		name: opts.name,
		level: config.baseLevel,
		log: function(level, msg, meta){

			// We want an array of arguments to apply to
			// `console.log` so we don't trail an `undefined` when
			// `meta` isn't passed.
			var args = [msg];
			if (meta !== void 0) args.push(meta);

			this.transports.forEach(transport => {
				if (config.levels[level] > config.levels[transport.level]) return;
				setTimeout(transport.log.bind(transport, level, msg, meta, noop), 0);
			});

			if (config.levels[level] > config.levels[this.level]) return;

			clog(level).apply(
				_console,
				(monochrome?[`${level}: [${opts.name}]`]:[
					'%c'+level+'%c: [%c'+opts.name+'%c]',
					'color: '+config.colors[level],
					'color: black',
					'color: '+opts.color.client,
					'color: black',
				]).concat(args)
			);
		},
		transports: [],
		add: function(transport, opts){
			this.transports.push(new transport(opts));
		},
		stack: common.stack,
	}

	Object.keys(config.levels).forEach(level => {
		// note that this has to be an ES-5 style function and cannot be an arrow function
		// because arguments doesn't bind to the arrow function's arguments; it would bind
		// to makeLogger's arguments.
		logger[level] = function(){
			logger.log.apply(logger, [level].concat([].slice.call(arguments)));
		}
	});

	(config.extraTransports||[])
		.forEach(transport => logger.add(transport, opts));

	return logger;
}

var getLogger = common.makeGetLogger(makeLogger);

var setLevel = function(group, level){

	// Update level for any future loggers.
	common.config[group].baseLevel = level;

	common.forEachLogger((logger, loggerGroup) => {
		if (loggerGroup === group) logger.level = level;
	});
}

var addTransport = function(group, transport){

	if (!common.config[group].extraTransports){
		common.config[group].extraTransports = [];
	}

	common.config[group].extraTransports.push(transport);

	common.forEachLogger((logger, loggerGroup) => {
		if (loggerGroup === group) logger.add(transport, logger.opts);
	});
}

module.exports = { getLogger, setLevel, addTransport };
<|endoftext|>


<commit_before>function Cell(nodeId, owner, position, mass, gameServer) {
    this.nodeId = nodeId;
    this.owner = owner; // playerTracker that owns this cell
    this.color = {
        r: 0,
        g: 255,
        b: 0
    };
    this.position = position;
    this.mass = mass; // Starting mass of the cell
    this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.spiked = 0; // If 1, then this cell has spikes around it

    this.killedBy; // Cell that ate this cell
    this.gameServer = gameServer;

    this.moveEngineSpeed = 0;
    this.moveDecay = 0.85;
    this.angle = 0; // Angle of movement
    this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getName = function() {
    if (this.owner) {
        return this.owner.name;
    } else {
        return "";
    }
};

Cell.prototype.setColor = function(color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.getColor = function() {
    return this.color;
};

Cell.prototype.getType = function() {
    return this.cellType;
};

Cell.prototype.getSize = function() {
    // Calculates radius based on cell mass
    return Math.ceil(Math.sqrt(100 * this.mass));
};

Cell.prototype.getSquareSize = function() {
    // R * R
    return (100 * this.mass) >> 0;
};

Cell.prototype.addMass = function(n) {
    // Check if the cell needs to autosplit before adding mass
    if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) {
        var splitMass = this.mass / 2;
        var randomAngle = Math.random() * 6.28; // Get random angle
        this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass);
    } else {
        this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass);
    }
    this.mass += n;
};

Cell.prototype.getSpeed = function() {
    // Based on 50ms ticks. If updateMoveEngine interval changes, change 50 to new value
    // (should possibly have a config value for this?)

    // Old formulas:
    // return 5 + (20 * (1 - (this.mass/(70+this.mass))));
    // return this.gameServer.config.playerSpeed * Math.pow(this.mass, -0.22) * 50 / 40;
    var t = Math.PI * Math.PI;
    return this.gameServer.config.playerSpeed * Math.pow(this.mass, -Math.PI / t / 1.5);
};

Cell.prototype.setAngle = function(radians) {
    this.angle = radians;
};

Cell.prototype.getAngle = function() {
    return this.angle;
};

Cell.prototype.setMoveEngineData = function(speed, ticks, decay) {
    this.moveEngineSpeed = speed;
    this.moveEngineTicks = ticks;
    this.moveDecay = isNaN(decay) ? 0.75 : decay;
};

Cell.prototype.getEatingRange = function() {
    return 0; // 0 for ejected cells
};

Cell.prototype.getKiller = function() {
    return this.killedBy;
};

Cell.prototype.setKiller = function(cell) {
    this.killedBy = cell;
};

// Functions

Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) {
    // Collision checking
    if (this.position.y > bottomY) {
        return false;
    }

    if (this.position.y < topY) {
        return false;
    }

    if (this.position.x > rightX) {
        return false;
    }

    if (this.position.x < leftX) {
        return false;
    }

    return true;
};

// This collision checking function is based on CIRCLE shape
Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) {
    // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells)
    // (O1O2 + r)^2 <= R^2
    // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2

    var dx = this.position.x - objectPosition.x;
    var dy = this.position.y - objectPosition.y;

    return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize);
};

Cell.prototype.visibleCheck = function(box, centerPos, cells) {
    // Checks if this cell is visible to the player
    var isThere = false;
    if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX);
    else {
        var cellSize = this.getSize();
        var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int)
        var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int)

        isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY);
    }
    if (isThere) {
        // It is
        // To save perfomance, check if any client's cell collides with this cell
        for (var i = 0; i < cells.length; i++) {
            var cell = cells[i];
            if (!cell) continue;

            var xs = this.position.x - cell.position.x;
            var ys = this.position.y - cell.position.y;
            var sqDist = xs * xs + ys * ys;

            var collideDist = cell.getSquareSize() + this.getSquareSize();

            if (sqDist < collideDist) {
                return 2;
            }// Colliding with one
        }
        return 1; // Not colliding with any
    }
    else return 0;
};

Cell.prototype.calcMovePhys = function(config) {
    // Move, twice as slower
    var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle));
    var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle));

    // Movement engine
    var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay;
    this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower

    // Ejected cell collision
    if (this.cellType == 3) {
        for (var i = 0; i < this.gameServer.nodesEjected.length; i++) {
            var check = this.gameServer.nodesEjected[i];

            if (check.nodeId == this.nodeId) continue; // Don't check for yourself

            var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y);
            var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit

            if (dist < allowDist) {
                // Two ejected cells collided
                var deltaX = this.position.x - check.position.x;
                var deltaY = this.position.y - check.position.y;
                var angle = Math.atan2(deltaX, deltaY);

                var move = allowDist - dist;

                X += Math.sin(angle) * move / 2;
                Y += Math.cos(angle) * move / 2;
            }
        }
    }

    // Border check - Bouncy physics
    var radius = 40;
    if ((this.position.x - radius) < -config.borderLeft) {
        // Flip angle horizontally - Left side
        this.angle = 6.28 - this.angle;
        X = -config.borderLeft + radius;
    }
    if ((this.position.x + radius) > config.borderRight) {
        // Flip angle horizontally - Right side
        this.angle = 6.28 - this.angle;
        X = config.borderRight - radius;
    }
    if ((this.position.y - radius) < -config.borderTop) {
        // Flip angle vertically - Top side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = -config.borderTop + radius;
    }
    if ((this.position.y + radius) > config.borderBottom) {
        // Flip angle vertically - Bottom side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = config.borderBottom - radius;
    }

    // Set position
    this.position.x = X;
    this.position.y = Y;
};

// Override these

Cell.prototype.sendUpdate = function() {
    // Whether or not to include this cell in the update packet
    return true;
};

Cell.prototype.onConsume = function(consumer, gameServer) {
    // Called when the cell is consumed
};

Cell.prototype.onAdd = function(gameServer) {
    // Called when this cell is added to the world
};

Cell.prototype.onRemove = function(gameServer) {
    // Called when this cell is removed
};

Cell.prototype.onAutoMove = function(gameServer) {
    // Called on each auto move engine tick
};

Cell.prototype.moveDone = function(gameServer) {
    // Called when this cell finished moving with the auto move engine
};

// Lib

Cell.prototype.abs = function(x) {
    return x < 0 ? -x : x;
};

Cell.prototype.getDist = function(x1, y1, x2, y2) {
    var xs = x2 - x1;
    xs = xs * xs;

    var ys = y2 - y1;
    ys = ys * ys;

    return Math.sqrt(xs + ys);
};
<commit_msg>Remove ticks argument as it isn't used anymore<commit_after>function Cell(nodeId, owner, position, mass, gameServer) {
    this.nodeId = nodeId;
    this.owner = owner; // playerTracker that owns this cell
    this.color = {
        r: 0,
        g: 255,
        b: 0
    };
    this.position = position;
    this.mass = mass; // Starting mass of the cell
    this.cellType = -1; // 0 = Player Cell, 1 = Food, 2 = Virus, 3 = Ejected Mass
    this.spiked = 0; // If 1, then this cell has spikes around it

    this.killedBy; // Cell that ate this cell
    this.gameServer = gameServer;

    this.moveEngineSpeed = 0;
    this.moveDecay = 0.85;
    this.angle = 0; // Angle of movement
    this.collisionRestoreTicks = 0; // Ticks left before cell starts checking for collision with client's cells
}

module.exports = Cell;

// Fields not defined by the constructor are considered private and need a getter/setter to access from a different class

Cell.prototype.getName = function() {
    if (this.owner) {
        return this.owner.name;
    } else {
        return "";
    }
};

Cell.prototype.setColor = function(color) {
    this.color.r = color.r;
    this.color.g = color.g;
    this.color.b = color.b;
};

Cell.prototype.getColor = function() {
    return this.color;
};

Cell.prototype.getType = function() {
    return this.cellType;
};

Cell.prototype.getSize = function() {
    // Calculates radius based on cell mass
    return Math.ceil(Math.sqrt(100 * this.mass));
};

Cell.prototype.getSquareSize = function() {
    // R * R
    return (100 * this.mass) >> 0;
};

Cell.prototype.addMass = function(n) {
    // Check if the cell needs to autosplit before adding mass
    if (this.mass > this.gameServer.config.playerMaxMass && this.owner.cells.length < this.gameServer.config.playerMaxCells) {
        var splitMass = this.mass / 2;
        var randomAngle = Math.random() * 6.28; // Get random angle
        this.gameServer.createPlayerCell(this.owner, this, randomAngle, splitMass);
    } else {
        this.mass = Math.min(this.mass, this.gameServer.config.playerMaxMass);
    }
    this.mass += n;
};

Cell.prototype.getSpeed = function() {
    // Based on 50ms ticks. If updateMoveEngine interval changes, change 50 to new value
    // (should possibly have a config value for this?)

    // Old formulas:
    // return 5 + (20 * (1 - (this.mass/(70+this.mass))));
    // return this.gameServer.config.playerSpeed * Math.pow(this.mass, -0.22) * 50 / 40;
    var t = Math.PI * Math.PI;
    return this.gameServer.config.playerSpeed * Math.pow(this.mass, -Math.PI / t / 1.5);
};

Cell.prototype.setAngle = function(radians) {
    this.angle = radians;
};

Cell.prototype.getAngle = function() {
    return this.angle;
};

Cell.prototype.setMoveEngineData = function(speed, decay) {
    this.moveEngineSpeed = speed;
    this.moveDecay = isNaN(decay) ? 0.75 : decay;
};

Cell.prototype.getEatingRange = function() {
    return 0; // 0 for ejected cells
};

Cell.prototype.getKiller = function() {
    return this.killedBy;
};

Cell.prototype.setKiller = function(cell) {
    this.killedBy = cell;
};

// Functions

Cell.prototype.collisionCheck = function(bottomY, topY, rightX, leftX) {
    // Collision checking
    if (this.position.y > bottomY) {
        return false;
    }

    if (this.position.y < topY) {
        return false;
    }

    if (this.position.x > rightX) {
        return false;
    }

    if (this.position.x < leftX) {
        return false;
    }

    return true;
};

// This collision checking function is based on CIRCLE shape
Cell.prototype.collisionCheck2 = function(objectSquareSize, objectPosition) {
    // IF (O1O2 + r <= R) THEN collided. (O1O2: distance b/w 2 centers of cells)
    // (O1O2 + r)^2 <= R^2
    // approximately, remove 2*O1O2*r because it requires sqrt(): O1O2^2 + r^2 <= R^2

    var dx = this.position.x - objectPosition.x;
    var dy = this.position.y - objectPosition.y;

    return (dx * dx + dy * dy + this.getSquareSize() <= objectSquareSize);
};

Cell.prototype.visibleCheck = function(box, centerPos, cells) {
    // Checks if this cell is visible to the player
    var isThere = false;
    if (this.mass < 100) isThere = this.collisionCheck(box.bottomY, box.topY, box.rightX, box.leftX);
    else {
        var cellSize = this.getSize();
        var lenX = cellSize + box.width >> 0; // Width of cell + width of the box (Int)
        var lenY = cellSize + box.height >> 0; // Height of cell + height of the box (Int)

        isThere = (this.abs(this.position.x - centerPos.x) < lenX) && (this.abs(this.position.y - centerPos.y) < lenY);
    }
    if (isThere) {
        // It is
        // To save perfomance, check if any client's cell collides with this cell
        for (var i = 0; i < cells.length; i++) {
            var cell = cells[i];
            if (!cell) continue;

            var xs = this.position.x - cell.position.x;
            var ys = this.position.y - cell.position.y;
            var sqDist = xs * xs + ys * ys;

            var collideDist = cell.getSquareSize() + this.getSquareSize();

            if (sqDist < collideDist) {
                return 2;
            }// Colliding with one
        }
        return 1; // Not colliding with any
    }
    else return 0;
};

Cell.prototype.calcMovePhys = function(config) {
    // Move, twice as slower
    var X = this.position.x + ((this.moveEngineSpeed / 2) * Math.sin(this.angle));
    var Y = this.position.y + ((this.moveEngineSpeed / 2) * Math.cos(this.angle));

    // Movement engine
    var speedDecrease = this.moveEngineSpeed - this.moveEngineSpeed * this.moveDecay;
    this.moveEngineSpeed -= speedDecrease / 2; // Decaying speed twice as slower

    // Ejected cell collision
    if (this.cellType == 3) {
        for (var i = 0; i < this.gameServer.nodesEjected.length; i++) {
            var check = this.gameServer.nodesEjected[i];

            if (check.nodeId == this.nodeId) continue; // Don't check for yourself

            var dist = this.getDist(this.position.x, this.position.y, check.position.x, check.position.y);
            var allowDist = this.getSize() + check.getSize(); // Allow cells to get in themselves a bit

            if (dist < allowDist) {
                // Two ejected cells collided
                var deltaX = this.position.x - check.position.x;
                var deltaY = this.position.y - check.position.y;
                var angle = Math.atan2(deltaX, deltaY);

                var move = allowDist - dist;

                X += Math.sin(angle) * move / 2;
                Y += Math.cos(angle) * move / 2;
            }
        }
    }

    // Border check - Bouncy physics
    var radius = 40;
    if ((this.position.x - radius) < -config.borderLeft) {
        // Flip angle horizontally - Left side
        this.angle = 6.28 - this.angle;
        X = -config.borderLeft + radius;
    }
    if ((this.position.x + radius) > config.borderRight) {
        // Flip angle horizontally - Right side
        this.angle = 6.28 - this.angle;
        X = config.borderRight - radius;
    }
    if ((this.position.y - radius) < -config.borderTop) {
        // Flip angle vertically - Top side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = -config.borderTop + radius;
    }
    if ((this.position.y + radius) > config.borderBottom) {
        // Flip angle vertically - Bottom side
        this.angle = (this.angle <= 3.14) ? 3.14 - this.angle : 9.42 - this.angle;
        Y = config.borderBottom - radius;
    }

    // Set position
    this.position.x = X;
    this.position.y = Y;
};

// Override these

Cell.prototype.sendUpdate = function() {
    // Whether or not to include this cell in the update packet
    return true;
};

Cell.prototype.onConsume = function(consumer, gameServer) {
    // Called when the cell is consumed
};

Cell.prototype.onAdd = function(gameServer) {
    // Called when this cell is added to the world
};

Cell.prototype.onRemove = function(gameServer) {
    // Called when this cell is removed
};

Cell.prototype.onAutoMove = function(gameServer) {
    // Called on each auto move engine tick
};

Cell.prototype.moveDone = function(gameServer) {
    // Called when this cell finished moving with the auto move engine
};

// Lib

Cell.prototype.abs = function(x) {
    return x < 0 ? -x : x;
};

Cell.prototype.getDist = function(x1, y1, x2, y2) {
    var xs = x2 - x1;
    xs = xs * xs;

    var ys = y2 - y1;
    ys = ys * ys;

    return Math.sqrt(xs + ys);
};
<|endoftext|>


<commit_before>/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/*global tinymce:true */

tinymce.PluginManager.add('link', function(editor) {
	function createLinkList(callback) {
		return function() {
			var linkList = editor.settings.link_list;

			if (typeof linkList == "string") {
				tinymce.util.XHR.send({
					url: linkList,
					success: function(text) {
						callback(tinymce.util.JSON.parse(text));
					}
				});
			} else if (typeof linkList == "function") {
				linkList(callback);
			} else {
				callback(linkList);
			}
		};
	}

	function buildListItems(inputList, itemCallback, startItems) {
		function appendItems(values, output) {
			output = output || [];

			tinymce.each(values, function(item) {
				var menuItem = {text: item.text || item.title};

				if (item.menu) {
					menuItem.menu = appendItems(item.menu);
				} else {
					menuItem.value = item.value;

					if (itemCallback) {
						itemCallback(menuItem);
					}
				}

				output.push(menuItem);
			});

			return output;
		}

		return appendItems(inputList, startItems || []);
	}

	function showDialog(linkList) {
		var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
		var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value;

		function linkListChangeHandler(e) {
			var textCtrl = win.find('#text');

			if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) {
				textCtrl.value(e.control.text());
			}

			win.find('#href').value(e.control.value());
		}

		function buildAnchorListControl(url) {
			var anchorList = [];

			tinymce.each(editor.dom.select('a:not([href])'), function(anchor) {
				var id = anchor.name || anchor.id;

				if (id) {
					anchorList.push({
						text: id,
						value: '#' + id,
						selected: url.indexOf('#' + id) != -1
					});
				}
			});

			if (anchorList.length) {
				anchorList.unshift({text: 'None', value: ''});

				return {
					name: 'anchor',
					type: 'listbox',
					label: 'Anchors',
					values: anchorList,
					onselect: linkListChangeHandler
				};
			}
		}

		function updateText() {
			if (!initialText && data.text.length === 0 && onlyText) {
				this.parent().parent().find('#text')[0].value(this.value());
			}
		}

		function urlChange(e) {
			var meta = e.meta || {};

			if (linkListCtrl) {
				linkListCtrl.value(editor.convertURL(this.value(), 'href'));
			}

			tinymce.each(e.meta, function(value, key) {
				win.find('#' + key).value(value);
			});

			if (!meta.text) {
				updateText.call(this);
			}
		}

		function isOnlyTextSelected(anchorElm) {
			var html = selection.getContent();

			// Partial html and not a fully selected anchor element
			if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) {
				return false;
			}

			if (anchorElm) {
				var nodes = anchorElm.childNodes, i;

				if (nodes.length === 0) {
					return false;
				}

				for (i = nodes.length - 1; i >= 0; i--) {
					if (nodes[i].nodeType != 3) {
						return false;
					}
				}
			}

			return true;
		}

		selectedElm = selection.getNode();
		anchorElm = dom.getParent(selectedElm, 'a[href]');
		onlyText = isOnlyTextSelected();

		data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'});
		data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';

		if (anchorElm) {
			data.target = dom.getAttrib(anchorElm, 'target');
		} else if (editor.settings.default_link_target) {
			data.target = editor.settings.default_link_target;
		}

		if ((value = dom.getAttrib(anchorElm, 'rel'))) {
			data.rel = value;
		}

		if ((value = dom.getAttrib(anchorElm, 'class'))) {
			data['class'] = value;
		}

		if ((value = dom.getAttrib(anchorElm, 'title'))) {
			data.title = value;
		}

		if (onlyText) {
			textListCtrl = {
				name: 'text',
				type: 'textbox',
				size: 40,
				label: 'Text to display',
				onchange: function() {
					data.text = this.value();
				}
			};
		}

		if (linkList) {
			linkListCtrl = {
				type: 'listbox',
				label: 'Link list',
				values: buildListItems(
					linkList,
					function(item) {
						item.value = editor.convertURL(item.value || item.url, 'href');
					},
					[{text: 'None', value: ''}]
				),
				onselect: linkListChangeHandler,
				value: editor.convertURL(data.href, 'href'),
				onPostRender: function() {
					linkListCtrl = this;
				}
			};
		}

		if (editor.settings.target_list !== false) {
			if (!editor.settings.target_list) {
				editor.settings.target_list = [
					{text: 'None', value: ''},
					{text: 'New window', value: '_blank'}
				];
			}

			targetListCtrl = {
				name: 'target',
				type: 'listbox',
				label: 'Target',
				values: buildListItems(editor.settings.target_list)
			};
		}

		if (editor.settings.rel_list) {
			relListCtrl = {
				name: 'rel',
				type: 'listbox',
				label: 'Rel',
				values: buildListItems(editor.settings.rel_list)
			};
		}

		if (editor.settings.link_class_list) {
			classListCtrl = {
				name: 'class',
				type: 'listbox',
				label: 'Class',
				values: buildListItems(
					editor.settings.link_class_list,
					function(item) {
						if (item.value) {
							item.textStyle = function() {
								return editor.formatter.getCssText({inline: 'a', classes: [item.value]});
							};
						}
					}
				)
			};
		}

		if (editor.settings.link_title !== false) {
			linkTitleCtrl = {
				name: 'title',
				type: 'textbox',
				label: 'Title',
				value: data.title
			};
		}

		win = editor.windowManager.open({
			title: 'Insert link',
			data: data,
			body: [
				{
					name: 'href',
					type: 'filepicker',
					filetype: 'file',
					size: 40,
					autofocus: true,
					label: 'Url',
					onchange: urlChange,
					onkeyup: updateText
				},
				textListCtrl,
				linkTitleCtrl,
				buildAnchorListControl(data.href),
				linkListCtrl,
				relListCtrl,
				targetListCtrl,
				classListCtrl
			],
			onSubmit: function(e) {
				/*eslint dot-notation: 0*/
				var href;

				data = tinymce.extend(data, e.data);
				href = data.href;

				// Delay confirm since onSubmit will move focus
				function delayedConfirm(message, callback) {
					var rng = editor.selection.getRng();

					window.setTimeout(function() {
						editor.windowManager.confirm(message, function(state) {
							editor.selection.setRng(rng);
							callback(state);
						});
					}, 0);
				}

				function insertLink() {
					var linkAttrs = {
						href: href,
						target: data.target ? data.target : null,
						rel: data.rel ? data.rel : null,
						"class": data["class"] ? data["class"] : null,
						title: data.title ? data.title : null
					};

					if (anchorElm) {
						editor.focus();

						if (onlyText && data.text != initialText) {
							if ("innerText" in anchorElm) {
								anchorElm.innerText = data.text;
							} else {
								anchorElm.textContent = data.text;
							}
						}

						dom.setAttribs(anchorElm, linkAttrs);

						selection.select(anchorElm);
						editor.undoManager.add();
					} else {
						if (onlyText) {
							editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text)));
						} else {
							editor.execCommand('mceInsertLink', false, linkAttrs);
						}
					}
				}

				if (!href) {
					editor.execCommand('unlink');
					return;
				}

				// Is email and not //user@domain.com
				if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) {
					delayedConfirm(
						'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
						function(state) {
							if (state) {
								href = 'mailto:' + href;
							}

							insertLink();
						}
					);

					return;
				}

				// Is not protocol prefixed
				if ((editor.settings.link_assume_external_targets && !/^\w+:/i.test(href)) ||
					(!editor.settings.link_assume_external_targets && /^\s*www\./i.test(href))) {
					delayedConfirm(
						'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?',
						function(state) {
							if (state) {
								href = 'http://' + href;
							}

							insertLink();
						}
					);

					return;
				}

				insertLink();
			}
		});
	}

	editor.addButton('link', {
		icon: 'link',
		tooltip: 'Insert/edit link',
		shortcut: 'Meta+K',
		onclick: createLinkList(showDialog),
		stateSelector: 'a[href]'
	});

	editor.addButton('unlink', {
		icon: 'unlink',
		tooltip: 'Remove link',
		cmd: 'unlink',
		stateSelector: 'a[href]'
	});

	editor.addShortcut('Meta+K', '', createLinkList(showDialog));
	editor.addCommand('mceLink', createLinkList(showDialog));

	this.showDialog = showDialog;

	editor.addMenuItem('link', {
		icon: 'link',
		text: 'Insert/edit link',
		shortcut: 'Meta+K',
		onclick: createLinkList(showDialog),
		stateSelector: 'a[href]',
		context: 'insert',
		prependToContext: true
	});
});
<commit_msg>improving validation of external links to accept a number after the 'www'<commit_after>/**
 * plugin.js
 *
 * Released under LGPL License.
 * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/*global tinymce:true */

tinymce.PluginManager.add('link', function(editor) {
	function createLinkList(callback) {
		return function() {
			var linkList = editor.settings.link_list;

			if (typeof linkList == "string") {
				tinymce.util.XHR.send({
					url: linkList,
					success: function(text) {
						callback(tinymce.util.JSON.parse(text));
					}
				});
			} else if (typeof linkList == "function") {
				linkList(callback);
			} else {
				callback(linkList);
			}
		};
	}

	function buildListItems(inputList, itemCallback, startItems) {
		function appendItems(values, output) {
			output = output || [];

			tinymce.each(values, function(item) {
				var menuItem = {text: item.text || item.title};

				if (item.menu) {
					menuItem.menu = appendItems(item.menu);
				} else {
					menuItem.value = item.value;

					if (itemCallback) {
						itemCallback(menuItem);
					}
				}

				output.push(menuItem);
			});

			return output;
		}

		return appendItems(inputList, startItems || []);
	}

	function showDialog(linkList) {
		var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
		var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value;

		function linkListChangeHandler(e) {
			var textCtrl = win.find('#text');

			if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) {
				textCtrl.value(e.control.text());
			}

			win.find('#href').value(e.control.value());
		}

		function buildAnchorListControl(url) {
			var anchorList = [];

			tinymce.each(editor.dom.select('a:not([href])'), function(anchor) {
				var id = anchor.name || anchor.id;

				if (id) {
					anchorList.push({
						text: id,
						value: '#' + id,
						selected: url.indexOf('#' + id) != -1
					});
				}
			});

			if (anchorList.length) {
				anchorList.unshift({text: 'None', value: ''});

				return {
					name: 'anchor',
					type: 'listbox',
					label: 'Anchors',
					values: anchorList,
					onselect: linkListChangeHandler
				};
			}
		}

		function updateText() {
			if (!initialText && data.text.length === 0 && onlyText) {
				this.parent().parent().find('#text')[0].value(this.value());
			}
		}

		function urlChange(e) {
			var meta = e.meta || {};

			if (linkListCtrl) {
				linkListCtrl.value(editor.convertURL(this.value(), 'href'));
			}

			tinymce.each(e.meta, function(value, key) {
				win.find('#' + key).value(value);
			});

			if (!meta.text) {
				updateText.call(this);
			}
		}

		function isOnlyTextSelected(anchorElm) {
			var html = selection.getContent();

			// Partial html and not a fully selected anchor element
			if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) {
				return false;
			}

			if (anchorElm) {
				var nodes = anchorElm.childNodes, i;

				if (nodes.length === 0) {
					return false;
				}

				for (i = nodes.length - 1; i >= 0; i--) {
					if (nodes[i].nodeType != 3) {
						return false;
					}
				}
			}

			return true;
		}

		selectedElm = selection.getNode();
		anchorElm = dom.getParent(selectedElm, 'a[href]');
		onlyText = isOnlyTextSelected();

		data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'});
		data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';

		if (anchorElm) {
			data.target = dom.getAttrib(anchorElm, 'target');
		} else if (editor.settings.default_link_target) {
			data.target = editor.settings.default_link_target;
		}

		if ((value = dom.getAttrib(anchorElm, 'rel'))) {
			data.rel = value;
		}

		if ((value = dom.getAttrib(anchorElm, 'class'))) {
			data['class'] = value;
		}

		if ((value = dom.getAttrib(anchorElm, 'title'))) {
			data.title = value;
		}

		if (onlyText) {
			textListCtrl = {
				name: 'text',
				type: 'textbox',
				size: 40,
				label: 'Text to display',
				onchange: function() {
					data.text = this.value();
				}
			};
		}

		if (linkList) {
			linkListCtrl = {
				type: 'listbox',
				label: 'Link list',
				values: buildListItems(
					linkList,
					function(item) {
						item.value = editor.convertURL(item.value || item.url, 'href');
					},
					[{text: 'None', value: ''}]
				),
				onselect: linkListChangeHandler,
				value: editor.convertURL(data.href, 'href'),
				onPostRender: function() {
					linkListCtrl = this;
				}
			};
		}

		if (editor.settings.target_list !== false) {
			if (!editor.settings.target_list) {
				editor.settings.target_list = [
					{text: 'None', value: ''},
					{text: 'New window', value: '_blank'}
				];
			}

			targetListCtrl = {
				name: 'target',
				type: 'listbox',
				label: 'Target',
				values: buildListItems(editor.settings.target_list)
			};
		}

		if (editor.settings.rel_list) {
			relListCtrl = {
				name: 'rel',
				type: 'listbox',
				label: 'Rel',
				values: buildListItems(editor.settings.rel_list)
			};
		}

		if (editor.settings.link_class_list) {
			classListCtrl = {
				name: 'class',
				type: 'listbox',
				label: 'Class',
				values: buildListItems(
					editor.settings.link_class_list,
					function(item) {
						if (item.value) {
							item.textStyle = function() {
								return editor.formatter.getCssText({inline: 'a', classes: [item.value]});
							};
						}
					}
				)
			};
		}

		if (editor.settings.link_title !== false) {
			linkTitleCtrl = {
				name: 'title',
				type: 'textbox',
				label: 'Title',
				value: data.title
			};
		}

		win = editor.windowManager.open({
			title: 'Insert link',
			data: data,
			body: [
				{
					name: 'href',
					type: 'filepicker',
					filetype: 'file',
					size: 40,
					autofocus: true,
					label: 'Url',
					onchange: urlChange,
					onkeyup: updateText
				},
				textListCtrl,
				linkTitleCtrl,
				buildAnchorListControl(data.href),
				linkListCtrl,
				relListCtrl,
				targetListCtrl,
				classListCtrl
			],
			onSubmit: function(e) {
				/*eslint dot-notation: 0*/
				var href;

				data = tinymce.extend(data, e.data);
				href = data.href;

				// Delay confirm since onSubmit will move focus
				function delayedConfirm(message, callback) {
					var rng = editor.selection.getRng();

					window.setTimeout(function() {
						editor.windowManager.confirm(message, function(state) {
							editor.selection.setRng(rng);
							callback(state);
						});
					}, 0);
				}

				function insertLink() {
					var linkAttrs = {
						href: href,
						target: data.target ? data.target : null,
						rel: data.rel ? data.rel : null,
						"class": data["class"] ? data["class"] : null,
						title: data.title ? data.title : null
					};

					if (anchorElm) {
						editor.focus();

						if (onlyText && data.text != initialText) {
							if ("innerText" in anchorElm) {
								anchorElm.innerText = data.text;
							} else {
								anchorElm.textContent = data.text;
							}
						}

						dom.setAttribs(anchorElm, linkAttrs);

						selection.select(anchorElm);
						editor.undoManager.add();
					} else {
						if (onlyText) {
							editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text)));
						} else {
							editor.execCommand('mceInsertLink', false, linkAttrs);
						}
					}
				}

				if (!href) {
					editor.execCommand('unlink');
					return;
				}

				// Is email and not //user@domain.com
				if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) {
					delayedConfirm(
						'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
						function(state) {
							if (state) {
								href = 'mailto:' + href;
							}

							insertLink();
						}
					);

					return;
				}

				// Is not protocol prefixed
				if ((editor.settings.link_assume_external_targets && !/^\w+:/i.test(href)) ||
					(!editor.settings.link_assume_external_targets && /^\s*www[\.|\d\.]/i.test(href))) {
					delayedConfirm(
						'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?',
						function(state) {
							if (state) {
								href = 'http://' + href;
							}

							insertLink();
						}
					);

					return;
				}

				insertLink();
			}
		});
	}

	editor.addButton('link', {
		icon: 'link',
		tooltip: 'Insert/edit link',
		shortcut: 'Meta+K',
		onclick: createLinkList(showDialog),
		stateSelector: 'a[href]'
	});

	editor.addButton('unlink', {
		icon: 'unlink',
		tooltip: 'Remove link',
		cmd: 'unlink',
		stateSelector: 'a[href]'
	});

	editor.addShortcut('Meta+K', '', createLinkList(showDialog));
	editor.addCommand('mceLink', createLinkList(showDialog));

	this.showDialog = showDialog;

	editor.addMenuItem('link', {
		icon: 'link',
		text: 'Insert/edit link',
		shortcut: 'Meta+K',
		onclick: createLinkList(showDialog),
		stateSelector: 'a[href]',
		context: 'insert',
		prependToContext: true
	});
});
<|endoftext|>


<commit_before>// FORM/POST PARAMETERS
// None
// HEADERS

// Content-Length: 954

// Host: requestb.in

// X-Gitlab-Event: Push Hook

// Total-Route-Time: 0

// X-Request-Id: ca31f1a6-b95b-4fb9-ad33-bd5b9cdb8be0

// Connect-Time: 2

// Content-Type: application/json

// Connection: close

// Via: 1.1 vegur

// matiasheffel [5:01 PM]
// the raw body

// matiasheffel [5:01 PM]
// {"object_kind":"push","before":"8fa95cebec33f63f8e080ffbd11f7b8bae61f9c6","after":"95071710195f2aefc4ba7a5a2fd7d119b4123482","ref":"refs/heads/master","checkout_sha":"95071710195f2aefc4ba7a5a2fd7d119b4123482","message":null,"user_id":1,"user_name":"Administrator","user_email":"user@example.com","project_id":1,"repository":{"name":"gitlab-project-test","url":"git@137.135.123.9:user/gitlab-project-test.git","description":"","homepage":"http://137.135.123.9/user/gitlab-project-test","git_http_url":"http://137.135.123.9/user/gitlab-project-test.git","git_ssh_url":"git@137.135.123.9:user/gitlab-project-test.git","visibility_level":20},"commits":[{"id":"95071710195f2aefc4ba7a5a2fd7d119b4123482","message":"S-0001 hello\n","timestamp":"2015-08-05T18:00:13-03:00","url":"http://137.135.123.9/user/gitlab-project-test/commit/95071710195f2aefc4ba7a5a2fd7d119b4123482","author":{"name":"Administrator","email":"user@example.com"}}],"total_commits_count":1}

// http://requestb.in/1mazrmi1?inspect

'use strict';

(function (gitLabTranslator) {

  gitLabTranslator.hasCorrectHeaders = function (headers) {
    return headers.hasOwnProperty('x-gitlab-event') && headers['x-gitlab-event'] === 'Push Hook';
  };

  gitLabTranslator.canTranslate = function (request) {
    var headers = request.headers;

    if (gitLabTranslator.hasCorrectHeaders(headers)) {
      return true;
    }
    return false;
  };
})(module.exports);<commit_msg>S-53159: oops forgot to commit generated .js<commit_after>// FORM/POST PARAMETERS
// None
// HEADERS

// Content-Length: 954

// Host: requestb.in

// X-Gitlab-Event: Push Hook

// Total-Route-Time: 0

// X-Request-Id: ca31f1a6-b95b-4fb9-ad33-bd5b9cdb8be0

// Connect-Time: 2

// Content-Type: application/json

// Connection: close

// Via: 1.1 vegur

// matiasheffel [5:01 PM]
// the raw body

// matiasheffel [5:01 PM]
// {"object_kind":"push","before":"8fa95cebec33f63f8e080ffbd11f7b8bae61f9c6","after":"95071710195f2aefc4ba7a5a2fd7d119b4123482","ref":"refs/heads/master","checkout_sha":"95071710195f2aefc4ba7a5a2fd7d119b4123482","message":null,"user_id":1,"user_name":"Administrator","user_email":"user@example.com","project_id":1,"repository":{"name":"gitlab-project-test","url":"git@137.135.123.9:user/gitlab-project-test.git","description":"","homepage":"http://137.135.123.9/user/gitlab-project-test","git_http_url":"http://137.135.123.9/user/gitlab-project-test.git","git_ssh_url":"git@137.135.123.9:user/gitlab-project-test.git","visibility_level":20},"commits":[{"id":"95071710195f2aefc4ba7a5a2fd7d119b4123482","message":"S-0001 hello\n","timestamp":"2015-08-05T18:00:13-03:00","url":"http://137.135.123.9/user/gitlab-project-test/commit/95071710195f2aefc4ba7a5a2fd7d119b4123482","author":{"name":"Administrator","email":"user@example.com"}}],"total_commits_count":1}

// http://requestb.in/1mazrmi1?inspect

/*
{
  "object_kind": "push",
  "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
  "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
  "ref": "refs/heads/master",
  "user_id": 4,
  "user_name": "John Smith",
  "user_email": "john@example.com",
  "project_id": 15,
  "repository": {
    "name": "Diaspora",
    "url": "git@example.com:mike/diasporadiaspora.git",
    "description": "",
    "homepage": "http://example.com/mike/diaspora",
    "git_http_url":"http://example.com/mike/diaspora.git",
    "git_ssh_url":"git@example.com:mike/diaspora.git",
    "visibility_level":0
  },
  "commits": [
    {
      "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
      "message": "Update Catalan translation to e38cb41.",
      "timestamp": "2011-12-12T14:27:31+02:00",
      "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
      "author": {
        "name": "Jordi Mallach",
        "email": "jordi@softcatala.org"
      }
    },
    {
      "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "message": "fixed readme",
      "timestamp": "2012-01-03T23:36:29+02:00",
      "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
      "author": {
        "name": "GitLab dev user",
        "email": "gitlabdev@dv6700.(none)"
      }
    }
  ],
  "total_commits_count": 4
}
*/

'use strict';

(function (gitLabTranslator) {
  var _ = require('underscore'),
      uuid = require('uuid-v4');

  gitLabTranslator.hasCorrectHeaders = function (headers) {
    return headers.hasOwnProperty('x-gitlab-event') && headers['x-gitlab-event'] === 'Push Hook';
  };

  gitLabTranslator.canTranslate = function (request) {

    // gitLab does not have a pusheEvent.repository.id field, and github does
    // gitLab does not have a commit.committer object, and github does
    var headers = request.headers;

    if (gitLabTranslator.hasCorrectHeaders(headers)) {
      return true;
    }
    return false;
  };

  gitLabTranslator.translatePush = function (pushEvent, instanceId, digestId, inboxId) {
    try {
      var branch = pushEvent.ref.split('/').pop();
      var repository = {
        // gitLab does not have a repository id
        // id: pushEvent.repository.id,
        name: pushEvent.repository.name
      };

      var events = _.map(pushEvent.commits, function (aCommit) {
        var commit = {
          sha: aCommit.id,
          commit: {
            author: aCommit.author,
            // gitLab does not have a commit.committer object. Using the same thing as author for now.
            // committer: {
            //   name: aCommit.committer.name,
            //   email: aCommit.committer.email,
            //   date: aCommit.timestamp
            // },
            committer: {
              name: aCommit.author.name,
              email: aCommit.author.email,
              date: aCommit.timestamp
            },
            message: aCommit.message
          },
          html_url: aCommit.url,
          repository: repository,
          branch: branch,
          originalMessage: aCommit
        };
        return {
          eventId: uuid(),
          eventType: 'GitLabCommitReceived',
          data: commit,
          metadata: {
            instanceId: instanceId,
            digestId: digestId,
            inboxId: inboxId
          }
        };
      });

      return events;
    } catch (ex) {
      console.log('GitLabException:');
      console.log(ex);
      throw ex;
    }
  };
})(module.exports);<|endoftext|>


<commit_before>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import themed from 'react-themed'
import classnames from 'classnames'
import autobind from 'autobind-decorator'
import get from 'lodash/get'
import LazyLoad, { forceCheck } from 'react-lazyload'
import { Button, ToggleButton, ListingComponents, ListingCell } from '@rentpath/react-ui-core'
import { Banner } from '../Banners'

const buttonPropTypes = PropTypes.shape({
  children: PropTypes.node,
  onClick: PropTypes.func,
  className: PropTypes.string,
  valueLocation: PropTypes.string,
})

const REACT_LAZYLOAD_PROP_DEFAULTS = {
  offset: [250, -100],
  resize: true,
  width: 318,
  height: 280,
}

@themed(/^Listing/)
export default class Listing extends Component {
  static propTypes = {
    index: PropTypes.number,
    listing: PropTypes.object,
    theme: PropTypes.object,
    className: PropTypes.string,
    onClick: PropTypes.func,
    photos: PropTypes.object,
    ratings: PropTypes.object,
    ctaButtons: PropTypes.arrayOf(buttonPropTypes),
    favoriteButton: buttonPropTypes,
    lazyLoad: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.bool,
    ]),
    isActive: PropTypes.bool,
    listingInfoComponents: PropTypes.node,
  }

  static defaultProps = {
    isActive: true,
    lazyLoad: true,
    theme: {},
    listing: {},
    ratings: {},
    photos: {},
  }

  constructor(props) {
    super(props)
    this.state = {
      loadedCarousel: false,
    }
  }

  shouldComponentUpdate(nextProps) {
    return this.props.isActive !== nextProps.isActive ||
    this.props.listing.id !== nextProps.listing.id
  }

  componentDidUpdate(prevProps) {
    if (this.props.isActive !== prevProps.isActive && !prevProps.isActive) {
      forceCheck()
    }
  }

  get listingPhotos() {
    return get(this.props.listing, 'photos', [])
  }

  @autobind
  handleCardClick() {
    const { index, listing, onClick } = this.props

    if (onClick) onClick(index, listing)
  }

  @autobind
  handleButtonClick(onClick) {
    const { isActive, listing } = this.props

    return event => {
      if (isActive && onClick) {
        onClick(this.props.listing)
      } else if (!isActive && this.props.onClick) {
        this.props.onClick(this.props.index, listing)
      }

      const shouldStopPropagation =
        isActive && event && event.stopPropagation

      if (shouldStopPropagation) event.stopPropagation()
    }
  }

  @autobind
  handleFavoriteClick(value, event) {
    const { isActive, listing, favoriteButton } = this.props
    const { onClick } = favoriteButton

    if (isActive && onClick) {
      onClick(this.props.listing, value)
    } else if (!isActive && this.props.onClick) {
      this.props.onClick(this.props.index, listing)
    }

    const shouldStopPropagation = isActive && event && event.stopPropagation

    if (shouldStopPropagation) {
      event.stopPropagation()
    }
  }

  @autobind
  handlePhotoCarouselSlide() {
    this.setState({
      loadedCarousel: true,
    })
  }

  renderCtaButtons() {
    const { ctaButtons } = this.props
    return ctaButtons.map((cta, index) => this.renderCtaButton(cta, index))
  }

  renderCtaButton(props, key) {
    const { theme, listing } = this.props
    const {
      className,
      onClick,
      valueLocation,
      children,
      ...rest
    } = props

    const buttonText = get(listing, valueLocation, children)
    const buttonProps = { ...rest }

    if (!buttonText) return null

    return (
      <Button
        {...buttonProps}
        className={classnames(
          theme.Listing_CtaButton,
          className,
        )}
        onClick={this.handleButtonClick(onClick)}
        key={key}
        data-tid="cta-button"
      >
        {buttonText}
      </Button>
    )
  }

  renderFavoriteButton() {
    const { theme, favoriteButton, isActive, listing } = this.props
    const { className } = favoriteButton

    return (
      <ToggleButton
        {...favoriteButton}
        value={listing.isFavorited}
        className={classnames(
          theme.Listing_FavoriteButton,
          className,
        )}
        inactive={!isActive}
        onClick={this.handleFavoriteClick}
      />
    )
  }

  renderBanners() {
    const { listing, theme } = this.props

    return (

        {listing.banners.map(banner =>
          (
            <Banner
              key={banner}
              name={banner}
            />
          )
        )}

    )
  }

  renderPhotoCarousel() {
    const { listing, theme, photos, isActive } = this.props
    let { lazyLoad } = this.props

    if (lazyLoad && typeof lazyLoad === 'boolean') {
      lazyLoad = typeof lazyLoad === 'boolean'
        ? REACT_LAZYLOAD_PROP_DEFAULTS
        : lazyLoad
    }

    if (!isActive) {
      const { server, dimensions } = photos
      const firstPhoto = this.listingPhotos[0] || {}
      listing.photo = { url: `${server}${firstPhoto.path}${dimensions}` }
    }

    return (

        {(isActive || this.state.loadedCarousel) &&
          <ListingComponents.Photos
            showNav
            {...photos}
            lazyLoad={lazyLoad}
            className={theme.Listing_Photos}
            onSlide={this.handlePhotoCarouselSlide}
          />
        }
        {this.renderPhoto(lazyLoad)}

    )
  }

  renderPhoto(lazyLoad) {
    if (lazyLoad) {
      return (
        <LazyLoad
          {...lazyLoad}
        >
          <ListingComponents.Photo />
        </LazyLoad>
      )
    }

    return (
      <ListingComponents.Photo />
    )
  }

  render() {
    const {
      theme,
      listing,
      onClick,
      className,
      photos,
      ratings,
      ctaButtons,
      favoriteButton,
      isActive,
      lazyLoad,
      listingInfoComponents,
      ...props
    } = this.props

    return (
      <ListingCell
        listing={listing}
        onClick={this.handleCardClick}
        className={classnames(
          className,
          theme.Listing,
          theme[`Listing-${isActive ? 'active' : 'inactive'}`],
        )}
        isActive={isActive}
        {...props}
      >
        {listing.banners && this.renderBanners()}
        {this.renderFavoriteButton()}
        {this.renderPhotoCarousel()}

            {listingInfoComponents}

            {this.renderCtaButtons()}

      </ListingCell>
    )
  }
}
<commit_msg>fix(react-ui-ag Listing component): listing component is not rerendering on isFavorited prop change<commit_after>import React, { Component } from 'react'
import PropTypes from 'prop-types'
import themed from 'react-themed'
import classnames from 'classnames'
import autobind from 'autobind-decorator'
import get from 'lodash/get'
import LazyLoad, { forceCheck } from 'react-lazyload'
import { Button, ToggleButton, ListingComponents, ListingCell } from '@rentpath/react-ui-core'
import { Banner } from '../Banners'

const buttonPropTypes = PropTypes.shape({
  children: PropTypes.node,
  onClick: PropTypes.func,
  className: PropTypes.string,
  valueLocation: PropTypes.string,
})

const REACT_LAZYLOAD_PROP_DEFAULTS = {
  offset: [250, -100],
  resize: true,
  width: 318,
  height: 280,
}

@themed(/^Listing/)
export default class Listing extends Component {
  static propTypes = {
    index: PropTypes.number,
    listing: PropTypes.object,
    theme: PropTypes.object,
    className: PropTypes.string,
    onClick: PropTypes.func,
    photos: PropTypes.object,
    ratings: PropTypes.object,
    ctaButtons: PropTypes.arrayOf(buttonPropTypes),
    favoriteButton: buttonPropTypes,
    lazyLoad: PropTypes.oneOfType([
      PropTypes.object,
      PropTypes.bool,
    ]),
    isActive: PropTypes.bool,
    listingInfoComponents: PropTypes.node,
  }

  static defaultProps = {
    isActive: true,
    lazyLoad: true,
    theme: {},
    listing: {},
    ratings: {},
    photos: {},
  }

  constructor(props) {
    super(props)
    this.state = {
      loadedCarousel: false,
    }
  }

  shouldComponentUpdate(nextProps) {
    const { isActive, listing } = this.props
    const { id, isFavorited } = listing

    return isActive !== nextProps.isActive ||
    id !== nextProps.listing.id ||
    isFavorited !== nextProps.listing.isFavorited
  }

  componentDidUpdate(prevProps) {
    if (this.props.isActive !== prevProps.isActive && !prevProps.isActive) {
      forceCheck()
    }
  }

  get listingPhotos() {
    return get(this.props.listing, 'photos', [])
  }

  @autobind
  handleCardClick() {
    const { index, listing, onClick } = this.props

    if (onClick) onClick(index, listing)
  }

  @autobind
  handleButtonClick(onClick) {
    const { isActive, listing } = this.props

    return event => {
      if (isActive && onClick) {
        onClick(this.props.listing)
      } else if (!isActive && this.props.onClick) {
        this.props.onClick(this.props.index, listing)
      }

      const shouldStopPropagation =
        isActive && event && event.stopPropagation

      if (shouldStopPropagation) event.stopPropagation()
    }
  }

  @autobind
  handleFavoriteClick(value, event) {
    const { isActive, listing, favoriteButton } = this.props
    const { onClick } = favoriteButton

    if (isActive && onClick) {
      onClick(this.props.listing, value)
    } else if (!isActive && this.props.onClick) {
      this.props.onClick(this.props.index, listing)
    }

    const shouldStopPropagation = isActive && event && event.stopPropagation

    if (shouldStopPropagation) {
      event.stopPropagation()
    }
  }

  @autobind
  handlePhotoCarouselSlide() {
    this.setState({
      loadedCarousel: true,
    })
  }

  renderCtaButtons() {
    const { ctaButtons } = this.props
    return ctaButtons.map((cta, index) => this.renderCtaButton(cta, index))
  }

  renderCtaButton(props, key) {
    const { theme, listing } = this.props
    const {
      className,
      onClick,
      valueLocation,
      children,
      ...rest
    } = props

    const buttonText = get(listing, valueLocation, children)
    const buttonProps = { ...rest }

    if (!buttonText) return null

    return (
      <Button
        {...buttonProps}
        className={classnames(
          theme.Listing_CtaButton,
          className,
        )}
        onClick={this.handleButtonClick(onClick)}
        key={key}
        data-tid="cta-button"
      >
        {buttonText}
      </Button>
    )
  }

  renderFavoriteButton() {
    const { theme, favoriteButton, isActive, listing } = this.props
    const { className } = favoriteButton

    return (
      <ToggleButton
        {...favoriteButton}
        value={listing.isFavorited}
        className={classnames(
          theme.Listing_FavoriteButton,
          className,
        )}
        inactive={!isActive}
        onClick={this.handleFavoriteClick}
      />
    )
  }

  renderBanners() {
    const { listing, theme } = this.props

    return (

        {listing.banners.map((banner, index) =>
          (
            <Banner
              key={`${banner}_${index}`}
              name={banner}
            />
          )
        )}

    )
  }

  renderPhotoCarousel() {
    const { listing, theme, photos, isActive } = this.props
    let { lazyLoad } = this.props

    if (lazyLoad && typeof lazyLoad === 'boolean') {
      lazyLoad = typeof lazyLoad === 'boolean'
        ? REACT_LAZYLOAD_PROP_DEFAULTS
        : lazyLoad
    }

    if (!isActive) {
      const { server, dimensions } = photos
      const firstPhoto = this.listingPhotos[0] || {}
      listing.photo = { url: `${server}${firstPhoto.path}${dimensions}` }
    }

    return (

        {(isActive || this.state.loadedCarousel) &&
          <ListingComponents.Photos
            showNav
            {...photos}
            lazyLoad={lazyLoad}
            className={theme.Listing_Photos}
            onSlide={this.handlePhotoCarouselSlide}
          />
        }
        {this.renderPhoto(lazyLoad)}

    )
  }

  renderPhoto(lazyLoad) {
    if (lazyLoad) {
      return (
        <LazyLoad
          {...lazyLoad}
        >
          <ListingComponents.Photo />
        </LazyLoad>
      )
    }

    return (
      <ListingComponents.Photo />
    )
  }

  render() {
    const {
      theme,
      listing,
      onClick,
      className,
      photos,
      ratings,
      ctaButtons,
      favoriteButton,
      isActive,
      lazyLoad,
      listingInfoComponents,
      ...props
    } = this.props

    return (
      <ListingCell
        listing={listing}
        onClick={this.handleCardClick}
        className={classnames(
          className,
          theme.Listing,
          theme[`Listing-${isActive ? 'active' : 'inactive'}`],
        )}
        isActive={isActive}
        {...props}
      >
        {listing.banners && this.renderBanners()}
        {this.renderFavoriteButton()}
        {this.renderPhotoCarousel()}

            {listingInfoComponents}

            {this.renderCtaButtons()}

      </ListingCell>
    )
  }
}
<|endoftext|>