<commit_before>import getWindow from 'get-window'
import invariant from 'tiny-invariant'
import warning from 'tiny-warning'
import { Value } from 'slate'

import findPath from './find-node'
import findRange from './find-range'

/**
 * Get the target range from a DOM `event`.
 *
 * @param {Event} event
 * @param {Editor} editor
 * @return {Range}
 */

function getEventRange(event, editor) {
  warning(
    false,
    'As of slate-react@0.22 the `getEventRange(event, editor)` helper is deprecated in favor of `editor.findEventRange(event)`.'
  )

  invariant(
    !Value.isValue(editor),
    'As of Slate 0.42.0, the `findNode` utility takes an `editor` instead of a `value`.'
  )

  if (event.nativeEvent) {
    event = event.nativeEvent
  }

  const { clientX: x, clientY: y, target } = event
  if (x == null || y == null) return null

  const { value } = editor
  const { document } = value
  const path = findPath(event.target, editor)
  if (!path) return null

  const node = document.getNode(path)

  // If the drop target is inside a void node, move it into either the next or
  // previous node, depending on which side the `x` and `y` coordinates are
  // closest to.
  if (editor.isVoid(node)) {
    const rect = target.getBoundingClientRect()
    const isPrevious =
      node.object === 'inline'
        ? x - rect.left < rect.left + rect.width - x
        : y - rect.top < rect.top + rect.height - y

    const range = document.createRange()
    const iterable = isPrevious ? 'previousTexts' : 'nextTexts'
    const move = isPrevious ? 'moveToEndOfNode' : 'moveToStartOfNode'
    const entry = document[iterable](path)

    if (entry) {
      const [n] = entry
      return range[move](n)
    }

    return null
  }

  // Else resolve a range from the caret position where the drop occured.
  const window = getWindow(target)
  let native

  // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
  if (window.document.caretRangeFromPoint) {
    native = window.document.caretRangeFromPoint(x, y)
  } else if (window.document.caretPositionFromPoint) {
    const position = window.document.caretPositionFromPoint(x, y)
    native = window.document.createRange()
    native.setStart(position.offsetNode, position.offset)
    native.setEnd(position.offsetNode, position.offset)
  } else if (window.document.body.createTextRange) {
    // COMPAT: In IE, `caretRangeFromPoint` and
    // `caretPositionFromPoint` don't exist. (2018/07/11)
    native = window.document.body.createTextRange()

    try {
      native.moveToPoint(x, y)
    } catch (error) {
      // IE11 will raise an `unspecified error` if `moveToPoint` is
      // called during a dropEvent.
      return null
    }
  }

  // Resolve a Slate range from the DOM range.
  const range = findRange(native, editor)
  if (!range) return null

  return range
}

/**
 * Export.
 *
 * @type {Function}
 */

export default getEventRange
<commit_msg>Fix several errors in the deprecated `getEventRange` (#2847)<commit_after>import getWindow from 'get-window'
import invariant from 'tiny-invariant'
import warning from 'tiny-warning'
import { Value } from 'slate'

import findPath from './find-path'
import findRange from './find-range'

/**
 * Get the target range from a DOM `event`.
 *
 * @param {Event} event
 * @param {Editor} editor
 * @return {Range}
 */

function getEventRange(event, editor) {
  warning(
    false,
    'As of slate-react@0.22 the `getEventRange(event, editor)` helper is deprecated in favor of `editor.findEventRange(event)`.'
  )

  invariant(
    !Value.isValue(editor),
    'As of Slate 0.42.0, the `findNode` utility takes an `editor` instead of a `value`.'
  )

  if (event.nativeEvent) {
    event = event.nativeEvent
  }

  const { clientX: x, clientY: y, target } = event
  if (x == null || y == null) return null

  const { value } = editor
  const { document } = value
  const path = findPath(event.target, editor)
  if (!path) return null

  const node = document.getNode(path)

  // If the drop target is inside a void node, move it into either the next or
  // previous node, depending on which side the `x` and `y` coordinates are
  // closest to.
  if (editor.isVoid(node)) {
    const rect = target.getBoundingClientRect()
    const isPrevious =
      node.object === 'inline'
        ? x - rect.left < rect.left + rect.width - x
        : y - rect.top < rect.top + rect.height - y

    const range = document.createRange()
    const move = isPrevious ? 'moveToEndOfNode' : 'moveToStartOfNode'
    const entry = document[isPrevious ? 'getPreviousText' : 'getNextText'](path)

    if (entry) {
      return range[move](entry)
    }

    return null
  }

  // Else resolve a range from the caret position where the drop occured.
  const window = getWindow(target)
  let native

  // COMPAT: In Firefox, `caretRangeFromPoint` doesn't exist. (2016/07/25)
  if (window.document.caretRangeFromPoint) {
    native = window.document.caretRangeFromPoint(x, y)
  } else if (window.document.caretPositionFromPoint) {
    const position = window.document.caretPositionFromPoint(x, y)
    native = window.document.createRange()
    native.setStart(position.offsetNode, position.offset)
    native.setEnd(position.offsetNode, position.offset)
  } else if (window.document.body.createTextRange) {
    // COMPAT: In IE, `caretRangeFromPoint` and
    // `caretPositionFromPoint` don't exist. (2018/07/11)
    native = window.document.body.createTextRange()

    try {
      native.moveToPoint(x, y)
    } catch (error) {
      // IE11 will raise an `unspecified error` if `moveToPoint` is
      // called during a dropEvent.
      return null
    }
  }

  // Resolve a Slate range from the DOM range.
  const range = findRange(native, editor)
  if (!range) return null

  return range
}

/**
 * Export.
 *
 * @type {Function}
 */

export default getEventRange
<|endoftext|>


<commit_before>scal.addMethods({
/*------------------------------- INTERNAL -------------------------------*/
    _setupPlanner: function(planner) {
        this.planner = {};
        this._eventIndex = {deleted: []};
        planner.each(function(plan) {
            this._setPlanner(plan);
        }.bind(this));
    },
	_setPlanner: function(plan) {
        var plannerPeriod = Object.isArray(plan.period) ? plan.period : (typeof plan.period) == 'object' ? [plan.period] : [new Date(plan.period)];
        plannerPeriod.each(function(planDate){
            if(!this.planner[planDate]) { this.planner[planDate] = []; }
            if(Object.isString(plan.cls) && Object.isString(plan.label)){
                this.planner[planDate].push({ cls: ['dayboxevent', plan.cls], val: plan.label });
                this._updateEindex(plan.label, planDate);
            } else {
                var cls = Object.isArray(plan.cls) ? plan.cls : [plan.cls];
                var labels = Object.isArray(plan.label) ? plan.label : [plan.label];
                while(cls.size() > labels.size()) {
                    labels.push(labels[0]);
                    }
                while(labels.size() > cls.size()) {
                    cls.push(cls[0]);
                }
                $A($R(0, cls.size() - 1)).each(function(v){
                    var label = labels.size() > 1 ? labels.shift() : labels[0];
                    this.planner[planDate].push({cls: ['dayboxevent', cls.shift()], val: label});
                    this._updateEindex(label, planDate);
                }.bind(this));
            }
       }.bind(this));
    },
    _updateEindex: function(val,dt) {
        if(!this._eventIndex[val]) { this._eventIndex[val] = []; }
        this._eventIndex[val].push(dt);
    },
    _updatePlanner: function(day, el) {
        el.innerHTML = '';
        if(this.planner[day]) {
            this.planner[day].each(function(plan) {
                plan.cls.push('dayboxevent');
                el.insert(new Element('p',{'class': plan.cls.join(' ') }).update(plan.val));
            });
        }
    },
/*------------------------------- PUBLIC -------------------------------*/
    getDatesByEvent: function(evt) {
        var dates = [];
        if(this._eventIndex[evt]){
            this._eventIndex[evt].each(function(d){
                this._eventIndex.deleted = this._eventIndex.deleted.without(d);
                dates.push(d);
            }.bind(this));
            return dates;
        }
        return false;
    },
    getEventsByDate: function(d) {
        var pevents = [];
        if(this.planner[d]) {
            this.planner[d].each(function(p){
                pevents.push(p.val);
            });
            return pevents;
        }
        return false;
    },
    getCurrentEvents: function() {
        // determine if we want just the current month or everything in current calendar view
        var currentMonth = arguments[0] ? this.currentdate.getMonth() : false;
        var plannerCheck = function(d) {
            if(currentMonth) { return this.planner[d] && (d.getMonth() == currentMonth) ? true : false; }
            else { return this.planner[d] ? true : false; }
        }.bind(this);
        var evts = [];
        this.dateRange.each(function(d,i) {
            if(plannerCheck(d)) {
                evts.push({dt: d, target: this.cells[i]});
            }
        }.bind(this));
        return evts;
    },
    removeEventsByDate: function(d) {
        if(this.planner[d]) {
            var cellIndex = this._getCellIndexByDate(d);
            if(Object.isNumber(arguments[1])) {
                var index = arguments[1];
                if(this.planner[d][index]){
                    delete this._eventIndex[this.planner[d][index]['val']];
                    this.planner[d].splice(index,1);
                }
                this.cells[cellIndex].select('.dayboxvalue p')[index].remove();
             } else {
                delete this.planner[d];
                this._eventIndex.deleted.push(d);
                this.cells[cellIndex].select('.dayboxvalue p').invoke('remove');
             }
        } else {
            return false;
        }
    },
    getEventElementsByDate: function(d) {
        return this.getElementByDate(d).select('p.dayboxevent');
    },
    getEventElementsByWeek: function(week) {
        return this.getElementsByWeek(week).collect(function(e){ return e.select('p.dayboxevent'); });
    },
    getSelectedEvents: function() {
        var selectedElement = this.getSelectedElement();
        return Object.isUndefined(selectedElement) ? false : selectedElement.select('p.dayboxevent');
    },
    getTodaysEvents: function() {
        return this.getTodaysElement().select('p.dayboxevent');
    },
    updateDayValue: function(week,day,value){
        var planvalues = Object.isArray(value) ? value : [value];
        var planclasses = arguments[3] ? Object.isString(arguments[3]) ? [arguments[3]] : arguments[3] : [];
        week -= 1;
        day -= 1;
        this.dateRange.eachSlice(7, function(wk,i) {
            if(i == week) {
                this._setPlanner({period: wk[day], cls: planclasses.clone(), label: planvalues.clone()});
                throw $break
            }
        }.bind(this));
        planclasses.push('dayboxevent');
        var cellvalue = '.cal_day_'+week+'_'+day+'_value';
        var el = this.element.select(cellvalue)[0];
        planvalues.each(function(val) {
            el.insert(new Element('p',{'class': planclasses.join(' ')}).update(value));
        });
        return el;
    },
    setPlannerValue: function(year,month,day,value){
        var planvalues = Object.isArray(value) ? value : [value];
        var plannerdate = new Date();
        plannerdate.setHours(0,0,0,0);
        plannerdate.setYear(year);
        plannerdate.setMonth(month-1);
        plannerdate.setDate(day);
        var planclasses = arguments[4] ? Object.isString(arguments[4]) ? [arguments[4]] : arguments[4] : [];
        this._setPlanner({period: plannerdate, cls: planclasses.clone(), label: planvalues.clone()});
        planclasses.push('dayboxevent');
        if(this.dateRange.first() > plannerdate || this.dateRange.last() < plannerdate){
            return; // return nothing if plannerdate isn't in the current month
        }
        var cellIndex = this._getCellIndexByDate(plannerdate);
        var el = this.cells[cellIndex].select('.dayboxvalue')[0];
        planvalues.each(function(val) {
            el.insert(new Element('p',{'class': planclasses.join(' ')}).update(val));
        });
        return el;
    }
});
<commit_msg>updated for better month setting in setPlannerValue<commit_after>scal.addMethods({
/*------------------------------- INTERNAL -------------------------------*/
    _setupPlanner: function(planner) {
        this.planner = {};
        this._eventIndex = {deleted: []};
        planner.each(function(plan) {
            this._setPlanner(plan);
        }.bind(this));
    },
	_setPlanner: function(plan) {
        var plannerPeriod = Object.isArray(plan.period) ? plan.period : (typeof plan.period) == 'object' ? [plan.period] : [new Date(plan.period)];
        plannerPeriod.each(function(planDate){
            if(!this.planner[planDate]) { this.planner[planDate] = []; }
            if(Object.isString(plan.cls) && Object.isString(plan.label)){
                this.planner[planDate].push({ cls: ['dayboxevent', plan.cls], val: plan.label });
                this._updateEindex(plan.label, planDate);
            } else {
                var cls = Object.isArray(plan.cls) ? plan.cls : [plan.cls];
                var labels = Object.isArray(plan.label) ? plan.label : [plan.label];
                while(cls.size() > labels.size()) {
                    labels.push(labels[0]);
                    }
                while(labels.size() > cls.size()) {
                    cls.push(cls[0]);
                }
                $A($R(0, cls.size() - 1)).each(function(v){
                    var label = labels.size() > 1 ? labels.shift() : labels[0];
                    this.planner[planDate].push({cls: ['dayboxevent', cls.shift()], val: label});
                    this._updateEindex(label, planDate);
                }.bind(this));
            }
       }.bind(this));
    },
    _updateEindex: function(val,dt) {
        if(!this._eventIndex[val]) { this._eventIndex[val] = []; }
        this._eventIndex[val].push(dt);
    },
    _updatePlanner: function(day, el) {
        el.innerHTML = '';
        if(this.planner[day]) {
            this.planner[day].each(function(plan) {
                plan.cls.push('dayboxevent');
                el.insert(new Element('p',{'class': plan.cls.join(' ') }).update(plan.val));
            });
        }
    },
/*------------------------------- PUBLIC -------------------------------*/
    getDatesByEvent: function(evt) {
        var dates = [];
        if(this._eventIndex[evt]){
            this._eventIndex[evt].each(function(d){
                this._eventIndex.deleted = this._eventIndex.deleted.without(d);
                dates.push(d);
            }.bind(this));
            return dates;
        }
        return false;
    },
    getEventsByDate: function(d) {
        var pevents = [];
        if(this.planner[d]) {
            this.planner[d].each(function(p){
                pevents.push(p.val);
            });
            return pevents;
        }
        return false;
    },
    getCurrentEvents: function() {
        // determine if we want just the current month or everything in current calendar view
        var currentMonth = arguments[0] ? this.currentdate.getMonth() : false;
        var plannerCheck = function(d) {
            if(currentMonth) { return this.planner[d] && (d.getMonth() == currentMonth) ? true : false; }
            else { return this.planner[d] ? true : false; }
        }.bind(this);
        var evts = [];
        this.dateRange.each(function(d,i) {
            if(plannerCheck(d)) {
                evts.push({dt: d, target: this.cells[i]});
            }
        }.bind(this));
        return evts;
    },
    removeEventsByDate: function(d) {
        if(this.planner[d]) {
            var cellIndex = this._getCellIndexByDate(d);
            if(Object.isNumber(arguments[1])) {
                var index = arguments[1];
                if(this.planner[d][index]){
                    delete this._eventIndex[this.planner[d][index]['val']];
                    this.planner[d].splice(index,1);
                }
                this.cells[cellIndex].select('.dayboxvalue p')[index].remove();
             } else {
                delete this.planner[d];
                this._eventIndex.deleted.push(d);
                this.cells[cellIndex].select('.dayboxvalue p').invoke('remove');
             }
        } else {
            return false;
        }
    },
    getEventElementsByDate: function(d) {
        return this.getElementByDate(d).select('p.dayboxevent');
    },
    getEventElementsByWeek: function(week) {
        return this.getElementsByWeek(week).collect(function(e){ return e.select('p.dayboxevent'); });
    },
    getSelectedEvents: function() {
        var selectedElement = this.getSelectedElement();
        return Object.isUndefined(selectedElement) ? false : selectedElement.select('p.dayboxevent');
    },
    getTodaysEvents: function() {
        return this.getTodaysElement().select('p.dayboxevent');
    },
    updateDayValue: function(week,day,value){
        var planvalues = Object.isArray(value) ? value : [value];
        var planclasses = arguments[3] ? Object.isString(arguments[3]) ? [arguments[3]] : arguments[3] : [];
        week -= 1;
        day -= 1;
        this.dateRange.eachSlice(7, function(wk,i) {
            if(i == week) {
                this._setPlanner({period: wk[day], cls: planclasses.clone(), label: planvalues.clone()});
                throw $break
            }
        }.bind(this));
        planclasses.push('dayboxevent');
        var cellvalue = '.cal_day_'+week+'_'+day+'_value';
        var el = this.element.select(cellvalue)[0];
        planvalues.each(function(val) {
            el.insert(new Element('p',{'class': planclasses.join(' ')}).update(value));
        });
        return el;
    },
    setPlannerValue: function(year,month,day,value){
        var planvalues = Object.isArray(value) ? value : [value];
        var plannerdate = new Date();
        plannerdate.setHours(0,0,0,0);
        plannerdate.setYear(year);
        plannerdate.setMonth(month-1,1);
        plannerdate.setDate(day);
        var planclasses = arguments[4] ? Object.isString(arguments[4]) ? [arguments[4]] : arguments[4] : [];
        this._setPlanner({period: plannerdate, cls: planclasses.clone(), label: planvalues.clone()});
        planclasses.push('dayboxevent');
        if(this.dateRange.first() > plannerdate || this.dateRange.last() < plannerdate){
            return; // return nothing if plannerdate isn't in the current month
        }
        var cellIndex = this._getCellIndexByDate(plannerdate);
        var el = this.cells[cellIndex].select('.dayboxvalue')[0];
        planvalues.each(function(val) {
            el.insert(new Element('p',{'class': planclasses.join(' ')}).update(val));
        });
        return el;
    }
});
<|endoftext|>


<commit_before>const path = require("path"),
	lru = require(path.join(__dirname, "lib", "tiny-lru.js")),
	precise = require("precise"),
	nth = 2e3,
	cache = lru(nth),
	data = new Array(nth);

function seed () {
	let i = -1;

	while (++i < nth) {
		data[i] = Math.floor(Math.random() * nth);
	}
}

function populate (arg, start = 0) {
	const nth = arg.max;
	let i = -1;

	while (++i < nth) {
		arg.set(i + start, data[i]);
	}
}

function bench (n = 0, x = 1) {
	seed();

	const timer = precise().start();

	populate(cache, n);
	timer.stop();
	console.log(`Run ${x} ${x === 1 ? "Set" : "Evict"} (${n === 0 ? "Low Keys" : "High Keys"}): ${timer.diff() / 1e6} ms`);
}

console.log(`Benchmarking ${nth} items (random value per run)`);
bench();
bench(nth, 2);
bench(void 0, 3);
bench(nth, 4);
<commit_msg>Updating benchmark<commit_after>const path = require("path"),
	lru = require(path.join(__dirname, "lib", "tiny-lru.js")),
	precise = require("precise"),
	nth = 2e3,
	cache = lru(nth),
	data = new Array(nth);

function seed () {
	let i = -1;

	while (++i < nth) {
		data[i] = Math.floor(Math.random() * nth);
	}
}

function populate (arg, start = 0) {
	const nth = arg.max;
	let i = -1;

	while (++i < nth) {
		arg.set(i + start, data[i]);
	}
}

function get (arg, start = 0) {
	const nth = arg.max;
	let i = -1;

	while (++i < nth) {
		arg.get(i + start, data[i]);
	}
}

function bench (n = 0, x = 1, type = "set") {
	if (type === "set") {
		seed();

		const timer = precise().start();

		populate(cache, n);
		timer.stop();
		console.log(`Run ${x} ${x === 1 ? "Set" : "Evict"} (${n === 0 ? "Low Keys" : "High Keys"}): ${timer.diff() / 1e6} ms`);
	} else if (type === "get") {
		const timer = precise().start();

		get(cache, n);
		timer.stop();
		console.log(`Run ${x} Get (${n === 0 ? "Low Keys" : "High Keys"}): ${timer.diff() / 1e6} ms`);
	}
}

console.log(`Benchmarking ${nth} items (random value per run)`);
bench();
bench(nth, 2);
bench(void 0, 3);
bench(nth, 4);
bench(nth, 5, "get");
<|endoftext|>


<commit_before>'use strict';

angular.module('rippleDemonstrator')
  .controller('PatientsListFullCtrl', function ($scope, $state, $stateParams, Report) {

    $scope.patients = [];

    var patientListQuery = {
      ageFrom: $stateParams.ageFrom,
      ageTo: $stateParams.ageTo,
      orderColumn: $stateParams.orderColumn,
      orderType: $stateParams.orderType,
      pageNumber: $stateParams.pageNumber,
      reportType: $stateParams.reportType,
      searchString: $stateParams.searchString
    }

    $scope.subHeader = $stateParams.queryType + $stateParams.reportType + ': ' + $stateParams.searchString + ' & Aged ' + $stateParams.ageFrom + ' to ' + $stateParams.ageTo;
    $scope.tab = 'patientInfo';

    Report.getTable(patientListQuery).then(function (result) {
      $scope.patients = result.data.patientDetails;
    });

    $scope.order = $stateParams.order || 'name';
    $scope.reverse = $stateParams.reverse === 'true';

    $scope.sort = function (field) {
      var reverse = $scope.reverse;

      if ($scope.order === field) {
        reverse = !reverse;
      }

      $state.transitionTo($state.current, _.extend($stateParams, {
        order: field,
        reverse: reverse
      }));
    };

    $scope.viewPatients = function (){
      $scope.tab = 'patientInfo';
    };

    $scope.viewDateTime = function () {
      $scope.tab = 'dateTime';
    };

    $scope.viewCounts = function (){
      $scope.tab = 'counts';
    };

    $scope.sortClass = function (field) {
      if ($scope.order === field) {
        return $scope.reverse ? 'sort-desc' : 'sort-asc';
      }
    };

    $scope.go = function (patient) {
      $state.go('patients-summary', {
        patientId: patient.id
      });
    };

  });
<commit_msg>Added time parser<commit_after>'use strict';

angular.module('rippleDemonstrator')
  .controller('PatientsListFullCtrl', function ($scope, $state, $stateParams, Report) {

    $scope.patients = [];

    var patientListQuery = {
      ageFrom: $stateParams.ageFrom,
      ageTo: $stateParams.ageTo,
      orderColumn: $stateParams.orderColumn,
      orderType: $stateParams.orderType,
      pageNumber: $stateParams.pageNumber,
      reportType: $stateParams.reportType,
      searchString: $stateParams.searchString
    }

    $scope.subHeader = $stateParams.queryType + $stateParams.reportType + ': ' + $stateParams.searchString + ' & Aged ' + $stateParams.ageFrom + ' to ' + $stateParams.ageTo;
    $scope.tab = 'patientInfo';

    Report.getTable(patientListQuery).then(function (result) {
      $scope.patients = result.data.patientDetails;
       for (var i = 0; i < $scope.patients.length; i++) {
        $scope.patients[i].ordersHeadline.latestEntry = $scope.processDateFormat(moment($scope.patients[i].ordersHeadline.latestEntry));
        $scope.patients[i].medsHeadline.latestEntry = $scope.processDateFormat(moment($scope.patients[i].medsHeadline.latestEntry));
        $scope.patients[i].resultsHeadline.latestEntry = $scope.processDateFormat(moment($scope.patients[i].resultsHeadline.latestEntry));
        $scope.patients[i].treatmentsHeadline.latestEntry = $scope.processDateFormat(moment($scope.patients[i].treatmentsHeadline.latestEntry));
      }
    });

    $scope.processDateFormat = function (dateString) {
      if(moment().diff(dateString, 'days') < 1){
          return dateString.format('h:mm a');
      }
      if(moment().startOf('year') <= dateString){
          return dateString.format('DD-MMM');
      }
      if(moment().startOf('year').subtract(1, 'year') < dateString){
          return dateString.format('MMM-YY');
      }
      return dateString.format('YYYY');
    }

    $scope.order = $stateParams.order || 'name';
    $scope.reverse = $stateParams.reverse === 'true';

    $scope.sort = function (field) {
      var reverse = $scope.reverse;

      if ($scope.order === field) {
        reverse = !reverse;
      }

      $state.transitionTo($state.current, _.extend($stateParams, {
        order: field,
        reverse: reverse
      }));
    };

    $scope.viewPatients = function (){
      $scope.tab = 'patientInfo';
    };

    $scope.viewDateTime = function () {
      $scope.tab = 'dateTime';
    };

    $scope.viewCounts = function (){
      $scope.tab = 'counts';
    };

    $scope.sortClass = function (field) {
      if ($scope.order === field) {
        return $scope.reverse ? 'sort-desc' : 'sort-asc';
      }
    };

    $scope.go = function (patient) {
      $state.go('patients-summary', {
        patientId: patient.id
      });
    };

  });
<|endoftext|>


<commit_before>//
/**
 * Created by tungtouch on 2/9/15.
 */

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
var colors = require('colors');
var dgram = require('dgram');
var message = new Buffer("Some bytes hello world bo bo bo world HEHEHEHE ahhaha hohoho hehe:");
var client = dgram.createSocket("udp4");
var async = require('async');

var max = 1000;
var numCluster = 100;
var arr = [];

for (var i = 0; i < max; i++) {
    arr.push(i);
}
var a = 0;

//[2GB]128.199.126.250 [8GB]128.199.109.202

if (cluster.isMaster) {

    // Keep track of http requests
    var numReqs = 0;
    var totalReqs = 0;
    var second = 0;
    if(totalReqs == 10000) {
        console.log(colors.blue("Result benchmark: ", colors.bold(totalReqs / second)));
    }else{
        setInterval(function() {
            console.log(
                colors.red("Number Request: ", numReqs), " | ",
                colors.yellow("Total Request: ", totalReqs), " | ",
                colors.blue("Seconds: ", second++));
            numReqs = 0;
        }, 1000);
    }
// Count requestes
    function messageHandler(msg) {
        if (msg.cmd && msg.cmd == 'notifyRequest') {
            numReqs += 1;
            totalReqs +=1;
        }
    }

    // Start workers and listen for messages containing notifyRequest

    for (var i = 0; i < numCluster; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        cluster.workers[id].on('message', messageHandler);
    });

} else {
    var q = async.queue(function(index, cb){
        //console.log("Index: ", index);
        setTimeout(function () {
            client.send(message, 0, message.length, 4444, "128.199.126.250", function (err) {
                //console.log("Request : ", a++);
                process.send({ cmd: 'notifyRequest' });
                if(err){
                    console.log('ERROR :', err);
                }
                cb(err);
            });
        }, 20);
    });

    q.push(arr);

}<commit_msg>Update UDPefef<commit_after>//
/**
 * Created by tungtouch on 2/9/15.
 */

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;
var colors = require('colors');
var dgram = require('dgram');
var message = new Buffer("Some bytes hello world bo bo bo world HEHEHEHE ahhaha hohoho hehe:");
var client = dgram.createSocket("udp4");
var async = require('async');

var max = 1000;
var numCluster = 100;
var arr = [];

for (var i = 0; i < max; i++) {
    arr.push(i);
}
var a = 0;

//[2GB]128.199.126.250 [8GB]128.199.109.202

if (cluster.isMaster) {

    // Keep track of http requests
    var numReqs = 0;
    var totalReqs = 0;
    var second = 0;
    var flag = 0;
    if(flag == 1) {
        console.log(colors.blue("Result benchmark: ", colors.bold(totalReqs / second)));
    }else{
        setInterval(function() {
            console.log(
                colors.red("Number Request: ", numReqs), " | ",
                colors.yellow("Total Request: ", totalReqs), " | ",
                colors.blue("Seconds: ", second++));
            numReqs = 0;
            if(totalReqs == max * numCluster) {
                flag = 1;
            }
        }, 1000);
    }
// Count requestes
    function messageHandler(msg) {
        if (msg.cmd && msg.cmd == 'notifyRequest') {
            numReqs += 1;
            totalReqs +=1;
        }
    }

    // Start workers and listen for messages containing notifyRequest

    for (var i = 0; i < numCluster; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        cluster.workers[id].on('message', messageHandler);
    });

} else {
    var q = async.queue(function(index, cb){
        //console.log("Index: ", index);
        setTimeout(function () {
            client.send(message, 0, message.length, 4444, "128.199.126.250", function (err) {
                //console.log("Request : ", a++);
                process.send({ cmd: 'notifyRequest' });
                if(err){
                    console.log('ERROR :', err);
                }
                cb(err);
            });
        }, 20);
    });

    q.push(arr);

}<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');

const URL = require('./url');
const Codec = require('../codec');
const Client = require('../httpclient');

const AppConfig = {
    clientid: 53999199,
    appid: 501004106
};

function writeFileAsync(filePath, data, options) {
    return new Promise((resolve, reject) => {
        fs.writeFile(filePath, data, options, error => {
            if (error) reject(error);
            resolve();
        });
    });
}

class QQ {
    constructor() {
        this.tokens = {
            uin: '',
            ptwebqq: '',
            vfwebqq: '',
            psessionid: ''
        };
        this.selfInfo = {};
        this.buddy = {};
        this.discu = {};
        this.group = {};
        this.buddyNameMap = new Map();
        this.discuNameMap = new Map();
        this.groupNameMap = new Map();
        this.client = new Client();
    }

    async run() {
        await this.login();
        await this.initInfo();
        await this.loopPoll();
    }

    async login() {
        log.info('(0/5) 开始登录，准备下载二维码');

        // Step0: prepare cookies, pgv_info and pgv_pvid
        // http://pingjs.qq.com/tcss.ping.js  tcss.run & _cookie.init
        let initCookies = {
            pgv_info: `ssid=s${Codec.randPgv()}`,
            pgv_pvid: Codec.randPgv()
        };
        this.client.setCookie(initCookies);
        await this.client.get(URL.loginPrepare);

        // Step1: download QRcode
        let qrCode = await this.client.get({ url: URL.qrcode, responseType: 'arraybuffer' });
        await writeFileAsync('/tmp/code.png', qrCode, 'binary');
        log.info('(1/5) 二维码下载完成，等待扫描');
        // open file, only for linux
        require('child_process').exec('xdg-open /tmp/code.png');

        // Step2:
        let scanSuccess = false;
        let quotRegxp = /'[^,]*'/g;
        let ptqrloginURL = URL.getPtqrloginURL(this.client.getCookie('qrsig'));
        let ptlogin4URL;
        do {
            let responseBody = await this.client.get({
                url: ptqrloginURL,
                headers: { Referer: URL.ptqrloginReferer },
            });
            log.debug(responseBody);
            let arr = responseBody.match(quotRegxp).map(i => i.substring(1, i.length - 1));
            if (arr[0] === '0') {
                scanSuccess = true;
                ptlogin4URL = arr[2];
            }
        } while (!scanSuccess);
        log.info('(2/5) 二维码扫描完成');
        // remove file, for linux(or macOS ?)
        require('child_process').exec('rm /tmp/code.png');

        // Step3: find token 'vfwebqq' in cookie
        // NOTICE: the request returns 302 when success. DO NOT REJECT 302.
        await this.client.get({
            url: ptlogin4URL,
            maxRedirects: 0,     // Axios follows redirect automatically, but we need to disable it here.
            validateStatus: status => status === 302,
            headers: { Referer: URL.ptlogin4Referer }
        });
        this.tokens.ptwebqq = this.client.getCookie('ptwebqq');
        log.info('(3/5) 获取 ptwebqq 成功');

        // Step4: request token 'vfwebqq'
        let vfwebqqResp = await this.client.get({
            url: URL.getVfwebqqURL(this.tokens.ptwebqq),
            headers: { Referer: URL.vfwebqqReferer }
        });
        log.debug(vfwebqqResp);
        this.tokens.vfwebqq = vfwebqqResp.result.vfwebqq;
        log.info('(4/5) 获取 vfwebqq 成功');

        // Step5: psessionid and uin
        let loginStat = await this.client.post({
            url: URL.login2,
            data: {
                ptwebqq: this.tokens.ptwebqq,
                clientid: AppConfig.clientid,
                psessionid: "",
                status: "online",
            },
            headers: {
                Origin: URL.login2Origin,
                Referer: URL.login2Referer
            }
        });
        log.debug(loginStat);
        this.tokens.uin = loginStat.result.uin;
        this.tokens.psessionid = loginStat.result.psessionid;
        log.info('(5/5) 获取 psessionid 和 uin 成功');
    }

    getSelfInfo() {
        log.info('开始获取用户信息');
        return this.client.get({
            url: URL.selfInfo,
            headers: { Referer: URL.referer130916 }
        });
    }

    getBuddy() {
        log.info('开始获取好友列表');
        return this.client.post({
            url: URL.getBuddy,
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    getOnlineBuddies() {
        log.info('开始获取好友在线状态');
        return this.client.get({
            url: URL.onlineBuddies(this.tokens.vfwebqq, this.tokens.psessionid),
            headers: {
                Referer: URL.referer151105
            }
        });
    }

    getGroup() {
        log.info('开始获取群列表');
        return this.client.post({
            url: URL.getGroup,
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    getDiscu() {
        log.info('开始获取讨论组列表');
        return this.client.post({
            url: URL.getDiscu(this.tokens.vfwebqq, this.tokens.psessionid),
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    async initInfo() {
        let manyInfo = await Promise.all([
            this.getSelfInfo(),
            this.getBuddy(),
            this.getOnlineBuddies(),
            this.getDiscu(),
            this.getGroup()
        ]);
        log.debug(JSON.stringify(manyInfo, null, 4));
        this.selfInfo = manyInfo[0].result;
        this.buddy = manyInfo[1].result;
        this.discu = manyInfo[3].result.dnamelist;
        this.group = manyInfo[4].result.gnamelist;
        let promises = this.group.map(async e => {
            let rawInfo = await this.getGroupInfo(e.code);
            e.info = rawInfo.result;
        });
        promises = promises.concat(this.discu.map(async e => {
            let rawInfo = await this.getDiscuInfo(e.did);
            e.info = rawInfo.result;
        }));
        manyInfo = await Promise.all(promises);
        log.debug(JSON.stringify(manyInfo, null, 4));
        log.info('信息初始化完成');
    }

    getBuddyName(uin) {
        let name = this.buddyNameMap.get(uin);
        if (name) return name;
        this.buddy.marknames.some(e => e.uin == uin ? name = e.markname : false);
        if (!name) this.buddy.info.some(e => e.uin == uin ? name = e.nick : false);
        this.buddyNameMap.set(uin, name);
        return name;
    }

    getDiscuName(did) {
        let name = this.discuNameMap.get(did);
        if (name) return name;
        this.discu.some(e => e.did == did ? name = e.name : false);
        this.discuNameMap.set(did, name);
        return name;
    }

    getDiscuInfo(did) {
        return this.client.get({
            url: URL.discuInfo(did, this.tokens.psessionid, this.tokens.vfwebqq),
            headers: { Referer: URL.referer151105 }
        });
    }

    getNameInDiscu(uin, did) {
        let nameKey = `${did}${uin}`;
        let name = this.discuNameMap.get(nameKey);
        if (name) return name;
        let discu;
        for (let d of this.discu) {
            if (d.did == did) {
                discu = d;
                break;
            }
        }
        discu.info.mem_info.some(i => i.uin == uin ? name = i.nick : false);
        this.discuNameMap.set(nameKey, name);
        return name;
    }

    getGroupName(groupCode) {
        let name = this.groupNameMap.get(groupCode);
        if (name) return name;
        this.group.some(e => e.code == groupCode ? name = e.name : false);
        this.groupNameMap.set(groupCode, name);
        return name;
    }

    getGroupInfo(code) {
        return this.client.get({
            url: URL.groupInfo(code, this.tokens.vfwebqq),
            headers: { Referer: URL.referer130916 }
        });
    }

    getNameInGroup(uin, groupCode) {
        let nameKey = `${groupCode}${uin}`;
        let name = this.groupNameMap.get(nameKey);
        if (name) return name;
        let group;
        for (let g of this.group) {
            if (g.code == groupCode) {
                group = g;
                break;
            }
        }
        group.info.cards.some(i => i.muin == uin ? name = i.card : false);
        if (!name) group.info.minfo.some(i => i.uin == uin ? name = i.nick : false);
        this.groupNameMap.set(nameKey, name);
        return name;
    }

    logMessage(msg) {
        if (typeof msg === 'string') return;
        let content = msg.result[0].value.content.filter(e => typeof e == 'string').join(' ');
        let { from_uin, send_uin } = msg.result[0].value;
        switch (msg.result[0].poll_type) {
            case 'message':
                log.info(`[新消息] ${this.getBuddyName(from_uin)} | ${content}`);
                break;
            case 'group_message':
                log.info(`[群消息] ${this.getGroupName(from_uin)} : ${this.getNameInGroup(send_uin, from_uin)} | ${content}`);
                break;
            case 'discu_message':
                log.info(`[讨论组] ${this.getDiscuName(from_uin)} : ${this.getNameInDiscu(send_uin, from_uin)} | ${content}`);
        }
    }

    async loopPoll() {
        log.info('开始接收消息...');
        do {
            let msgContent = await this.client.post({
                url: URL.poll,
                data: {
                    ptwebqq: this.tokens.ptwebqq,
                    clientid: AppConfig.clientid,
                    psessionid: this.tokens.psessionid,
                    key: ''
                },
                headers: {
                    Origin: URL.msgOrigin,
                    Referer: URL.referer151105
                },
                responseType: 'text',
                validateStatus: status => status === 200 || status === 504
            });
            log.debug(msgContent);
            try {
                this.logMessage(msgContent);
            } catch (error) {
                log.error(error);
            }
        } while (true);
    }
}

module.exports = QQ;<commit_msg>big improvement<commit_after>'use strict';

const fs = require('fs');

const URL = require('./url');
const Codec = require('../codec');
const Client = require('../httpclient');
const MessageAgent = require('./message-agent');

const AppConfig = {
    clientid: 53999199,
    appid: 501004106
};

function writeFileAsync(filePath, data, options) {
    return new Promise((resolve, reject) => {
        fs.writeFile(filePath, data, options, error => {
            if (error) reject(error);
            resolve();
        });
    });
}

class QQ {
    constructor(...msgHandlers) {
        this.tokens = {
            uin: '',
            ptwebqq: '',
            vfwebqq: '',
            psessionid: ''
        };
        this.selfInfo = {};
        this.buddy = {};
        this.discu = {};
        this.group = {};
        this.buddyNameMap = new Map();
        this.discuNameMap = new Map();
        this.groupNameMap = new Map();
        this.client = new Client();
        this.messageAgent = null;
        this.msgHandlers = msgHandlers;
    }

    async run() {
        await this.login();
        await this.initInfo();
        await this.loopPoll();
    }

    async login() {
        log.info('(0/5) 开始登录，准备下载二维码');

        // Step0: prepare cookies, pgv_info and pgv_pvid
        // http://pingjs.qq.com/tcss.ping.js  tcss.run & _cookie.init
        let initCookies = {
            pgv_info: `ssid=s${Codec.randPgv()}`,
            pgv_pvid: Codec.randPgv()
        };
        this.client.setCookie(initCookies);
        await this.client.get(URL.loginPrepare);

        // Step1: download QRcode
        let qrCode = await this.client.get({ url: URL.qrcode, responseType: 'arraybuffer' });
        await writeFileAsync('/tmp/code.png', qrCode, 'binary');
        log.info('(1/5) 二维码下载完成，等待扫描');
        // open file, only for linux
        require('child_process').exec('xdg-open /tmp/code.png');

        // Step2:
        let scanSuccess = false;
        let quotRegxp = /'[^,]*'/g;
        let ptqrloginURL = URL.getPtqrloginURL(this.client.getCookie('qrsig'));
        let ptlogin4URL;
        do {
            let responseBody = await this.client.get({
                url: ptqrloginURL,
                headers: { Referer: URL.ptqrloginReferer },
            });
            log.debug(responseBody);
            let arr = responseBody.match(quotRegxp).map(i => i.substring(1, i.length - 1));
            if (arr[0] === '0') {
                scanSuccess = true;
                ptlogin4URL = arr[2];
            }
        } while (!scanSuccess);
        log.info('(2/5) 二维码扫描完成');
        // remove file, for linux(or macOS ?)
        require('child_process').exec('rm /tmp/code.png');

        // Step3: find token 'vfwebqq' in cookie
        // NOTICE: the request returns 302 when success. DO NOT REJECT 302.
        await this.client.get({
            url: ptlogin4URL,
            maxRedirects: 0,     // Axios follows redirect automatically, but we need to disable it here.
            validateStatus: status => status === 302,
            headers: { Referer: URL.ptlogin4Referer }
        });
        this.tokens.ptwebqq = this.client.getCookie('ptwebqq');
        log.info('(3/5) 获取 ptwebqq 成功');

        // Step4: request token 'vfwebqq'
        let vfwebqqResp = await this.client.get({
            url: URL.getVfwebqqURL(this.tokens.ptwebqq),
            headers: { Referer: URL.vfwebqqReferer }
        });
        log.debug(vfwebqqResp);
        this.tokens.vfwebqq = vfwebqqResp.result.vfwebqq;
        log.info('(4/5) 获取 vfwebqq 成功');

        // Step5: psessionid and uin
        let loginStat = await this.client.post({
            url: URL.login2,
            data: {
                ptwebqq: this.tokens.ptwebqq,
                clientid: AppConfig.clientid,
                psessionid: "",
                status: "online",
            },
            headers: {
                Origin: URL.login2Origin,
                Referer: URL.login2Referer
            }
        });
        log.debug(loginStat);
        this.tokens.uin = loginStat.result.uin;
        this.tokens.psessionid = loginStat.result.psessionid;
        this.messageAgent = new MessageAgent({
            psessionid: this.tokens.psessionid
        });
        log.info('(5/5) 获取 psessionid 和 uin 成功');
    }

    getSelfInfo() {
        log.info('开始获取用户信息');
        return this.client.get({
            url: URL.selfInfo,
            headers: { Referer: URL.referer130916 }
        });
    }

    getBuddy() {
        log.info('开始获取好友列表');
        return this.client.post({
            url: URL.getBuddy,
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    getOnlineBuddies() {
        log.info('开始获取好友在线状态');
        return this.client.get({
            url: URL.onlineBuddies(this.tokens.vfwebqq, this.tokens.psessionid),
            headers: {
                Referer: URL.referer151105
            }
        });
    }

    getGroup() {
        log.info('开始获取群列表');
        return this.client.post({
            url: URL.getGroup,
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    getDiscu() {
        log.info('开始获取讨论组列表');
        return this.client.post({
            url: URL.getDiscu(this.tokens.vfwebqq, this.tokens.psessionid),
            headers: {
                Referer: URL.referer130916
            },
            data: {
                vfwebqq: this.tokens.vfwebqq,
                hash: Codec.hashU(this.tokens.uin, this.tokens.ptwebqq)
            }
        });
    }

    async initInfo() {
        let manyInfo = await Promise.all([
            this.getSelfInfo(),
            this.getBuddy(),
            this.getOnlineBuddies(),
            this.getDiscu(),
            this.getGroup()
        ]);
        log.debug(JSON.stringify(manyInfo, null, 4));
        this.selfInfo = manyInfo[0].result;
        this.buddy = manyInfo[1].result;
        this.discu = manyInfo[3].result.dnamelist;
        this.group = manyInfo[4].result.gnamelist;
        let promises = this.group.map(async e => {
            let rawInfo = await this.getGroupInfo(e.code);
            e.info = rawInfo.result;
        });
        promises = promises.concat(this.discu.map(async e => {
            let rawInfo = await this.getDiscuInfo(e.did);
            e.info = rawInfo.result;
        }));
        manyInfo = await Promise.all(promises);
        log.debug(JSON.stringify(manyInfo, null, 4));
        log.info('信息初始化完成');
    }

    getBuddyName(uin) {
        let name = this.buddyNameMap.get(uin);
        if (name) return name;
        this.buddy.marknames.some(e => e.uin == uin ? name = e.markname : false);
        if (!name) this.buddy.info.some(e => e.uin == uin ? name = e.nick : false);
        this.buddyNameMap.set(uin, name);
        return name;
    }

    getDiscuName(did) {
        let name = this.discuNameMap.get(did);
        if (name) return name;
        this.discu.some(e => e.did == did ? name = e.name : false);
        this.discuNameMap.set(did, name);
        return name;
    }

    getDiscuInfo(did) {
        return this.client.get({
            url: URL.discuInfo(did, this.tokens.psessionid, this.tokens.vfwebqq),
            headers: { Referer: URL.referer151105 }
        });
    }

    getNameInDiscu(uin, did) {
        let nameKey = `${did}${uin}`;
        let name = this.discuNameMap.get(nameKey);
        if (name) return name;
        let discu;
        for (let d of this.discu) {
            if (d.did == did) {
                discu = d;
                break;
            }
        }
        discu.info.mem_info.some(i => i.uin == uin ? name = i.nick : false);
        this.discuNameMap.set(nameKey, name);
        return name;
    }

    getGroupName(groupCode) {
        let name = this.groupNameMap.get(groupCode);
        if (name) return name;
        this.group.some(e => e.gid == groupCode ? name = e.name : false);
        this.groupNameMap.set(groupCode, name);
        return name;
    }

    getGroupInfo(code) {
        return this.client.get({
            url: URL.groupInfo(code, this.tokens.vfwebqq),
            headers: { Referer: URL.referer130916 }
        });
    }

    getNameInGroup(uin, groupCode) {
        let nameKey = `${groupCode}${uin}`;
        let name = this.groupNameMap.get(nameKey);
        if (name) return name;
        let group;
        for (let g of this.group) {
            if (g.gid == groupCode) {
                group = g;
                break;
            }
        }
        group.info.cards.some(i => i.muin == uin ? name = i.card : false);
        if (!name) group.info.minfo.some(i => i.uin == uin ? name = i.nick : false);
        this.groupNameMap.set(nameKey, name);
        return name;
    }

    logMessage(msg) {
        if (typeof msg === 'string') return;
        let content = msg.result[0].value.content.filter(e => typeof e == 'string').join(' ');
        let { from_uin, send_uin } = msg.result[0].value;
        switch (msg.result[0].poll_type) {
            case 'message':
                log.info(`[新消息] ${this.getBuddyName(from_uin)} | ${content}`);
                break;
            case 'group_message':
                log.info(`[群消息] ${this.getGroupName(from_uin)} : ${this.getNameInGroup(send_uin, from_uin)} | ${content}`);
                break;
            case 'discu_message':
                log.info(`[讨论组] ${this.getDiscuName(from_uin)} : ${this.getNameInDiscu(send_uin, from_uin)} | ${content}`);
        }
    }

    handelMsgRecv(msg) {
        if (typeof msg === 'string') return;
        let content = msg.result[0].value.content.filter(e => typeof e == 'string').join(' ');
        let { from_uin, send_uin } = msg.result[0].value;
        let msgParsed = { content };
        switch (msg.result[0].poll_type) {
            case 'message':
                msgParsed.type = 'buddy';
                msgParsed.id = from_uin;
                msgParsed.name = this.getBuddyName(from_uin);
                break;
            case 'group_message':
                msgParsed.type = 'group';
                msgParsed.id = send_uin;
                msgParsed.name = this.getNameInGroup(send_uin, from_uin);
                msgParsed.groupId = from_uin;
                msgParsed.groupName = this.getGroupName(from_uin);
                break;
            case 'discu_message':
                msgParsed.type = 'discu';
                msgParsed.id = send_uin;
                msgParsed.name = this.getNameInDiscu(send_uin, from_uin);
                msgParsed.discuId = from_uin;
                msgParsed.discuName = this.getDiscuName(from_uin);
        }
        this.msgHandlers.forEach(handler => handler.tryHandle(msgParsed, this));
    }

    async loopPoll() {
        log.info('开始接收消息...');
        do {
            let msgContent = await this.client.post({
                url: URL.poll,
                data: {
                    ptwebqq: this.tokens.ptwebqq,
                    clientid: AppConfig.clientid,
                    psessionid: this.tokens.psessionid,
                    key: ''
                },
                headers: {
                    Origin: URL.msgOrigin,
                    Referer: URL.referer151105
                },
                responseType: 'text',
                validateStatus: status => status === 200 || status === 504
            });
            log.debug(msgContent);
            try {
                this.logMessage(msgContent);
                this.handelMsgRecv(msgContent);
            } catch (error) {
                log.error(error);
            }
        } while (true);
    }

    async innerSendMsg(url, key, id, content) {
        const resp = await this.client.post({
            url,
            data: this.messageAgent.build(key, id, content),
            headers: { Referer: URL.referer151105 }
        });
        log.debug(resp);
        return resp;
    }

    async sendBuddyMsg(uin, content) {
        const resp = await this.innerSendMsg(URL.buddyMsg, 'buddy', uin, content);
        if (resp.errCode === 0) {
            log.info(`发消息给好友 ${this.getBuddyName(uin)} : ${content}`);
        }
    }

    async sendDiscuMsg(did, content) {
        const resp = await this.innerSendMsg(URL.discuMsg, 'discu', did, content);
        if (resp.errCode === 0) {
            log.info(`发消息给讨论组 ${this.getDiscuName(did)} : ${content}`);
        }
    }

    async sendGroupMsg(gid, content) {
        const resp = await this.innerSendMsg(URL.groupMsg, 'group', gid, content);
        if (resp.errCode === 0) {
            log.info(`发消息给群 ${this.getGroupName(gid)} : ${content}`);
        }
    }
}

module.exports = QQ;<|endoftext|>


<commit_before>'use strict'
/**
 * Created by tungtouch on 2/9/15.
 */

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

var dgram = require('dgram');
var message = new Buffer("Some bytes hello world bo bo bo world HEHEHEHE ahhaha hohoho hehe:");
var client = dgram.createSocket("udp4");
var async = require('async');

var max = 1000;
var numCluster = 100;
var arr = [];

for (var i = 0; i < max; i++) {
    arr.push(i);
}
var a = 0;

//[2GB]128.199.126.250 [8GB]128.199.109.202

var q = async.queue(function(index, cb){
    setTimeout(function () {
        client.send(message, 0, message.length, 4444, "128.199.126.250", function (err) {
            console.log("Request : ", a++);
            if(err){
                console.log('ERROR :', err);
            }
            cb(err);
        });
    }, 1);
});

if (cluster.isMaster) {
    // Fork workers.
    for (var m = 0; m < numCPUs; m++) {
        console.log("Starting Benchmark!", m);
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else if (cluster.isWorker) {
    console.log('I am worker #' + cluster.worker.id);
} else {

    // Workers can share any TCP connection
    // In this case its a HTTP server
    q.push(arr);
}
cluster.on('online', function(worker) {
    console.log("Yay, the worker responded after it was forked");
});

<commit_msg>Update UDPefef<commit_after>'use strict'
/**
 * Created by tungtouch on 2/9/15.
 */

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

var dgram = require('dgram');
var message = new Buffer("Some bytes hello world bo bo bo world HEHEHEHE ahhaha hohoho hehe:");
var client = dgram.createSocket("udp4");
var async = require('async');

var max = 1000;
var numCluster = 100;
var arr = [];

for (var i = 0; i < max; i++) {
    arr.push(i);
}
var a = 0;

//[2GB]128.199.126.250 [8GB]128.199.109.202

var q = async.queue(function(index, cb){
    setTimeout(function () {
        client.send(message, 0, message.length, 4444, "128.199.126.250", function (err) {
            console.log("Request : ", a++);
            if(err){
                console.log('ERROR :', err);
            }
            cb(err);
        });
    }, 1);
});

if (cluster.isMaster) {
    // Fork workers.
    for (var m = 0; m < numCPUs; m++) {
        console.log("Starting Benchmark!", m);
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else if (cluster.isWorker) {
    console.log('I am worker #' + cluster.worker.id);
    q.push(arr);
} else {

    // Workers can share any TCP connection
    // In this case its a HTTP server

}
cluster.on('online', function(worker) {
    console.log("Yay, the worker responded after it was forked");
});

<|endoftext|>


<commit_before><commit_msg>Transition animation utility for examples<commit_after><|endoftext|>


<commit_before>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

var validation_count = 0; // helper for acceptance testing of validation logic

/**
 * Generate the HTML for the form on the Modal
 */
function transModalDivForm() {
    return new FormBuilder('transForm')
        .addHidden('trans_frm_id', 'id', '')
        .addDatePicker('trans_frm_date', 'date', 'Date')
        .addCurrency('trans_frm_amount', 'amount', 'Amount', { helpBlock: 'Transaction amount (positive for expenses, negative for income).' })
        .addText('trans_frm_description', 'description', 'Description')
        .addLabelToValueSelect('trans_frm_account', 'account', 'Account', acct_names_to_id, 'None', true)
        .addCheckbox(
            'trans_frm_is_split', 'is_split', 'Budget Split?', false,
            { inputHtml: 'onchange="transModalHandleSplit()"' }
        )
        .addHTML('

')
        .addLabelToValueSelect('trans_frm_budget', 'budget', 'Budget', budget_names_to_id, 'None', true)
        .addHTML(
            '
' +
            '
Budgets
' +
            '
' +
            transModalBudgetSplitRowHtml(0) +
            transModalBudgetSplitRowHtml(1) +
            '
' +
            '

' +
            '
Add Budget
' +
            '
'
        )
        .addText('trans_frm_notes', 'notes', 'Notes')
        .addHTML('

')
        .render();
}

/**
 * Ajax callback to fill in the modalDiv with data on a Transaction.
 */
function transModalDivFillAndShow(msg) {
    $('#modalLabel').text('Edit Transaction ' + msg['id']);
    $('#trans_frm_id').val(msg['id']);
    $('#trans_frm_description').val(msg['description']);
    $('#trans_frm_date').val(msg['date']['str']);
    $('#trans_frm_amount').val(msg['actual_amount']);
    $('#trans_frm_account option[value=' + msg['account_id'] + ']').prop('selected', 'selected').change();
    $('#trans_frm_notes').val(msg['notes']);
    if(msg['transfer_id'] !== null) {
      $('#trans_frm_transfer_p').html(
        'This transaction is one half of a transfer, along with Transaction ' + msg['transfer_id'] + '.'
      );
      $('#trans_frm_transfer_p').show();
    }
    if(msg['budgets'].length == 1) {
        $('#trans_frm_budget option[value=' + msg['budgets'][0]['id'] + ']').prop('selected', 'selected').change();
    } else {
        $('#trans_frm_is_split').prop('checked', true);
        $('#trans_frm_budget_group').hide();
        $('#trans_frm_split_budget_container').show();
        for(var i = 0; i < msg['budgets'].length; i++) {
            if(i > 1) { $('#trans_frm_budget_splits_div').append(transModalBudgetSplitRowHtml(i)); }
            var budg = msg['budgets'][i];
            $('#trans_frm_budget_' + i + ' option[value=' + budg['id'] + ']').prop('selected', 'selected').change();
            $('#trans_frm_budget_amount_' + i).val(budg['amount']);
        }
    }
    $("#modalDiv").modal('show');
}

/**
 * Show the Transaction modal popup, optionally populated with
 * information for one Transaction. This function calls
 * :js:func:`transModalDivForm` to generate the form HTML,
 * :js:func:`transModalDivFillAndShow` to populate the form for editing,
 * and :js:func:`handleForm` to handle the Submit action (using
 * :js:func:`transModalFormSerialize` as a custom serialization function).
 *
 * @param {number} id - the ID of the Transaction to show a modal for,
 *   or null to show modal to add a new Transaction.
 * @param {(Object|null)} dataTableObj - passed on to :js:func:`handleForm`
 */
function transModal(id, dataTableObj) {
    $('#modalBody').empty();
    $('#modalBody').append(transModalDivForm());
    $('#trans_frm_date').val(isoformat(BIWEEKLYBUDGET_DEFAULT_DATE));
    $('#trans_frm_date_input_group').datepicker({
        todayBtn: "linked",
        autoclose: true,
        todayHighlight: true,
        format: 'yyyy-mm-dd'
    });
    $('#modalSaveButton').off();
    $('#modalSaveButton').click(function() {
        var valid = validateTransModalSplits();
        if (valid == null) {
            $('#modalSaveButton').prop('disabled', false);
            $('#budget-split-feedback').html('');
            handleForm('modalBody', 'transForm', '/forms/transaction', dataTableObj, transModalFormSerialize);
        } else {
            $('#budget-split-feedback').html('
' + valid + '
');
            $('#modalSaveButton').prop('disabled', true);
        }
    }).show();
    if(id) {
        var url = "/ajax/transactions/" + id;
        $.ajax(url).done(transModalDivFillAndShow);
    } else {
        $('#modalLabel').text('Add New Transaction');
        $('#trans_frm_account option[value=' + default_account_id + ']').prop('selected', 'selected').change();
        $("#modalDiv").modal('show');
    }
}

/**
 * Custom serialization function passed to :js:func:`handleForm` for
 * Transaction modal forms generated by :js:func:`transModal`. This handles
 * serialization of Transaction forms that may have a budget split, generating
 * data with a ``budgets`` Object (hash/mapping/dict) with budget ID keys and
 * amount values, suitable for passing directly to
 * :py:meth:`~.Transaction.set_budget_amounts`.
 *
 * @param {String} form_id the ID of the form on the page.
 */
function transModalFormSerialize(form_id) {
    var data = serializeForm(form_id);
    data['budgets'] = {};
    var num_rows = $('.budget_split_row').length;
    var rownum = 0;
    if($('#trans_frm_is_split').prop('checked')) {
        for (rownum = 0; rownum < num_rows; rownum++) {
            var bid = $('#trans_frm_budget_' + rownum).find(':selected').val();
            if(bid != 'None') {
                data['budgets'][bid] = $('#trans_frm_budget_amount_' + rownum).val();
            }
        }
    } else {
        data['budgets'][data['budget']] = data['amount'];
    }
    // strip out the budget_ and amount_ items
    delete data['budget'];
    delete data['is_split'];
    for (rownum = 0; rownum < num_rows; rownum++) {
        delete data['budget_' + rownum];
        delete data['amount_' + rownum];
    }
    return data;
}

/**
 * Handler for change of the "Budget Split?" (``#trans_frm_is_split``) checkbox.
 */
function transModalHandleSplit() {
    if($('#trans_frm_is_split').prop('checked')) {
        // split
        $('#trans_frm_budget_group').hide();
        $('#trans_frm_split_budget_container').show();
        var oldid = $('#trans_frm_budget').find(':selected').val();
        if(oldid != 'None') { $('#trans_frm_budget_0 option[value=' + oldid + ']').prop('selected', 'selected').change(); }
    } else {
        // not split
        $('#trans_frm_split_budget_container').hide();
        $('#trans_frm_budget_group').show();
    }
}

/**
 * Function to validate Transaction modal split budgets. Returns null if valid
 * or otherwise a String error message.
 */
function validateTransModalSplits() {
    if(! $('#trans_frm_is_split').prop('checked')) { return null; }
    var budget_ids = [];
    var total = 0.0;
    for (rownum = 0; rownum < $('.budget_split_row').length; rownum++) {
        var bid = $('#trans_frm_budget_' + rownum).find(':selected').val();
        if(bid != 'None') {
            if(budget_ids.indexOf(bid) > -1) {
                return 'Error: A given budget may only be specified once.';
            }
            budget_ids.push(bid);
        }
        if($('#trans_frm_budget_amount_' + rownum).val() != '') {
            total = total + parseFloat($('#trans_frm_budget_amount_' + rownum).val());
        }
    }
    var amt = parseFloat($('#trans_frm_amount').val());
    // Note: workaround for JS floating point math issues...
    if(amt.toFixed(4) != total.toFixed(4)) {
        return 'Error: Sum of budget allocations (' + total.toFixed(4) + ') must equal transaction amount (' + amt.toFixed(4) + ').';
    }
    return null;
}

/**
 * Handler for the "Add Budget" link on trans modal when using budget split.
 */
function transModalAddSplitBudget() {
    var next_row_num = $('.budget_split_row').length;
    $('#trans_frm_budget_splits_div').append(transModalBudgetSplitRowHtml(next_row_num));
    transModalSplitBudgetChanged(next_row_num);
}

/**
 * Triggered when a form element for budget splits loses focus. Calls
 * :js:func:`validateTransModalSplits` and updates the warning div with the
 * result.
 */
function budgetSplitBlur() {
    var res = validateTransModalSplits();
    if (res == null) {
        $('#budget-split-feedback').html('');
        $('#modalSaveButton').prop('disabled', false);
        validation_count = validation_count + 1;
        return null;
    }
    $('#budget-split-feedback').html('
' + res + '
');
    $('#modalSaveButton').prop('disabled', true);
    validation_count = validation_count + 1;
}

/**
 * Generate HTML for a budget div inside the split budgets div.
 *
 * @param {Integer} row_num - the budget split row number
 */
function transModalBudgetSplitRowHtml(row_num) {
    var html = '
';
    // budget select
    html += '
';
    html += '<select id="trans_frm_budget_' + row_num + '" name="budget_' + row_num + '" class="form-control" onblur="budgetSplitBlur()" onchange="transModalSplitBudgetChanged(' + row_num + ')">';
    html += '<option value="None"></option>';
    Object.keys(budget_names_to_id).forEach(function (key) {
        html += '<option value="' + budget_names_to_id[key] + '">' + key + '</option>'
    });
    html += '</select>';
    html += '
'; // .form-group #trans_frm_budget_group
    // amount
    html += '
';
    html += '
';
    html += '$';
    html += '<input class="form-control" id="trans_frm_budget_amount_' + row_num + '" name="amount_' + row_num + '" type="text" onblur="budgetSplitBlur()">';
    html += '
'; // .input-group
    html += '
'; // .form-group #trans_frm_budget_amount_group
    // close overall div
    html += '
'; // .budget_split_row
    return html;
}

/**
 * Called when a budget split dropdown is changed. If its amount box is empty,
 * set it to the transaction amount minus the sum of all other budget splits.
 *
 * @param {Integer} row_num - the budget split row number
 */
function transModalSplitBudgetChanged(row_num) {
    if($('#trans_frm_budget_amount_' + row_num).val() != '') { return null; }
    var amt = parseFloat($('#trans_frm_amount').val());
    var total = 0.0;
    for (var rownum = 0; rownum < $('.budget_split_row').length; rownum++) {
        if($('#trans_frm_budget_amount_' + rownum).val() != '') {
            total = total + parseFloat($('#trans_frm_budget_amount_' + rownum).val());
        }
    }
    var remainder = amt - total;
    if(remainder > 0) {
        $('#trans_frm_budget_amount_' + row_num).val(remainder.toFixed(2));
    }
}
<commit_msg>issue #196 - refactor JS to make requirements of this issue easier to implement<commit_after>/*
The latest version of this package is available at:
<http://github.com/jantman/biweeklybudget>

################################################################################
Copyright 2016 Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>

    This file is part of biweeklybudget, also known as biweeklybudget.

    biweeklybudget is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    biweeklybudget is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with biweeklybudget.  If not, see <http://www.gnu.org/licenses/>.

The Copyright and Authors attributions contained herein may not be removed or
otherwise altered, except to add the Author attribution of a contributor to
this work. (Additional Terms pursuant to Section 7b of the AGPL v3)
################################################################################
While not legally required, I sincerely request that anyone who finds
bugs please submit them at <https://github.com/jantman/biweeklybudget> or
to me via email, and that you send any contributions or improvements
either as a pull request on GitHub, or to me via email.
################################################################################

AUTHORS:
Jason Antman <jason@jasonantman.com> <http://www.jasonantman.com>
################################################################################
*/

var validation_count = 0; // helper for acceptance testing of validation logic

/**
 * Generate the HTML for the form on the Modal
 */
function transModalDivForm() {
    return new FormBuilder('transForm')
        .addHidden('trans_frm_id', 'id', '')
        .addDatePicker('trans_frm_date', 'date', 'Date')
        .addCurrency('trans_frm_amount', 'amount', 'Amount', { helpBlock: 'Transaction amount (positive for expenses, negative for income).' })
        .addText('trans_frm_description', 'description', 'Description')
        .addLabelToValueSelect('trans_frm_account', 'account', 'Account', acct_names_to_id, 'None', true)
        .addCheckbox(
            'trans_frm_is_split', 'is_split', 'Budget Split?', false,
            { inputHtml: 'onchange="transModalHandleSplit()"' }
        )
        .addHTML('

')
        .addLabelToValueSelect('trans_frm_budget', 'budget', 'Budget', budget_names_to_id, 'None', true)
        .addHTML(
            '
' +
            '
Budgets
' +
            '
' +
            transModalBudgetSplitRowHtml(0) +
            transModalBudgetSplitRowHtml(1) +
            '
' +
            '

' +
            '
Add Budget
' +
            '
'
        )
        .addText('trans_frm_notes', 'notes', 'Notes')
        .addHTML('

')
        .render();
}

/**
 * Ajax callback to fill in the modalDiv with data on a Transaction.
 */
function transModalDivFillAndShow(msg) {
    $('#modalLabel').text('Edit Transaction ' + msg['id']);
    $('#trans_frm_id').val(msg['id']);
    $('#trans_frm_description').val(msg['description']);
    $('#trans_frm_date').val(msg['date']['str']);
    $('#trans_frm_amount').val(msg['actual_amount']);
    $('#trans_frm_account option[value=' + msg['account_id'] + ']').prop('selected', 'selected').change();
    $('#trans_frm_notes').val(msg['notes']);
    if(msg['transfer_id'] !== null) {
      $('#trans_frm_transfer_p').html(
        'This transaction is one half of a transfer, along with Transaction ' + msg['transfer_id'] + '.'
      );
      $('#trans_frm_transfer_p').show();
    }
    if(msg['budgets'].length == 1) {
        selectBudget(null, msg['budgets'][0]['id']);
    } else {
        $('#trans_frm_is_split').prop('checked', true);
        $('#trans_frm_budget_group').hide();
        $('#trans_frm_split_budget_container').show();
        for(var i = 0; i < msg['budgets'].length; i++) {
            if(i > 1) { $('#trans_frm_budget_splits_div').append(transModalBudgetSplitRowHtml(i)); }
            var budg = msg['budgets'][i];
            selectBudget(i, budg['id']);
            $('#trans_frm_budget_amount_' + i).val(budg['amount']);
        }
    }
    $("#modalDiv").modal('show');
}

/**
 * Select a budget in a budget select element. If ``sel_num`` is null then
 * select in ``#trans_frm_budget``, else it is expected to be an integer
 * and the selection will be made in ``trans_frm_budget_<sel_num>``.
 *
 * @param {(number|null)} sel_num - The ``trans_frm_budget_`` Select element
 *   suffix, or else null for the ``trans_frm_budget`` select.
 * @param {number} budg_id - The ID of the budget to select.
 */
function selectBudget(sel_num, budg_id) {
    if(sel_num == null) {
        $('#trans_frm_budget option[value=' + budg_id + ']').prop('selected', 'selected').change();
    } else {
        $('#trans_frm_budget_' + sel_num + ' option[value=' + budg_id + ']').prop('selected', 'selected').change();
    }
}

/**
 * Show the Transaction modal popup, optionally populated with
 * information for one Transaction. This function calls
 * :js:func:`transModalDivForm` to generate the form HTML,
 * :js:func:`transModalDivFillAndShow` to populate the form for editing,
 * and :js:func:`handleForm` to handle the Submit action (using
 * :js:func:`transModalFormSerialize` as a custom serialization function).
 *
 * @param {number} id - the ID of the Transaction to show a modal for,
 *   or null to show modal to add a new Transaction.
 * @param {(Object|null)} dataTableObj - passed on to :js:func:`handleForm`
 */
function transModal(id, dataTableObj) {
    $('#modalBody').empty();
    $('#modalBody').append(transModalDivForm());
    $('#trans_frm_date').val(isoformat(BIWEEKLYBUDGET_DEFAULT_DATE));
    $('#trans_frm_date_input_group').datepicker({
        todayBtn: "linked",
        autoclose: true,
        todayHighlight: true,
        format: 'yyyy-mm-dd'
    });
    $('#modalSaveButton').off();
    $('#modalSaveButton').click(function() {
        var valid = validateTransModalSplits();
        if (valid == null) {
            $('#modalSaveButton').prop('disabled', false);
            $('#budget-split-feedback').html('');
            handleForm('modalBody', 'transForm', '/forms/transaction', dataTableObj, transModalFormSerialize);
        } else {
            $('#budget-split-feedback').html('
' + valid + '
');
            $('#modalSaveButton').prop('disabled', true);
        }
    }).show();
    if(id) {
        var url = "/ajax/transactions/" + id;
        $.ajax(url).done(transModalDivFillAndShow);
    } else {
        $('#modalLabel').text('Add New Transaction');
        $('#trans_frm_account option[value=' + default_account_id + ']').prop('selected', 'selected').change();
        $("#modalDiv").modal('show');
    }
}

/**
 * Custom serialization function passed to :js:func:`handleForm` for
 * Transaction modal forms generated by :js:func:`transModal`. This handles
 * serialization of Transaction forms that may have a budget split, generating
 * data with a ``budgets`` Object (hash/mapping/dict) with budget ID keys and
 * amount values, suitable for passing directly to
 * :py:meth:`~.Transaction.set_budget_amounts`.
 *
 * @param {String} form_id the ID of the form on the page.
 */
function transModalFormSerialize(form_id) {
    var data = serializeForm(form_id);
    data['budgets'] = {};
    var num_rows = $('.budget_split_row').length;
    var rownum = 0;
    if($('#trans_frm_is_split').prop('checked')) {
        for (rownum = 0; rownum < num_rows; rownum++) {
            var bid = $('#trans_frm_budget_' + rownum).find(':selected').val();
            if(bid != 'None') {
                data['budgets'][bid] = $('#trans_frm_budget_amount_' + rownum).val();
            }
        }
    } else {
        data['budgets'][data['budget']] = data['amount'];
    }
    // strip out the budget_ and amount_ items
    delete data['budget'];
    delete data['is_split'];
    for (rownum = 0; rownum < num_rows; rownum++) {
        delete data['budget_' + rownum];
        delete data['amount_' + rownum];
    }
    return data;
}

/**
 * Handler for change of the "Budget Split?" (``#trans_frm_is_split``) checkbox.
 */
function transModalHandleSplit() {
    if($('#trans_frm_is_split').prop('checked')) {
        // split
        $('#trans_frm_budget_group').hide();
        $('#trans_frm_split_budget_container').show();
        var oldid = $('#trans_frm_budget').find(':selected').val();
        if(oldid != 'None') { $('#trans_frm_budget_0 option[value=' + oldid + ']').prop('selected', 'selected').change(); }
    } else {
        // not split
        $('#trans_frm_split_budget_container').hide();
        $('#trans_frm_budget_group').show();
    }
}

/**
 * Function to validate Transaction modal split budgets. Returns null if valid
 * or otherwise a String error message.
 */
function validateTransModalSplits() {
    if(! $('#trans_frm_is_split').prop('checked')) { return null; }
    var budget_ids = [];
    var total = 0.0;
    for (rownum = 0; rownum < $('.budget_split_row').length; rownum++) {
        var bid = $('#trans_frm_budget_' + rownum).find(':selected').val();
        if(bid != 'None') {
            if(budget_ids.indexOf(bid) > -1) {
                return 'Error: A given budget may only be specified once.';
            }
            budget_ids.push(bid);
        }
        if($('#trans_frm_budget_amount_' + rownum).val() != '') {
            total = total + parseFloat($('#trans_frm_budget_amount_' + rownum).val());
        }
    }
    var amt = parseFloat($('#trans_frm_amount').val());
    // Note: workaround for JS floating point math issues...
    if(amt.toFixed(4) != total.toFixed(4)) {
        return 'Error: Sum of budget allocations (' + total.toFixed(4) + ') must equal transaction amount (' + amt.toFixed(4) + ').';
    }
    return null;
}

/**
 * Handler for the "Add Budget" link on trans modal when using budget split.
 */
function transModalAddSplitBudget() {
    var next_row_num = $('.budget_split_row').length;
    $('#trans_frm_budget_splits_div').append(transModalBudgetSplitRowHtml(next_row_num));
    transModalSplitBudgetChanged(next_row_num);
}

/**
 * Triggered when a form element for budget splits loses focus. Calls
 * :js:func:`validateTransModalSplits` and updates the warning div with the
 * result.
 */
function budgetSplitBlur() {
    var res = validateTransModalSplits();
    if (res == null) {
        $('#budget-split-feedback').html('');
        $('#modalSaveButton').prop('disabled', false);
        validation_count = validation_count + 1;
        return null;
    }
    $('#budget-split-feedback').html('
' + res + '
');
    $('#modalSaveButton').prop('disabled', true);
    validation_count = validation_count + 1;
}

/**
 * Generate HTML for a budget div inside the split budgets div.
 *
 * @param {Integer} row_num - the budget split row number
 */
function transModalBudgetSplitRowHtml(row_num) {
    var html = '
';
    // budget select
    html += '
';
    html += '<select id="trans_frm_budget_' + row_num + '" name="budget_' + row_num + '" class="form-control" onblur="budgetSplitBlur()" onchange="transModalSplitBudgetChanged(' + row_num + ')">';
    html += '<option value="None"></option>';
    Object.keys(budget_names_to_id).forEach(function (key) {
        html += '<option value="' + budget_names_to_id[key] + '">' + key + '</option>'
    });
    html += '</select>';
    html += '
'; // .form-group #trans_frm_budget_group
    // amount
    html += '
';
    html += '
';
    html += '$';
    html += '<input class="form-control" id="trans_frm_budget_amount_' + row_num + '" name="amount_' + row_num + '" type="text" onblur="budgetSplitBlur()">';
    html += '
'; // .input-group
    html += '
'; // .form-group #trans_frm_budget_amount_group
    // close overall div
    html += '
'; // .budget_split_row
    return html;
}

/**
 * Called when a budget split dropdown is changed. If its amount box is empty,
 * set it to the transaction amount minus the sum of all other budget splits.
 *
 * @param {Integer} row_num - the budget split row number
 */
function transModalSplitBudgetChanged(row_num) {
    if($('#trans_frm_budget_amount_' + row_num).val() != '') { return null; }
    var amt = parseFloat($('#trans_frm_amount').val());
    var total = 0.0;
    for (var rownum = 0; rownum < $('.budget_split_row').length; rownum++) {
        if($('#trans_frm_budget_amount_' + rownum).val() != '') {
            total = total + parseFloat($('#trans_frm_budget_amount_' + rownum).val());
        }
    }
    var remainder = amt - total;
    if(remainder > 0) {
        $('#trans_frm_budget_amount_' + row_num).val(remainder.toFixed(2));
    }
}
<|endoftext|>


<commit_before><commit_msg>Use user type choices for populating choices rather than tying to redundant color dictionary - Use prototype.forEach<commit_after><|endoftext|>


<commit_before>import buildUrl from 'build-url';
import moment from 'moment';

import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { waitForRedirect } from '../helpers/navigation';
import { waitUntilElementFound, elementPresentOnPage, clickButton } from '../helpers/elements-interactions';
import {
  NORMAL_TXN_TYPE,
  INSTALLMENTS_TXN_TYPE,
  TRANSACTION_STATUS,
} from '../constants';
import getAllMonthMoments from '../helpers/dates';
import { fixInstallments, sortTransactionsByDate, filterOldTransactions } from '../helpers/transactions';

const BASE_ACTIONS_URL = 'https://online.max.co.il';
const BASE_API_ACTIONS_URL = 'https://onlinelcapi.max.co.il';
const BASE_WELCOME_URL = 'https://www.max.co.il';
const NORMAL_TYPE_NAME = 'רגילה';
const ATM_TYPE_NAME = 'חיוב עסקות מיידי';
const INTERNET_SHOPPING_TYPE_NAME = 'אינטרנט/חו"ל';
const INSTALLMENTS_TYPE_NAME = 'תשלומים';
const MONTHLY_CHARGE_TYPE_NAME = 'חיוב חודשי';
const ONE_MONTH_POSTPONED_TYPE_NAME = 'דחוי חודש';
const MONTHLY_POSTPONED_TYPE_NAME = 'דחוי לחיוב החודשי';
const THIRTY_DAYS_PLUS_TYPE_NAME = 'עסקת 30 פלוס';
const TWO_MONTHS_POSTPONED_TYPE_NAME = 'דחוי חודשיים';
const MONTHLY_CHARGE_PLUS_INTEREST_TYPE_NAME = 'חודשי + ריבית';
const CREDIT_TYPE_NAME = 'קרדיט';

const INVALID_DETAILS_SELECTOR = '#popupWrongDetails';
const LOGIN_ERROR_SELECTOR = '#popupCardHoldersLoginError';

function redirectOrDialog(page) {
  return Promise.race([
    waitForRedirect(page, 20000, false, [BASE_WELCOME_URL, `${BASE_WELCOME_URL}/`]),
    waitUntilElementFound(page, INVALID_DETAILS_SELECTOR, true),
    waitUntilElementFound(page, LOGIN_ERROR_SELECTOR, true),
  ]);
}

function getTransactionsUrl(monthMoment) {
  const month = monthMoment.month() + 1;
  const year = monthMoment.year();
  const date = `${year}-${month}-01`;

  /**
   * url explanation:
   * userIndex: -1 for all account owners
   * cardIndex: -1 for all cards under the account
   * all other query params are static, beside the date which changes for request per month
   */
  return buildUrl(BASE_API_ACTIONS_URL, {
    path: `/api/registered/transactionDetails/getTransactionsAndGraphs?filterData={"userIndex":-1,"cardIndex":-1,"monthView":true,"date":"${date}","dates":{"startDate":"0","endDate":"0"}}&v=V3.13-HF.6.26`,
  });
}

function getTransactionType(txnTypeStr) {
  const cleanedUpTxnTypeStr = txnTypeStr.replace('\t', ' ').trim();
  switch (cleanedUpTxnTypeStr) {
    case ATM_TYPE_NAME:
    case NORMAL_TYPE_NAME:
    case MONTHLY_CHARGE_TYPE_NAME:
    case ONE_MONTH_POSTPONED_TYPE_NAME:
    case MONTHLY_POSTPONED_TYPE_NAME:
    case THIRTY_DAYS_PLUS_TYPE_NAME:
    case TWO_MONTHS_POSTPONED_TYPE_NAME:
    case INTERNET_SHOPPING_TYPE_NAME:
    case MONTHLY_CHARGE_PLUS_INTEREST_TYPE_NAME:
      return NORMAL_TXN_TYPE;
    case INSTALLMENTS_TYPE_NAME:
    case CREDIT_TYPE_NAME:
      return INSTALLMENTS_TXN_TYPE;
    default:
      throw new Error(`Unknown transaction type ${cleanedUpTxnTypeStr}`);
  }
}

function getInstallmentsInfo(comments) {
  if (!comments) {
    return null;
  }
  const matches = comments.match(/\d+/g);
  if (!matches || matches.length < 2) {
    return null;
  }

  return {
    number: parseInt(matches[0], 10),
    total: parseInt(matches[1], 10),
  };
}

function mapTransaction(rawTransaction) {
  const isPending = rawTransaction.paymentDate === null;
  const processedDate = moment(isPending ?
    rawTransaction.purchaseDate :
    rawTransaction.paymentDate).toISOString();
  const status = isPending ? TRANSACTION_STATUS.PENDING : TRANSACTION_STATUS.COMPLETED;

  return {
    type: getTransactionType(rawTransaction.planName),
    date: moment(rawTransaction.purchaseDate).toISOString(),
    processedDate,
    originalAmount: -rawTransaction.originalAmount,
    originalCurrency: rawTransaction.originalCurrency,
    chargedAmount: -rawTransaction.actualPaymentAmount,
    description: rawTransaction.merchantName.trim(),
    memo: rawTransaction.comments,
    installments: getInstallmentsInfo(rawTransaction.comments),
    status,
  };
}

async function getCurrentTransactions(page) {
  const preHtmlElement = await page.$('pre');

  const strJsonResponse = await page.evaluate((item) => {
    return item.innerText;
  }, preHtmlElement);

  const response = JSON.parse(strJsonResponse);

  const transactionsByAccount = {};
  response.result.transactions.forEach((transaction) => {
    if (!transactionsByAccount[transaction.shortCardNumber]) {
      transactionsByAccount[transaction.shortCardNumber] = [];
    }

    const mappedTransaction = mapTransaction(transaction);
    transactionsByAccount[transaction.shortCardNumber].push(mappedTransaction);
  });

  return transactionsByAccount;
}

async function fetchTransactionsForMonth(browser, navigateToFunc, monthMoment) {
  const page = await browser.newPage();
  const url = getTransactionsUrl(monthMoment);

  await navigateToFunc(url, page);
  const txns = await getCurrentTransactions(page);
  await page.close();
  return txns;
}

function addResult(allResults, result) {
  const clonedResults = Object.assign({}, allResults);
  Object.keys(result).forEach((accountNumber) => {
    if (!clonedResults[accountNumber]) {
      clonedResults[accountNumber] = [];
    }
    clonedResults[accountNumber].push(...result[accountNumber]);
  });
  return clonedResults;
}

function prepareTransactions(txns, startMoment, combineInstallments) {
  let clonedTxns = Array.from(txns);
  if (!combineInstallments) {
    clonedTxns = fixInstallments(clonedTxns);
  }
  clonedTxns = sortTransactionsByDate(clonedTxns);
  clonedTxns = filterOldTransactions(clonedTxns, startMoment, combineInstallments);
  return clonedTxns;
}

async function fetchTransactions(browser, options, navigateToFunc) {
  const defaultStartMoment = moment().subtract(1, 'years');
  const startDate = options.startDate || defaultStartMoment.toDate();
  const startMoment = moment.max(defaultStartMoment, moment(startDate));
  const allMonths = getAllMonthMoments(startMoment, false);

  let allResults = {};
  for (let i = 0; i < allMonths.length; i += 1) {
    const result = await fetchTransactionsForMonth(browser, navigateToFunc, allMonths[i]);
    allResults = addResult(allResults, result);
  }

  Object.keys(allResults).forEach((accountNumber) => {
    let txns = allResults[accountNumber];
    txns = prepareTransactions(txns, startMoment, options.combineInstallments);
    allResults[accountNumber] = txns;
  });

  return allResults;
}

function getPossibleLoginResults(page) {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [`${BASE_WELCOME_URL}/homepage/personal`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [`${BASE_ACTIONS_URL}/Anonymous/Login/PasswordExpired.aspx`];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [async () => {
    return elementPresentOnPage(page, INVALID_DETAILS_SELECTOR);
  }];
  urls[LOGIN_RESULT.UNKNOWN_ERROR] = [async () => {
    return elementPresentOnPage(page, LOGIN_ERROR_SELECTOR);
  }];
  return urls;
}

function createLoginFields(inputGroupName, credentials) {
  return [
    { selector: `#${inputGroupName}_txtUserName`, value: credentials.username },
    { selector: '#txtPassword', value: credentials.password },
  ];
}

class LeumiCardScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    const inputGroupName = 'PlaceHolderMain_CardHoldersLogin1';
    return {
      loginUrl: `${BASE_ACTIONS_URL}/Anonymous/Login/CardholdersLogin.aspx`,
      fields: createLoginFields(inputGroupName, credentials),
      submitButtonSelector: `#${inputGroupName}_btnLogin`,
      preAction: async () => {
        if (await elementPresentOnPage(this.page, '#closePopup')) {
          await clickButton(this.page, '#closePopup');
        }
      },
      postAction: async () => redirectOrDialog(this.page),
      possibleResults: getPossibleLoginResults(this.page),
    };
  }

  async fetchData() {
    const results = await fetchTransactions(this.browser, this.options, this.navigateTo);
    const accounts = Object.keys(results).map((accountNumber) => {
      return {
        accountNumber,
        txns: results[accountNumber],
      };
    });

    return {
      success: true,
      accounts,
    };
  }
}

export default LeumiCardScraper;
<commit_msg>using fetchGetWithinPage for REST calls<commit_after>import buildUrl from 'build-url';
import moment from 'moment';
import { fetchGetWithinPage } from '../helpers/fetch';
import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { waitForRedirect } from '../helpers/navigation';
import { waitUntilElementFound, elementPresentOnPage, clickButton } from '../helpers/elements-interactions';
import {
  NORMAL_TXN_TYPE,
  INSTALLMENTS_TXN_TYPE,
  TRANSACTION_STATUS,
} from '../constants';
import getAllMonthMoments from '../helpers/dates';
import { fixInstallments, sortTransactionsByDate, filterOldTransactions } from '../helpers/transactions';

const BASE_ACTIONS_URL = 'https://online.max.co.il';
const BASE_API_ACTIONS_URL = 'https://onlinelcapi.max.co.il';
const BASE_WELCOME_URL = 'https://www.max.co.il';
const NORMAL_TYPE_NAME = 'רגילה';
const ATM_TYPE_NAME = 'חיוב עסקות מיידי';
const INTERNET_SHOPPING_TYPE_NAME = 'אינטרנט/חו"ל';
const INSTALLMENTS_TYPE_NAME = 'תשלומים';
const MONTHLY_CHARGE_TYPE_NAME = 'חיוב חודשי';
const ONE_MONTH_POSTPONED_TYPE_NAME = 'דחוי חודש';
const MONTHLY_POSTPONED_TYPE_NAME = 'דחוי לחיוב החודשי';
const THIRTY_DAYS_PLUS_TYPE_NAME = 'עסקת 30 פלוס';
const TWO_MONTHS_POSTPONED_TYPE_NAME = 'דחוי חודשיים';
const MONTHLY_CHARGE_PLUS_INTEREST_TYPE_NAME = 'חודשי + ריבית';
const CREDIT_TYPE_NAME = 'קרדיט';

const INVALID_DETAILS_SELECTOR = '#popupWrongDetails';
const LOGIN_ERROR_SELECTOR = '#popupCardHoldersLoginError';

function redirectOrDialog(page) {
  return Promise.race([
    waitForRedirect(page, 20000, false, [BASE_WELCOME_URL, `${BASE_WELCOME_URL}/`]),
    waitUntilElementFound(page, INVALID_DETAILS_SELECTOR, true),
    waitUntilElementFound(page, LOGIN_ERROR_SELECTOR, true),
  ]);
}

function getTransactionsUrl(monthMoment) {
  const month = monthMoment.month() + 1;
  const year = monthMoment.year();
  const date = `${year}-${month}-01`;

  /**
     * url explanation:
     * userIndex: -1 for all account owners
     * cardIndex: -1 for all cards under the account
     * all other query params are static, beside the date which changes for request per month
     */
  return buildUrl(BASE_API_ACTIONS_URL, {
    path: `/api/registered/transactionDetails/getTransactionsAndGraphs?filterData={"userIndex":-1,"cardIndex":-1,"monthView":true,"date":"${date}","dates":{"startDate":"0","endDate":"0"}}&v=V3.13-HF.6.26`,
  });
}

function getTransactionType(txnTypeStr) {
  const cleanedUpTxnTypeStr = txnTypeStr.replace('\t', ' ').trim();
  switch (cleanedUpTxnTypeStr) {
    case ATM_TYPE_NAME:
    case NORMAL_TYPE_NAME:
    case MONTHLY_CHARGE_TYPE_NAME:
    case ONE_MONTH_POSTPONED_TYPE_NAME:
    case MONTHLY_POSTPONED_TYPE_NAME:
    case THIRTY_DAYS_PLUS_TYPE_NAME:
    case TWO_MONTHS_POSTPONED_TYPE_NAME:
    case INTERNET_SHOPPING_TYPE_NAME:
    case MONTHLY_CHARGE_PLUS_INTEREST_TYPE_NAME:
      return NORMAL_TXN_TYPE;
    case INSTALLMENTS_TYPE_NAME:
    case CREDIT_TYPE_NAME:
      return INSTALLMENTS_TXN_TYPE;
    default:
      throw new Error(`Unknown transaction type ${cleanedUpTxnTypeStr}`);
  }
}

function getInstallmentsInfo(comments) {
  if (!comments) {
    return null;
  }
  const matches = comments.match(/\d+/g);
  if (!matches || matches.length < 2) {
    return null;
  }

  return {
    number: parseInt(matches[0], 10),
    total: parseInt(matches[1], 10),
  };
}

function mapTransaction(rawTransaction) {
  const isPending = rawTransaction.paymentDate === null;
  const processedDate = moment(isPending ?
    rawTransaction.purchaseDate :
    rawTransaction.paymentDate).toISOString();
  const status = isPending ? TRANSACTION_STATUS.PENDING : TRANSACTION_STATUS.COMPLETED;

  return {
    type: getTransactionType(rawTransaction.planName),
    date: moment(rawTransaction.purchaseDate).toISOString(),
    processedDate,
    originalAmount: -rawTransaction.originalAmount,
    originalCurrency: rawTransaction.originalCurrency,
    chargedAmount: -rawTransaction.actualPaymentAmount,
    description: rawTransaction.merchantName.trim(),
    memo: rawTransaction.comments,
    installments: getInstallmentsInfo(rawTransaction.comments),
    status,
  };
}

async function fetchTransactionsForMonth(page, monthMoment) {
  const url = getTransactionsUrl(monthMoment);

  const data = await fetchGetWithinPage(page, url);

  const transactionsByAccount = {};
  data.result.transactions.forEach((transaction) => {
    if (!transactionsByAccount[transaction.shortCardNumber]) {
      transactionsByAccount[transaction.shortCardNumber] = [];
    }

    const mappedTransaction = mapTransaction(transaction);
    transactionsByAccount[transaction.shortCardNumber].push(mappedTransaction);
  });

  return transactionsByAccount;
}

function addResult(allResults, result) {
  const clonedResults = Object.assign({}, allResults);
  Object.keys(result).forEach((accountNumber) => {
    if (!clonedResults[accountNumber]) {
      clonedResults[accountNumber] = [];
    }
    clonedResults[accountNumber].push(...result[accountNumber]);
  });
  return clonedResults;
}

function prepareTransactions(txns, startMoment, combineInstallments) {
  let clonedTxns = Array.from(txns);
  if (!combineInstallments) {
    clonedTxns = fixInstallments(clonedTxns);
  }
  clonedTxns = sortTransactionsByDate(clonedTxns);
  clonedTxns = filterOldTransactions(clonedTxns, startMoment, combineInstallments);
  return clonedTxns;
}

async function fetchTransactions(page, options) {
  const defaultStartMoment = moment().subtract(1, 'years');
  const startDate = options.startDate || defaultStartMoment.toDate();
  const startMoment = moment.max(defaultStartMoment, moment(startDate));
  const allMonths = getAllMonthMoments(startMoment, true);

  let allResults = {};
  for (let i = 0; i < allMonths.length; i += 1) {
    const result = await fetchTransactionsForMonth(page, allMonths[i]);
    allResults = addResult(allResults, result);
  }

  Object.keys(allResults).forEach((accountNumber) => {
    let txns = allResults[accountNumber];
    txns = prepareTransactions(txns, startMoment, options.combineInstallments);
    allResults[accountNumber] = txns;
  });

  return allResults;
}

function getPossibleLoginResults(page) {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [`${BASE_WELCOME_URL}/homepage/personal`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [`${BASE_ACTIONS_URL}/Anonymous/Login/PasswordExpired.aspx`];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [async () => {
    return elementPresentOnPage(page, INVALID_DETAILS_SELECTOR);
  }];
  urls[LOGIN_RESULT.UNKNOWN_ERROR] = [async () => {
    return elementPresentOnPage(page, LOGIN_ERROR_SELECTOR);
  }];
  return urls;
}

function createLoginFields(inputGroupName, credentials) {
  return [
    { selector: `#${inputGroupName}_txtUserName`, value: credentials.username },
    { selector: '#txtPassword', value: credentials.password },
  ];
}

class LeumiCardScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    const inputGroupName = 'PlaceHolderMain_CardHoldersLogin1';
    return {
      loginUrl: `${BASE_ACTIONS_URL}/Anonymous/Login/CardholdersLogin.aspx`,
      fields: createLoginFields(inputGroupName, credentials),
      submitButtonSelector: `#${inputGroupName}_btnLogin`,
      preAction: async () => {
        if (await elementPresentOnPage(this.page, '#closePopup')) {
          await clickButton(this.page, '#closePopup');
        }
      },
      postAction: async () => redirectOrDialog(this.page),
      possibleResults: getPossibleLoginResults(this.page),
    };
  }

  async fetchData() {
    const results = await fetchTransactions(this.page, this.options);
    const accounts = Object.keys(results).map((accountNumber) => {
      return {
        accountNumber,
        txns: results[accountNumber],
      };
    });

    return {
      success: true,
      accounts,
    };
  }
}

export default LeumiCardScraper;
<|endoftext|>