<commit_before>var fs = require('fs')
  , path = require('path')
  , request = require('request')
  , qs = require('querystring')
  , _ = require('lodash')
  , colors = require('colors')
  , urlUtil = require('url')
  , delay = false

// delay is for For async requests to 3rd parties for photos,
// we will delay the overall response until delay is false again
// A bit of a hack, but it works.

var twitter_config = JSON.parse( fs.readFileSync( path.resolve(__dirname, 'twitter-config.json'), 'utf-8' ) )

// Check the response mime type if it is
// text/html and it contains "capacity" in
// the response, then it is over capacity
function isTwitterOverCapacity(){
  console.warn('Not implemented: isTwitterOverCapatcity()')
  return false
}

// A comparison of the current media item with the
// first cached item to determine when to stop iterating
// through the set of aggregate media items.
function isItemFirstCacheItem(item, cached_item){
  return item === cached_item
}

// Let's normalize the response of media data to only include
// the photos we want.
function normalizeTwitterData(data,req,res){

  var normalized = {
    error: false,
    error_message: '',
    media: []
  }

  // console.dir(req.session.twitter)

  if( isTwitterOverCapacity() ){
    normalized.error = true
    normalized.error_message = "It appears Twitter is over capacity. Try again."
  }
  else{

    console.log("Twitter Response array length: %s\n", data.length)
    // If the media response is empty...
    if(!data.length){
      normalized.error = true
      normalized.error_message = "We were unable to fetch any images from your media timeline."
      console.dir(data)
      return processNormalizedResponse(normalized, req, res)
    }

    // We use a for loop so we can break out if
    // the cache is met
    for(i=0, len = data.length; i<len; i++){

      var el = data[i]

      // console.dir(el)
      // TODO: Session won't stash this much data so switch this logic to pull from redis.
      if( req.session.media_cache && isItemFirstCacheItem(el.id, req.session.media_cache.latest_id ) ){
        console.log('\n\nCache is same so we are breaking...\n\n')
        // update latest_id in cache
        normalized.latest_id = el.id
        // Now process the the response with new updated normalized data
        processNormalizedResponse(normalized, req, res)
        break
      }

      // The first time we run this, we need to stash the latest_id
      if( i === len-1 ){
        normalized.latest_id = data[0].id
      }

      // In the case of just pic.twitter.com....
      if(el.entities && el.entities.media && el.entities.media.length){

        var twitMediaObj = el.entities.media[0]

        var item = {
          full_url: twitMediaObj.media_url+":large",
          thumb_url: twitMediaObj.media_url+":thumb",
        }

        normalized.media.push(item)

      }
      // Otherwise, we have 3rd part attributed providers.
      else if(el.entities && el.entities.urls && el.entities.urls.length && el.entities.urls[0].display_url){

        var url = el.entities.urls[el.entities.urls.length-1].display_url

        if(url.indexOf('instagr.am') > -1){

          var item = {
            full_url: "https://" + url + "/media/?size=l",
            thumb_url: "https://" + url + "/media/?size=t",
          }

          normalized.media.push(item)

        }
        if(url.indexOf('twimg.com') > -1){

        // https://p.twimg.com/A128d2CCIAAPt--.jpg:small
        // https://p.twimg.com/A128d2CCIAAPt--.jpg:large

        }
        if(url.indexOf('flickr.com') > -1){
          /*

          */

          // WILL NEED TO FIGURE OUT FLICKR RESPONSE

          // Requires API call via photo ID, or just use twitter:

          /*
          https://widgets.platform.twitter.com/services/rest?jsoncallback=jQuery15205886323538143188_1346683837796&format=json&api_key=2a56884b56a00758525eaa2fee16a798&method=flickr.photos.getInfo&photo_id=7275880290&_=1346683837809
          */

          console.dir(el)
        }
        if(url.indexOf('twitpic.com') > -1){

          // POP OFF ID OF END OF TWITPIC URL
          // http://twitpic.com/9k68zj

          var url_id = url.split('com')[1] // includes slash

          var item = {
            full_url: "https://twitpic.com/show/iphone" + url_id,
            thumb_url: "https://twitpic.com/show/iphone" + url_id,
          }

          normalized.media.push(item)

        }
        // if(url.indexOf('etsy.am') > -1){}

        if(url.indexOf('twitgoo.com') > -1){
          // http://twitgoo.com/66akxy/img

          var item = {
            full_url: url + '/img',
            thumb_url: url + '/img',
          }

          normalized.media.push(item)

        }
        if(url.indexOf('dailybooth.com') > -1){

          request({
            followRedirect: false,
            uri: 'http://'+url,
            callback: function(e,r,b){
              if(e) console.error(e)

              var id = r.headers.location.split('/').pop()

              request({
                uri: "https://api.dailybooth.com/v1/picture/" + id + ".json",
                json: true,
                callback: function(e,r,b){
                  if(e) return console.error(e)
                  /*
                  urls:
                    { tiny: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/tiny/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
                      small: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/small/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
                      medium: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/medium/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
                      large: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/large/78e4b486c80b29435504d94fdf626b7c_29230840.jpg'
                    }

                  { error:
                     { error: 'rate_limit',
                       error_description: 'Rate limit exceeded.',
                       error_code: 412 } }
                  var item = {
                    full_url: b.urls.large + '/img',
                    thumb_url: b.urls.small + '/img',
                  }
                  */

                  if( (r.statusCode >= 400) || b.error) {
                    console.log('Fail. '.red +'Status Code for Daily Booth request:  %s', r.statusCode)
                    console.error(b.error)
                  }
                  else{

                    var item = {
                      full_url: b.urls.large,
                      thumb_url: b.urls.small,
                    }

                    normalized.media.push(item)
                  }

                  delay = false

                  processNormalizedResponse(normalized, req, res)

                } // end inner request callback
              }) // end request()
            } // end outer request callback
          }) // end request()

          // Because of the async nature of the above request() calls,
          // we set the delay here and mark it false in the last callback
          if(!delay) delay = true

        }
        if(url.indexOf('yfrog.com') > -1){
          // console.dir(el)

          var item = {
            full_url: "https://" + url + ":tw1",
            thumb_url: "https://" + url + ":tw1",
          }

          normalized.media.push(item)

        }
        // if(url.indexOf('lockerz.am') > -1){}
        // if(url.indexOf('kiva.am') > -1){}

        // if(url.indexOf('kickstarter.com') > -1){}
        if(url.indexOf('dipdive.com') > -1){
          isVideo = false || true; // could be photo

        }
        // if(url.indexOf('photobucket.com') > -1){}
        // if(url.indexOf('with.me') > -1){}
        // if(url.indexOf('facebook.com') > -1){}
        if(url.indexOf('deviantart.com') > -1 || url.indexOf('fav.me') > -1){

          // TODO: IMAGES DON'T SHOW UP?

        }

      } // end if el.entities.url

    } // end forloop

  } // end else capacity

  return processNormalizedResponse(normalized, req, res)

}

// Process the response, cache the normalized data
// TODO: Cache the response
function processNormalizedResponse(normalized, req, res){
  if(!delay){
    // stash normalized in cache and return normalized
    console.log('\n\nDone!\n\n')
    if(!req.session.media_cache) req.session.media_cache = normalized
    // console.dir(req.session.media_cache)
    return res.json(normalized)
  }
  else{
    setTimeout(function(){
      // We have to check again for delay due to race conditions.
      if(delay) return processNormalizedResponse(normalized, req, res)
    },10)
  }
}

// Twitter OAuth
exports.Twitter = {
  config: twitter_config,
  generateAuthUrl: function(req,res,cb){

    var url = twitter_config.request_token_URL
      , oauth = {
                  callback: twitter_config.callback_URL
                  , consumer_key: twitter_config.consumer_key
                  , consumer_secret: twitter_config.consumer_secret
                }

    // Create your auth_url for the view
    request.post({url:url, oauth:oauth}, function (e, r, body) {

      if(e) return cb(e,null)

      var auth_url = twitter_config.authorize_URL_https + "?" + body

      // console.log(auth_url + " is the auth_url")

      cb(null,auth_url)

    }) // end request.post()

  },
  getMediaTimeline: function(req,res,cb){

    if(!req.session.twitter.oauth) return res.status(403).send('User not authorized. Please reauthenticate with twitter.')

    // console.log('We have a session so lets get media timeline')
    // var url = 'http://api.twitter.com/1/statuses/user_timeline.json?'
    var url = 'http://api.twitter.com/1/statuses/media_timeline.json?'
      , params =
        { screen_name: req.session.twitter.oauth.screen_name
        , user_id: req.session.twitter.oauth.user_id
        , count: 20000
        , include_entities: true
        , offset: 0
        , score: true
        , mode: 'photos'
        , filter: false
        , is_event: false
        }

    // console.dir(perm_token)

    url += qs.stringify(params)

    // console.dir(req.session.twitter.oauth)

    request.get({url:url, oauth: req.session.twitter.oauth, json:true}, function (e, r, data) {
      if(e) return console.error(e)

      // console.dir(data)

      return normalizeTwitterData(data, req, res)

    })

  },
  pipeToTwitter: function(echo, req, res){

    if(!req.session.twitter.oauth){
      res.type('text/plain')
      return res.status(403).send("You are not authenticated with Twitter.")
    }

    // TODO: EVENTUALLY WE WILL NEED TO CHECK THE
    // https://api.twitter.com/1/help/configuration.json
    // RESPONSE THAT CONTAINS SHORT URL CHARS AND MAX MEDIA UPLOADS
    // SEE https://dev.twitter.com/docs/api/1/get/help/configuration

    var oauth = req.session.twitter.oauth
    var uri = 'https://upload.twitter.com/1/statuses/update_with_media.json'
    var method = 'POST'

    var r = request.post({
      oauth: req.session.twitter.oauth,
      uri: uri,
      callback: function(e,r,data){
        if(e) {
          console.error(e)
          return res.json(e)
        }
        if(data) {
          // NOTE:  If the user tries to exceed the number of updates allowed,
          // this method will also return an HTTP 403 error, similar to POST statuses/update.
          // TODO: CHECK FOR THIS!!
          return res.json(data)
        }
      }
    })

    var form = r.form()
    form.append('status', echo.caption || "photopi.pe")
    form.append('media[]', fs.createReadStream(echo.fullPhotoPath))

  }
} // end exports.Twitter

/* These are videos:

case "WhoSay":
case "Photozou":
case "Vimeo":
case "Ustream":
case "Youtube":
case "Vevo":
case "TwitVid":
case "GoogleVideo":
case "JustinTV":
case "MTV":
case "WashingtonPost":
case "MSNBC":
case "CNN":
case "Apple":
case "Rdio":
case "SlideShare":
case "BlipTV":
case "Livestream":
case "WallStreetJournal":
case "Hulu":
case "NHL":
case "Meetup":
case "Plancast":
case "Gowalla":
case "Foursquare":
case "Amazon":
case "AolVideo":
case "WordPress":
case "Pepsi":
*/<commit_msg>removed dailybooth since it is shut down...<commit_after>var fs = require('fs')
  , path = require('path')
  , request = require('request')
  , qs = require('querystring')
  , _ = require('lodash')
  , colors = require('colors')
  , urlUtil = require('url')
  , delay = false

// delay is for For async requests to 3rd parties for photos,
// we will delay the overall response until delay is false again
// A bit of a hack, but it works.

var twitter_config = JSON.parse( fs.readFileSync( path.resolve(__dirname, 'twitter-config.json'), 'utf-8' ) )

// Check the response mime type if it is
// text/html and it contains "capacity" in
// the response, then it is over capacity
function isTwitterOverCapacity(){
  console.warn('Not implemented: isTwitterOverCapatcity()')
  return false
}

// A comparison of the current media item with the
// first cached item to determine when to stop iterating
// through the set of aggregate media items.
function isItemFirstCacheItem(item, cached_item){
  return item === cached_item
}

// Let's normalize the response of media data to only include
// the photos we want.
function normalizeTwitterData(data,req,res){

  var normalized = {
    error: false,
    error_message: '',
    media: []
  }

  // console.dir(req.session.twitter)

  if( isTwitterOverCapacity() ){
    normalized.error = true
    normalized.error_message = "It appears Twitter is over capacity. Try again."
  }
  else{

    console.log("Twitter Response array length: %s\n", data.length)
    // If the media response is empty...
    if(!data.length){
      normalized.error = true
      normalized.error_message = "We were unable to fetch any images from your media timeline."
      console.dir(data)
      return processNormalizedResponse(normalized, req, res)
    }

    // We use a for loop so we can break out if
    // the cache is met
    for(i=0, len = data.length; i<len; i++){

      var el = data[i]

      // console.dir(el)
      // TODO: Session won't stash this much data so switch this logic to pull from redis.
      if( req.session.media_cache && isItemFirstCacheItem(el.id, req.session.media_cache.latest_id ) ){
        console.log('\n\nCache is same so we are breaking...\n\n')
        // update latest_id in cache
        normalized.latest_id = el.id
        // Now process the the response with new updated normalized data
        processNormalizedResponse(normalized, req, res)
        break
      }

      // The first time we run this, we need to stash the latest_id
      if( i === len-1 ){
        normalized.latest_id = data[0].id
      }

      // In the case of just pic.twitter.com....
      if(el.entities && el.entities.media && el.entities.media.length){

        var twitMediaObj = el.entities.media[0]

        var item = {
          full_url: twitMediaObj.media_url+":large",
          thumb_url: twitMediaObj.media_url+":thumb",
        }

        normalized.media.push(item)

      }
      // Otherwise, we have 3rd part attributed providers.
      else if(el.entities && el.entities.urls && el.entities.urls.length && el.entities.urls[0].display_url){

        var url = el.entities.urls[el.entities.urls.length-1].display_url

        if(url.indexOf('instagr.am') > -1){

          var item = {
            full_url: "https://" + url + "/media/?size=l",
            thumb_url: "https://" + url + "/media/?size=t",
          }

          normalized.media.push(item)

        }
        if(url.indexOf('twimg.com') > -1){

        // https://p.twimg.com/A128d2CCIAAPt--.jpg:small
        // https://p.twimg.com/A128d2CCIAAPt--.jpg:large

        }
        if(url.indexOf('flickr.com') > -1){
          /*

          */

          // WILL NEED TO FIGURE OUT FLICKR RESPONSE

          // Requires API call via photo ID, or just use twitter:

          /*
          https://widgets.platform.twitter.com/services/rest?jsoncallback=jQuery15205886323538143188_1346683837796&format=json&api_key=2a56884b56a00758525eaa2fee16a798&method=flickr.photos.getInfo&photo_id=7275880290&_=1346683837809
          */

          console.dir(el)
        }
        if(url.indexOf('twitpic.com') > -1){

          // POP OFF ID OF END OF TWITPIC URL
          // http://twitpic.com/9k68zj

          var url_id = url.split('com')[1] // includes slash

          var item = {
            full_url: "https://twitpic.com/show/iphone" + url_id,
            thumb_url: "https://twitpic.com/show/iphone" + url_id,
          }

          normalized.media.push(item)

        }
        // if(url.indexOf('etsy.am') > -1){}

        if(url.indexOf('twitgoo.com') > -1){
          // http://twitgoo.com/66akxy/img

          var item = {
            full_url: url + '/img',
            thumb_url: url + '/img',
          }

          normalized.media.push(item)

        }
        // Daily booth closed down....
        // if(url.indexOf('dailybooth.com') > -1){
        //   console.log(url)
        //   request({
        //     followRedirect: false,
        //     uri: 'http://'+url,
        //     callback: function(e,r,b){
                // need to handle errors better...
        //       if(e) console.error(e)
        //
        //       console.dir(r)
        //
        //       var id = r.headers.location.split('/').pop()
        //
        //       request({
        //         uri: "https://api.dailybooth.com/v1/picture/" + id + ".json",
        //         json: true,
        //         callback: function(e,r,b){
        //           if(e) return console.error(e)
        //           /*
        //           urls:
        //             { tiny: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/tiny/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
        //               small: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/small/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
        //               medium: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/medium/78e4b486c80b29435504d94fdf626b7c_29230840.jpg',
        //               large: 'http://d1oi94rh653f1l.cloudfront.net/15/pictures/large/78e4b486c80b29435504d94fdf626b7c_29230840.jpg'
        //             }
        //
        //           { error:
        //              { error: 'rate_limit',
        //                error_description: 'Rate limit exceeded.',
        //                error_code: 412 } }
        //           var item = {
        //             full_url: b.urls.large + '/img',
        //             thumb_url: b.urls.small + '/img',
        //           }
        //           */
        //
        //           if( (r.statusCode >= 400) || b.error) {
        //             console.log('Fail. '.red +'Status Code for Daily Booth request:  %s', r.statusCode)
        //             console.error(b.error)
        //           }
        //           else{
        //
        //             var item = {
        //               full_url: b.urls.large,
        //               thumb_url: b.urls.small,
        //             }
        //
        //             normalized.media.push(item)
        //           }
        //
        //           delay = false
        //
        //           processNormalizedResponse(normalized, req, res)
        //
        //         } // end inner request callback
        //       }) // end request()
        //     } // end outer request callback
        //   }) // end request()
        //
        //   // Because of the async nature of the above request() calls,
        //   // we set the delay here and mark it false in the last callback
        //   if(!delay) delay = true
        //
        // }
        if(url.indexOf('yfrog.com') > -1){
          // console.dir(el)

          var item = {
            full_url: "https://" + url + ":tw1",
            thumb_url: "https://" + url + ":tw1",
          }

          normalized.media.push(item)

        }
        // if(url.indexOf('lockerz.am') > -1){}
        // if(url.indexOf('kiva.am') > -1){}

        // if(url.indexOf('kickstarter.com') > -1){}
        if(url.indexOf('dipdive.com') > -1){
          isVideo = false || true; // could be photo

        }
        // if(url.indexOf('photobucket.com') > -1){}
        // if(url.indexOf('with.me') > -1){}
        // if(url.indexOf('facebook.com') > -1){}
        if(url.indexOf('deviantart.com') > -1 || url.indexOf('fav.me') > -1){

          // TODO: IMAGES DON'T SHOW UP?

        }

      } // end if el.entities.url

    } // end forloop

  } // end else capacity

  return processNormalizedResponse(normalized, req, res)

}

// Process the response, cache the normalized data
// TODO: Cache the response
function processNormalizedResponse(normalized, req, res){
  if(!delay){
    // stash normalized in cache and return normalized
    console.log('\n\nDone!\n\n')
    if(!req.session.media_cache) req.session.media_cache = normalized
    // console.dir(req.session.media_cache)
    return res.json(normalized)
  }
  else{
    setTimeout(function(){
      // We have to check again for delay due to race conditions.
      if(delay) return processNormalizedResponse(normalized, req, res)
    },10)
  }
}

// Twitter OAuth
exports.Twitter = {
  config: twitter_config,
  generateAuthUrl: function(req,res,cb){

    var url = twitter_config.request_token_URL
      , oauth = {
                  callback: twitter_config.callback_URL
                  , consumer_key: twitter_config.consumer_key
                  , consumer_secret: twitter_config.consumer_secret
                }

    // Create your auth_url for the view
    request.post({url:url, oauth:oauth}, function (e, r, body) {

      if(e) return cb(e,null)

      var auth_url = twitter_config.authorize_URL_https + "?" + body

      // console.log(auth_url + " is the auth_url")

      cb(null,auth_url)

    }) // end request.post()

  },
  getMediaTimeline: function(req,res,cb){

    if(!req.session.twitter.oauth) return res.status(403).send('User not authorized. Please reauthenticate with twitter.')

    // console.log('We have a session so lets get media timeline')
    // var url = 'http://api.twitter.com/1/statuses/user_timeline.json?'
    var url = 'http://api.twitter.com/1/statuses/media_timeline.json?'
      , params =
        { screen_name: req.session.twitter.oauth.screen_name
        , user_id: req.session.twitter.oauth.user_id
        , count: 20000
        , include_entities: true
        , offset: 0
        , score: true
        , mode: 'photos'
        , filter: false
        , is_event: false
        }

    // console.dir(perm_token)

    url += qs.stringify(params)

    // console.dir(req.session.twitter.oauth)

    request.get({url:url, oauth: req.session.twitter.oauth, json:true}, function (e, r, data) {
      if(e) return console.error(e)

      // console.dir(data)

      return normalizeTwitterData(data, req, res)

    })

  },
  pipeToTwitter: function(echo, req, res){

    if(!req.session.twitter.oauth){
      res.type('text/plain')
      return res.status(403).send("You are not authenticated with Twitter.")
    }

    // TODO: EVENTUALLY WE WILL NEED TO CHECK THE
    // https://api.twitter.com/1/help/configuration.json
    // RESPONSE THAT CONTAINS SHORT URL CHARS AND MAX MEDIA UPLOADS
    // SEE https://dev.twitter.com/docs/api/1/get/help/configuration

    var oauth = req.session.twitter.oauth
    var uri = 'https://upload.twitter.com/1/statuses/update_with_media.json'
    var method = 'POST'

    var r = request.post({
      oauth: req.session.twitter.oauth,
      uri: uri,
      callback: function(e,r,data){
        if(e) {
          console.error(e)
          return res.json(e)
        }
        if(data) {
          // NOTE:  If the user tries to exceed the number of updates allowed,
          // this method will also return an HTTP 403 error, similar to POST statuses/update.
          // TODO: CHECK FOR THIS!!
          return res.json(data)
        }
      }
    })

    var form = r.form()
    form.append('status', echo.caption || "photopi.pe")
    form.append('media[]', fs.createReadStream(echo.fullPhotoPath))

  }
} // end exports.Twitter

/* These are videos:

case "WhoSay":
case "Photozou":
case "Vimeo":
case "Ustream":
case "Youtube":
case "Vevo":
case "TwitVid":
case "GoogleVideo":
case "JustinTV":
case "MTV":
case "WashingtonPost":
case "MSNBC":
case "CNN":
case "Apple":
case "Rdio":
case "SlideShare":
case "BlipTV":
case "Livestream":
case "WallStreetJournal":
case "Hulu":
case "NHL":
case "Meetup":
case "Plancast":
case "Gowalla":
case "Foursquare":
case "Amazon":
case "AolVideo":
case "WordPress":
case "Pepsi":
*/<|endoftext|>


<commit_before>// Copyright 2014-2015, University of Colorado Boulder

/**
 * Scenery node that draws a backspace icon.
 * This was originally created for use on keypads, but may have other applications.
 *
 * @author John Blanco
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Path = require( 'SCENERY/nodes/Path' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Shape = require( 'KITE/Shape' );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function BackspaceIcon( options ) {

    options = _.extend( {
      stroke: 'black',
      lineWidth: 1,
      lineJoin: 'round',
      lineCap: 'square',
      size: new Dimension2( 15, 10 )
    }, options );

    var iconShape = new Shape();

    // the outline, tip points left, described clockwise from the tip
    var tipWidth = options.size.width / 3;
    iconShape.moveTo( 0, tipWidth )
      .lineTo( tipWidth, 0 )
      .lineTo( options.size.width, 0 )
      .lineTo( options.size.width, options.size.height )
      .lineTo( tipWidth, options.size.height )
      .close();

    // the x in the middle
    var left = 0.47 * options.size.width;
    var right = 0.73 * options.size.width;
    var top = 0.3 * options.size.height;
    var bottom = 0.7 * options.size.height;
    iconShape.moveTo( left, top )
      .lineTo( right, bottom )
      .moveTo( right, top )
      .lineTo( left, bottom );

    Path.call( this, iconShape, options );
  }

  sceneryPhet.register( 'BackspaceIcon', BackspaceIcon );

  return inherit( Path, BackspaceIcon );
} );<commit_msg>doc, https://github.com/phetsims/scenery-phet/issues/284<commit_after>// Copyright 2014-2015, University of Colorado Boulder

/**
 * Scenery node that draws a backspace icon.
 * This was originally created for use on keypads, but may have other applications.
 *
 * @author John Blanco
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var Dimension2 = require( 'DOT/Dimension2' );
  var inherit = require( 'PHET_CORE/inherit' );
  var Path = require( 'SCENERY/nodes/Path' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Shape = require( 'KITE/Shape' );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function BackspaceIcon( options ) {

    options = _.extend( {
      stroke: 'black',
      lineWidth: 1,
      lineJoin: 'round',
      lineCap: 'square',
      size: new Dimension2( 15, 10 )
    }, options );

    var iconShape = new Shape();

    // the outline, tip points left, described clockwise from the tip
    var tipWidth = options.size.width / 3;
    iconShape.moveTo( 0, tipWidth )
      .lineTo( tipWidth, 0 )
      .lineTo( options.size.width, 0 )
      .lineTo( options.size.width, options.size.height )
      .lineTo( tipWidth, options.size.height )
      .close();

    // the x in the middle, multipliers determined empirically
    var left = 0.47 * options.size.width;
    var right = 0.73 * options.size.width;
    var top = 0.3 * options.size.height;
    var bottom = 0.7 * options.size.height;
    iconShape.moveTo( left, top )
      .lineTo( right, bottom )
      .moveTo( right, top )
      .lineTo( left, bottom );

    Path.call( this, iconShape, options );
  }

  sceneryPhet.register( 'BackspaceIcon', BackspaceIcon );

  return inherit( Path, BackspaceIcon );
} );<|endoftext|>


<commit_before>/*jshint -W030 */
var gremlin = require('../');

describe('Nashorn syntax', function() {
  it('should handle .localeCompare() in lieu of <=> operator', function() {
    var client = gremlin.createClient({ language: 'nashorn' });
    var script = function() {
      g.V().order(function(a, b) {
        return a.get().value('name').localeCompare(b.get().value('name'));
      });
    };

    client.execute(script, function(err, response) {
      (err === null).should.be.true;
      response.result[0].properties.name.should.equal('blueprints');
      response.result[5].properties.name.should.equal('stephen');
    });
  });
});<commit_msg>Update tests to match classic tinkerpop graph<commit_after>/*jshint -W030 */
var gremlin = require('../');

describe('Nashorn syntax', function() {
  it('should handle .localeCompare() in lieu of <=> operator', function() {
    var client = gremlin.createClient({ language: 'nashorn' });
    var script = function() {
      g.V().order(function(a, b) {
        return a.get().value('name').localeCompare(b.get().value('name'));
      });
    };

    client.execute(script, function(err, response) {
      (err === null).should.be.true;
      response.result[0].properties.name.should.equal('josh');
      response.result[5].properties.name.should.equal('vadas');
    });
  });
});<|endoftext|>


<commit_before>define([
    'underscore', 'jquery', 'kendo'
], function (_, $, kendo) {
    'use strict';

    var NOW = new Date();
    var DEFAULTS = kendo.ui.DateTimePicker.fn.options;

    function quadState(disabled, readonly, isValid, noControl) {
        if (noControl) {
            return 'noControl';
        } else if (disabled) {
            // disabled beats readonly
            return 'formFieldDisabled';
        } else if (readonly) {
            return 'formFieldReadonly';
        } else if (!isValid[0]) {
            return 'formFieldError';
        } else {
            return null;
        }
    }

    /**
     * Handle the unusual format used by TypeInfo for specifying the current time.
     * @param date
     * @returns {Date}
     */
    function parseDate(date) {
        return (date === 'NOW') ? NOW : date;
    }

    function setKendoDateState(kendoWidget, valueAsDate, disabled, readonly, max, min) {
        kendoWidget.value(valueAsDate);
        kendoWidget.min(parseDate(min || DEFAULTS.min));
        kendoWidget.max(parseDate(max || DEFAULTS.max));
        setKendoDisabledReadonly(kendoWidget, disabled, readonly);
    }

    function setKendoNumberState(kendoWidget, value, disabled, readonly) {
        setKendoNumberValue(kendoWidget, value);
        setKendoDisabledReadonly(kendoWidget, disabled, readonly);
    }

    function setKendoNumberValue(kendoWidget, value) {
        kendoWidget.value(value);
    }

    function setKendoDisabledReadonly(kendoWidget, disabled, readonly) {
        if (disabled) {
            // disabled beats readonly
            kendoWidget.enable(false);
        } else if (readonly) {
            kendoWidget.readonly(true);
        } else {
            kendoWidget.enable(true);
        }
    }

    function attachFormTooltips($body) {

        // The tooltip for the [i] button and the label
        $body.kendoTooltip({
            prefix: 'Info',
            filter: '.hasTooltip .formLabel',
            position: 'top',
            showOn: 'click',
            width: 320,
            content: function (e) {
                return e.target.parents('.hasTooltip').attr('data-tooltip');
            },
            show: function () {
                this.popup.element.addClass('formTooltip');
            }
        });

        // and the tooltip for invalid fields
        $body.kendoTooltip({
            prefix: 'Error',
            filter: '.hasErrorTooltip .formElement',
            position: 'bottom',
            showOn: 'mouseenter',
            showAfter: 1000,
            width: 240,
            content: function (e) {
                return e.target.parents('.hasErrorTooltip').attr('data-error-tooltip');
            },
            show: function () {
                this.popup.element.addClass('formErrorTooltip');
            }
        });
    }

    function hideErrorTooltip() {
        var $body = $('body');

        $body.data('kendoErrorTooltip').hide();
    }

    function refreshErrorTooltip() {
        var $body = $('body');
        $body.data('kendoErrorTooltip').refresh();
    }

    kendo.ui.Tooltip.fn.hide = function () {
        if (this.popup) {
            this.popup.close();
        }
        // (AHG) If we're in the middle of a delay to show the popup, we want to cancel the delayed show too.
        if (this.timeout) {
            clearTimeout(this.timeout);
        }
    };

    return {
        quadState: quadState,
        parseDate: parseDate,
        attachFormTooltips: attachFormTooltips,
        hideErrorTooltip: hideErrorTooltip,
        refreshErrorTooltip: refreshErrorTooltip,
        setKendoDateState: setKendoDateState,
        setKendoNumberState: setKendoNumberState,
        setKendoNumberValue: setKendoNumberValue,
        setKendoDisabledReadonly: setKendoDisabledReadonly
    };
});<commit_msg>Reset the dateView to the current month when the widget value is cleared. #38<commit_after>define([
    'underscore', 'jquery', 'kendo'
], function (_, $, kendo) {
    'use strict';

    var NOW = new Date();
    var DEFAULTS = kendo.ui.DateTimePicker.fn.options;

    function quadState(disabled, readonly, isValid, noControl) {
        if (noControl) {
            return 'noControl';
        } else if (disabled) {
            // disabled beats readonly
            return 'formFieldDisabled';
        } else if (readonly) {
            return 'formFieldReadonly';
        } else if (!isValid[0]) {
            return 'formFieldError';
        } else {
            return null;
        }
    }

    /**
     * Handle the unusual format used by TypeInfo for specifying the current time.
     * @param date
     * @returns {Date}
     */
    function parseDate(date) {
        return (date === 'NOW') ? NOW : date;
    }

    function setKendoDateState(kendoWidget, valueAsDate, disabled, readonly, max, min) {
        kendoWidget.value(valueAsDate);
        kendoWidget.min(parseDate(min || DEFAULTS.min));
        kendoWidget.max(parseDate(max || DEFAULTS.max));

        if (valueAsDate === null) {
            // If the value is being cleared, the dateView also needs to be reset to use the current month
            kendoWidget.dateView.value(kendo.calendar.restrictValue(null, kendoWidget.options.min, kendoWidget.options.max));
        }

        setKendoDisabledReadonly(kendoWidget, disabled, readonly);
    }

    function setKendoNumberState(kendoWidget, value, disabled, readonly) {
        setKendoNumberValue(kendoWidget, value);
        setKendoDisabledReadonly(kendoWidget, disabled, readonly);
    }

    function setKendoNumberValue(kendoWidget, value) {
        kendoWidget.value(value);
    }

    function setKendoDisabledReadonly(kendoWidget, disabled, readonly) {
        if (disabled) {
            // disabled beats readonly
            kendoWidget.enable(false);
        } else if (readonly) {
            kendoWidget.readonly(true);
        } else {
            kendoWidget.enable(true);
        }
    }

    function attachFormTooltips($body) {

        // The tooltip for the [i] button and the label
        $body.kendoTooltip({
            prefix: 'Info',
            filter: '.hasTooltip .formLabel',
            position: 'top',
            showOn: 'click',
            width: 320,
            content: function (e) {
                return e.target.parents('.hasTooltip').attr('data-tooltip');
            },
            show: function () {
                this.popup.element.addClass('formTooltip');
            }
        });

        // and the tooltip for invalid fields
        $body.kendoTooltip({
            prefix: 'Error',
            filter: '.hasErrorTooltip .formElement',
            position: 'bottom',
            showOn: 'mouseenter',
            showAfter: 1000,
            width: 240,
            content: function (e) {
                return e.target.parents('.hasErrorTooltip').attr('data-error-tooltip');
            },
            show: function () {
                this.popup.element.addClass('formErrorTooltip');
            }
        });
    }

    function hideErrorTooltip() {
        var $body = $('body');

        $body.data('kendoErrorTooltip').hide();
    }

    function refreshErrorTooltip() {
        var $body = $('body');
        $body.data('kendoErrorTooltip').refresh();
    }

    kendo.ui.Tooltip.fn.hide = function () {
        if (this.popup) {
            this.popup.close();
        }
        // (AHG) If we're in the middle of a delay to show the popup, we want to cancel the delayed show too.
        if (this.timeout) {
            clearTimeout(this.timeout);
        }
    };

    return {
        quadState: quadState,
        parseDate: parseDate,
        attachFormTooltips: attachFormTooltips,
        hideErrorTooltip: hideErrorTooltip,
        refreshErrorTooltip: refreshErrorTooltip,
        setKendoDateState: setKendoDateState,
        setKendoNumberState: setKendoNumberState,
        setKendoNumberValue: setKendoNumberValue,
        setKendoDisabledReadonly: setKendoDisabledReadonly
    };
});<|endoftext|>


<commit_before>var spawn = require('child_process').spawn
var exec = require('child_process').exec
var async = require('async');
var etcdjs = require('etcdjs')
var flatten = require('etcd-flatten')
var tape     = require('tape')
var config = require('../lib/config')()
var concat = require('concat-stream')
var state = {}

function runCommands(commands, done){

	async.forEachSeries(commands, function(command, nextCmd){
		var process = spawn(command, [], {
			stdio:[null, process.stdout, process.stderr]
		})

		process.on('error', nextCmd)
		process.on('close', nextcmd)
	}, done)
}

// start viking - this will boot etcd and get the registry running
tape('initialize', function(t){

	var commands = [
		'ssh viking-0 viking start --seed',
		'ssh viking-1 viking start',
		'ssh viking-2 viking start'
	]

	runCommands(commands, function(err){
		if(err){
			t.fail(err)
			return t.end()
		}

		t.end()

	})

})

tape('shutdown', function(t){

	var commands = [
		'ssh viking-2 viking stop',
		'ssh viking-2 sudo viking reset',
		'ssh viking-1 viking stop',
		'ssh viking-1 sudo viking reset',
		'ssh viking-0 viking stop',
		'ssh viking-0 sudo viking reset'
	]

	runCommands(commands, function(err){
		if(err){
			t.fail(err)
			return t.end()
		}

		t.end()

	})

})<commit_msg>process bad variable name<commit_after>var spawn = require('child_process').spawn
var exec = require('child_process').exec
var async = require('async');
var etcdjs = require('etcdjs')
var flatten = require('etcd-flatten')
var tape     = require('tape')
var config = require('../lib/config')()
var concat = require('concat-stream')
var state = {}

function runCommands(commands, done){

	async.forEachSeries(commands, function(command, nextCmd){
		var p = spawn(command, [], {
			stdio:[null, process.stdout, process.stderr]
		})

		p.on('error', nextCmd)
		p.on('close', nextcmd)
	}, done)
}

// start viking - this will boot etcd and get the registry running
tape('initialize', function(t){

	var commands = [
		'ssh viking-0 viking start --seed',
		'ssh viking-1 viking start',
		'ssh viking-2 viking start'
	]

	runCommands(commands, function(err){
		if(err){
			t.fail(err)
			return t.end()
		}

		t.end()

	})

})

tape('shutdown', function(t){

	var commands = [
		'ssh viking-2 viking stop',
		'ssh viking-2 sudo viking reset',
		'ssh viking-1 viking stop',
		'ssh viking-1 sudo viking reset',
		'ssh viking-0 viking stop',
		'ssh viking-0 sudo viking reset'
	]

	runCommands(commands, function(err){
		if(err){
			t.fail(err)
			return t.end()
		}

		t.end()

	})

})<|endoftext|>


<commit_before>// Copyright 2013, University of Colorado

/**
 * The navigation bar at the bottom of the screen.
 * For a single-tab sim, it shows the name of the sim at the left and the PhET Logo and options menu at the right.
 * For a multi-tab sim, it shows icons for all of the other tabs, with the tab name at the left and the PhET Logo and options menu at the right.
 *
 * @author Sam Reid
 */
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var BoundsNode = require( 'SUN/BoundsNode' );
  var Layout = require( 'JOIST/Layout' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var SimPopupMenu = require( 'JOIST/SimPopupMenu' );

  function NavigationBar( sim, tabs, model ) {
    var navigationBar = this;
    Node.call( this );

    //Space between the icons and the bottom of the play area
    var verticalPadding = 2;

    var fontSize = 36;

    //Create the text labels once because in this version of Scenery (4/18/2013) they are expensive to create because they must be accurately sized.
    this.textLabel = new Node();
    var phetLabel = new Text( "PhET", {fontSize: fontSize, fill: 'yellow'} );
    this.addChild( this.textLabel );
    var optionsButton = new BoundsNode( new FontAwesomeNode( 'reorder', {fill: '#fff'} ), {cursor: 'pointer'} );

    //Creating the popup menu dynamically (when needed) causes a temporary black screen on the iPad (perhaps because of canvas accurate text bounds)
    var simPopupMenu = new SimPopupMenu( sim );
    optionsButton.addInputListener( {
                                      // mousedown or touchstart (pointer pressed down over the node)
                                      down: function( event ) {
                                        simPopupMenu.x = navigationBar.navBarWidth - simPopupMenu.width - 2;
                                        simPopupMenu.y = window.innerHeight - simPopupMenu.height - navigationBar.height / 2 + 4;
                                        var overlayScene = sim.createAndAddOverlay( simPopupMenu );
                                        overlayScene.addInputListener( {down: function() {
                                          sim.removeOverlay( overlayScene );
                                        }} );
                                      }
                                    } );

    this.phetLabelAndButton = new HBox( {spacing: 10, children: [phetLabel, optionsButton]} );
    this.addChild( this.phetLabelAndButton );

    //Create the nodes to be used for the tab icons
    var index = 0;
    var tabChildren = _.map( tabs, function( tab ) {
      tab.index = index++;
      var child = new Node( {children: [tab.icon], cursor: 'pointer'} );
      child.tab = tab;
      child.scale( (100 - verticalPadding * 2) / child.tab.icon.height );

      var textLabel = new Text( tab.name, {fontSize: 26, fill: 'black'} );
      var outline = new Rectangle( 0, 0, textLabel.width + 10, textLabel.height + 10, 10, 10, {fill: 'white'} );
      textLabel.centerX = outline.width / 2;
      textLabel.centerY = outline.height / 2;
      outline.addChild( textLabel );

      child.largeTextLabel = outline;
      child.addInputListener( { down: function() {
        model.tabIndex = tab.index;
        model.showHomeScreen = false;
      }} );
      return child;
    } );

    //Add everything to the scene

    if ( tabs.length > 1 ) {
      for ( var i = 0; i < tabChildren.length; i++ ) {
        this.addChild( tabChildren[i] );
        this.addChild( tabChildren[i].largeTextLabel );
      }
    }
    else if ( tabs.length == 1 ) {
      this.addChild( tabChildren[0].largeTextLabel );
    }

    //add the home icon
    this.homeIcon = new BoundsNode( new FontAwesomeNode( 'home', {fill: '#fff'} ), {cursor: 'pointer'} );
    this.homeIcon.addInputListener( {down: function() { model.showHomeScreen = true; }} );
    if ( tabs.length > 1 ) {
      this.addChild( this.homeIcon );
    }

    this.navBarHeight = 40;
    this.navBarScale = 1;
    this.navBarWidth = 768;

    this.relayout = function() {
      var height = this.navBarHeight;
      console.log( height );
      var tabIndex = navigationBar.tabIndex;
      //Update size and opacity of each icon
      var selectedChild = null;
      for ( var i = 0; i < tabChildren.length; i++ ) {
        var child = tabChildren[i];
        child.invalidateBounds();
        var selected = tabIndex === child.tab.index;
        child.selected = selected;
        child.opacity = selected ? 1 : 0.5;
        child.resetTransform();
        var tabScale = selected ? (height - verticalPadding * 2) / child.tab.icon.height : (height - verticalPadding * 2) / child.tab.icon.height * 0.75;
        child.scale( tabScale );
        child.largeTextLabel.visible = selected;
        if ( selected ) {
          selectedChild = child;
        }
      }

      //Compute layout bounds
      var width = 0;
      for ( var i = 0; i < tabChildren.length; i++ ) {
        var child = tabChildren[i];
        width = width + child.width;
      }
      var spacing = 10;
      width = width + spacing * (tabChildren.length - 1);

      selectedChild.largeTextLabel.setScaleMagnitude( this.navBarScale );
      selectedChild.largeTextLabel.centerY = this.navBarHeight / 2;

      //Lay out the components from left to right
      if ( tabs.length == 1 ) {
        selectedChild.largeTextLabel.left = 15;
      }
      else {

        //put the center right in the middle
        var centerX = this.navBarWidth / 2;

        var x = this.navBarWidth / 2 - width / 2;
        selectedChild.largeTextLabel.right = x - 25;

        for ( var i = 0; i < tabChildren.length; i++ ) {
          var child = tabChildren[i];
          child.x = x;
          child.y = verticalPadding;
          x += child.width + spacing;
        }
        navigationBar.homeIcon.setScaleMagnitude( this.navBarScale );
        navigationBar.homeIcon.centerY = this.navBarHeight / 2;
        navigationBar.homeIcon.left = x + 15;
      }
      this.phetLabelAndButton.setScaleMagnitude( this.navBarScale );
      this.phetLabelAndButton.right = this.navBarWidth - 5;
      this.phetLabelAndButton.centerY = this.navBarHeight / 2;
    };

    this.layout = function( scale, width, height ) {
      this.navBarScale = scale;
      this.navBarWidth = width;
      this.navBarHeight = height;
      navigationBar.relayout();
    };

    //On initialization and when the tab changes, update the size of the icons and the layout of the icons and text
    model.link( 'tabIndex', function( tabIndex ) {
      navigationBar.tabIndex = tabIndex;
      navigationBar.relayout();
    } );
  }

  inherit( NavigationBar, Node );

  return NavigationBar;
} );<commit_msg>show small text labels under the icons in the nav bar<commit_after>// Copyright 2013, University of Colorado

/**
 * The navigation bar at the bottom of the screen.
 * For a single-tab sim, it shows the name of the sim at the left and the PhET Logo and options menu at the right.
 * For a multi-tab sim, it shows icons for all of the other tabs, with the tab name at the left and the PhET Logo and options menu at the right.
 *
 * @author Sam Reid
 */
define( function( require ) {
  "use strict";

  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var Text = require( 'SCENERY/nodes/Text' );
  var FontAwesomeNode = require( 'SUN/FontAwesomeNode' );
  var BoundsNode = require( 'SUN/BoundsNode' );
  var Layout = require( 'JOIST/Layout' );
  var Rectangle = require( 'SCENERY/nodes/Rectangle' );
  var inherit = require( 'PHET_CORE/inherit' );
  var SimPopupMenu = require( 'JOIST/SimPopupMenu' );

  function NavigationBar( sim, tabs, model ) {
    var navigationBar = this;
    Node.call( this );

    //Space between the icons and the bottom of the play area
    var verticalPadding = 2;

    var fontSize = 36;

    //Create the text labels once because in this version of Scenery (4/18/2013) they are expensive to create because they must be accurately sized.
    this.textLabel = new Node();
    var phetLabel = new Text( "PhET", {fontSize: fontSize, fill: 'yellow'} );
    this.addChild( this.textLabel );
    var optionsButton = new BoundsNode( new FontAwesomeNode( 'reorder', {fill: '#fff'} ), {cursor: 'pointer'} );

    //Creating the popup menu dynamically (when needed) causes a temporary black screen on the iPad (perhaps because of canvas accurate text bounds)
    var simPopupMenu = new SimPopupMenu( sim );
    optionsButton.addInputListener( {
                                      // mousedown or touchstart (pointer pressed down over the node)
                                      down: function( event ) {
                                        simPopupMenu.x = navigationBar.navBarWidth - simPopupMenu.width - 2;
                                        simPopupMenu.y = window.innerHeight - simPopupMenu.height - navigationBar.height / 2 + 4;
                                        var overlayScene = sim.createAndAddOverlay( simPopupMenu );
                                        overlayScene.addInputListener( {down: function() {
                                          sim.removeOverlay( overlayScene );
                                        }} );
                                      }
                                    } );

    this.phetLabelAndButton = new HBox( {spacing: 10, children: [phetLabel, optionsButton]} );
    this.addChild( this.phetLabelAndButton );

    //Create the nodes to be used for the tab icons
    var index = 0;
    var tabChildren = _.map( tabs, function( tab ) {
      tab.index = index++;
      var child = new Node( {children: [tab.icon], cursor: 'pointer'} );
      child.tab = tab;
      child.scale( (100 - verticalPadding * 2) / child.tab.icon.height );

      var textLabel = new Text( tab.name, {fontSize: 26, fill: 'black'} );
      var outline = new Rectangle( 0, 0, textLabel.width + 10, textLabel.height + 10, 10, 10, {fill: 'white'} );
      textLabel.centerX = outline.width / 2;
      textLabel.centerY = outline.height / 2;
      outline.addChild( textLabel );

      child.largeTextLabel = outline;
      child.smallTextLabel = new Text( tab.name, {fontSize: 10, fill: 'white', visible: true} );

      child.addInputListener( { down: function() {
        model.tabIndex = tab.index;
        model.showHomeScreen = false;
      }} );
      return child;
    } );

    //Add everything to the scene

    if ( tabs.length > 1 ) {
      for ( var i = 0; i < tabChildren.length; i++ ) {
        this.addChild( tabChildren[i] );
        this.addChild( tabChildren[i].largeTextLabel );
        this.addChild( tabChildren[i].smallTextLabel );
      }
    }
    else if ( tabs.length == 1 ) {
      this.addChild( tabChildren[0].largeTextLabel );
    }

    //add the home icon
    this.homeIcon = new BoundsNode( new FontAwesomeNode( 'home', {fill: '#fff'} ), {cursor: 'pointer'} );
    this.homeIcon.addInputListener( {down: function() { model.showHomeScreen = true; }} );
    if ( tabs.length > 1 ) {
      this.addChild( this.homeIcon );
    }

    this.navBarHeight = 40;
    this.navBarScale = 1;
    this.navBarWidth = 768;

    this.relayout = function() {
      var height = this.navBarHeight;
      console.log( height );
      var tabIndex = navigationBar.tabIndex;
      //Update size and opacity of each icon
      var selectedChild = null;
      for ( var i = 0; i < tabChildren.length; i++ ) {
        var child = tabChildren[i];
        child.invalidateBounds();
        var selected = tabIndex === child.tab.index;
        child.selected = selected;
        child.opacity = selected ? 1 : 0.5;
        child.resetTransform();
        var tabScale = selected ? (height - verticalPadding * 2) / child.tab.icon.height : (height - verticalPadding * 2) / child.tab.icon.height * 0.75;
        child.scale( tabScale );
        child.largeTextLabel.visible = selected;
        if ( selected ) {
          selectedChild = child;
        }
        child.smallTextLabel.setScaleMagnitude( this.navBarScale );
      }

      //Compute layout bounds
      var width = 0;
      for ( var i = 0; i < tabChildren.length; i++ ) {
        var child = tabChildren[i];
        width = width + child.width;
      }
      var spacing = 10;
      width = width + spacing * (tabChildren.length - 1);

      selectedChild.largeTextLabel.setScaleMagnitude( this.navBarScale );
      selectedChild.largeTextLabel.centerY = this.navBarHeight / 2;

      //Lay out the components from left to right
      if ( tabs.length == 1 ) {
        selectedChild.largeTextLabel.left = 15;
      }
      else {

        //put the center right in the middle
        var x = this.navBarWidth / 2 - width / 2;
        selectedChild.largeTextLabel.right = x - 25;

        for ( var i = 0; i < tabChildren.length; i++ ) {
          var child = tabChildren[i];
          child.x = x;
          child.y = verticalPadding;
          child.smallTextLabel.visible = (selectedChild !== child);
          if ( child !== selectedChild ) {
            child.smallTextLabel.x = child.x;
            child.smallTextLabel.top = child.bottom - 1;
          }
          x += child.width + spacing;
        }
        navigationBar.homeIcon.setScaleMagnitude( this.navBarScale );
        navigationBar.homeIcon.centerY = this.navBarHeight / 2;
        navigationBar.homeIcon.left = x + 15;
      }
      this.phetLabelAndButton.setScaleMagnitude( this.navBarScale );
      this.phetLabelAndButton.right = this.navBarWidth - 5;
      this.phetLabelAndButton.centerY = this.navBarHeight / 2;
    };

    this.layout = function( scale, width, height ) {
      this.navBarScale = scale;
      this.navBarWidth = width;
      this.navBarHeight = height;
      navigationBar.relayout();
    };

    //On initialization and when the tab changes, update the size of the icons and the layout of the icons and text
    model.link( 'tabIndex', function( tabIndex ) {
      navigationBar.tabIndex = tabIndex;
      navigationBar.relayout();
    } );
  }

  inherit( NavigationBar, Node );

  return NavigationBar;
} );<|endoftext|>


<commit_before>'use strict';
var BaseModel = require('model-toolkit').BaseModel;
var uom = require('../../master/uom');
var ColorType = require('../../master/color-type');

module.exports = class ProductionOrderDetail extends BaseModel {
    constructor(source) {
        super('production-order-detail', '1.0.0');

        this.code='';
        this.colorRequest=''; //warna yang diminta
        this.colorTemplate=''; //acuan warna/desain
        this.colorTypeId={};
        this.colorType=new ColorType();
        this.quantity=0;
        this.uomId={};
        this.uom=new uom();
        this.copy(source);
    }
};<commit_msg>fixing detail prod order<commit_after>'use strict';
var BaseModel = require('model-toolkit').BaseModel;
var uom = require('../../master/uom');
var ColorType = require('../../master/color-type');

module.exports = class ProductionOrderDetail extends BaseModel {
    constructor(source) {
        super('production-order-detail', '1.0.0');

        this.code='';
        this.colorRequest=''; //warna yang diminta
        this.colorTemplate=''; //acuan warna/desain
        this.colorTypeId=null;
        this.colorType=null;
        this.quantity=0;
        this.uomId={};
        this.uom=new uom();
        this.copy(source);
    }
};<|endoftext|>


<commit_before>/**
 * Copyright (c) 2014 Virtue Studios
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions\:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/* globals EventType */
var _varbit = require('engine/var/bit');
var _map = require('engine/map');
var _config = require('engine/config');

var CONST = require('const');
var chat = require('chat');
var dialog = require('dialog');
var widget = require('widget');
var util = require('util');
var inv = require('inv');

var houseBuilder = require('./house-builder');
var RoomType = require('./room');

module.exports = (function () {
	return {
		init : init
	};

	function init (scriptManager) {
		//Build option on door hotpots
		scriptManager.bind(EventType.OPLOC5, [ 15305, 15306, 15307, 15308, 15309, 15310, 15311, 15312,
				15313, 15314, 15315, 15316, 15317 ], function (ctx) {
			var player = ctx.player;
			var posX = _map.getLocalX(player);
			var posY = _map.getLocalY(player);
			var level = _map.getLevel(player);
			var zoneX = Math.floor(posX / 8);
			var zoneY = Math.floor(posY / 8);
			if (posX % 8 === 0) {
				zoneX--;//Building to the west
			} else if (posX % 8 === 7) {
				zoneX++;//Building to the east
			} else if (posY % 8 === 0) {
				zoneY--;//Building to the south
			} else if (posY % 8 === 7) {
				zoneY++;//Building to the north
			} else {
				throw "Invalid coordinates: "+_map.getCoords(player);
			}
			var roomId = houseBuilder.loadRoom(player, zoneX, zoneY, level);
			if (roomId !== -1 && _varbit(player, 1528) !== 0) {
				dialog.builder(player).confirm("TODO: Dialog for confirming room removal")
					.then(function () {
						houseBuilder.removeRoom(player, roomId, zoneX, zoneY, level);
					});
			} else {
				roomId = houseBuilder.findEmptyRoom(player);
				if (roomId === -1) {
					chat.sendMessage(player, "[TODO: Fix message] You already have too many rooms...");
				} else {
					_varbit(player, 1524, zoneX);
					_varbit(player, 1525, zoneY);
					_varbit(player, 1526, level);
					widget.openCentral(player, 402);
				}
			}
		});

		scriptManager.bind(EventType.IF_BUTTON, 402, function (ctx) {
			var player = ctx.player;
			switch (ctx.component) {
			case 73://Build parlor
				widget.closeAll(player);
				buildRoom(player, RoomType.PARLOUR);
				return;
			case 82://Build garden
				widget.closeAll(player);
				buildRoom(player, RoomType.GARDEN);
				return;
			case 91://Build kitchen
				widget.closeAll(player);
				buildRoom(player, RoomType.KITCHEN);
				return;
			case 99://Build dining room
				widget.closeAll(player);
				buildRoom(player, RoomType.DINING);
				return;
			case 107://Build workshop
				widget.closeAll(player);
				buildRoom(player, RoomType.WORKSHOP);
				return;
			case 115://Build bedroom
				widget.closeAll(player);
				buildRoom(player, RoomType.BEDROOM);
				return;
			default:
				util.defaultHandler(ctx, "room selection");
				return;
			}
		});
	}

	function buildRoom(player, roomType) {
		chat.sendDebugMessage(player, "Building room: "+_config.objName(roomType.objId));
		var zoneX = _varbit(player, 1524);
		var zoneY = _varbit(player, 1525);
		var level = _varbit(player, 1526);

		var playerZoneY = Math.floor(_map.getLocalY(player) / 8);
		var doorPos = -1;
		if (zoneY < playerZoneY) {
			doorPos = 0;//Door needed in the north
		} else if (zoneY > playerZoneY) {
			doorPos = 2;//Door needed in the south
		} else {
			var playerZoneX = Math.floor(_map.getLocalX(player) / 8);
			if (zoneX < playerZoneX) {
				doorPos = 1;//Door needed in the east
			} else if (zoneX > playerZoneX) {
				doorPos = 3;//Door needed in the south
			} else {
				throw "Invalid player position: "+_map.getCoords(player)+" for zone "+zoneX+","+zoneY+","+level;
			}
		}
		var rotation = 0;
		while (!roomType.doors[(rotation+doorPos) & 0x3]) {
			rotation++;
		}

		dialog.multi2(player,
			"TODO: Add support for rotating rooms...",
			"Build", function () {
				inv.take(player, CONST.COINS, _config.objCost(roomType.objId));
				houseBuilder.addRoom(player, roomType.objId, zoneX, zoneY, level, rotation);
			}, "Cancel");
	}
})();
<commit_msg>Add check for construction level before building a room<commit_after>/**
 * Copyright (c) 2014 Virtue Studios
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions\:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/* globals EventType, Stat */
var _varbit = require('engine/var/bit');
var _map = require('engine/map');
var _config = require('engine/config');

var CONST = require('const');
var chat = require('chat');
var dialog = require('dialog');
var widget = require('widget');
var util = require('util');
var inv = require('inv');
var stat = require('stat');

var houseBuilder = require('./house-builder');
var RoomType = require('./room');

module.exports = (function () {
	return {
		init : init
	};

	function init (scriptManager) {
		//Build option on door hotpots
		scriptManager.bind(EventType.OPLOC5, [ 15305, 15306, 15307, 15308, 15309, 15310, 15311, 15312,
				15313, 15314, 15315, 15316, 15317 ], function (ctx) {
			var player = ctx.player;
			var posX = _map.getLocalX(player);
			var posY = _map.getLocalY(player);
			var level = _map.getLevel(player);
			var zoneX = Math.floor(posX / 8);
			var zoneY = Math.floor(posY / 8);
			if (posX % 8 === 0) {
				zoneX--;//Building to the west
			} else if (posX % 8 === 7) {
				zoneX++;//Building to the east
			} else if (posY % 8 === 0) {
				zoneY--;//Building to the south
			} else if (posY % 8 === 7) {
				zoneY++;//Building to the north
			} else {
				throw "Invalid coordinates: "+_map.getCoords(player);
			}
			var roomId = houseBuilder.loadRoom(player, zoneX, zoneY, level);
			if (roomId !== -1 && _varbit(player, 1528) !== 0) {
				dialog.builder(player).confirm("TODO: Dialog for confirming room removal")
					.then(function () {
						houseBuilder.removeRoom(player, roomId, zoneX, zoneY, level);
					});
			} else {
				roomId = houseBuilder.findEmptyRoom(player);
				if (roomId === -1) {
					chat.sendMessage(player, "[TODO: Fix message] You already have too many rooms...");
				} else {
					_varbit(player, 1524, zoneX);
					_varbit(player, 1525, zoneY);
					_varbit(player, 1526, level);
					widget.openCentral(player, 402);
				}
			}
		});

		scriptManager.bind(EventType.IF_BUTTON, 402, function (ctx) {
			var player = ctx.player;
			switch (ctx.component) {
			case 73://Build parlor
				widget.closeAll(player);
				buildRoom(player, RoomType.PARLOUR);
				return;
			case 82://Build garden
				widget.closeAll(player);
				buildRoom(player, RoomType.GARDEN);
				return;
			case 91://Build kitchen
				widget.closeAll(player);
				buildRoom(player, RoomType.KITCHEN);
				return;
			case 99://Build dining room
				widget.closeAll(player);
				buildRoom(player, RoomType.DINING);
				return;
			case 107://Build workshop
				widget.closeAll(player);
				buildRoom(player, RoomType.WORKSHOP);
				return;
			case 115://Build bedroom
				widget.closeAll(player);
				buildRoom(player, RoomType.BEDROOM);
				return;
			default:
				util.defaultHandler(ctx, "room selection");
				return;
			}
		});
	}

	function buildRoom(player, roomType) {
		if (stat.getLevel(player, Stat.CONSTRUCTION) < _config.objParam(roomType.objId, 23)) {
			chat.sendDebugMessage(player, "You need a construction level of "+_config.objParam(roomType.objId, 23)+" to build this room.");
			return;
		}
		chat.sendDebugMessage(player, "Building room: "+_config.objName(roomType.objId));
		var zoneX = _varbit(player, 1524);
		var zoneY = _varbit(player, 1525);
		var level = _varbit(player, 1526);

		var playerZoneY = Math.floor(_map.getLocalY(player) / 8);
		var doorPos = -1;
		if (zoneY < playerZoneY) {
			doorPos = 0;//Door needed in the north
		} else if (zoneY > playerZoneY) {
			doorPos = 2;//Door needed in the south
		} else {
			var playerZoneX = Math.floor(_map.getLocalX(player) / 8);
			if (zoneX < playerZoneX) {
				doorPos = 1;//Door needed in the east
			} else if (zoneX > playerZoneX) {
				doorPos = 3;//Door needed in the south
			} else {
				throw "Invalid player position: "+_map.getCoords(player)+" for zone "+zoneX+","+zoneY+","+level;
			}
		}
		var rotation = 0;
		while (!roomType.doors[(rotation+doorPos) & 0x3]) {
			rotation++;
		}

		dialog.multi2(player,
			"TODO: Add support for rotating rooms...",
			"Build", function () {
				inv.take(player, CONST.COINS, _config.objCost(roomType.objId));
				houseBuilder.addRoom(player, roomType.objId, zoneX, zoneY, level, rotation);
			}, "Cancel");
	}
})();
<|endoftext|>


<commit_before>'use strict';
// Operations on equation nodes

const math = require('../../../index');

const Equation = require('./Equation');
const EquationStatus = require('./EquationStatus');
const MathChangeTypes = require('./MathChangeTypes');
const Negative = require('./Negative.js');
const NodeCreator = require('./NodeCreator');
const NodeType = require('./NodeType');
const PolynomialTermNode = require('./PolynomialTermNode');
const Symbols = require('./Symbols.js');

class EquationOperations {};

// Ensures that the given equation has the given symbolName on the left side,
// by swapping the right and left sides if it is only in the right side.
// So 3 = x would become x = 3.
EquationOperations.ensureSymbolInLeftNode = function(equation, symbolName) {
  const leftSideSymbolTerm = Symbols.getFirstSymbolTerm(
    equation.leftNode, symbolName);
  const rightSideSymbolTerm = Symbols.getFirstSymbolTerm(
    equation.rightNode, symbolName);

  if (!leftSideSymbolTerm) {
    if (rightSideSymbolTerm) {
      equation = new Equation(
        equation.rightNode, equation.leftNode, equation.comparator)
      return new EquationStatus(equation, true, MathChangeTypes.SWAP_SIDES);
    }
    else {
      throw Error('No term with symbol: ' + symbolName)
    }
  }
  return new EquationStatus(equation);
}

// TODO: Ensures that a symbol is not in the denominator by multiplying
// both sides by the whatever order of the symbol necessary.
// This is blocked on the simplifying functionality of canceling symbols in fractions
EquationOperations.removeSymbolFromDenominator = function(equation, symbolName) {
  // pass for now
  return new EquationStatus(equation);
}

// Removes the given symbolName from the right side by adding, multiplying, subtracting
// or dividing it from both sides as appropriate
// TODO: support inverting functions e.g. sqrt, ^, log etc.
EquationOperations.removeSymbolFromRightSide = function(equation, symbolName) {
  const symbolTerm = Symbols.getFirstSymbolTerm(equation.rightNode, symbolName);

  let inverseOp, inverseTerm, changeType;
  if (symbolTerm){
    if (NodeType.isOperator(equation.rightNode)) {
      if (equation.rightNode.op === '+') {
        if (Negative.isNegative(symbolTerm)) {
          inverseOp = '+';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = Negative.negate(symbolTerm);
        } else {
          inverseOp = '-';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = symbolTerm;
        }

        return performTermOperationOnEquation(
            equation, inverseOp, inverseTerm, changeType)
      }
      else {
        throw Error('Unsupported operation: ' + symbolTerm.op);
      }
    }
    else if (NodeType.isUnaryMinus(equation.rightNode)) {
      inverseOp = '+';
      changeType = MathChangeTypes.ADD_TO_BOTH_SIDES;
      inverseTerm = symbolTerm.args[0];

      return performTermOperationOnEquation(
          equation, inverseOp, inverseTerm, changeType)
    }
  }

  return new EquationStatus(equation);
}

// Isolates the given symbolName to the left side by adding, multiplying, subtracting
// or dividing all other symbols and constants from both sides appropriately
// TODO: support inverting functions e.g. sqrt, ^, log etc.
EquationOperations.isolateSymbolOnLeftSide = function(equation, symbolName) {
  let nonSymbolTerm = Symbols.getLastNonSymbolTerm(equation.leftNode, symbolName);

  let inverseOp, inverseTerm, changeType;
  if (nonSymbolTerm) {
    if (NodeType.isOperator(equation.leftNode)) {
      if (equation.leftNode.op === '+') {
        if (Negative.isNegative(nonSymbolTerm)) {
          inverseOp = '+';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = Negative.negate(nonSymbolTerm);
        } else {
          inverseOp = '-';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else if (equation.leftNode.op === '*') {
        if (NodeType.isConstantFraction(nonSymbolTerm)) {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_BOTH_SIDES_BY_INVERSE_FRACTION;
          inverseTerm = NodeCreator.operator(
            '/', [nonSymbolTerm.args[1], nonSymbolTerm.args[0]]);

        } else {
          inverseOp = '/';
          changeType = MathChangeTypes.DIVIDE_FROM_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else if (equation.leftNode.op === '/') {
        if (NodeType.isConstantFraction(nonSymbolTerm) &&
            PolynomialTermNode.isPolynomialTerm(equation.leftNode)) {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_BOTH_SIDES_BY_INVERSE_FRACTION;
          inverseTerm = NodeCreator.operator(
            '/', [nonSymbolTerm.args[1], nonSymbolTerm.args[0]]);
        } else {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_TO_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else {
        throw Error('Unsupported operation: ' + equation.leftNode.op);
      }

      return performTermOperationOnEquation(
        equation, inverseOp, inverseTerm, changeType);
    }
    else if (NodeType.isUnaryMinus(equation.leftNode)) {
      inverseOp = '*';
      changeType = MathChangeTypes.MULTIPLE_BOTH_SIDES_BY_NEGATIVE_ONE;
      inverseTerm =  NodeCreator.constant(-1);

      return performTermOperationOnEquation(
          equation, inverseOp, inverseTerm, changeType);
    }
  }

  return new EquationStatus(equation);
}

function performTermOperationOnEquation(equation, op, term, changeType) {
  const leftNode = NodeCreator.operator(op, [equation.leftNode, term]);
  const rightNode = NodeCreator.operator(op, [equation.rightNode, term]);
  equation = new Equation(leftNode, rightNode, equation.comparator);
  return new EquationStatus(equation, true, changeType);
}

module.exports = EquationOperations;
<commit_msg>when adding a negated term, the change is adding the term to both sides not subtracting<commit_after>'use strict';
// Operations on equation nodes

const math = require('../../../index');

const Equation = require('./Equation');
const EquationStatus = require('./EquationStatus');
const MathChangeTypes = require('./MathChangeTypes');
const Negative = require('./Negative.js');
const NodeCreator = require('./NodeCreator');
const NodeType = require('./NodeType');
const PolynomialTermNode = require('./PolynomialTermNode');
const Symbols = require('./Symbols.js');

class EquationOperations {};

// Ensures that the given equation has the given symbolName on the left side,
// by swapping the right and left sides if it is only in the right side.
// So 3 = x would become x = 3.
EquationOperations.ensureSymbolInLeftNode = function(equation, symbolName) {
  const leftSideSymbolTerm = Symbols.getFirstSymbolTerm(
    equation.leftNode, symbolName);
  const rightSideSymbolTerm = Symbols.getFirstSymbolTerm(
    equation.rightNode, symbolName);

  if (!leftSideSymbolTerm) {
    if (rightSideSymbolTerm) {
      equation = new Equation(
        equation.rightNode, equation.leftNode, equation.comparator)
      return new EquationStatus(equation, true, MathChangeTypes.SWAP_SIDES);
    }
    else {
      throw Error('No term with symbol: ' + symbolName)
    }
  }
  return new EquationStatus(equation);
}

// TODO: Ensures that a symbol is not in the denominator by multiplying
// both sides by the whatever order of the symbol necessary.
// This is blocked on the simplifying functionality of canceling symbols in fractions
EquationOperations.removeSymbolFromDenominator = function(equation, symbolName) {
  // pass for now
  return new EquationStatus(equation);
}

// Removes the given symbolName from the right side by adding, multiplying, subtracting
// or dividing it from both sides as appropriate
// TODO: support inverting functions e.g. sqrt, ^, log etc.
EquationOperations.removeSymbolFromRightSide = function(equation, symbolName) {
  const symbolTerm = Symbols.getFirstSymbolTerm(equation.rightNode, symbolName);

  let inverseOp, inverseTerm, changeType;
  if (symbolTerm){
    if (NodeType.isOperator(equation.rightNode)) {
      if (equation.rightNode.op === '+') {
        if (Negative.isNegative(symbolTerm)) {
          inverseOp = '+';
          changeType = MathChangeTypes.ADD_TO_BOTH_SIDES;
          inverseTerm = Negative.negate(symbolTerm);
        } else {
          inverseOp = '-';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = symbolTerm;
        }

        return performTermOperationOnEquation(
            equation, inverseOp, inverseTerm, changeType)
      }
      else {
        throw Error('Unsupported operation: ' + symbolTerm.op);
      }
    }
    else if (NodeType.isUnaryMinus(equation.rightNode)) {
      inverseOp = '+';
      changeType = MathChangeTypes.ADD_TO_BOTH_SIDES;
      inverseTerm = symbolTerm.args[0];

      return performTermOperationOnEquation(
          equation, inverseOp, inverseTerm, changeType)
    }
  }

  return new EquationStatus(equation);
}

// Isolates the given symbolName to the left side by adding, multiplying, subtracting
// or dividing all other symbols and constants from both sides appropriately
// TODO: support inverting functions e.g. sqrt, ^, log etc.
EquationOperations.isolateSymbolOnLeftSide = function(equation, symbolName) {
  let nonSymbolTerm = Symbols.getLastNonSymbolTerm(equation.leftNode, symbolName);

  let inverseOp, inverseTerm, changeType;
  if (nonSymbolTerm) {
    if (NodeType.isOperator(equation.leftNode)) {
      if (equation.leftNode.op === '+') {
        if (Negative.isNegative(nonSymbolTerm)) {
          inverseOp = '+';
          changeType = MathChangeTypes.ADD_TO_BOTH_SIDES;
          inverseTerm = Negative.negate(nonSymbolTerm);
        } else {
          inverseOp = '-';
          changeType = MathChangeTypes.SUBTRACT_FROM_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else if (equation.leftNode.op === '*') {
        if (NodeType.isConstantFraction(nonSymbolTerm)) {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_BOTH_SIDES_BY_INVERSE_FRACTION;
          inverseTerm = NodeCreator.operator(
            '/', [nonSymbolTerm.args[1], nonSymbolTerm.args[0]]);

        } else {
          inverseOp = '/';
          changeType = MathChangeTypes.DIVIDE_FROM_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else if (equation.leftNode.op === '/') {
        if (NodeType.isConstantFraction(nonSymbolTerm) &&
            PolynomialTermNode.isPolynomialTerm(equation.leftNode)) {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_BOTH_SIDES_BY_INVERSE_FRACTION;
          inverseTerm = NodeCreator.operator(
            '/', [nonSymbolTerm.args[1], nonSymbolTerm.args[0]]);
        } else {
          inverseOp = '*';
          changeType = MathChangeTypes.MULTIPLY_TO_BOTH_SIDES;
          inverseTerm = nonSymbolTerm;
        }
      }
      else {
        throw Error('Unsupported operation: ' + equation.leftNode.op);
      }

      return performTermOperationOnEquation(
        equation, inverseOp, inverseTerm, changeType);
    }
    else if (NodeType.isUnaryMinus(equation.leftNode)) {
      inverseOp = '*';
      changeType = MathChangeTypes.MULTIPLE_BOTH_SIDES_BY_NEGATIVE_ONE;
      inverseTerm =  NodeCreator.constant(-1);

      return performTermOperationOnEquation(
          equation, inverseOp, inverseTerm, changeType);
    }
  }

  return new EquationStatus(equation);
}

function performTermOperationOnEquation(equation, op, term, changeType) {
  const leftNode = NodeCreator.operator(op, [equation.leftNode, term]);
  const rightNode = NodeCreator.operator(op, [equation.rightNode, term]);
  equation = new Equation(leftNode, rightNode, equation.comparator);
  return new EquationStatus(equation, true, changeType);
}

module.exports = EquationOperations;
<|endoftext|>


<commit_before>'use strict'
const TestRunner = require('test-runner')
const commandLineArgs = require('../')
const a = require('assert')

const runner = new TestRunner()

runner.test('partial: simple', function () {
  const definitions = [
    { name: 'one', type: Boolean }
  ]
  const argv = [ '--two', 'two', '--one', 'two' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    one: true,
    _unknown: [ '--two', 'two', 'two' ]
  })
})

runner.test('partial: defaultOption', function () {
  const definitions = [
    { name: 'files', type: String, defaultOption: true, multiple: true }
  ]
  const argv = [ '--files', 'file1', '--one', 'file2' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2' ],
    _unknown: [ '--one' ]
  })
})

runner.test('partial: defaultOption 2', function () {
  const definitions = [
    { name: 'files', type: String, defaultOption: true, multiple: true },
    { name: 'one', type: Boolean },
    { name: 'two', alias: 't', defaultValue: 2 }
  ]
  const argv = [ 'file1', '--one', 'file2', '-t', '--two=3', 'file3', '-ab' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2', 'file3' ],
    two: '3',
    one: true,
    _unknown: [ '-a', '-b' ]
  })
})

runner.test('partial: multiple', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true }
  ]
  const argv = [ 'file1', '--files', 'file2', '-t', '--two=3', 'file3', '-ab', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file2', 'file4' ],
    _unknown: [ 'file1', '-t', '--two=3', 'file3', '-a', '-b' ]
  })
})

runner.test('unknown options: rejected defaultOption values end up in _unknown', function () {
  const definitions = [
    { name: 'foo', type: String },
    { name: 'verbose', alias: 'v', type: Boolean },
    { name: 'libs', type: String, defaultOption: true }
  ]
  const argv = [ '--foo', 'bar', '-v', 'libfn', '--libarg', 'val1', '-r' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    foo: 'bar',
    verbose: true,
    libs: 'libfn',
    _unknown: [ '--libarg', 'val1', '-r' ]
  })
})

runner.test('partial: groups', function () {
  const definitions = [
    { name: 'one', group: 'a' },
    { name: 'two', group: 'a' },
    { name: 'three', group: 'b' }
  ]
  const argv = [ '--one', '1', '--two', '2', '--three', '3', 'ham', '--cheese' ]
  a.deepStrictEqual(commandLineArgs(definitions, { argv, partial: true }), {
    a: {
      one: '1',
      two: '2'
    },
    b: {
      three: '3'
    },
    _all: {
      one: '1',
      two: '2',
      three: '3'
    },
    _unknown: [ 'ham', '--cheese' ]
  })
})

runner.test('partial: multiple groups and _none', function () {
  const definitions = [
    { name: 'one', group: ['a', 'f'] },
    { name: 'two', group: ['a', 'g'] },
    { name: 'three' }
  ]
  const argv = [ '--cheese', '--one', '1', 'ham', '--two', '2', '--three', '3', '-c' ]
  a.deepStrictEqual(commandLineArgs(definitions, { argv, partial: true }), {
    a: {
      one: '1',
      two: '2'
    },
    f: {
      one: '1'
    },
    g: {
      two: '2'
    },
    _none: {
      three: '3'
    },
    _all: {
      one: '1',
      two: '2',
      three: '3'
    },
    _unknown: [ '--cheese', 'ham', '-c' ]
  })
})

runner.test('partial: defaultOption with --option=value notation', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ 'file1', 'file2', '--unknown=something' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2' ],
    _unknown: [ '--unknown=something' ]
  })
})

runner.test('partial: defaultOption with --option=value notation 2', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ 'file1', 'file2', '--unknown=something', '--files', 'file3', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2', 'file3', 'file4' ],
    _unknown: [ '--unknown=something' ]
  })
})

runner.test('partial: defaultOption with --option=value notation 3', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ '--unknown', 'file1', '--another', 'something', 'file2', '--unknown=something', '--files', 'file3', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'something', 'file2', 'file3', 'file4' ],
    _unknown: [ '--unknown', '--another', '--unknown=something' ]
  })
})

runner.test('partial: mulitple unknowns with same name', function () {
  const definitions = [
    { name: 'file' }
  ]
  const argv = [ '--unknown', '--unknown=something', '--file=file1', '--unknown' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    file: 'file1',
    _unknown: [ '--unknown', '--unknown=something', '--unknown' ]
  })
})
<commit_msg>test a default option with a value equal to its default value<commit_after>'use strict'
const TestRunner = require('test-runner')
const commandLineArgs = require('../')
const a = require('assert')

const runner = new TestRunner()

runner.test('partial: simple', function () {
  const definitions = [
    { name: 'one', type: Boolean }
  ]
  const argv = [ '--two', 'two', '--one', 'two' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    one: true,
    _unknown: [ '--two', 'two', 'two' ]
  })
})

runner.test('partial: defaultOption', function () {
  const definitions = [
    { name: 'files', type: String, defaultOption: true, multiple: true }
  ]
  const argv = [ '--files', 'file1', '--one', 'file2' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2' ],
    _unknown: [ '--one' ]
  })
})

runner.test('partial: defaultOption 2', function () {
  const definitions = [
    { name: 'files', type: String, defaultOption: true, multiple: true },
    { name: 'one', type: Boolean },
    { name: 'two', alias: 't', defaultValue: 2 }
  ]
  const argv = [ 'file1', '--one', 'file2', '-t', '--two=3', 'file3', '-ab' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2', 'file3' ],
    two: '3',
    one: true,
    _unknown: [ '-a', '-b' ]
  })
})

runner.test('partial: defaultOption with value equal to defaultValue', function () {
  const definitions = [
    { name: 'file', type: String, defaultOption: true, defaultValue: 'file1' },
  ]
  const argv = [ 'file1', '--two=3', '--four', '5' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    file: 'file1',
    _unknown: [ '--two', '3', '--four', '5' ]
  })
})

runner.test('partial: multiple', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true }
  ]
  const argv = [ 'file1', '--files', 'file2', '-t', '--two=3', 'file3', '-ab', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file2', 'file4' ],
    _unknown: [ 'file1', '-t', '--two=3', 'file3', '-a', '-b' ]
  })
})

runner.test('unknown options: rejected defaultOption values end up in _unknown', function () {
  const definitions = [
    { name: 'foo', type: String },
    { name: 'verbose', alias: 'v', type: Boolean },
    { name: 'libs', type: String, defaultOption: true }
  ]
  const argv = [ '--foo', 'bar', '-v', 'libfn', '--libarg', 'val1', '-r' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    foo: 'bar',
    verbose: true,
    libs: 'libfn',
    _unknown: [ '--libarg', 'val1', '-r' ]
  })
})

runner.test('partial: groups', function () {
  const definitions = [
    { name: 'one', group: 'a' },
    { name: 'two', group: 'a' },
    { name: 'three', group: 'b' }
  ]
  const argv = [ '--one', '1', '--two', '2', '--three', '3', 'ham', '--cheese' ]
  a.deepStrictEqual(commandLineArgs(definitions, { argv, partial: true }), {
    a: {
      one: '1',
      two: '2'
    },
    b: {
      three: '3'
    },
    _all: {
      one: '1',
      two: '2',
      three: '3'
    },
    _unknown: [ 'ham', '--cheese' ]
  })
})

runner.test('partial: multiple groups and _none', function () {
  const definitions = [
    { name: 'one', group: ['a', 'f'] },
    { name: 'two', group: ['a', 'g'] },
    { name: 'three' }
  ]
  const argv = [ '--cheese', '--one', '1', 'ham', '--two', '2', '--three', '3', '-c' ]
  a.deepStrictEqual(commandLineArgs(definitions, { argv, partial: true }), {
    a: {
      one: '1',
      two: '2'
    },
    f: {
      one: '1'
    },
    g: {
      two: '2'
    },
    _none: {
      three: '3'
    },
    _all: {
      one: '1',
      two: '2',
      three: '3'
    },
    _unknown: [ '--cheese', 'ham', '-c' ]
  })
})

runner.test('partial: defaultOption with --option=value notation', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ 'file1', 'file2', '--unknown=something' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2' ],
    _unknown: [ '--unknown=something' ]
  })
})

runner.test('partial: defaultOption with --option=value notation 2', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ 'file1', 'file2', '--unknown=something', '--files', 'file3', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'file2', 'file3', 'file4' ],
    _unknown: [ '--unknown=something' ]
  })
})

runner.test('partial: defaultOption with --option=value notation 3', function () {
  const definitions = [
    { name: 'files', type: String, multiple: true, defaultOption: true }
  ]
  const argv = [ '--unknown', 'file1', '--another', 'something', 'file2', '--unknown=something', '--files', 'file3', '--files=file4' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    files: [ 'file1', 'something', 'file2', 'file3', 'file4' ],
    _unknown: [ '--unknown', '--another', '--unknown=something' ]
  })
})

runner.test('partial: mulitple unknowns with same name', function () {
  const definitions = [
    { name: 'file' }
  ]
  const argv = [ '--unknown', '--unknown=something', '--file=file1', '--unknown' ]
  const options = commandLineArgs(definitions, { argv, partial: true })
  a.deepStrictEqual(options, {
    file: 'file1',
    _unknown: [ '--unknown', '--unknown=something', '--unknown' ]
  })
})
<|endoftext|>


<commit_before>const GLib = imports.gi.GLib;
const ComponentLoader = imports.componentLoader;
const Options = imports.options;
const Utils = imports.utils;

const CmdOptions = [
    {
        name: 'help',
        shortName: 'h',
        requireArgument: false,
        defaultValue: false,
        help: 'Print this screen'
    },
];

//
let addFile = function(path, directory) {
    Utils.copyFile(path, directory + '/' + path);
    return path;
};

let addFileContent = function(path, content, directory) {
    Utils.saveTextFileContent(directory + '/' + path, content);
    return path;
};

let addModule = function(loader, module, directory) {
    let files = [];
    // strip vpath scheme to get relative directory
    let dir = /[\w\d]+:\/\/(.*)/.exec(module.vpath)[1] + '/';

    // Add components
    for (let componentName in module.noflo.components) {
        let componentPath = module.noflo.components[componentName];
        files.push(addFileContent(dir + componentPath,
                                  loader.getComponentCode(module.normalizedName + '/' + componentName),
                                  directory));
    }

    // Add graphs
    for (let graphName in module.noflo.graphs) {
        let graphPath = module.noflo.graphs[graphName];
        files.push(addFileContent(dir + graphPath,
                                  loader.getComponentCode(module.normalizedName + '/' + graphName),
                                  directory));
    }

    // Loader
    if (module.noflo.loader) {
        files.push(addFileContent(dir + module.noflo.loader,
                                  Utils.loadTextFileContent(Utils.resolvePath(module.vpath + '/' + module.noflo.loader)),
                                  directory));
    }

    // index
    files.push(addFileContent(dir + 'component.json',
                              Utils.loadTextFileContent(Utils.resolvePath(module.vpath + '/component.json')),
                              directory));

    return files;
};

let generateIndex = function(files) {
    let data =
        '<?xml version="1.0" encoding="UTF-8"?>\n' +
        '<gresources>\n' +
        ' <gresource prefix="/org/gnome/noflo-gnome">\n';

    for (let i in files)
        data += '  <file>' + files[i] + '</file>\n';

    data +=
        ' </gresource>\n' +
        '</gresources>';

    return data;
};

//
let exec = function(args) {
    let options = Options.parseArguments(CmdOptions, args);

    if (options.options.help) {
        Options.printHelp('noflo-gnome bundle', CmdOptions);
        return 0;
    }

    // Get library loader
    let loader = new ComponentLoader.ComponentLoader({
        baseDir: '/noflo-runtime-base',
        paths: [ 'library://components',
                 'local://components', ],
    });
    loader.loadModules();

    // Load manifest
    let manifestPath = GLib.getenv('PWD') + '/manifest.json';
    if (!GLib.file_test(manifestPath, GLib.FileTest.EXISTS))
        throw new Error('Cannot find repository manifest');

    // Parse manifest
    let manifest = Utils.parseJSON(Utils.loadTextFileContent(manifestPath));

    // Create temporary directory
    let files = [];
    let tmpDir = GLib.dir_make_tmp('XXXXXX');
    log('output dir : ' + tmpDir);

    // Add ui files
    for (let i in manifest.ui)
        files.push(addFile(manifest.ui[i].file, tmpDir));

    // Add DBus files
    for (let i in manifest.dbus)
        files.push(addFile(manifest.dbus[i].file, tmpDir));

    // Add main module
    let components = {};
    let modules = {};

    let mainGraph = loader.mainGraph.getDefinition();
    for (let i in mainGraph.processes) {
        let cmp = mainGraph.processes[i].component;
        if (components[cmp])
            continue;

        components[cmp] = true;

        let module = loader.getComponentModule(cmp);
        if (!module ||
            modules[module.name] || // drop already loaded modules
            module.name == loader.applicationName) // drop app components
            continue;

        modules[module.name] = module;
        files = files.concat(addModule(loader, module, tmpDir));
    }

    // TODO: add app components

    // GResource index
    log('number of files: ' + files.length);
    Utils.saveTextFileContent(tmpDir + '/app.xml',
                              generateIndex(files));

    // Generate resource file
    log('glib-compile-resources' +
                                 ' --sourcedir ' + tmpDir +
                                 ' --target app.gresource' +
                                 ' ' + tmpDir + '/app.xml');
    GLib.spawn_command_line_sync('glib-compile-resources' +
                                 ' --sourcedir ' + tmpDir +
                                 ' --target app.gresource' +
                                 ' ' + tmpDir + '/app.xml');

    return 0;
};
<commit_msg>runtime: bundle: add application graphs/components to generated file<commit_after>const GLib = imports.gi.GLib;
const ComponentLoader = imports.componentLoader;
const Options = imports.options;
const Utils = imports.utils;

const CmdOptions = [
    {
        name: 'help',
        shortName: 'h',
        requireArgument: false,
        defaultValue: false,
        help: 'Print this screen'
    },
];

//
let addFile = function(path, subdir, dest) {
    Utils.copyFile(path, dest + '/' + subdir + '/' + path);
    return subdir + '/' + path;
};

let addFileContent = function(path, content, dest) {
    Utils.saveTextFileContent(dest + '/' + path, content);
    return path;
};

let addModule = function(loader, module, dest) {
    let files = [];
    // strip vpath scheme to get relative directory
    let dir = /[\w\d]+:\/\/(.*)/.exec(module.vpath)[1] + '/';

    // Add components
    for (let componentName in module.noflo.components) {
        let componentPath = module.noflo.components[componentName];
        files.push(addFileContent(dir + componentPath,
                                  loader.getComponentCode(module.normalizedName + '/' + componentName),
                                  dest));
    }

    // Add graphs
    for (let graphName in module.noflo.graphs) {
        let graphPath = module.noflo.graphs[graphName];
        files.push(addFileContent(dir + graphPath,
                                  loader.getComponentCode(module.normalizedName + '/' + graphName),
                                  dest));
    }

    // Loader
    if (module.noflo.loader) {
        files.push(addFileContent(dir + module.noflo.loader,
                                  Utils.loadTextFileContent(Utils.resolvePath(module.vpath + '/' + module.noflo.loader)),
                                  dest));
    }

    // index
    files.push(addFileContent(dir + 'component.json',
                              Utils.loadTextFileContent(Utils.resolvePath(module.vpath + '/component.json')),
                              dest));

    return files;
};

let generateIndex = function(files) {
    let data =
        '<?xml version="1.0" encoding="UTF-8"?>\n' +
        '<gresources>\n' +
        ' <gresource prefix="/org/gnome/noflo-gnome">\n';

    for (let i in files)
        data += '  <file>' + files[i] + '</file>\n';

    data +=
        ' </gresource>\n' +
        '</gresources>';

    return data;
};

//
let exec = function(args) {
    let options = Options.parseArguments(CmdOptions, args);

    if (options.options.help) {
        Options.printHelp('noflo-gnome bundle', CmdOptions);
        return 0;
    }

    // Get library loader
    let loader = new ComponentLoader.ComponentLoader({
        baseDir: '/noflo-runtime-base',
        paths: [ 'library://components',
                 'local://components', ],
    });
    loader.loadModules();

    // Load manifest
    let manifestPath = GLib.getenv('PWD') + '/manifest.json';
    if (!GLib.file_test(manifestPath, GLib.FileTest.EXISTS))
        throw new Error('Cannot find repository manifest');

    // Parse manifest
    let manifest = Utils.parseJSON(Utils.loadTextFileContent(manifestPath));

    // Create temporary directory
    let files = [];
    let tmpDir = GLib.dir_make_tmp('XXXXXX');
    log('output dir : ' + tmpDir);

    // Add ui files
    for (let i in manifest.ui)
        files.push(addFile(manifest.ui[i].file,
                           'application',
                           tmpDir));

    // Add DBus files
    for (let i in manifest.dbus)
        files.push(addFile(manifest.dbus[i].file,
                           'application',
                           tmpDir));

    // Add main module
    let components = {};
    let modules = {};

    let mainGraph = loader.mainGraph.getDefinition();
    for (let i in mainGraph.processes) {
        let cmp = mainGraph.processes[i].component;
        if (components[cmp])
            continue;

        components[cmp] = true;

        let module = loader.getComponentModule(cmp);
        if (!module ||
            modules[module.name] || // drop already loaded modules
            module.name == loader.applicationName) // drop app components
            continue;

        modules[module.name] = module;
        files = files.concat(addModule(loader, module, tmpDir));
    }

    // Add application components
    for (let componentName in manifest.noflo.components) {
        let componentPath = manifest.noflo.components[componentName];
        files.push(addFile(componentPath,
                           'application',
                           tmpDir));
    }

    // Add application graphs
    for (let graphName in manifest.noflo.graphs) {
        let graphPath = manifest.noflo.graphs[graphName];
        files.push(addFile(graphPath,
                           'application',
                           tmpDir));
    }

    // GResource index
    Utils.saveTextFileContent(tmpDir + '/app.xml',
                              generateIndex(files));

    // Generate resource file
    GLib.spawn_command_line_sync('glib-compile-resources' +
                                 ' --sourcedir ' + tmpDir +
                                 ' --target app.gresource' +
                                 ' ' + tmpDir + '/app.xml');

    log('Bundled ' + files.length + ' files');

    return 0;
};
<|endoftext|>