<commit_before>var async = require('async');
//var spawn = require('child_process').spawn;
//var child = spawn('node', ['../DB/orderLog.js']);
var events = require('./eventEmitter.js');
var config = require('../config.js');
var util = require('../util.js');

var avgOps = 0;
var orderCount = 0;
var matchedTrades = 0;
var startTime = new Date().getTime();
var lastTraded = null;

// Asks: willing to sell currency1 for currency2
var asks = [];
// Bids: willing to buy currency1 with currency2
var bids = [];

/*child.stderr.on('data', function(data){
  console.log('err data:');
  console.log(data.toString());
  console.log('end of err data');
});

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data){
  console.log('Child response:', data);
});*/

function getLastTrade(cb){
  if(lastTraded){
    lastTraded.price = Number(lastTraded.price).toFixed(2);
    lastTraded.amount = Number(lastTraded.amount).toFixed(0);
  }
  cb(lastTraded);
}

function getBidsAndAsks(maxNo, cb){
  var bidsAndAsks = {
    bids : bids.slice(0,maxNo),
    asks : asks.slice(0,maxNo)
  };

  for(var index in bidsAndAsks.bids){
    bidsAndAsks.bids[index].price = Number(bidsAndAsks.bids[index].price).toFixed(2);
    bidsAndAsks.bids[index].amount = Number(bidsAndAsks.bids[index].amount).toFixed(0);
  }

  for(var index in bidsAndAsks.asks){
    bidsAndAsks.asks[index].price = Number(bidsAndAsks.asks[index].price).toFixed(2);
    bidsAndAsks.asks[index].amount = Number(bidsAndAsks.asks[index].amount).toFixed(0);
  }

  cb(bidsAndAsks);
}

function getStats(cb){
  var stats = {
    avgOps : avgOps,
    matchedTrades : matchedTrades,
    bidsLength : bids.length,
    asksLength : asks.length,
    lastTrade : lastTraded
  };

  cb(stats);
}

events.on('newOrder', function(order){
  var arrivalTime = new Date().getTime();
  orderCount++;
  // Operation processing
  var elapsedStartTime = (arrivalTime - startTime)/1000;
  avgOps = orderCount/elapsedStartTime;

  updateOrderBook(order);
  accounts.UpdateReserveAmounts(order);
});

function handleBid(order){
  order.amount = util.Round(order.amount, config.currency1.constant);
  // No matching asks
  if((asks.length == 0)||(asks.length > 0 && order.price < asks[0].price)){
    if(bids.length == 0 || order.price > bids[0].price){ // Update first element in list
      bids.splice(0, 0, order);
    } else {
      var i = getPosition(0, bids.length-1, bids, order, 'desc');
      bids.splice(i, 0, order);
    }
  } else { // This bid matches with an ask
    if(asks[0].amount > order.amount){
      var match = {
        order1: asks[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      asks[0].amount -= order.amount;
      asks[0].amount = util.Round(asks[0].amount, config.currency1.constant);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else if (asks[0].amount == order.amount) {
      var match = {
        order1: asks[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      asks.splice(0, 1);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else {
      var match = {
        order1: asks[0],
        order2: order,
        amount: asks[0].amount
      };
      events.emit('matched', match);
      var newOrder = Object.assign({}, order);
      newOrder.amount -= asks[0].amount;
      newOrder.amount = util.Round(newOrder.amount, config.currency1.constant);
      newOrder.parentOrder = order;
      asks.splice(0, 1);
      events.emit('matchExecuted', match);
      updateOrderBook(newOrder);
    }
  }
}

function handleAsk(order){
  order.amount = util.Round(order.amount, config.currency1.constant);
  // No matching bids
  if((bids.length == 0)||(bids.length > 0 && order.price > bids[0].price)){
    if(asks.length == 0 || order.price < asks[0].price){ // Update first element in list
      asks.splice(0, 0, order);
    } else {
      var i = getPosition(0, asks.length-1, asks, order, 'asc');
      asks.splice(i, 0, order);
    }
  } else { // This ask matches with a bid
    if(bids[0].amount > order.amount){
      var match = {
        order1: bids[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      bids[0].amount -= order.amount;
      bids[0].amount = util.Round(bids[0].amount, config.currency1.constant);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else if (bids[0].amount == order.amount){
      var match = {
        order1: bids[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      bids.splice(0, 1);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else {
      var match = {
        order1: bids[0],
        order2: order,
        amount: bids[0].amount
      };
      events.emit('matched', match);
      var newOrder = Object.assign({}, order);
      newOrder.amount -= bids[0].amount;
      newOrder.amount = util.Round(newOrder.amount, config.currency1.constant);
      newOrder.parentOrder = order;
      bids.splice(0, 1);
      events.emit('matchExecuted', match);
      updateOrderBook(newOrder);
    }
  }
}

function updateOrderBook(order){
  if(order.type == 'bid'){
    handleBid(order);
  } else if(order.type == 'ask'){ // order.type ask
    handleAsk(order);
  } else if(order.type == 'cancelbid'){
    var i = getIndex(0, bids.length-1, bids, order, 'desc');
    if(i !== null){
      bids.splice(i, 1);
    }
  } else if(order.type == 'updatebid'){
    var i = getIndex(0, bids.length-1, bids, order, 'desc');
    if(i !== null && bids[i] !== undefined){
      bids[i].amount = order.amount;
      bids[i].amount = util.Round(bids[i].amount, config.currency2.constant);
    }
  } else if(order.type == 'cancelask'){
    var i = getIndex(0, asks.length-1, asks, order, 'asc');
    if(i !== null){
      asks.splice(i, 1);
    }
  } else if(order.type == 'updateask'){
    var i = getIndex(0, asks.length-1, asks, order, 'asc');
    if(i !== null && asks[i] !== undefined){
      asks[i].amount = order.amount;
      asks[i].amount = util.Round(asks[i].amount, config.currency1.constant);
    }
  }
}

function getPosition(startIndex, endIndex, array, order, direction){
  var middleIndex = Math.floor((startIndex+endIndex)/2);
  //console.log(startIndex+', '+endIndex+', '+order.price+', '+array[middleIndex].price);
  if(endIndex-startIndex <= 1){
    if(direction == 'desc' && array[endIndex].price > order.price){
      return endIndex+1;
    } else if(direction == 'desc' && array[endIndex].price < order.price){
      return endIndex;
    } else if(direction == 'asc' && array[endIndex].price < order.price){
      return endIndex+1;
    } else if(direction == 'asc' && array[endIndex].price > order.price){
      return endIndex;
    } else if(array[endIndex].price = order.price){
      var i = 1;
      while((endIndex+i) < array.length && array[endIndex+i].price == order.price){
        i++;
      }
      return endIndex+i;
    } else {
      console.log('1) Position not found');
      return null;
    }
  } else if(direction == 'desc' && array[middleIndex].price < order.price){ // bids
    return getPosition(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'desc' && array[middleIndex].price > order.price){ // bids
    return getPosition(middleIndex, endIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price > order.price){ // asks
    return getPosition(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price < order.price){ // asks
    return getPosition(middleIndex, endIndex, array, order, direction);
  } else if(array[middleIndex].price = order.price){
    var i = 1;
    while((middleIndex+i) < array.length && array[middleIndex+i].price == order.price){
      i++;
    }
    return middleIndex+i;
  } else {
    console.log('2) Position not found');
    return null;
  }
}

function getIndex(startIndex, endIndex, array, order, direction){
  var middleIndex = Math.floor((startIndex+endIndex)/2);
  if((endIndex-startIndex <= 1) || array[middleIndex].id == order.id){
    if(array[startIndex].id == order.id){
      return startIndex;
    } else if(array[endIndex].id == order.id){
      return endIndex;
    } else if(array[middleIndex].id == order.id){
      return middleIndex;
    } else {
      //console.log('1) Not found:', order);
      return null;
    }
  } else if(direction == 'desc' && array[middleIndex].price < order.price){ // bids
    return getIndex(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'desc' && array[middleIndex].price > order.price){ // bids
    return getIndex(middleIndex, endIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price > order.price){ // asks
    return getIndex(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price < order.price){ // asks
    return getIndex(middleIndex, endIndex, array, order, direction);
  } else if (array[middleIndex].price == order.price && array[middleIndex].id != order.id){
    var i = 1;
    var j = 1;
    while(((middleIndex+i < array.length)&&(array[middleIndex+i].price == order.price))
      || ((middleIndex-j >= 0)&&(array[middleIndex-j].price == order.price))){
      if(array[middleIndex+i].id == order.id){
        return middleIndex+i;
      } else if (array[middleIndex-j].id == order.id){
        return middleIndex-j;
      }
      i++;
      j++;
    }
    //console.log('2) Not found:', order);
    return null;
  }
}

events.on('matched', function(match){
  //auditTotals();
});

// This hands the logging of matched order to the child thread
events.on('matchExecuted', function(match){
  /*child.stdin.write('\n');
  child.stdin.write(JSON.stringify(match));
  child.stdin.write('\n');*/
  orderLog.AddLog(match);
});

events.on('matchExecuted', function(match){
  accounts.UpdateAccountBalances(match);
  //accounts.AuditTotals();
});

events.on('matchExecuted', function(match){
  matchedTrades++;
});

events.on('orderExecuted', function(order){
  var bidsAndAsks = bids.concat(asks);
  accounts.AuditOrdersToReservedBalances(bidsAndAsks);
});

events.on('matchExecuted', function(match){
  lastTraded = {
    price: match.order1.price,
    amount: match.amount
  };
});

// Assume just one currency pair right now, BTCUSD
function submitNewOrderForMatching(newOrderFromUI, cb){
  // TODO: this needs to move to a place where account balances are checked before the order is
  // allowed on the orderbook
  /*if(order.type == 'ask' && order.amount > (account.currency1 - account.reservedCurrency1)){
    cb(null);
  } else if(order.type == 'bid'
      && (order.amount*order.price) > (account.currency2 - account.reservedCurrency2)){
    cb(null)
  } else {
    cb(order);
  }*/
  events.emit('newOrder', newOrderFromUI);
  cb({submitted: true});
}

module.exports = function(modules){

  orderLog = modules.orderLog;
  accounts = require('./accountManagement.js')(modules);

  var functions = {};

  functions.SubmitNewOrderForMatching = submitNewOrderForMatching;
  functions.GetStats = getStats;
  functions.GetBidsAndAsks = getBidsAndAsks;
  functions.GetLastTrade = getLastTrade;
  return functions;
};

<commit_msg>Made declaration of orderLog and accounts explicit.<commit_after>var async = require('async');
//var spawn = require('child_process').spawn;
//var child = spawn('node', ['../DB/orderLog.js']);
var events = require('./eventEmitter.js');
var config = require('../config.js');
var util = require('../util.js');

var orderLog;
var accounts;

var avgOps = 0;
var orderCount = 0;
var matchedTrades = 0;
var startTime = new Date().getTime();
var lastTraded = null;

// Asks: willing to sell currency1 for currency2
var asks = [];
// Bids: willing to buy currency1 with currency2
var bids = [];

/*child.stderr.on('data', function(data){
  console.log('err data:');
  console.log(data.toString());
  console.log('end of err data');
});

child.stdout.setEncoding('utf8');
child.stdout.on('data', function(data){
  console.log('Child response:', data);
});*/

function getLastTrade(cb){
  if(lastTraded){
    lastTraded.price = Number(lastTraded.price).toFixed(2);
    lastTraded.amount = Number(lastTraded.amount).toFixed(0);
  }
  cb(lastTraded);
}

function getBidsAndAsks(maxNo, cb){
  var bidsAndAsks = {
    bids : bids.slice(0,maxNo),
    asks : asks.slice(0,maxNo)
  };

  for(var index in bidsAndAsks.bids){
    bidsAndAsks.bids[index].price = Number(bidsAndAsks.bids[index].price).toFixed(2);
    bidsAndAsks.bids[index].amount = Number(bidsAndAsks.bids[index].amount).toFixed(0);
  }

  for(var index in bidsAndAsks.asks){
    bidsAndAsks.asks[index].price = Number(bidsAndAsks.asks[index].price).toFixed(2);
    bidsAndAsks.asks[index].amount = Number(bidsAndAsks.asks[index].amount).toFixed(0);
  }

  cb(bidsAndAsks);
}

function getStats(cb){
  var stats = {
    avgOps : avgOps,
    matchedTrades : matchedTrades,
    bidsLength : bids.length,
    asksLength : asks.length,
    lastTrade : lastTraded
  };

  cb(stats);
}

events.on('newOrder', function(order){
  var arrivalTime = new Date().getTime();
  orderCount++;
  // Operation processing
  var elapsedStartTime = (arrivalTime - startTime)/1000;
  avgOps = orderCount/elapsedStartTime;

  updateOrderBook(order);
  accounts.UpdateReserveAmounts(order);
});

function handleBid(order){
  order.amount = util.Round(order.amount, config.currency1.constant);
  // No matching asks
  if((asks.length == 0)||(asks.length > 0 && order.price < asks[0].price)){
    if(bids.length == 0 || order.price > bids[0].price){ // Update first element in list
      bids.splice(0, 0, order);
    } else {
      var i = getPosition(0, bids.length-1, bids, order, 'desc');
      bids.splice(i, 0, order);
    }
  } else { // This bid matches with an ask
    if(asks[0].amount > order.amount){
      var match = {
        order1: asks[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      asks[0].amount -= order.amount;
      asks[0].amount = util.Round(asks[0].amount, config.currency1.constant);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else if (asks[0].amount == order.amount) {
      var match = {
        order1: asks[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      asks.splice(0, 1);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else {
      var match = {
        order1: asks[0],
        order2: order,
        amount: asks[0].amount
      };
      events.emit('matched', match);
      var newOrder = Object.assign({}, order);
      newOrder.amount -= asks[0].amount;
      newOrder.amount = util.Round(newOrder.amount, config.currency1.constant);
      newOrder.parentOrder = order;
      asks.splice(0, 1);
      events.emit('matchExecuted', match);
      updateOrderBook(newOrder);
    }
  }
}

function handleAsk(order){
  order.amount = util.Round(order.amount, config.currency1.constant);
  // No matching bids
  if((bids.length == 0)||(bids.length > 0 && order.price > bids[0].price)){
    if(asks.length == 0 || order.price < asks[0].price){ // Update first element in list
      asks.splice(0, 0, order);
    } else {
      var i = getPosition(0, asks.length-1, asks, order, 'asc');
      asks.splice(i, 0, order);
    }
  } else { // This ask matches with a bid
    if(bids[0].amount > order.amount){
      var match = {
        order1: bids[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      bids[0].amount -= order.amount;
      bids[0].amount = util.Round(bids[0].amount, config.currency1.constant);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else if (bids[0].amount == order.amount){
      var match = {
        order1: bids[0],
        order2: order,
        amount: order.amount
      };
      events.emit('matched', match);
      bids.splice(0, 1);
      events.emit('matchExecuted', match);
      events.emit('orderExecuted', order);
    } else {
      var match = {
        order1: bids[0],
        order2: order,
        amount: bids[0].amount
      };
      events.emit('matched', match);
      var newOrder = Object.assign({}, order);
      newOrder.amount -= bids[0].amount;
      newOrder.amount = util.Round(newOrder.amount, config.currency1.constant);
      newOrder.parentOrder = order;
      bids.splice(0, 1);
      events.emit('matchExecuted', match);
      updateOrderBook(newOrder);
    }
  }
}

function updateOrderBook(order){
  if(order.type == 'bid'){
    handleBid(order);
  } else if(order.type == 'ask'){ // order.type ask
    handleAsk(order);
  } else if(order.type == 'cancelbid'){
    var i = getIndex(0, bids.length-1, bids, order, 'desc');
    if(i !== null){
      bids.splice(i, 1);
    }
  } else if(order.type == 'updatebid'){
    var i = getIndex(0, bids.length-1, bids, order, 'desc');
    if(i !== null && bids[i] !== undefined){
      bids[i].amount = order.amount;
      bids[i].amount = util.Round(bids[i].amount, config.currency2.constant);
    }
  } else if(order.type == 'cancelask'){
    var i = getIndex(0, asks.length-1, asks, order, 'asc');
    if(i !== null){
      asks.splice(i, 1);
    }
  } else if(order.type == 'updateask'){
    var i = getIndex(0, asks.length-1, asks, order, 'asc');
    if(i !== null && asks[i] !== undefined){
      asks[i].amount = order.amount;
      asks[i].amount = util.Round(asks[i].amount, config.currency1.constant);
    }
  }
}

function getPosition(startIndex, endIndex, array, order, direction){
  var middleIndex = Math.floor((startIndex+endIndex)/2);
  //console.log(startIndex+', '+endIndex+', '+order.price+', '+array[middleIndex].price);
  if(endIndex-startIndex <= 1){
    if(direction == 'desc' && array[endIndex].price > order.price){
      return endIndex+1;
    } else if(direction == 'desc' && array[endIndex].price < order.price){
      return endIndex;
    } else if(direction == 'asc' && array[endIndex].price < order.price){
      return endIndex+1;
    } else if(direction == 'asc' && array[endIndex].price > order.price){
      return endIndex;
    } else if(array[endIndex].price = order.price){
      var i = 1;
      while((endIndex+i) < array.length && array[endIndex+i].price == order.price){
        i++;
      }
      return endIndex+i;
    } else {
      console.log('1) Position not found');
      return null;
    }
  } else if(direction == 'desc' && array[middleIndex].price < order.price){ // bids
    return getPosition(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'desc' && array[middleIndex].price > order.price){ // bids
    return getPosition(middleIndex, endIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price > order.price){ // asks
    return getPosition(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price < order.price){ // asks
    return getPosition(middleIndex, endIndex, array, order, direction);
  } else if(array[middleIndex].price = order.price){
    var i = 1;
    while((middleIndex+i) < array.length && array[middleIndex+i].price == order.price){
      i++;
    }
    return middleIndex+i;
  } else {
    console.log('2) Position not found');
    return null;
  }
}

function getIndex(startIndex, endIndex, array, order, direction){
  var middleIndex = Math.floor((startIndex+endIndex)/2);
  if((endIndex-startIndex <= 1) || array[middleIndex].id == order.id){
    if(array[startIndex].id == order.id){
      return startIndex;
    } else if(array[endIndex].id == order.id){
      return endIndex;
    } else if(array[middleIndex].id == order.id){
      return middleIndex;
    } else {
      //console.log('1) Not found:', order);
      return null;
    }
  } else if(direction == 'desc' && array[middleIndex].price < order.price){ // bids
    return getIndex(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'desc' && array[middleIndex].price > order.price){ // bids
    return getIndex(middleIndex, endIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price > order.price){ // asks
    return getIndex(startIndex, middleIndex, array, order, direction);
  } else if(direction == 'asc' && array[middleIndex].price < order.price){ // asks
    return getIndex(middleIndex, endIndex, array, order, direction);
  } else if (array[middleIndex].price == order.price && array[middleIndex].id != order.id){
    var i = 1;
    var j = 1;
    while(((middleIndex+i < array.length)&&(array[middleIndex+i].price == order.price))
      || ((middleIndex-j >= 0)&&(array[middleIndex-j].price == order.price))){
      if(array[middleIndex+i].id == order.id){
        return middleIndex+i;
      } else if (array[middleIndex-j].id == order.id){
        return middleIndex-j;
      }
      i++;
      j++;
    }
    //console.log('2) Not found:', order);
    return null;
  }
}

events.on('matched', function(match){
  //auditTotals();
});

// This hands the logging of matched order to the child thread
events.on('matchExecuted', function(match){
  /*child.stdin.write('\n');
  child.stdin.write(JSON.stringify(match));
  child.stdin.write('\n');*/
  orderLog.AddLog(match);
});

events.on('matchExecuted', function(match){
  accounts.UpdateAccountBalances(match);
  //accounts.AuditTotals();
});

events.on('matchExecuted', function(match){
  matchedTrades++;
});

events.on('orderExecuted', function(order){
  var bidsAndAsks = bids.concat(asks);
  accounts.AuditOrdersToReservedBalances(bidsAndAsks);
});

events.on('matchExecuted', function(match){
  lastTraded = {
    price: match.order1.price,
    amount: match.amount
  };
});

// Assume just one currency pair right now, BTCUSD
function submitNewOrderForMatching(newOrderFromUI, cb){
  // TODO: this needs to move to a place where account balances are checked before the order is
  // allowed on the orderbook
  /*if(order.type == 'ask' && order.amount > (account.currency1 - account.reservedCurrency1)){
    cb(null);
  } else if(order.type == 'bid'
      && (order.amount*order.price) > (account.currency2 - account.reservedCurrency2)){
    cb(null)
  } else {
    cb(order);
  }*/
  events.emit('newOrder', newOrderFromUI);
  cb({submitted: true});
}

module.exports = function(modules){

  orderLog = modules.orderLog;
  accounts = require('./accountManagement.js')(modules);

  var functions = {};

  functions.SubmitNewOrderForMatching = submitNewOrderForMatching;
  functions.GetStats = getStats;
  functions.GetBidsAndAsks = getBidsAndAsks;
  functions.GetLastTrade = getLastTrade;
  return functions;
};

<|endoftext|>


<commit_before>/**
 * @file BaseButton component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';

export default class BaseButton extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            focused: false
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.focusHandler = ::this.focusHandler;
        this.blurHandler = ::this.blurHandler;
        this.startRipple = ::this.startRipple;
        this.endRipple = ::this.endRipple;

    }

    touchTapHandler(e) {
        e.preventDefault();
        const {disabled, isLoading, onTouchTap} = this.props;
        !disabled && !isLoading && onTouchTap && onTouchTap(e);
    }

    focusHandler(e) {
        this.setState({
            focused: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(e);
        });
    }

    blurHandler(e) {
        this.setState({
            focused: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(e);
        });
    }

    startRipple(e) {
        this.refs.touchRipple.addRipple(e);
    }

    endRipple() {
        this.refs.touchRipple.removeRipple();
    }

    render() {

        const {

                children, className, style, theme, isRounded, isCircular, disableTouchRipple,
                iconCls, rightIconCls, type, value, disabled, readOnly, isLoading, rippleDisplayCenter,
                tip, tipPosition, renderer, onMouseEnter, onMouseLeave,

                ...restProps

            } = this.props,
            {focused} = this.state,

            buttonClassName = (focused ? ' focused' : '') + (theme ? ` theme-${theme}` : '')
                + (isCircular ? ' button-circular' : (isRounded ? ' button-rounded' : ''))
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                <button {...restProps}
                        className={'base-button' + buttonClassName}
                        style={style}
                        type={type}
                        disabled={disabled || isLoading}
                        readOnly={readOnly}
                        onTouchTap={this.touchTapHandler}
                        onFocus={this.focusHandler}
                        onBlur={this.blurHandler}
                        onMouseEnter={onMouseEnter}
                        onMouseLeave={onMouseLeave}>

                    {
                        isLoading && loadingIconPosition === 'left' ?
                            <CircularLoading className="button-icon button-icon-left button-loading-icon"
                                             size="small"/>
                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        renderer && typeof renderer === 'function' ?
                            renderer(this.props)
                            :
                            {value}
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {children}

                    {
                        disableTouchRipple ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

                </button>

            </TipProvider>
        );

    }
};

BaseButton.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    isRounded: PropTypes.bool,
    isCircular: PropTypes.bool,

    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    type: PropTypes.string,
    disabled: PropTypes.bool,
    readOnly: PropTypes.bool,
    isLoading: PropTypes.bool,
    disableTouchRipple: PropTypes.bool,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(TipProvider.Position)),

    rippleDisplayCenter: PropTypes.bool,

    renderer: PropTypes.func,
    onTouchTap: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func

};

BaseButton.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    isRounded: false,
    isCircular: false,

    value: null,
    disabled: false,
    readOnly: false,
    type: 'button',
    isLoading: false,
    disableTouchRipple: false,

    rippleDisplayCenter: false,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: TipProvider.Position.BOTTOM

};<commit_msg>update BaseButton: optimize code<commit_after>/**
 * @file BaseButton component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';

export default class BaseButton extends Component {

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            focused: false
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.focusHandler = ::this.focusHandler;
        this.blurHandler = ::this.blurHandler;
        this.startRipple = ::this.startRipple;
        this.endRipple = ::this.endRipple;

    }

    touchTapHandler(e) {
        e.preventDefault();
        const {disabled, isLoading, onTouchTap} = this.props;
        !disabled && !isLoading && onTouchTap && onTouchTap(e);
    }

    focusHandler(e) {
        this.setState({
            focused: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(e);
        });
    }

    blurHandler(e) {
        this.setState({
            focused: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(e);
        });
    }

    startRipple(e) {
        this.refs.touchRipple.addRipple(e);
    }

    endRipple() {
        this.refs.touchRipple.removeRipple();
    }

    render() {

        const {

                children, className, style, theme, isRounded, isCircular, disableTouchRipple,
                iconCls, rightIconCls, type, value, disabled, readOnly, isLoading, rippleDisplayCenter,
                tip, tipPosition, renderer, onMouseEnter, onMouseLeave,

                ...restProps

            } = this.props,
            {focused} = this.state,

            buttonClassName = (focused ? ' focused' : '') + (theme ? ` theme-${theme}` : '')
                + (isCircular ? ' button-circular' : (isRounded ? ' button-rounded' : ''))
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                <button {...restProps}
                        className={'base-button' + buttonClassName}
                        style={style}
                        type={type}
                        disabled={disabled || isLoading}
                        readOnly={readOnly}
                        onTouchTap={this.touchTapHandler}
                        onFocus={this.focusHandler}
                        onBlur={this.blurHandler}
                        onMouseEnter={onMouseEnter}
                        onMouseLeave={onMouseLeave}>

                    {
                        isLoading && loadingIconPosition === 'left' ?
                            <CircularLoading className="button-icon button-icon-left button-loading-icon"
                                             size="small"/>
                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        renderer && typeof renderer === 'function' ?
                            renderer(this.props)
                            :
                            {value}
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {children}

                    {
                        disableTouchRipple ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

                </button>

            </TipProvider>
        );

    }
};

BaseButton.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    isRounded: PropTypes.bool,
    isCircular: PropTypes.bool,

    value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
    type: PropTypes.string,
    disabled: PropTypes.bool,
    readOnly: PropTypes.bool,
    isLoading: PropTypes.bool,
    disableTouchRipple: PropTypes.bool,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    rippleDisplayCenter: PropTypes.bool,

    renderer: PropTypes.func,
    onTouchTap: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func

};

BaseButton.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    isRounded: false,
    isCircular: false,

    value: null,
    disabled: false,
    readOnly: false,
    type: 'button',
    isLoading: false,
    disableTouchRipple: false,

    rippleDisplayCenter: false,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: Position.BOTTOM

};<|endoftext|>


<commit_before>export default (config, githubApi) => {
    const digestCommit = function* (parsedContent, typoFix) {
        const author = {
            name: config.committer.name,
            email: config.committer.email,
            date: parsedContent.comment.createdDate,
        };

        // Create tree with edited file
        const tree = yield githubApi.createTree({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            baseTree: typoFix.baseTree,
            tree: [typoFix.tree],
        });

        // Commit the change
        const commit = yield githubApi.createCommit({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            commitAuthor: author,
            commitTree: tree.sha,
            commitParents: typoFix.parents,
            commitMessage: `Typo fix authored by ${parsedContent.comment.sender}

${author.name} is configured to automatically commit change authored by specific syntax in a comment.
See the trigger at ${parsedContent.comment.url}`,
        });

        yield githubApi.updateReference({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            reference: `heads/${parsedContent.pullRequest.ref}`,
            sha: commit.sha,
            force: true,
        });

        return commit;
    };

    const replyToAuthor = function* (parsedContent, message) {
        yield githubApi.replyToPullRequestReviewComment({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            pullRequestNumber: parsedContent.pullRequest.number,
            commentId: parsedContent.comment.id,
            message,
        });
    };

    const commit = function* (parsedContent, fixedContent) {
        if (!fixedContent || fixedContent.length === 0) {
            yield replyToAuthor(parsedContent, `:confused: @${parsedContent.comment.sender}, I did not understand the request.`);

            return false;
        }

        const digestedCommits = fixedContent.map(fix => digestCommit(parsedContent, fix));

        try {
            const commits = yield digestedCommits; // Commit & push
            const commitIds = commits.map(commit => commit.sha);

            yield replyToAuthor(parsedContent, `:white_check_mark: @${parsedContent.comment.sender}, check out my commits!
I have fixed your typo(s) at ${commitIds.join(', ')}`);

            return true;
        } catch (error) {
            console.error('An error occured while commiting', error);

            yield replyToAuthor(parsedContent, `:warning: @${parsedContent.comment.sender}, an error occured.
Be to to check all my commits!`);

            return false;
        }
    };

    return { commit, digestCommit };
};
<commit_msg>Typo in a typo fixer (sic)<commit_after>export default (config, githubApi) => {
    const digestCommit = function* (parsedContent, typoFix) {
        const author = {
            name: config.committer.name,
            email: config.committer.email,
            date: parsedContent.comment.createdDate,
        };

        // Create tree with edited file
        const tree = yield githubApi.createTree({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            baseTree: typoFix.baseTree,
            tree: [typoFix.tree],
        });

        // Commit the change
        const commit = yield githubApi.createCommit({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            commitAuthor: author,
            commitTree: tree.sha,
            commitParents: typoFix.parents,
            commitMessage: `Typo fix authored by ${parsedContent.comment.sender}

${author.name} is configured to automatically commit change authored by specific syntax in a comment.
See the trigger at ${parsedContent.comment.url}`,
        });

        yield githubApi.updateReference({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            reference: `heads/${parsedContent.pullRequest.ref}`,
            sha: commit.sha,
            force: true,
        });

        return commit;
    };

    const replyToAuthor = function* (parsedContent, message) {
        yield githubApi.replyToPullRequestReviewComment({
            repoUser: parsedContent.repository.user,
            repoName: parsedContent.repository.name,
            pullRequestNumber: parsedContent.pullRequest.number,
            commentId: parsedContent.comment.id,
            message,
        });
    };

    const commit = function* (parsedContent, fixedContent) {
        if (!fixedContent || fixedContent.length === 0) {
            yield replyToAuthor(parsedContent, `:confused: @${parsedContent.comment.sender}, I did not understand the request.`);

            return false;
        }

        const digestedCommits = fixedContent.map(fix => digestCommit(parsedContent, fix));

        try {
            const commits = yield digestedCommits; // Commit & push
            const commitIds = commits.map(commit => commit.sha);

            yield replyToAuthor(parsedContent, `:white_check_mark: @${parsedContent.comment.sender}, check out my commits!
I have fixed your typo(s) at ${commitIds.join(', ')}`);

            return true;
        } catch (error) {
            console.error('An error occured while commiting', error);

            yield replyToAuthor(parsedContent, `:warning: @${parsedContent.comment.sender}, an error occured.
Be sure to check all my commits!`);

            return false;
        }
    };

    return { commit, digestCommit };
};
<|endoftext|>


<commit_before>/**
 * @file BaseButton component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';

export default class BaseButton extends Component {

    static Theme = Theme;
    static TipPosition = Position;

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            focused: false
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.focusHandler = ::this.focusHandler;
        this.blurHandler = ::this.blurHandler;
        this.startRipple = ::this.startRipple;
        this.endRipple = ::this.endRipple;

    }

    touchTapHandler(e) {
        e.preventDefault();
        const {disabled, isLoading, onTouchTap} = this.props;
        !disabled && !isLoading && onTouchTap && onTouchTap(e);
    }

    focusHandler(e) {
        this.setState({
            focused: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(e);
        });
    }

    blurHandler(e) {
        this.setState({
            focused: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(e);
        });
    }

    startRipple(e) {
        this.refs.touchRipple.addRipple(e);
    }

    endRipple() {
        this.refs.touchRipple.removeRipple();
    }

    render() {

        const {

                children, className, style, theme, isRounded, isCircular, disableTouchRipple,
                iconCls, rightIconCls, type, value, disabled, readOnly, isLoading, rippleDisplayCenter,
                tip, tipPosition, renderer, onMouseEnter, onMouseLeave,

                ...restProps

            } = this.props,
            {focused} = this.state,

            buttonClassName = (focused ? ' focused' : '') + (theme ? ` theme-${theme}` : '')
                + (isCircular ? ' button-circular' : (isRounded ? ' button-rounded' : ''))
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                <button {...restProps}
                        className={'base-button' + buttonClassName}
                        style={style}
                        type={type}
                        disabled={disabled || isLoading}
                        readOnly={readOnly}
                        onTouchTap={this.touchTapHandler}
                        onFocus={this.focusHandler}
                        onBlur={this.blurHandler}
                        onMouseEnter={onMouseEnter}
                        onMouseLeave={onMouseLeave}>

                    {
                        isLoading && loadingIconPosition === 'left' ?
                            <CircularLoading className="button-icon button-icon-left button-loading-icon"
                                             size="small"/>
                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        renderer && typeof renderer === 'function' ?
                            renderer(this.props)
                            :
                            (
                                value ?
                                    {value}
                                    :
                                    null
                            )
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {children}

                    {
                        disableTouchRipple ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

                </button>

            </TipProvider>
        );

    }
};

BaseButton.propTypes = {

    className: PropTypes.string,
    style: PropTypes.object,
    theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

    isRounded: PropTypes.bool,
    isCircular: PropTypes.bool,

    value: PropTypes.any,
    type: PropTypes.string,
    disabled: PropTypes.bool,
    readOnly: PropTypes.bool,
    isLoading: PropTypes.bool,
    disableTouchRipple: PropTypes.bool,

    iconCls: PropTypes.string,
    rightIconCls: PropTypes.string,

    tip: PropTypes.string,
    tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

    rippleDisplayCenter: PropTypes.bool,

    renderer: PropTypes.func,
    onTouchTap: PropTypes.func,
    onFocus: PropTypes.func,
    onBlur: PropTypes.func,
    onMouseEnter: PropTypes.func,
    onMouseLeave: PropTypes.func

};

BaseButton.defaultProps = {

    className: null,
    style: null,
    theme: Theme.DEFAULT,

    isRounded: false,
    isCircular: false,

    value: null,
    disabled: false,
    readOnly: false,
    type: 'button',
    isLoading: false,
    disableTouchRipple: false,

    rippleDisplayCenter: false,

    iconCls: null,
    rightIconCls: null,

    tip: null,
    tipPosition: Position.BOTTOM

};<commit_msg>update BaseButton<commit_after>/**
 * @file BaseButton component
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import React, {Component} from 'react';
import PropTypes from 'prop-types';

import CircularLoading from '../CircularLoading';
import TipProvider from '../TipProvider';
import TouchRipple from '../TouchRipple';
import Theme from '../Theme';

import Util from '../_vendors/Util';
import Position from '../_statics/Position';

export default class BaseButton extends Component {

    static Theme = Theme;
    static TipPosition = Position;

    static propTypes = {

        className: PropTypes.string,
        style: PropTypes.object,
        theme: PropTypes.oneOf(Util.enumerateValue(Theme)),

        isRounded: PropTypes.bool,
        isCircular: PropTypes.bool,

        value: PropTypes.any,
        type: PropTypes.string,
        disabled: PropTypes.bool,
        readOnly: PropTypes.bool,
        isLoading: PropTypes.bool,
        disableTouchRipple: PropTypes.bool,

        iconCls: PropTypes.string,
        rightIconCls: PropTypes.string,

        tip: PropTypes.string,
        tipPosition: PropTypes.oneOf(Util.enumerateValue(Position)),

        rippleDisplayCenter: PropTypes.bool,

        renderer: PropTypes.func,
        onTouchTap: PropTypes.func,
        onFocus: PropTypes.func,
        onBlur: PropTypes.func,
        onMouseEnter: PropTypes.func,
        onMouseLeave: PropTypes.func

    };

    static defaultProps = {

        className: null,
        style: null,
        theme: Theme.DEFAULT,

        isRounded: false,
        isCircular: false,

        value: null,
        disabled: false,
        readOnly: false,
        type: 'button',
        isLoading: false,
        disableTouchRipple: false,

        rippleDisplayCenter: false,

        iconCls: null,
        rightIconCls: null,

        tip: null,
        tipPosition: Position.BOTTOM

    };

    constructor(props, ...restArgs) {

        super(props, ...restArgs);

        this.state = {
            focused: false
        };

        this.touchTapHandler = ::this.touchTapHandler;
        this.focusHandler = ::this.focusHandler;
        this.blurHandler = ::this.blurHandler;
        this.startRipple = ::this.startRipple;
        this.endRipple = ::this.endRipple;

    }

    touchTapHandler(e) {
        e.preventDefault();
        const {disabled, isLoading, onTouchTap} = this.props;
        !disabled && !isLoading && onTouchTap && onTouchTap(e);
    }

    focusHandler(e) {
        this.setState({
            focused: true
        }, () => {
            const {onFocus} = this.props;
            onFocus && onFocus(e);
        });
    }

    blurHandler(e) {
        this.setState({
            focused: false
        }, () => {
            const {onBlur} = this.props;
            onBlur && onBlur(e);
        });
    }

    startRipple(e) {
        this.refs.touchRipple.addRipple(e);
    }

    endRipple() {
        this.refs.touchRipple.removeRipple();
    }

    render() {

        const {

                children, className, style, theme, isRounded, isCircular, disableTouchRipple,
                iconCls, rightIconCls, type, value, disabled, readOnly, isLoading, rippleDisplayCenter,
                tip, tipPosition, renderer, onMouseEnter, onMouseLeave,

                ...restProps

            } = this.props,
            {focused} = this.state,

            buttonClassName = (focused ? ' focused' : '') + (theme ? ` theme-${theme}` : '')
                + (isCircular ? ' button-circular' : (isRounded ? ' button-rounded' : ''))
                + (className ? ' ' + className : ''),

            loadingIconPosition = (rightIconCls && !iconCls) ? 'right' : 'left';

        return (
            <TipProvider text={tip}
                         position={tipPosition}>

                <button {...restProps}
                        className={'base-button' + buttonClassName}
                        style={style}
                        type={type}
                        disabled={disabled || isLoading}
                        readOnly={readOnly}
                        onTouchTap={this.touchTapHandler}
                        onFocus={this.focusHandler}
                        onBlur={this.blurHandler}
                        onMouseEnter={onMouseEnter}
                        onMouseLeave={onMouseLeave}>

                    {
                        isLoading && loadingIconPosition === 'left' ?
                            <CircularLoading className="button-icon button-icon-left button-loading-icon"
                                             size="small"/>
                            :
                            (
                                iconCls ?
                                    <i className={`button-icon button-icon-left ${iconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {
                        renderer && typeof renderer === 'function' ?
                            renderer(this.props)
                            :
                            (
                                value ?
                                    {value}
                                    :
                                    null
                            )
                    }

                    {
                        isLoading && loadingIconPosition === 'right' ?
                            <CircularLoading className="button-icon button-icon-right button-loading-icon"
                                             size="small"/>
                            :
                            (
                                rightIconCls ?
                                    <i className={`button-icon button-icon-right ${rightIconCls}`}
                                       aria-hidden="true">
                                    :
                                    null
                            )
                    }

                    {children}

                    {
                        disableTouchRipple ?
                            null
                            :
                            <TouchRipple ref="touchRipple"
                                         className={disabled || isLoading ? 'hidden' : ''}
                                         displayCenter={rippleDisplayCenter}/>
                    }

                </button>

            </TipProvider>
        );

    }
};<|endoftext|>


<commit_before>/**
 *
 * @author: daxingplay<daxingplay@gmail.com>
 * @time: 13-1-23 16:22
 * @description:
 */

var UglifyJS = require('uglify-js'),
    fs = require('fs'),
    path = require('path'),
    os = require('os'),
    _ = require('lodash'),
    iconv = require('iconv-lite'),
    getAst = require('./get-ast'),
    utils = require('./utils'),
    dependencies = require('./dependencies');

function Compiler(config){
    this.modules = {};
    this.fileList = [];
    this.analyzedModules = [];
    this.combinedModules = [];
    this.buildComboModules = [];
    this.buildAnalyzedModules = [];
    this.config  = config;
    this.packages = config.packages;
}

Compiler.prototype._addModuleName = function(moduleContent, modName){
    //add module path
    var start = moduleContent.indexOf('KISSY.add(');
    if(start == -1) {
        start = moduleContent.indexOf('.add(');
        if(start != -1) {
            start = start + 5;
        }
    } else {
        start = start + 10;
    }

    var end = moduleContent.indexOf('function', start);

    //find it
    if(start > -1 && end > start) {
        //KISSY.add(/*xxx*/function(xxx)) or KISSY.add('xxx', function(xxx))
        //remove user comments but preserve user defined module name.
        var oldModuleName = moduleContent.substring(start, end),
            moduleNameRegResult = oldModuleName.match(/^(\s*)\/\*(.*)\*\/(\s*)$/);
        if(moduleNameRegResult !== null || oldModuleName.replace(/[\r\n\s]/g, '') === ''){
            // replace user comments or white spaces with my module name.
            moduleContent = moduleContent.replace(oldModuleName, '\'' + modName + '\', ');
        }
    } else if(start > -1 && end == start) {
        //KISSY.add(function(xxx))
        moduleContent = [moduleContent.slice(0, start), '\'' + modName + '\',', moduleContent.slice(end)].join('');
    }

    return moduleContent;
};
Compiler.prototype._isFileIgnored = function(filePath){
    return utils.isExcluded(filePath, this.config.ignoreFiles);
};
Compiler.prototype._isModuleExcluded = function(modName){
    return utils.isExcluded(modName, this.config.exclude);
};
Compiler.prototype._getModuleRealPathByParent = function(requiredModule, parentModule){
    var self = this;
    if(requiredModule.match(/^\./) && _.isObject(parentModule)){
        return path.resolve(path.dirname(parentModule.path), utils.addPathSuffix(requiredModule));
    }else if(!requiredModule.match(/\//) || requiredModule.match(/^gallery\//)){
        // may be kissy module.
        return requiredModule;
    }else{
        for(var i = 0; i < self.packages.length; i++){
            var pkg = self.packages[i],
                mod = requiredModule.match(new RegExp(pkg.name + '\/(.*)'));
            if(mod && mod[1]){
                var modulePath = path.resolve(pkg.path, pkg.name, utils.addPathSuffix(mod[1]));
                if(fs.existsSync(modulePath)){
                    return modulePath;
                }
            }
        }
    }
    var lastPath = path.resolve(path.dirname(parentModule.path), utils.addPathSuffix(requiredModule));
    return fs.existsSync(lastPath) ? lastPath : false;
};
Compiler.prototype._getModuleRealPathByPkg = function(filePath, pkg){
    return path.resolve(pkg.path, pkg.name, utils.addPathSuffix(filePath));
};
/**
 * get module's package by its full path.
 * @param filePath this is file full path.
 * @return {*}
 * @private
 */
Compiler.prototype._getPackage = function(filePath){
    var self = this,
        pkgPath = '',
        modPkg = null;
    if(filePath.match(/^[a-zA-Z]+$/)){
        return 'kissy';
    }
    if(filePath.match(/^gallery\//)){
        return 'gallery';
    }
    if(!fs.existsSync(filePath)){
        return null;
    }
    // TODO optimize.
    for(var i = 0; i < self.packages.length; i++){
        var pkg = self.packages[i];

        var dirname = path.dirname(filePath);
        //by hanwen 2013-08-16
        //是否属于当前包
        var isBelongPkg = false;

        if(filePath.match(new RegExp(pkg.name))){
            isBelongPkg = true;
        } else if (pkg.ignorePackageNameInUri && dirname.indexOf(pkg.path) > -1) {
            //增加ignorePackageNameInUri支持，使用路径判断，如果pkg.path和
            //filePath的路径进行对比
            isBelongPkg = true;
        }

        if(isBelongPkg){

            //如果有配置ignorePackageNameInUri, 把pkg.name忽略
            var name = pkg.ignorePackageNameInUri ? '' : pkg.name;
            var curRelativePath = path.relative(path.resolve(pkg.path, name), filePath);

            if(pkgPath == '' || pkgPath.length > curRelativePath.length){
                pkgPath = curRelativePath;
                modPkg = pkg;
            }

        }
    }
    return modPkg;
};

Compiler.prototype._getModuleNameFromRequire = function(requirePath){
    var matches = /^([^\/]+?)\/\S+/.exec(requirePath);
    return matches && matches[1] ? matches[1] : '';
};
Compiler.prototype._moduleName = function(filePath, pkg){
    var relativePkgPath = path.relative(pkg.path, filePath).replace(/\\/g, '/'),
        moduleName = relativePkgPath.replace(/^\.\//, '');
    if(!utils.startWith(relativePkgPath, pkg.name + '/') && pkg.name !== 'kissy'){
        moduleName = pkg.name + '/' + moduleName;
    }
    return moduleName.replace(/\.js$/i, '');
};
Compiler.prototype._buildCombo = function(mod){
    var self = this,
        result = {};
    _.forEach(mod.dependencies, function(subMod){
        if((subMod.pkg == 'kissy' || subMod.status != 'ok') && _.indexOf(self.buildAnalyzedModules, subMod.name) === -1){
            self.buildComboModules.push(subMod.name);
            !_.isEmpty(subMod.dependencies) && self._buildCombo(subMod);
        }
        self.buildAnalyzedModules.push(subMod.name);
    });
    result[mod.name] = self.buildComboModules;
    return result;
};

Compiler.prototype._mapModuleName = function(modName){
    var self = this,
        rules = self.config.map;
    if(modName && !_.isEmpty(rules)){
        _.forEach(rules, function(rule){
            modName = modName.replace(new RegExp(rule[0]), rule[1]);
        });
    }
    return modName;
};
Compiler.prototype._mapContent = function(content, mod){
    var self = this,
        rules = self.config.map;
    if(content && !_.isEmpty(content)){
        _.forEach(mod.requires, function(requirePath){
            if(!/^\.{1,2}/.test(requirePath)){
                var mappedRequirePath = requirePath;
                _.forEach(rules, function(rule){
                    mappedRequirePath = requirePath.replace(new RegExp(rule[0]), rule[1]);
                });
                if(mappedRequirePath != requirePath){
                    // TODO require regex is toooooooooooo complex. So I chose the simple one.
                    content = content.replace(new RegExp("(['\"])" + requirePath + "(['\"])"), '$1' + mappedRequirePath + '$2');
                }
            }
        });
    }
    return content;
};
Compiler.prototype._generateRealModuleContent = function(mod){
    var self = this;
    if(!mod || !mod.path) return '';
    var fileContent = fs.readFileSync(mod.path);
    var modContent = iconv.decode(fileContent, mod.charset);
    // fix issue 23: remove file BOM before combo.
    if(/^\uFEFF/.test(modContent)){
        modContent = modContent.toString().replace(/^\uFEFF/, '');
    }

    modContent = self._mapContent(modContent, mod);
    return self._addModuleName(modContent, mod.name);
};

Compiler.prototype.getAst = getAst;

/**
 * analyze a file's dependencies.
 * @param inputFilePath please use absolute file path.
 * @return {*}
 */
Compiler.prototype.analyze = function(inputFilePath){
    var self = this,
        mod = null,
        modRequires,
        modName,
        modPkg,
        modType,
        modRealPath;
    if(!inputFilePath) return null;
    // analyze module package
    modPkg = self._getPackage(inputFilePath);
    switch (modPkg){
        case null:
            !self.config.silent && console.log('cannot get package for %s, use the first path in require path instead.', inputFilePath);
            mod = {
                name: inputFilePath,
                pkg: self._getModuleNameFromRequire(inputFilePath),
                status: 'missing'
            };
            self.modules[mod.name] = mod;
            break;
        case 'kissy':
            mod = {
                name: inputFilePath,
                pkg: 'kissy',
                status: 'ok'
            };
            self.modules[mod.name] = mod;
            break;
        case 'gallery':
            mod = {
                name: inputFilePath,
                pkg: 'gallery',
                status: 'ok'
            };
            self.modules[mod.name] = mod;
            break;
        default :
            // get module real path based on package path.
            modRealPath = self._getModuleRealPathByPkg(inputFilePath, modPkg);
            if(!self._isFileIgnored(modRealPath)){
                modName = self._moduleName(modRealPath, modPkg);
                // map module names if user configured map rules.
                modName = self._mapModuleName(modName);
                if(_.indexOf(self.fileList, modRealPath) === -1){
                    // to avoid recursive analyze.
                    self.fileList.push(modRealPath);
                    if(!modRealPath.match(/\.css$/)){
                        // get this file's dependencies.
                        modRequires = dependencies(modRealPath);
                        // if user named module himself, use his name. map rules will not work then.
                        if(_.isPlainObject(modRequires) && modRequires.name){
                            modName = modRequires.name;
                            modRequires = modRequires.deps;
                        }
                        modType = 'js';
                    }else{
                        modRequires = [];
                        modType = 'css';
                    }
                    var isModExcluded = self._isModuleExcluded(modName);
                    // add this module to list.
                    mod = {
                        name: modName,
                        pkg: modPkg,
                        status: isModExcluded ? 'excluded' : 'ok',
                        path: modRealPath,
                        requires: modRequires,
                        dependencies: [],
                        dependencyTree: {},
                        charset: modPkg.charset,
                        type: modType
                    };
                    self.modules[modName] = mod;

                    if(modType === 'js'){
                        // analyze sub modules' dependencies recursively.
                        _.forEach(modRequires, function(subModPath){
                            var subMod = self.analyze(self._getModuleRealPathByParent(subModPath, mod));
                            if(subMod){
                                mod.dependencies.push(subMod);
                                mod.dependencyTree[subMod.name] = subMod.dependencyTree;
                            }
                        });

                        !isModExcluded && self.analyzedModules.push(modName);
                    }
                }else{
                    mod = self.modules[modName];
                }
            }
            break;
    }
    return mod;
};

Compiler.prototype.build = function(inputFilePath, outputFilePath, outputCharset){
    var self = this,
        result = self.analyze(inputFilePath),
        combinedComment = [
            '/*',
            'combined files : ',
            '',
            self.analyzedModules.join(os.EOL),
            '',
            '*/',
            ''
        ].join(os.EOL);

    // deal with output charset. if not specified, use charset in config.
    outputCharset = (typeof outputCharset === 'undefined' || outputCharset === null) ? self.config.charset : outputCharset;

    var combineFile = self.config.suffix ? outputFilePath.replace(/\.js$/i, self.config.suffix + '.js') : outputFilePath;
    if(path.extname(combineFile) != '.js'){
        combineFile = path.resolve(combineFile, path.basename(inputFilePath, '.js') + self.config.suffix + '.js');
    }
    result.outputFile = combineFile;

    //prepare output dir
    utils.mkdirsSync(path.dirname(combineFile));

    // if exists, unlink first, otherwise, there may be some problems with the file encoding.
    if(fs.existsSync(combineFile)){
        fs.unlinkSync(combineFile);
    }

    var fd = fs.openSync(combineFile, 'w');
    var combinedCommentBuffer = iconv.encode(combinedComment, outputCharset);
    fs.writeSync(fd, combinedCommentBuffer, 0, combinedCommentBuffer.length);
    fs.closeSync(fd);

    fd = fs.openSync(combineFile, 'a');

    _.forEach(self.analyzedModules, function(modName){
        var mod = self.modules[modName];
        // add EOL to end of file.
        var modContent = self._generateRealModuleContent(mod) + os.EOL;
        var buffer = iconv.encode(modContent, outputCharset);
        fs.writeSync(fd, buffer, 0, buffer.length);
        self.combinedModules.push(modName);
    });

    fs.closeSync(fd);

    !self.config.silent && console.info('[ok]'.bold.green+' %s ===> %s', inputFilePath, outputFilePath);

    result.autoCombo = self._buildCombo(result);

    result.combined = self.combinedModules;

    return result;
};
Compiler.prototype.combo = function(fixModuleName){
    var self = this,
        content = [];
    if(_.isObject(self.modules)){
        _.forEach(self.modules, function(mod, modName){
            if(_.indexOf(self.combinedModules, modName) === -1 && !_.isEmpty(mod.dependencies)){
                var requires = [];
                _.forEach(mod.dependencies, function(subMod){
                    requires.push("'" + subMod.name + "'");
                });
                content.push("'" + modName + "': { requires: [" + requires.join(', ') + "]}");
            }
            if(fixModuleName === true && mod && mod.path){
                var modContent = self._generateRealModuleContent(mod);
                var buffer = iconv.encode(modContent, mod.charset);
                fs.writeFileSync(mod.path, buffer);
            }
        });
    }
    return content.length ? "KISSY.config('modules', {" + os.EOL + " " + content.join("," + os.EOL + " ") + " " + os.EOL + "});" : "";
};

module.exports = Compiler;
<commit_msg>add alias<commit_after>/**
 *
 * @author: daxingplay<daxingplay@gmail.com>
 * @time: 13-1-23 16:22
 * @description:
 */

var UglifyJS = require('uglify-js'),
    fs = require('fs'),
    path = require('path'),
    os = require('os'),
    _ = require('lodash'),
    iconv = require('iconv-lite'),
    getAst = require('./get-ast'),
    utils = require('./utils'),
    dependencies = require('./dependencies');

function Compiler(config){
    this.modules = {};
    this.fileList = [];
    this.analyzedModules = [];
    this.combinedModules = [];
    this.buildComboModules = [];
    this.buildAnalyzedModules = [];
    this.config  = config;
    this.packages = config.packages;
}

Compiler.prototype._addModuleName = function(moduleContent, modName){
    //add module path
    var start = moduleContent.indexOf('KISSY.add(');
    if(start == -1) {
        start = moduleContent.indexOf('.add(');
        if(start != -1) {
            start = start + 5;
        }
    } else {
        start = start + 10;
    }

    var end = moduleContent.indexOf('function', start);

    //find it
    if(start > -1 && end > start) {
        //KISSY.add(/*xxx*/function(xxx)) or KISSY.add('xxx', function(xxx))
        //remove user comments but preserve user defined module name.
        var oldModuleName = moduleContent.substring(start, end),
            moduleNameRegResult = oldModuleName.match(/^(\s*)\/\*(.*)\*\/(\s*)$/);
        if(moduleNameRegResult !== null || oldModuleName.replace(/[\r\n\s]/g, '') === ''){
            // replace user comments or white spaces with my module name.
            moduleContent = moduleContent.replace(oldModuleName, '\'' + modName + '\', ');
        }
    } else if(start > -1 && end == start) {
        //KISSY.add(function(xxx))
        moduleContent = [moduleContent.slice(0, start), '\'' + modName + '\',', moduleContent.slice(end)].join('');
    }

    return moduleContent;
};
Compiler.prototype._isFileIgnored = function(filePath){
    return utils.isExcluded(filePath, this.config.ignoreFiles);
};
Compiler.prototype._isModuleExcluded = function(modName){
    return utils.isExcluded(modName, this.config.exclude);
};
Compiler.prototype._getModuleRealPathByParent = function(requiredModule, parentModule){
    var self = this;
    if(requiredModule.match(/^\./) && _.isObject(parentModule)){
        return path.resolve(path.dirname(parentModule.path), utils.addPathSuffix(requiredModule));
    }else if(!requiredModule.match(/\//) || requiredModule.match(/^gallery\//)){
        // may be kissy module.
        return requiredModule;
    }else{
        for(var i = 0; i < self.packages.length; i++){
            var pkg = self.packages[i],
                mod = requiredModule.match(new RegExp(pkg.name + '\/(.*)'));
            if(mod && mod[1]){
                var modulePath = path.resolve(pkg.path, pkg.name, utils.addPathSuffix(mod[1]));
                if(fs.existsSync(modulePath)){
                    return modulePath;
                }
            }
        }
    }
    var lastPath = path.resolve(path.dirname(parentModule.path), utils.addPathSuffix(requiredModule));
    return fs.existsSync(lastPath) ? lastPath : false;
};
Compiler.prototype._getModuleRealPathByPkg = function(filePath, pkg){
    return path.resolve(pkg.path, pkg.name, utils.addPathSuffix(filePath));
};
/**
 * get module's package by its full path.
 * @param filePath this is file full path.
 * @return {*}
 * @private
 */
Compiler.prototype._getPackage = function(filePath){
    var self = this,
        pkgPath = '',
        modPkg = null;
    if(filePath.match(/^[a-zA-Z]+$/)){
        return 'kissy';
    }
    if(filePath.match(/^gallery\//)){
        return 'gallery';
    }
    if(!fs.existsSync(filePath)){
        return null;
    }
    // TODO optimize.
    for(var i = 0; i < self.packages.length; i++){
        var pkg = self.packages[i];

        var dirname = path.dirname(filePath);
        //by hanwen 2013-08-16
        //是否属于当前包
        var isBelongPkg = false;

        if(filePath.match(new RegExp(pkg.name))){
            isBelongPkg = true;
        } else if (pkg.ignorePackageNameInUri && dirname.indexOf(pkg.path) > -1) {
            //增加ignorePackageNameInUri支持，使用路径判断，如果pkg.path和
            //filePath的路径进行对比
            isBelongPkg = true;
        }

        if(isBelongPkg){

            //如果有配置ignorePackageNameInUri, 把pkg.name忽略
            var name = pkg.ignorePackageNameInUri ? '' : pkg.name;
            var curRelativePath = path.relative(path.resolve(pkg.path, name), filePath);

            if(pkgPath == '' || pkgPath.length > curRelativePath.length){
                pkgPath = curRelativePath;
                modPkg = pkg;
            }

        }
    }
    return modPkg;
};

Compiler.prototype._getModuleNameFromRequire = function(requirePath){
    var matches = /^([^\/]+?)\/\S+/.exec(requirePath);
    return matches && matches[1] ? matches[1] : '';
};
Compiler.prototype._moduleName = function(filePath, pkg){
    var relativePkgPath = path.relative(pkg.path, filePath).replace(/\\/g, '/'),
        moduleName = relativePkgPath.replace(/^\.\//, '');
    if(!utils.startWith(relativePkgPath, pkg.name + '/') && pkg.name !== 'kissy'){
        moduleName = pkg.name + '/' + moduleName;
    }
    return moduleName.replace(/\.js$/i, '');
};
Compiler.prototype._buildCombo = function(mod){
    var self = this,
        result = {};
    _.forEach(mod.dependencies, function(subMod){
        if((subMod.pkg == 'kissy' || subMod.status != 'ok') && _.indexOf(self.buildAnalyzedModules, subMod.name) === -1){
            self.buildComboModules.push(subMod.name);
            !_.isEmpty(subMod.dependencies) && self._buildCombo(subMod);
        }
        self.buildAnalyzedModules.push(subMod.name);
    });
    result[mod.name] = self.buildComboModules;
    return result;
};

Compiler.prototype._mapModuleName = function(modName){
    var self = this,
        rules = self.config.map;
    if(modName && !_.isEmpty(rules)){
        _.forEach(rules, function(rule){
            modName = modName.replace(new RegExp(rule[0]), rule[1]);
        });
    }
    return modName;
};
Compiler.prototype._mapContent = function(content, mod){
    var self = this,
        rules = self.config.map;
    if(content && !_.isEmpty(content)){
        _.forEach(mod.requires, function(requirePath){
            if(!/^\.{1,2}/.test(requirePath)){
                var mappedRequirePath = requirePath;
                _.forEach(rules, function(rule){
                    mappedRequirePath = requirePath.replace(new RegExp(rule[0]), rule[1]);
                });
                if(mappedRequirePath != requirePath){
                    // TODO require regex is toooooooooooo complex. So I chose the simple one.
                    content = content.replace(new RegExp("(['\"])" + requirePath + "(['\"])"), '$1' + mappedRequirePath + '$2');
                }
            }
        });
    }
    return content;
};
Compiler.prototype._generateRealModuleContent = function(mod){
    var self = this;
    if(!mod || !mod.path) return '';
    var fileContent = fs.readFileSync(mod.path);
    var modContent = iconv.decode(fileContent, mod.charset);
    // fix issue 23: remove file BOM before combo.
    if(/^\uFEFF/.test(modContent)){
        modContent = modContent.toString().replace(/^\uFEFF/, '');
    }

    modContent = self._mapContent(modContent, mod);
    return self._addModuleName(modContent, mod.name);
};
Compiler.prototype._alias = function(modName){
    var self = this;
    if(self.config.modules && self.config.modules[modName]){
        var modConfig = self.config.modules[modName];
        if(modConfig.alias){
            var modNew = {
                name: modConfig.alias,
                path: '',
                pkg: self._getModuleNameFromRequire(modConfig.alias)
            };
            if(modNew.pkg && self.packages[modNew.pkg]){
                var modPkg = self.packages[modNew.pkg];
                if(modPkg.ignorePackageNameInUri){
                    modNew.path = path.resolve(modPkg.path, '../', modNew.name);
                }else{
                    modNew.path = path.resolve(modPkg.path, modNew.name);
                }
                if(fs.existsSync(modNew.path)){
                    return modNew;
                }
            }
        }
    }
    return null;
};

Compiler.prototype.getAst = getAst;

/**
 * analyze a file's dependencies.
 * @param inputFilePath please use absolute file path.
 * @return {*}
 */
Compiler.prototype.analyze = function(inputFilePath){
    var self = this,
        mod = null,
        modRequires,
        modName,
        modPkg,
        modType,
        modRealPath;
    if(!inputFilePath) return null;
    // analyze module package
    modPkg = self._getPackage(inputFilePath);
    switch (modPkg){
        case null:
            !self.config.silent && console.log('cannot get package for %s, use the first path in require path instead.', inputFilePath);
            mod = {
                name: inputFilePath,
                pkg: self._getModuleNameFromRequire(inputFilePath),
                status: 'missing'
            };
            self.modules[mod.name] = mod;
            break;
        case 'kissy':
            mod = {
                name: inputFilePath,
                pkg: 'kissy',
                status: 'ok'
            };
            self.modules[mod.name] = mod;
            break;
        case 'gallery':
            mod = {
                name: inputFilePath,
                pkg: 'gallery',
                status: 'ok'
            };
            self.modules[mod.name] = mod;
            break;
        default :
            // get module real path based on package path.
            modRealPath = self._getModuleRealPathByPkg(inputFilePath, modPkg);
            if(!self._isFileIgnored(modRealPath)){
                modName = self._moduleName(modRealPath, modPkg);
                // map module names if user configured map rules.
                modName = self._mapModuleName(modName);
                var modAlias = self._alias(modName);
                if(modAlias){
                    modName = modAlias.name;
                    modPkg = modAlias.pkg;
                    modRealPath = modAlias.path;
                }
                if(_.indexOf(self.fileList, modRealPath) === -1){
                    // to avoid recursive analyze.
                    self.fileList.push(modRealPath);
                    if(!modRealPath.match(/\.css$/)){
                        // get this file's dependencies.
                        modRequires = dependencies(modRealPath);
                        // if user named module himself, use his name. map rules will not work then.
                        if(_.isPlainObject(modRequires) && modRequires.name){
                            modName = modRequires.name;
                            modRequires = modRequires.deps;
                        }
                        modType = 'js';
                    }else{
                        modRequires = [];
                        modType = 'css';
                    }
                    var isModExcluded = self._isModuleExcluded(modName);
                    // add this module to list.
                    mod = {
                        name: modName,
                        pkg: modPkg,
                        status: isModExcluded ? 'excluded' : 'ok',
                        path: modRealPath,
                        requires: modRequires,
                        dependencies: [],
                        dependencyTree: {},
                        charset: modPkg.charset,
                        type: modType
                    };
                    self.modules[modName] = mod;

                    if(modType === 'js'){
                        // analyze sub modules' dependencies recursively.
                        _.forEach(modRequires, function(subModPath){
                            var subMod = self.analyze(self._getModuleRealPathByParent(subModPath, mod));
                            if(subMod){
                                mod.dependencies.push(subMod);
                                mod.dependencyTree[subMod.name] = subMod.dependencyTree;
                            }
                        });

                        !isModExcluded && self.analyzedModules.push(modName);
                    }
                }else{
                    mod = self.modules[modName];
                }
            }
            break;
    }
    return mod;
};

Compiler.prototype.build = function(inputFilePath, outputFilePath, outputCharset){
    var self = this,
        result = self.analyze(inputFilePath),
        combinedComment = [
            '/*',
            'combined files : ',
            '',
            self.analyzedModules.join(os.EOL),
            '',
            '*/',
            ''
        ].join(os.EOL);

    // deal with output charset. if not specified, use charset in config.
    outputCharset = (typeof outputCharset === 'undefined' || outputCharset === null) ? self.config.charset : outputCharset;

    var combineFile = self.config.suffix ? outputFilePath.replace(/\.js$/i, self.config.suffix + '.js') : outputFilePath;
    if(path.extname(combineFile) != '.js'){
        combineFile = path.resolve(combineFile, path.basename(inputFilePath, '.js') + self.config.suffix + '.js');
    }
    result.outputFile = combineFile;

    //prepare output dir
    utils.mkdirsSync(path.dirname(combineFile));

    // if exists, unlink first, otherwise, there may be some problems with the file encoding.
    if(fs.existsSync(combineFile)){
        fs.unlinkSync(combineFile);
    }

    var fd = fs.openSync(combineFile, 'w');
    var combinedCommentBuffer = iconv.encode(combinedComment, outputCharset);
    fs.writeSync(fd, combinedCommentBuffer, 0, combinedCommentBuffer.length);
    fs.closeSync(fd);

    fd = fs.openSync(combineFile, 'a');

    _.forEach(self.analyzedModules, function(modName){
        var mod = self.modules[modName];
        // add EOL to end of file.
        var modContent = self._generateRealModuleContent(mod) + os.EOL;
        var buffer = iconv.encode(modContent, outputCharset);
        fs.writeSync(fd, buffer, 0, buffer.length);
        self.combinedModules.push(modName);
    });

    fs.closeSync(fd);

    !self.config.silent && console.info('[ok]'.bold.green+' %s ===> %s', inputFilePath, outputFilePath);

    result.autoCombo = self._buildCombo(result);

    result.combined = self.combinedModules;

    return result;
};
Compiler.prototype.combo = function(fixModuleName){
    var self = this,
        content = [];
    if(_.isObject(self.modules)){
        _.forEach(self.modules, function(mod, modName){
            if(_.indexOf(self.combinedModules, modName) === -1 && !_.isEmpty(mod.dependencies)){
                var requires = [];
                _.forEach(mod.dependencies, function(subMod){
                    requires.push("'" + subMod.name + "'");
                });
                content.push("'" + modName + "': { requires: [" + requires.join(', ') + "]}");
            }
            if(fixModuleName === true && mod && mod.path){
                var modContent = self._generateRealModuleContent(mod);
                var buffer = iconv.encode(modContent, mod.charset);
                fs.writeFileSync(mod.path, buffer);
            }
        });
    }
    return content.length ? "KISSY.config('modules', {" + os.EOL + " " + content.join("," + os.EOL + " ") + " " + os.EOL + "});" : "";
};

module.exports = Compiler;
<|endoftext|>


<commit_before>
/*!
 * Jade - Compiler
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var nodes = require('./nodes')
  , filters = require('./filters')
  , doctypes = require('./doctypes')
  , selfClosing = require('./self-closing')
  , utils = require('./utils');

/**
 * Initialize `Compiler` with the given `node`.
 *
 * @param {Node} node
 * @param {Object} options
 * @api public
 */

var Compiler = module.exports = function Compiler(node, options) {
  this.options = options = options || {};
  this.node = node;
};

/**
 * Compiler prototype.
 */

Compiler.prototype = {

  /**
   * Compile parse tree to JavaScript.
   *
   * @api public
   */

  compile: function(){
    this.buf = [];
    this.visit(this.node);
    return this.buf.join('\n');
  },

  /**
   * Buffer the given `str` optionally escaped.
   *
   * @param {String} str
   * @param {Boolean} esc
   * @api public
   */

  buffer: function(str, esc){
    if (esc) str = utils.escape(str);
    this.buf.push("buf.push('" + str + "');");
  },

  /**
   * Buffer the given `node`'s lineno.
   *
   * @param {Node} node
   * @api public
   */

  line: function(node){
    if (node.instrumentLineNumber === false) return;
    this.buf.push('__.lineno = ' + node.line + ';');
  },

  /**
   * Visit `node`.
   *
   * @param {Node} node
   * @api public
   */

  visit: function(node){
    this.line(node);
    return this.visitNode(node);
  },

  /**
   * Visit `node`.
   *
   * @param {Node} node
   * @api public
   */

  visitNode: function(node){
    return this['visit' + node.constructor.name](node);
  },

  /**
   * Visit all nodes in `block`.
   *
   * @param {Block} block
   * @api public
   */

  visitBlock: function(block){
    for (var i = 0, len = block.length; i < len; ++i) {
      this.visit(block[i]);
    }
  },

  /**
   * Visit `doctype`. Sets terse mode to `true` when html 5
   * is used, causing self-closing tags to end with ">" vs "/>",
   * and boolean attributes are not mirrored.
   *
   * @param {Doctype} doctype
   * @api public
   */

  visitDoctype: function(doctype){
    var name = doctype.val;
    if ('5' == name) this.terse = true;
    doctype = doctypes[name || 'default'];
    this.xml = 0 == doctype.indexOf('<?xml');
    if (!doctype) throw new Error('unknown doctype "' + name + '"');
    this.buffer(doctype);
  },

  /**
   * Visit `tag` buffering tag markup, generating
   * attributes, visiting the `tag`'s code and block.
   *
   * @param {Tag} tag
   * @api public
   */

  visitTag: function(tag){
    var name = tag.name;

    if (~selfClosing.indexOf(name) && !this.xml) {
      this.buffer('<' + name);
      this.visitAttributes(tag.attrs);
      this.terse
        ? this.buffer('>')
        : this.buffer('/>');
    } else {
      // Optimize attributes buffering
      if (tag.attrs.length) {
        this.buffer('<' + name);
        if (tag.attrs.length) this.visitAttributes(tag.attrs);
        this.buffer('>');
      } else {
        this.buffer('<' + name + '>');
      }
      if (tag.code) this.visitCode(tag.code);
      if (tag.text) this.buffer(utils.text(tag.text.join('\\n').trimLeft()));
      this.visit(tag.block);
      this.buffer('</' + name + '>');
    }
  },

  /**
   * Visit `filter`, throwing when the filter does not exist.
   *
   * @param {Filter} filter
   * @api public
   */

  visitFilter: function(filter){
    var fn = filters[filter.name];
    if (!fn) throw new Error('unknown filter ":' + filter.name + '"');
    if (filter.block instanceof nodes.Block) {
      this.buf.push(fn(filter.block, this, filter.attrs));
    } else {
      this.buffer(fn(utils.text(filter.block.join('\\n')), filter.attrs));
    }
  },

  /**
   * Visit `text` node.
   *
   * @param {Text} text
   * @api public
   */

  visitText: function(text){
    this.buffer(utils.text(text.join('\\n'), true));
  },

  /**
   * Visit a `comment`, only buffering when the buffer flag is set.
   *
   * @param {Comment} comment
   * @api public
   */

  visitComment: function(comment){
    if (!comment.buffer) return;
    this.buffer('<!--' + utils.escape(comment.val) + '-->');
  },

  /**
   * Visit a `BlockComment`.
   *
   * @param {Comment} comment
   * @api public
   */

  visitBlockComment: function(comment){
    if (0 == comment.val.indexOf('if')) {
      this.buffer('<!--[' + comment.val + ']>');
      this.visit(comment.block);
      this.buffer('<![endif]-->');
    } else {
      this.buffer('<!--' + comment.val);
      this.visit(comment.block);
      this.buffer('-->');
    }
  },

  /**
   * Visit `code`, respecting buffer / escape flags.
   * If the code is followed by a block, wrap it in
   * a self-calling function.
   *
   * @param {Code} code
   * @api public
   */

  visitCode: function(code){
    // Wrap code blocks with {}.
    // we only wrap unbuffered code blocks ATM
    // since they are usually flow control

    // Buffer code
    if (code.buffer) {
      var val = code.val.trimLeft();
      if (code.escape) val = 'escape(' + val + ')';
      this.buf.push("buf.push(" + val + ");");
    } else {
      this.buf.push(code.val);
    }

    // Block support
    if (code.block) {
      if (!code.buffer) this.buf.push('{');
      this.visit(code.block);
      if (!code.buffer) this.buf.push('}');
    }
  },

  /**
   * Visit `each` block.
   *
   * @param {Each} each
   * @api public
   */

  visitEach: function(each){
    this.buf.push(''
      + '// iterate ' + each.obj + '\n'
      + '(function(){\n'
      + '  if (\'number\' == typeof ' + each.obj + '.length) {\n'
      + '    for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
      + '      var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');

    this.visit(each.block);

    this.buf.push(''
      + '    }\n'
      + '  } else {\n'
      + '    for (var ' + each.key + ' in ' + each.obj + ') {\n'
      + '      var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');

    this.visit(each.block);

    this.buf.push('   }\n  }\n}).call(this);\n');
  },

  /**
   * Visit `attrs`.
   *
   * @param {Array} attrs
   * @api public
   */

  visitAttributes: function(attrs){
    var buf = []
      , classes = [];
    if (this.terse) buf.push('terse: true');
    attrs.forEach(function(attr){
      if (attr.name == 'class') {
        classes.push('(' + attr.val + ')');
      } else {
        var pair = "'" + attr.name + "':(" + attr.val + ')';
        buf.push(pair);
      }
    });
    if (classes.length) {
      classes = classes.join(" + ' ' + ");
      buf.push("class: " + classes);
    }
    this.buf.push("buf.push(attrs({ " + buf.join(', ') + " }));");
  }
};
<commit_msg>Added "" for undefined prop default. Closes #130<commit_after>
/*!
 * Jade - Compiler
 * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var nodes = require('./nodes')
  , filters = require('./filters')
  , doctypes = require('./doctypes')
  , selfClosing = require('./self-closing')
  , utils = require('./utils');

/**
 * Initialize `Compiler` with the given `node`.
 *
 * @param {Node} node
 * @param {Object} options
 * @api public
 */

var Compiler = module.exports = function Compiler(node, options) {
  this.options = options = options || {};
  this.node = node;
};

/**
 * Compiler prototype.
 */

Compiler.prototype = {

  /**
   * Compile parse tree to JavaScript.
   *
   * @api public
   */

  compile: function(){
    this.buf = [];
    this.visit(this.node);
    return this.buf.join('\n');
  },

  /**
   * Buffer the given `str` optionally escaped.
   *
   * @param {String} str
   * @param {Boolean} esc
   * @api public
   */

  buffer: function(str, esc){
    if (esc) str = utils.escape(str);
    this.buf.push("buf.push('" + str + "');");
  },

  /**
   * Buffer the given `node`'s lineno.
   *
   * @param {Node} node
   * @api public
   */

  line: function(node){
    if (node.instrumentLineNumber === false) return;
    this.buf.push('__.lineno = ' + node.line + ';');
  },

  /**
   * Visit `node`.
   *
   * @param {Node} node
   * @api public
   */

  visit: function(node){
    this.line(node);
    return this.visitNode(node);
  },

  /**
   * Visit `node`.
   *
   * @param {Node} node
   * @api public
   */

  visitNode: function(node){
    return this['visit' + node.constructor.name](node);
  },

  /**
   * Visit all nodes in `block`.
   *
   * @param {Block} block
   * @api public
   */

  visitBlock: function(block){
    for (var i = 0, len = block.length; i < len; ++i) {
      this.visit(block[i]);
    }
  },

  /**
   * Visit `doctype`. Sets terse mode to `true` when html 5
   * is used, causing self-closing tags to end with ">" vs "/>",
   * and boolean attributes are not mirrored.
   *
   * @param {Doctype} doctype
   * @api public
   */

  visitDoctype: function(doctype){
    var name = doctype.val;
    if ('5' == name) this.terse = true;
    doctype = doctypes[name || 'default'];
    this.xml = 0 == doctype.indexOf('<?xml');
    if (!doctype) throw new Error('unknown doctype "' + name + '"');
    this.buffer(doctype);
  },

  /**
   * Visit `tag` buffering tag markup, generating
   * attributes, visiting the `tag`'s code and block.
   *
   * @param {Tag} tag
   * @api public
   */

  visitTag: function(tag){
    var name = tag.name;

    if (~selfClosing.indexOf(name) && !this.xml) {
      this.buffer('<' + name);
      this.visitAttributes(tag.attrs);
      this.terse
        ? this.buffer('>')
        : this.buffer('/>');
    } else {
      // Optimize attributes buffering
      if (tag.attrs.length) {
        this.buffer('<' + name);
        if (tag.attrs.length) this.visitAttributes(tag.attrs);
        this.buffer('>');
      } else {
        this.buffer('<' + name + '>');
      }
      if (tag.code) this.visitCode(tag.code);
      if (tag.text) this.buffer(utils.text(tag.text.join('\\n').trimLeft()));
      this.visit(tag.block);
      this.buffer('</' + name + '>');
    }
  },

  /**
   * Visit `filter`, throwing when the filter does not exist.
   *
   * @param {Filter} filter
   * @api public
   */

  visitFilter: function(filter){
    var fn = filters[filter.name];
    if (!fn) throw new Error('unknown filter ":' + filter.name + '"');
    if (filter.block instanceof nodes.Block) {
      this.buf.push(fn(filter.block, this, filter.attrs));
    } else {
      this.buffer(fn(utils.text(filter.block.join('\\n')), filter.attrs));
    }
  },

  /**
   * Visit `text` node.
   *
   * @param {Text} text
   * @api public
   */

  visitText: function(text){
    this.buffer(utils.text(text.join('\\n'), true));
  },

  /**
   * Visit a `comment`, only buffering when the buffer flag is set.
   *
   * @param {Comment} comment
   * @api public
   */

  visitComment: function(comment){
    if (!comment.buffer) return;
    this.buffer('<!--' + utils.escape(comment.val) + '-->');
  },

  /**
   * Visit a `BlockComment`.
   *
   * @param {Comment} comment
   * @api public
   */

  visitBlockComment: function(comment){
    if (0 == comment.val.indexOf('if')) {
      this.buffer('<!--[' + comment.val + ']>');
      this.visit(comment.block);
      this.buffer('<![endif]-->');
    } else {
      this.buffer('<!--' + comment.val);
      this.visit(comment.block);
      this.buffer('-->');
    }
  },

  /**
   * Visit `code`, respecting buffer / escape flags.
   * If the code is followed by a block, wrap it in
   * a self-calling function.
   *
   * @param {Code} code
   * @api public
   */

  visitCode: function(code){
    // Wrap code blocks with {}.
    // we only wrap unbuffered code blocks ATM
    // since they are usually flow control

    // Buffer code
    if (code.buffer) {
      var val = code.val.trimLeft();
      val = '(' + val + ') || ""';
      if (code.escape) val = 'escape(' + val + ')';
      this.buf.push("buf.push(" + val + ");");
    } else {
      this.buf.push(code.val);
    }

    // Block support
    if (code.block) {
      if (!code.buffer) this.buf.push('{');
      this.visit(code.block);
      if (!code.buffer) this.buf.push('}');
    }
  },

  /**
   * Visit `each` block.
   *
   * @param {Each} each
   * @api public
   */

  visitEach: function(each){
    this.buf.push(''
      + '// iterate ' + each.obj + '\n'
      + '(function(){\n'
      + '  if (\'number\' == typeof ' + each.obj + '.length) {\n'
      + '    for (var ' + each.key + ' = 0, $$l = ' + each.obj + '.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
      + '      var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');

    this.visit(each.block);

    this.buf.push(''
      + '    }\n'
      + '  } else {\n'
      + '    for (var ' + each.key + ' in ' + each.obj + ') {\n'
      + '      var ' + each.val + ' = ' + each.obj + '[' + each.key + '];\n');

    this.visit(each.block);

    this.buf.push('   }\n  }\n}).call(this);\n');
  },

  /**
   * Visit `attrs`.
   *
   * @param {Array} attrs
   * @api public
   */

  visitAttributes: function(attrs){
    var buf = []
      , classes = [];
    if (this.terse) buf.push('terse: true');
    attrs.forEach(function(attr){
      if (attr.name == 'class') {
        classes.push('(' + attr.val + ')');
      } else {
        var pair = "'" + attr.name + "':(" + attr.val + ')';
        buf.push(pair);
      }
    });
    if (classes.length) {
      classes = classes.join(" + ' ' + ");
      buf.push("class: " + classes);
    }
    this.buf.push("buf.push(attrs({ " + buf.join(', ') + " }));");
  }
};
<|endoftext|>