<commit_before>/**
  * @constructor
  */

var elementLayout = new function()
{
  const
  PADDING_TOP = 0,
  PADDING_RIGHT = 1,
  PADDING_BOTTOM = 2,
  PADDING_LEFT = 3,
  BORDER_TOP_WIDTH = 4,
  BORDER_RIGHT_WIDTH = 5,
  BORDER_BOTTOM_WIDTH = 6,
  BORDER_LEFT_WIDTH = 7,
  MARGIN_TOP = 8,
  MARGIN_RIGHT = 9,
  MARGIN_BOTTOM = 10,
  MARGIN_LEFT = 11,
  WIDTH = 12,
  HEIGHT = 13,
  OFFSET_TOP= 1,
  OFFSET_LEFT= 2,
  OFFSET_WIDTH= 3,
  OFFSET_HEIGHT= 4,
  SCROLL_TOP= 5,
  SCROLL_LEFT= 6,
  SCROLL_WIDTH= 7,
  SCROLL_HEIGHT= 8,
  CLIENT_TOP= 9,
  CLIENT_LEFT= 10,
  CLIENT_WIDTH= 11,
  CLIENT_HEIGHT= 12,
  OFFSETS =
  [
    '',
    'offsetTop',
    'offsetLeft',
    'offsetWidth',
    'offsetHeight',
    'scrollTop',
    'scrollLeft',
    'scrollWidth',
    'scrollHeight',
    'clientTop',
    'clientLeft',
    'clientWidth',
    'clientHeight'
  ],
  GET_OFFSETS_SCRIPT = "\
    (function(ele)\
    {\
      var \
      parent = ele,\
      log = ele.nodeName + '|0;',\
      offsetParent = ele.offsetParent,\
      offsets =\
      [\
        '" + OFFSETS[OFFSET_TOP] + "',\
        '" + OFFSETS[OFFSET_LEFT] + "',\
        '" + OFFSETS[OFFSET_WIDTH] + "',\
        '" + OFFSETS[OFFSET_HEIGHT] + "',\
        '" + OFFSETS[SCROLL_TOP] + "',\
        '" + OFFSETS[SCROLL_LEFT] + "',\
        '" + OFFSETS[SCROLL_WIDTH] + "',\
        '" + OFFSETS[SCROLL_HEIGHT] + "',\
        '" + OFFSETS[CLIENT_TOP] + "',\
        '" + OFFSETS[CLIENT_LEFT] + "',\
        '" + OFFSETS[CLIENT_WIDTH] + "',\
        '" + OFFSETS[CLIENT_HEIGHT] + "'\
      ],\
      offset = '',\
      i = 0;\
      while( ( parent = parent.parentNode ) && parent.nodeType == 1 )\
      {\
        if(parent == offsetParent)\
        {\
          log = parent.nodeName + '|1,' + log;\
          offsetParent = parent.offsetParent;\
        }\
        else\
        {\
          log = parent.nodeName + '|0,' + log;\
        }\
      };\
      for( ; offset = offsets[i]; i++)\
      {\
        log += ele[offset] + ';';\
      }\
      return log;\
    })(ele)\
    ";
  var layout_map = [];

  var setup_layout_map = function()
  {
    var prop = '', i = 0;
    for (; prop = css_index_map[i]; i++)
    {
      switch(prop)
      {
        case 'padding-top':
        {
          layout_map[PADDING_TOP] = i;
          break;
        }
        case 'padding-right':
        {
          layout_map[PADDING_RIGHT] = i;
          break;
        }
        case 'padding-bottom':
        {
          layout_map[PADDING_BOTTOM] = i;
          break;
        }
        case 'padding-left':
        {
          layout_map[PADDING_LEFT] = i;
          break;
        }
        case 'border-top-width':
        {
          layout_map[BORDER_TOP_WIDTH] = i;
          break;
        }
        case 'border-right-width':
        {
          layout_map[BORDER_RIGHT_WIDTH] = i;
          break;
        }
        case 'border-bottom-width':
        {
          layout_map[BORDER_BOTTOM_WIDTH] = i;
          break;
        }
        case 'border-left-width':
        {
          layout_map[BORDER_LEFT_WIDTH] = i;
          break;
        }
        case 'margin-top':
        {
          layout_map[MARGIN_TOP] = i;
          break;
        }
        case 'margin-right':
        {
          layout_map[MARGIN_RIGHT] = i;
          break;
        }
        case 'margin-bottom':
        {
          layout_map[MARGIN_BOTTOM] = i;
          break;
        }
        case 'margin-left':
        {
          layout_map[MARGIN_LEFT] = i;
          break;
        }
        case 'width':
        {
          layout_map[WIDTH] = i;
          break;
        }
        case 'height':
        {
          layout_map[HEIGHT] = i;
          break;
        }
      }
    }
  }

  var __selectedElement = null;
  var __comp_style = null;
  var __offsets_values = "";
  var __views = ['css-layout'];

  var onElementSelected = function(msg)
  {
    __comp_style = null;
    __offsets_values = "";
    __selectedElement = {rt_id: msg.rt_id,  obj_id: msg.obj_id};

    var i = 0, view_id = '';
    for ( i = 0; view_id = __views[i]; i++)
    {
      views[view_id].update();
    }
  }

  this.getLayoutValues = function(org_args)
  {
    if( !__selectedElement)
    {
      return null;
    }
    if(__comp_style)
    {
      return __comp_style;
    }

    var
    rt_id = __selectedElement.rt_id,
    obj_id = __selectedElement.obj_id;

    if( stylesheets.hasStylesheetsRuntime(rt_id) )
    {
      var tag = tagManager.setCB(null, handleGetMetricsData, [rt_id, obj_id, org_args]);
      services['ecmascript-debugger'].cssGetStyleDeclarations( tag, rt_id, obj_id, '10000', 'json' );
    }
    else
    {
      stylesheets.getStylesheets(__selectedElement.rt_id, arguments);
    }
    return null;
  }

  var handleGetMetricsData = function(xml, rt_id, obj_id, org_args)
  {
    var
    json = xml.getNodeData('matching-style-declarations'),
    declarations = null;

    if( json )
    {
      declarations = eval('(' + json +')');
      __comp_style =  declarations[0];
      if( !layout_map.length )
      {
        setup_layout_map();
      }
      if( org_args && !org_args[0].__call_count )
      {
        org_args[0].__call_count = 1;
        org_args.callee.apply(null, org_args)
      }
    }
  }

  this.getOffsetsValues = function(org_args)
  {
    if( !__selectedElement)
    {
      return null;
    }
    if(__offsets_values)
    {
      return __offsets_values;
    }

    var
    rt_id = __selectedElement.rt_id,
    obj_id = __selectedElement.obj_id,
    tag = tagManager.setCB(null, handleGetOffsetsData, [rt_id, obj_id, org_args] );

    services['ecmascript-debugger'].eval( tag, rt_id, '', '', GET_OFFSETS_SCRIPT, ['ele', obj_id]);
    return null;
  }

  this.prettyprintOffsetValues = function()
  {
    var
    data = __offsets_values.split(';'),
    ret = '
' +  ui_strings.M_VIEW_SUB_LABEL_PARENT_OFFSETS + '
<parent-node-chain>',
    i = 0,
    chain = data[0].split(','),
    cur = '';

    for( i = 0; cur = chain[i]; i++)
    {
      chain[i] = cur.split('|');
    }
    ret += document.renderInner(templates.breadcrumb(dom_data.getCSSPath(chain)));
    ret += "</parent-node-chain>
" +  ui_strings.M_VIEW_SUB_LABEL_OFFSET_VALUES + "
<offsets>";
    for( i = 1; data[i]; i++ )
    {
      ret += "<item>" +
        "<key>" + OFFSETS[i] + "</key>" +
        "<value>" + data[i] + "</value>" +
        "</item>";
    }
    ret += "</offsets>";
    return ret;
  }

  var handleGetOffsetsData = function(xml, rt_id, obj_id, org_args)
  {
    if( xml.getNodeData('status') == 'completed' )
    {
      __offsets_values = xml.getNodeData('string')
      if( org_args && !org_args[0].__call_count )
      {
        org_args[0].__call_count = 1;
        org_args.callee.apply(null, org_args)
      }
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        'handleGetOffsetsData failed')
    }
  }

  this.metricsTemplate = function(styles)
  {

    return \
    ['ul', ['li',
    ['ul',
      ['li',['p','\u00a0',['span', 'margin']]],
      ['li', __comp_style[layout_map[MARGIN_TOP]]],
      ['li']
    ],
    ['ul', ['li', __comp_style[layout_map[MARGIN_LEFT]]], ['li',
      ['ul',
        ['li',['p','\u00a0',['span', 'border']]],
        ['li', __comp_style[layout_map[BORDER_TOP_WIDTH]]],
        ['li']
      ],
      ['ul', ['li', __comp_style[layout_map[BORDER_LEFT_WIDTH]]], ['li',
        ['ul',
          ['li',['p','\u00a0',['span', 'padding']]],
          ['li', __comp_style[layout_map[PADDING_TOP]]],
          ['li']
        ],
        ['ul',
          ['li', __comp_style[layout_map[PADDING_LEFT]]],
          ['li',
            ['ul', ['li', __comp_style[layout_map[WIDTH]]]],
            ['ul', ['li', __comp_style[layout_map[HEIGHT]]]],
            ['ul', ['li', '\u00a0']],
            'class', 'dimension'],
          ['li', __comp_style[layout_map[PADDING_RIGHT]]]
        ],
        ['ul', ['li', __comp_style[layout_map[PADDING_BOTTOM]], 'colspan', '3']],
        'class', 'padding'], ['li', __comp_style[layout_map[BORDER_RIGHT_WIDTH]]]],
      ['ul', ['li', __comp_style[layout_map[BORDER_BOTTOM_WIDTH]], 'colspan', '3']],
      'class', 'border'], ['li', __comp_style[layout_map[MARGIN_RIGHT]]]],
    ['ul', ['li', __comp_style[layout_map[MARGIN_BOTTOM]], 'colspan', '3']],
    'class', 'margin']];
  }

  messages.addListener('element-selected', onElementSelected);

}

<commit_msg>more to fix DFL-844<commit_after>/**
  * @constructor
  */

var elementLayout = new function()
{
  const
  PADDING_TOP = 0,
  PADDING_RIGHT = 1,
  PADDING_BOTTOM = 2,
  PADDING_LEFT = 3,
  BORDER_TOP_WIDTH = 4,
  BORDER_RIGHT_WIDTH = 5,
  BORDER_BOTTOM_WIDTH = 6,
  BORDER_LEFT_WIDTH = 7,
  MARGIN_TOP = 8,
  MARGIN_RIGHT = 9,
  MARGIN_BOTTOM = 10,
  MARGIN_LEFT = 11,
  WIDTH = 12,
  HEIGHT = 13,
  OFFSET_TOP= 1,
  OFFSET_LEFT= 2,
  OFFSET_WIDTH= 3,
  OFFSET_HEIGHT= 4,
  SCROLL_TOP= 5,
  SCROLL_LEFT= 6,
  SCROLL_WIDTH= 7,
  SCROLL_HEIGHT= 8,
  CLIENT_TOP= 9,
  CLIENT_LEFT= 10,
  CLIENT_WIDTH= 11,
  CLIENT_HEIGHT= 12,
  OFFSETS =
  [
    '',
    'offsetTop',
    'offsetLeft',
    'offsetWidth',
    'offsetHeight',
    'scrollTop',
    'scrollLeft',
    'scrollWidth',
    'scrollHeight',
    'clientTop',
    'clientLeft',
    'clientWidth',
    'clientHeight'
  ],
  GET_OFFSETS_SCRIPT = "\
    (function(ele)\
    {\
      var \
      parent = ele,\
      log = ele.nodeName + '|0;',\
      offsetParent = ele.offsetParent,\
      offsets =\
      [\
        '" + OFFSETS[OFFSET_TOP] + "',\
        '" + OFFSETS[OFFSET_LEFT] + "',\
        '" + OFFSETS[OFFSET_WIDTH] + "',\
        '" + OFFSETS[OFFSET_HEIGHT] + "',\
        '" + OFFSETS[SCROLL_TOP] + "',\
        '" + OFFSETS[SCROLL_LEFT] + "',\
        '" + OFFSETS[SCROLL_WIDTH] + "',\
        '" + OFFSETS[SCROLL_HEIGHT] + "',\
        '" + OFFSETS[CLIENT_TOP] + "',\
        '" + OFFSETS[CLIENT_LEFT] + "',\
        '" + OFFSETS[CLIENT_WIDTH] + "',\
        '" + OFFSETS[CLIENT_HEIGHT] + "'\
      ],\
      offset = '',\
      i = 0;\
      while( ( parent = parent.parentNode ) && parent.nodeType == 1 )\
      {\
        if(parent == offsetParent)\
        {\
          log = parent.nodeName + '|1,' + log;\
          offsetParent = parent.offsetParent;\
        }\
        else\
        {\
          log = parent.nodeName + '|0,' + log;\
        }\
      };\
      for( ; offset = offsets[i]; i++)\
      {\
        log += ele[offset] + ';';\
      }\
      return log;\
    })(ele)\
    ";
  var layout_map = [];

  var setup_layout_map = function()
  {
    var prop = '', i = 0;
    for (; prop = css_index_map[i]; i++)
    {
      switch(prop)
      {
        case 'padding-top':
        {
          layout_map[PADDING_TOP] = i;
          break;
        }
        case 'padding-right':
        {
          layout_map[PADDING_RIGHT] = i;
          break;
        }
        case 'padding-bottom':
        {
          layout_map[PADDING_BOTTOM] = i;
          break;
        }
        case 'padding-left':
        {
          layout_map[PADDING_LEFT] = i;
          break;
        }
        case 'border-top-width':
        {
          layout_map[BORDER_TOP_WIDTH] = i;
          break;
        }
        case 'border-right-width':
        {
          layout_map[BORDER_RIGHT_WIDTH] = i;
          break;
        }
        case 'border-bottom-width':
        {
          layout_map[BORDER_BOTTOM_WIDTH] = i;
          break;
        }
        case 'border-left-width':
        {
          layout_map[BORDER_LEFT_WIDTH] = i;
          break;
        }
        case 'margin-top':
        {
          layout_map[MARGIN_TOP] = i;
          break;
        }
        case 'margin-right':
        {
          layout_map[MARGIN_RIGHT] = i;
          break;
        }
        case 'margin-bottom':
        {
          layout_map[MARGIN_BOTTOM] = i;
          break;
        }
        case 'margin-left':
        {
          layout_map[MARGIN_LEFT] = i;
          break;
        }
        case 'width':
        {
          layout_map[WIDTH] = i;
          break;
        }
        case 'height':
        {
          layout_map[HEIGHT] = i;
          break;
        }
      }
    }
  }

  var __selectedElement = null;
  var __comp_style = null;
  var __offsets_values = "";
  var __views = ['css-layout'];

  var onElementSelected = function(msg)
  {
    __comp_style = null;
    __offsets_values = "";
    __selectedElement = {rt_id: msg.rt_id,  obj_id: msg.obj_id};

    var i = 0, view_id = '';
    for ( i = 0; view_id = __views[i]; i++)
    {
      views[view_id].update();
    }
  }

  this.getLayoutValues = function(org_args)
  {
    if( !__selectedElement)
    {
      return null;
    }
    if(__comp_style)
    {
      return __comp_style;
    }

    var
    rt_id = __selectedElement.rt_id,
    obj_id = __selectedElement.obj_id;

    if( stylesheets.hasStylesheetsRuntime(rt_id) )
    {
      var tag = tagManager.setCB(null, handleGetMetricsData, [rt_id, obj_id, org_args]);
      services['ecmascript-debugger'].cssGetStyleDeclarations( tag, rt_id, obj_id, '10000', 'json' );
    }
    else
    {
      stylesheets.getStylesheets(__selectedElement.rt_id, arguments);
    }
    return null;
  }

  var handleGetMetricsData = function(xml, rt_id, obj_id, org_args)
  {
    var
    json = xml.getNodeData('matching-style-declarations'),
    declarations = null;

    if( json )
    {
      declarations = eval('(' + json +')');
      __comp_style =  declarations[0];
      if( !layout_map.length )
      {
        setup_layout_map();
      }
      if( org_args && !org_args[0].__call_count )
      {
        org_args[0].__call_count = 1;
        org_args.callee.apply(null, org_args)
      }
    }
  }

  this.getOffsetsValues = function(org_args)
  {
    if( !__selectedElement)
    {
      return null;
    }
    if(__offsets_values)
    {
      return __offsets_values;
    }

    var
    rt_id = __selectedElement.rt_id,
    obj_id = __selectedElement.obj_id,
    tag = tagManager.setCB(null, handleGetOffsetsData, [rt_id, obj_id, org_args] );

    services['ecmascript-debugger'].eval( tag, rt_id, '', '', GET_OFFSETS_SCRIPT, ['ele', obj_id]);
    return null;
  }

  this.prettyprintOffsetValues = function()
  {
    var
    data = __offsets_values.split(';'),
    ret = '
' +  ui_strings.M_VIEW_SUB_LABEL_PARENT_OFFSETS + '
<parent-node-chain>',
    i = 0,
    chain = data[0].split(','),
    cur = '';

    for( i = 0; cur = chain[i]; i++)
    {
      chain[i] = cur.split('|');
    }
    ret += document.renderInner(templates.breadcrumb(dom_data.getCSSPath(chain)));
    ret += "</parent-node-chain>
" +  ui_strings.M_VIEW_SUB_LABEL_OFFSET_VALUES + "
<offsets>";
    for( i = 1; data[i]; i++ )
    {
      ret += "<item>" +
        "<key>" + OFFSETS[i] + "</key>" +
        "<value>" + data[i] + "</value>" +
        "</item>";
    }
    ret += "</offsets>";
    return ret;
  }

  var handleGetOffsetsData = function(xml, rt_id, obj_id, org_args)
  {
    if( xml.getNodeData('status') == 'completed' )
    {
      __offsets_values = xml.getNodeData('string')
      if( org_args && !org_args[0].__call_count )
      {
        org_args[0].__call_count = 1;
        org_args.callee.apply(null, org_args)
      }
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        'handleGetOffsetsData failed')
    }
  }

  this.metricsTemplate = function(styles)
  {

    return (
    ['ul', ['li',
    ['ul',
      ['li',['p','\u00a0',['span', 'margin']]],
      ['li', __comp_style[layout_map[MARGIN_TOP]]],
      ['li']
    ],
    ['ul', ['li', __comp_style[layout_map[MARGIN_LEFT]]], ['li',
      ['ul',
        ['li',['p','\u00a0',['span', 'border']]],
        ['li', __comp_style[layout_map[BORDER_TOP_WIDTH]]],
        ['li']
      ],
      ['ul', ['li', __comp_style[layout_map[BORDER_LEFT_WIDTH]]], ['li',
        ['ul',
          ['li',['p','\u00a0',['span', 'padding']]],
          ['li', __comp_style[layout_map[PADDING_TOP]]],
          ['li']
        ],
        ['ul',
          ['li', __comp_style[layout_map[PADDING_LEFT]]],
          ['li',
            ['ul', ['li', __comp_style[layout_map[WIDTH]]]],
            ['ul', ['li', __comp_style[layout_map[HEIGHT]]]],
            ['ul', ['li', '\u00a0']],
            'class', 'dimension'],
          ['li', __comp_style[layout_map[PADDING_RIGHT]]]
        ],
        ['ul', ['li', __comp_style[layout_map[PADDING_BOTTOM]], 'colspan', '3']],
        'class', 'padding'], ['li', __comp_style[layout_map[BORDER_RIGHT_WIDTH]]]],
      ['ul', ['li', __comp_style[layout_map[BORDER_BOTTOM_WIDTH]], 'colspan', '3']],
      'class', 'border'], ['li', __comp_style[layout_map[MARGIN_RIGHT]]]],
    ['ul', ['li', __comp_style[layout_map[MARGIN_BOTTOM]], 'colspan', '3']],
    'class', 'margin']] );
  }

  messages.addListener('element-selected', onElementSelected);

}

<|endoftext|>


<commit_before><commit_msg>drop escuchar command<commit_after><|endoftext|>


<commit_before>/**
Form with single input element, two buttons and two states: normal/loading.
Applied as jQuery method to DIV tag (not to form tag!)
Editableform is linked with one of input types, e.g. 'text' or 'select'.

@class editableform
@uses text
@uses textarea
**/
(function ($) {

    var EditableForm = function (element, options) {
        this.options = $.extend({}, $.fn.editableform.defaults, options);
        this.$div = $(element); //div (usually), containing form
        this.initInput();
    };

    EditableForm.prototype = {
        constructor: EditableForm,
        initInput: function() {
            var TypeConstructor, typeOptions;

            //create input of specified type
            if(typeof $.fn.editableform.types[this.options.type] === 'function') {
                TypeConstructor = $.fn.editableform.types[this.options.type];
                typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults));
                this.input = new TypeConstructor(typeOptions);
            } else {
                $.error('Unknown type: '+ this.options.type);
                return;
            }

            this.value = this.input.str2value(this.options.value);
        },
        initTemplate: function() {
            this.$form = $($.fn.editableform.template);
        },
        /**
        Renders editableform

        @method render
        **/
        render: function() {
            this.$loading = $($.fn.editableform.loading);
            this.$div.empty().append(this.$loading);
            this.showLoading();

            this.initTemplate();

            /**
            Fired when rendering starts
            @event rendering
            @param {Object} event event object
            **/
            this.$div.triggerHandler('rendering');

            //render input
            $.when(this.input.render())
            .then($.proxy(function () {
                //place input
                this.$form.find('div.control-group').prepend(this.input.$input);
                //attach 'cancel' handler
                this.$form.find('button[type=button]').click($.proxy(this.cancel, this));
                //append form to container
                this.$div.append(this.$form);
                if(this.input.error) {
                    this.error(this.input.error);
                    this.$form.find('button[type=submit]').attr('disabled', true);
                    this.input.$input.attr('disabled', true);
                } else {
                    this.error(false);
                    this.input.$input.removeAttr('disabled');
                    this.$form.find('button[type=submit]').removeAttr('disabled');
                    this.input.value2input(this.value);
                    this.$form.submit($.proxy(this.submit, this));
                }
                this.showForm();
            }, this));
        },
        cancel: function() {
            /**
            Fired when form was cancelled by user
            @event cancel
            @param {Object} event event object
            **/
            this.$div.triggerHandler('cancel');
        },
        showLoading: function() {
            var fw, fh, iw, ih;
            //set loading size equal to form
            if(this.$form) {
                fh = this.$form.outerHeight() || 0;
                fw = this.$form.outerWidth() || 0;
                ih = (this.input && this.input.$input.outerHeight()) || 0;
                iw = (this.input && this.input.$input.outerWidth()) || 0;
                if(fh || ih) {
                    this.$loading.height(fh > ih ? fh : ih);
                }
                if(fw || iw) {
                    this.$loading.width(fw > iw ? fw : iw);
                }
                this.$form.hide();
            }
            this.$loading.show();
        },

        showForm: function() {
            this.$loading.hide();
            this.$form.show();
            this.input.activate();
            /**
            Fired when form is shown
            @event show
            @param {Object} event event object
            **/
            this.$div.triggerHandler('show');
        },

        error: function(msg) {
            var $group = this.$form.find('.control-group'),
                $block = this.$form.find('.editable-error-block');

            if(msg === false) {
                $group.removeClass($.fn.editableform.errorGroupClass);
                $block.removeClass($.fn.editableform.errorBlockClass).empty().hide();
            } else {
                $group.addClass($.fn.editableform.errorGroupClass);
                $block.addClass($.fn.editableform.errorBlockClass).text(msg).show();
            }
        },

        submit: function(e) {
            e.stopPropagation();
            e.preventDefault();

            var error,
                //get value from input
                newValue = this.input.input2value(),
                newValueStr;

            //validation
            if (error = this.validate(newValue)) {
                this.error(error);
                this.showForm();
                return;
            }

            //value as string
            newValueStr = this.input.value2str(newValue);

            //if value not changed --> cancel
            /*jslint eqeq: true*/
            if (newValueStr == this.input.value2str(this.value)) {
            /*jslint eqeq: false*/
                this.cancel();
                return;
            }

            //sending data to server
            $.when(this.save(newValueStr))
            .done($.proxy(function(response) {
               this.error(false);
               this.value = newValue;
               /**
               Fired when form is submitted
               @event save
               @param {Object} event event object
               @param {Object} params additional params
                    @param {mixed} params.newValue submitted value
                    @param {Object} params.response ajax response

               @example
                   $('#form-div').on('save'), function(e, params){
                       if(params.newValue === 'username') {...}
                   });
               **/
               this.$div.triggerHandler('save', {newValue: newValue, response: response});
            }, this))
            .fail($.proxy(function(xhr) {
               this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!');
               this.showForm();
            }, this));
        },

        save: function(value) {
            var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk,
                send = !!((this.options.url !== undefined) && (this.options.url !== null) && ((this.options.send === 'always') || (this.options.send === 'auto' && pk))),
                params;

            if (send) { //send to server
                this.showLoading();

                //standart params
                params = {
                    name: this.options.name || '',
                    value: value,
                    pk: pk
                };

                //additional params
                if(typeof this.options.params === 'function') {
                    $.extend(params, this.options.params.call(this, params));
                } else {
                    //try parse json in single quotes (from data-params attribute)
                    this.options.params = $.fn.editableform.utils.tryParseJson(this.options.params, true);
                    $.extend(params, this.options.params);
                }

                if(typeof this.options.url === 'function') { //user's function
                    return this.options.url.call(this, params);
                } else {  //send ajax to server and return deferred object
                    return $.ajax({
                        url     : this.options.url,
                        data    : params,
                        type    : 'post',
                        dataType: 'json'
                    });
                }
            }
        },

        validate: function (value) {
            if (value === undefined) {
                value = this.value;
            }
            if (typeof this.options.validate === 'function') {
                return this.options.validate.call(this, value);
            }
        },

       option: function(key, value) {
          this.options[key] = value;
       }
    };

    /*
    Initialize editableform. Applied to jQuery object.

    @method $().editableform(options)
    @params {Object} options
    @example
        var $form = $('<div>').editableform({
            type: 'text',
            name: 'username',
            url: 'post.php',
            value: 'vitaliy'
        });

        //to display form you should call 'render' method
        $form.editableform('render');
    */
    $.fn.editableform = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            data = $this.data('editableform'),
            options = typeof option === 'object' && option;
            if (!data) {
                $this.data('editableform', (data = new EditableForm(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //keep link to constructor to allow inheritance
    $.fn.editableform.Constructor = EditableForm;

    //defaults
    $.fn.editableform.defaults = {
        /* see also defaults for input */

        /**
        Type of input. Can be text|textarea|select|date

        @property type
        @type string
        @default 'text'
        **/
        type: 'text',
        /**
        Url for submit, e.g. post.php
        If function - it will be called instead of ajax. Function can return deferred object to run fail/done callbacks.

        @property url
        @type string|function
        @default null
        @example
        url: function(params) {
           if(params.value === 'abc') {
               var d = new $.Deferred;
               return d.reject('field cannot be "abc"'); //returning error via deferred object
           } else {
               someModel.set(params.name, params.value); //save data in some js model
           }
        }
        **/
        url:null,
        /**
        Additional params for submit. Function can be used to calculate params dynamically
        @example
        params: function() {
           return { a: 1 };
        }

        @property params
        @type object|function
        @default null
        **/
        params:null,
        /**
        Name of field. Will be submitted on server. Can be taken from id attribute

        @property name
        @type string
        @default null
        **/
        name: null,
        /**
        Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. {id: 1, lang: 'en'}.
        Can be calculated dinamically via function.

        @property pk
        @type string|object|function
        @default null
        **/
        pk: null,
        /**
        Initial value. If not defined - will be taken from element's content.
        For __select__ type should be defined (as it is ID of shown text).

        @property value
        @type string|object
        @default null
        **/
        value: null,
        /**
        Strategy for sending data on server. Can be auto|always|never.
        When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element.

        @property send
        @type string
        @default 'auto'
        **/
        send: 'auto',
        /**
        Function for client-side validation. If returns string - means validation not passed and string showed as error.

        @property validate
        @type function
        @default null
        @example
        validate: function(value) {
            if($.trim(value) == '') {
                return 'This field is required';
            }
        }
        **/
        validate: null
    };

    /*
      Note: following params could redefined in engine: bootstrap or jqueryui:
      Classes 'control-group' and 'editable-error-block' must always present!
    */
      $.fn.editableform.template = '<form class="form-inline editableform">
' +
    ' <button type="submit">Ok</button> <button type="button">Cancel</button>
' +
    '

' +
    '</form>';

      //loading div
      $.fn.editableform.loading = '

';

      //error class attahced to control-group
      $.fn.editableform.errorGroupClass = null;

      //error class attahced to editable-error-block
      $.fn.editableform.errorBlockClass = 'editable-error';

      //input types
      $.fn.editableform.types = {};
      //utils
      $.fn.editableform.utils = {};

}(window.jQuery));<commit_msg>rename in form $div to $element<commit_after>/**
Form with single input element, two buttons and two states: normal/loading.
Applied as jQuery method to DIV tag (not to form tag!)
Editableform is linked with one of input types, e.g. 'text' or 'select'.

@class editableform
@uses text
@uses textarea
**/
(function ($) {

    var EditableForm = function (element, options) {
        this.options = $.extend({}, $.fn.editableform.defaults, options);
        this.$element = $(element); //div (usually), containing form. not form tag!
        this.initInput();
    };

    EditableForm.prototype = {
        constructor: EditableForm,
        initInput: function() {
            var TypeConstructor, typeOptions;

            //create input of specified type
            if(typeof $.fn.editableform.types[this.options.type] === 'function') {
                TypeConstructor = $.fn.editableform.types[this.options.type];
                typeOptions = $.fn.editableform.utils.sliceObj(this.options, Object.keys(TypeConstructor.defaults));
                this.input = new TypeConstructor(typeOptions);
            } else {
                $.error('Unknown type: '+ this.options.type);
                return;
            }

            this.value = this.input.str2value(this.options.value);
        },
        initTemplate: function() {
            this.$form = $($.fn.editableform.template);
        },
        /**
        Renders editableform

        @method render
        **/
        render: function() {
            this.$loading = $($.fn.editableform.loading);
            this.$element.empty().append(this.$loading);
            this.showLoading();

            this.initTemplate();

            /**
            Fired when rendering starts
            @event rendering
            @param {Object} event event object
            **/
            this.$element.triggerHandler('rendering');

            //render input
            $.when(this.input.render())
            .then($.proxy(function () {
                //place input
                this.$form.find('div.control-group').prepend(this.input.$input);
                //attach 'cancel' handler
                this.$form.find('button[type=button]').click($.proxy(this.cancel, this));
                //append form to container
                this.$element.append(this.$form);
                if(this.input.error) {
                    this.error(this.input.error);
                    this.$form.find('button[type=submit]').attr('disabled', true);
                    this.input.$input.attr('disabled', true);
                } else {
                    this.error(false);
                    this.input.$input.removeAttr('disabled');
                    this.$form.find('button[type=submit]').removeAttr('disabled');
                    this.input.value2input(this.value);
                    this.$form.submit($.proxy(this.submit, this));
                }
                this.showForm();
            }, this));
        },
        cancel: function() {
            /**
            Fired when form was cancelled by user
            @event cancel
            @param {Object} event event object
            **/
            this.$element.triggerHandler('cancel');
        },
        showLoading: function() {
            var fw, fh, iw, ih;
            //set loading size equal to form
            if(this.$form) {
                fh = this.$form.outerHeight() || 0;
                fw = this.$form.outerWidth() || 0;
                ih = (this.input && this.input.$input.outerHeight()) || 0;
                iw = (this.input && this.input.$input.outerWidth()) || 0;
                if(fh || ih) {
                    this.$loading.height(fh > ih ? fh : ih);
                }
                if(fw || iw) {
                    this.$loading.width(fw > iw ? fw : iw);
                }
                this.$form.hide();
            }
            this.$loading.show();
        },

        showForm: function() {
            this.$loading.hide();
            this.$form.show();
            this.input.activate();
            /**
            Fired when form is shown
            @event show
            @param {Object} event event object
            **/
            this.$element.triggerHandler('show');
        },

        error: function(msg) {
            var $group = this.$form.find('.control-group'),
                $block = this.$form.find('.editable-error-block');

            if(msg === false) {
                $group.removeClass($.fn.editableform.errorGroupClass);
                $block.removeClass($.fn.editableform.errorBlockClass).empty().hide();
            } else {
                $group.addClass($.fn.editableform.errorGroupClass);
                $block.addClass($.fn.editableform.errorBlockClass).text(msg).show();
            }
        },

        submit: function(e) {
            e.stopPropagation();
            e.preventDefault();

            var error,
                //get value from input
                newValue = this.input.input2value(),
                newValueStr;

            //validation
            if (error = this.validate(newValue)) {
                this.error(error);
                this.showForm();
                return;
            }

            //value as string
            newValueStr = this.input.value2str(newValue);

            //if value not changed --> cancel
            /*jslint eqeq: true*/
            if (newValueStr == this.input.value2str(this.value)) {
            /*jslint eqeq: false*/
                this.cancel();
                return;
            }

            //sending data to server
            $.when(this.save(newValueStr))
            .done($.proxy(function(response) {
               this.error(false);
               this.value = newValue;
               /**
               Fired when form is submitted
               @event save
               @param {Object} event event object
               @param {Object} params additional params
                    @param {mixed} params.newValue submitted value
                    @param {Object} params.response ajax response

               @example
                   $('#form-div').on('save'), function(e, params){
                       if(params.newValue === 'username') {...}
                   });
               **/
               this.$element.triggerHandler('save', {newValue: newValue, response: response});
            }, this))
            .fail($.proxy(function(xhr) {
               this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!');
               this.showForm();
            }, this));
        },

        save: function(value) {
            var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk,
                send = !!((this.options.url !== undefined) && (this.options.url !== null) && ((this.options.send === 'always') || (this.options.send === 'auto' && pk))),
                params;

            if (send) { //send to server
                this.showLoading();

                //standart params
                params = {
                    name: this.options.name || '',
                    value: value,
                    pk: pk
                };

                //additional params
                if(typeof this.options.params === 'function') {
                    $.extend(params, this.options.params.call(this, params));
                } else {
                    //try parse json in single quotes (from data-params attribute)
                    this.options.params = $.fn.editableform.utils.tryParseJson(this.options.params, true);
                    $.extend(params, this.options.params);
                }

                if(typeof this.options.url === 'function') { //user's function
                    return this.options.url.call(this, params);
                } else {  //send ajax to server and return deferred object
                    return $.ajax({
                        url     : this.options.url,
                        data    : params,
                        type    : 'post',
                        dataType: 'json'
                    });
                }
            }
        },

        validate: function (value) {
            if (value === undefined) {
                value = this.value;
            }
            if (typeof this.options.validate === 'function') {
                return this.options.validate.call(this, value);
            }
        },

       option: function(key, value) {
          this.options[key] = value;
       }
    };

    /*
    Initialize editableform. Applied to jQuery object.

    @method $().editableform(options)
    @params {Object} options
    @example
        var $form = $('<div>').editableform({
            type: 'text',
            name: 'username',
            url: 'post.php',
            value: 'vitaliy'
        });

        //to display form you should call 'render' method
        $form.editableform('render');
    */
    $.fn.editableform = function (option) {
        var args = arguments;
        return this.each(function () {
            var $this = $(this),
            data = $this.data('editableform'),
            options = typeof option === 'object' && option;
            if (!data) {
                $this.data('editableform', (data = new EditableForm(this, options)));
            }

            if (typeof option === 'string') { //call method
                data[option].apply(data, Array.prototype.slice.call(args, 1));
            }
        });
    };

    //keep link to constructor to allow inheritance
    $.fn.editableform.Constructor = EditableForm;

    //defaults
    $.fn.editableform.defaults = {
        /* see also defaults for input */

        /**
        Type of input. Can be text|textarea|select|date

        @property type
        @type string
        @default 'text'
        **/
        type: 'text',
        /**
        Url for submit, e.g. post.php
        If function - it will be called instead of ajax. Function can return deferred object to run fail/done callbacks.

        @property url
        @type string|function
        @default null
        @example
        url: function(params) {
           if(params.value === 'abc') {
               var d = new $.Deferred;
               return d.reject('field cannot be "abc"'); //returning error via deferred object
           } else {
               someModel.set(params.name, params.value); //save data in some js model
           }
        }
        **/
        url:null,
        /**
        Additional params for submit. Function can be used to calculate params dynamically
        @example
        params: function() {
           return { a: 1 };
        }

        @property params
        @type object|function
        @default null
        **/
        params:null,
        /**
        Name of field. Will be submitted on server. Can be taken from id attribute

        @property name
        @type string
        @default null
        **/
        name: null,
        /**
        Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. {id: 1, lang: 'en'}.
        Can be calculated dinamically via function.

        @property pk
        @type string|object|function
        @default null
        **/
        pk: null,
        /**
        Initial value. If not defined - will be taken from element's content.
        For __select__ type should be defined (as it is ID of shown text).

        @property value
        @type string|object
        @default null
        **/
        value: null,
        /**
        Strategy for sending data on server. Can be auto|always|never.
        When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element.

        @property send
        @type string
        @default 'auto'
        **/
        send: 'auto',
        /**
        Function for client-side validation. If returns string - means validation not passed and string showed as error.

        @property validate
        @type function
        @default null
        @example
        validate: function(value) {
            if($.trim(value) == '') {
                return 'This field is required';
            }
        }
        **/
        validate: null
    };

    /*
      Note: following params could redefined in engine: bootstrap or jqueryui:
      Classes 'control-group' and 'editable-error-block' must always present!
    */
      $.fn.editableform.template = '<form class="form-inline editableform">
' +
    ' <button type="submit">Ok</button> <button type="button">Cancel</button>
' +
    '

' +
    '</form>';

      //loading div
      $.fn.editableform.loading = '

';

      //error class attahced to control-group
      $.fn.editableform.errorGroupClass = null;

      //error class attahced to editable-error-block
      $.fn.editableform.errorBlockClass = 'editable-error';

      //input types
      $.fn.editableform.types = {};
      //utils
      $.fn.editableform.utils = {};

}(window.jQuery));<|endoftext|>


<commit_before><commit_msg>Function to populate list now exists.<commit_after><|endoftext|>


<commit_before><commit_msg>Main AMD script that kicks things off<commit_after><|endoftext|>


<commit_before><commit_msg>roomManager now only runs over homeRooms<commit_after><|endoftext|>


<commit_before>var elasticsearch = require("elasticsearch");
var util = require('util');

var express = require('express');
var app = express();
var elastic_ip = process.env.ELASTIC_IP;

app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://172.18.105.87:8080');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);

    // Pass to next layer of middleware
    next();
});

app.get('/query-elasticsearch', function (req, res) {

    function addOrUpdateNodeInList(nodeIPAddress, listOfNodes) {
        existing_node = listOfNodes.find((n) => { return n.name == nodeIPAddress; })
        if (existing_node === undefined) {
            new_node = {
                name: nodeIPAddress,
                metadata: {},
                renderer: "focusedChild"
            };
            listOfNodes.push(new_node);
        }
    }

    ipsToTags = {};

    nodes = [{
        name: 'INTERNET' // Required... this is the entry node
    }];
    connections = [];

    tagsWithIPs = getTagsWithIPs(elastic_ip);
    tagsWithIPs.then(function (response) {
        // console.log(util.inspect(response, { depth: null }));
        response.aggregations.nodes.buckets.forEach(function (node) {
            ipsToTags[node.ipAddresses.buckets[0].key] = node.key;
        }, this);
        // console.log(util.inspect(ipsToTags, { depth: null }));
    }).catch(console.trace);;

    requestsByNode = getRequestsByNode(elastic_ip);
    requestsByNode.then(function (response) {
        request_nodes = response.aggregations.nodes.buckets;
        // console.log(util.inspect(request_nodes, { depth: null }));
        request_nodes.forEach(function (node) {
            // add source node to the node list if not already in it
            nodeIPAddress = node.ipAddresses.buckets[0].key;
            addOrUpdateNodeInList(nodeIPAddress, nodes);

            //add incoming http connections
            node.http_in.sources.buckets.forEach(function (connection) {
                connectionMetrics = connection.transactionStatus.buckets.reduce((metrics, status) => {
                    if (status.key == "OK") {
                        metrics.normal += status.doc_count;
                    } else if (status.key == "ERROR") {
                        metrics.error += status.doc_count;
                    } else {
                        console.log(`found unhandled transaction status '${status.key}'`)
                    }
                    return metrics;
                }, { normal: 0, error: 0 });
                connections.push({ source: connection.key, target: nodeIPAddress, metrics: connectionMetrics, metadata: { request_type: "http" } });
                // add source node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            //add outgoing http connections
            node.http_out.destinations.buckets.forEach(function (connection) {
                connectionMetrics = connection.transactionStatus.buckets.reduce((metrics, status) => {
                    if (status.key == "OK") {
                        metrics.normal += status.doc_count;
                    } else if (status.key == "ERROR") {
                        metrics.error += status.doc_count;
                    } else {
                        console.log(`found unhandled transaction status '${status.key}'`)
                    }
                    return metrics;
                }, { normal: 0, error: 0 });
                connections.push({ source: nodeIPAddress, target: connection.key, metrics: connectionMetrics, metadata: { request_type: "http" } });
                // add destination node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            // add incoming generic tcp flow connections
            node.flows.sources.buckets.forEach(function (connection) {
                existingConnection = connections.find((c) => { return (c.source == connection.key && c.target == nodeIPAddress); });
                if (existingConnection === undefined) {
                    connections.push({ source: connection.key, target: nodeIPAddress, metrics: { normal: connection.doc_count }, metadata: { request_type: "flow" } });
                } else {
                    existingConnection.metrics.normal += connection.doc_count;
                }
                // add source node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            // add outgoing generic tcp flow connections
            node.flows.destinations.buckets.forEach(function (connection) {
                existingConnection = connections.find((c) => { return (c.source == nodeIPAddress && c.target == connection.key); });
                if (existingConnection === undefined) {
                    connections.push({ source: nodeIPAddress, target: connection.key, metrics: { normal: connection.doc_count }, metadata: { request_type: "flow" } });
                } else {
                    existingConnection.metrics.normal += connection.doc_count;
                }
                // add destination node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

        }, this);

        addTagsToNodeList(nodes, ipsToTags);

        vizceral_data = {
            // Which graph renderer to use for this graph (currently only 'global' and 'region')
            renderer: 'global',
            // since the root object is a node, it has a name too.
            name: 'edge',
            // OPTIONAL: The maximum volume seen recently to relatively measure particle density. This 'global' maxVolume is optional because it can be calculated by using all of the required sub-node maxVolumes.
            maxVolume: 100000,
            // list of nodes for this graph
            nodes: [
                {
                    renderer: 'region',
                    layout: 'ltrTree',
                    // OPTIONAL Override the default layout used for the renderer.
                    name: 'bosh-region',
                    // Unix timestamp. Only checked at this level of nodes. Last time the data was updated (Needed because the client could be passed stale data when loaded)
                    updated: Date.now(),
                    metadata: {},
                    // The maximum volume seen recently to relatively measure particle density
                    maxVolume: 100000,
                    nodes: nodes,
                    connections: connections
                }
            ]
        };
        console.log(`data that will be sent to the user '${util.inspect(vizceral_data, { depth: null })}'`);
        res.send(vizceral_data);

    }).catch(console.trace);
});

app.listen(8081, function () {
    console.log('Server started');
});

function addTagsToNodeList(nodes, ipsToTags) {
    nodes.forEach(function (node) {
        tagForIP = ipsToTags[node.name];
        if (tagForIP !== undefined) {
            node.displayName = tagForIP;
        }
    }, this);
}

function getTagsWithIPs(elastic_ip) {
    var client = elasticsearch.Client({ host: `${elastic_ip}:9200`, apiVersion: "2.3" });

    return client.search({
        index: 'packetbeat-*',
        body: {
            "size": 0,
            "query": {
                "match_all": {}
            },
            "aggregations": {
                "nodes": {
                    "terms": { "field": "tags" },
                    "aggregations": {
                        "ipAddresses": {
                            "terms": { "field": "fields.ip" }
                        }
                    }
                }
            }
        }
    });
}

function getRequestsByNode(elastic_ip) {
    var client = elasticsearch.Client({ host: `${elastic_ip}:9200`, apiVersion: "2.3" });

    return client.search({
        index: 'packetbeat-*',
        body: {
            "size": 0,
            // "query": {
            //     "bool": {
            //         "filter": {
            //             "bool": {
            //                 "must": [
            //                     {
            //                         "range": {
            //                             "@timestamp": { "gt": "now-1h" }
            //                         }
            //                     }
            //                 ]
            //             }
            //         }
            //     }
            // },
            "aggregations": {
                "nodes": {
                    "terms": { "field": "tags" },
                    "aggregations": {
                        "ipAddresses": {
                            "terms": { "field": "fields.ip" }
                        },
                        "flows": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "exists": { "field": "dest.ip" }
                                        },
                                        {
                                            "term": { "type": "flow" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "dest.port": "9200" }
                                        },
                                        {
                                            "term": { "dest.ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "sources": {
                                    "terms": { "field": "source.ip" }
                                },
                                "destinations": {
                                    "terms": { "field": "dest.ip" }
                                }
                            }
                        },
                        "http_in": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "term": { "type": "http" }
                                        },
                                        {
                                            "term": { "direction": "in" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "sources": {
                                    "terms": {
                                        "field": "client_ip"
                                    },
                                    "aggregations": {
                                        "transactionStatus": {
                                            "terms": { "field": "status" }
                                        }
                                    }
                                }
                            }
                        },
                        "http_out": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "term": { "type": "http" }
                                        },
                                        {
                                            "term": { "direction": "out" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "destinations": {
                                    "terms": {
                                        "field": "ip"
                                    },
                                    "aggregations": {
                                        "transactionStatus": {
                                            "terms": { "field": "status" }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    });
}
<commit_msg>Typos<commit_after>var elasticsearch = require("elasticsearch");
var util = require('util');

var express = require('express');
var app = express();
var elastic_ip = process.env.ELASTIC_IP;

app.use(function (req, res, next) {

    // Website you wish to allow to connect
    res.setHeader('Access-Control-Allow-Origin', 'http://172.18.105.87:8080');

    // Request methods you wish to allow
    res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');

    // Request headers you wish to allow
    res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');

    // Set to true if you need the website to include cookies in the requests sent
    // to the API (e.g. in case you use sessions)
    res.setHeader('Access-Control-Allow-Credentials', true);

    // Pass to next layer of middleware
    next();
});

app.get('/query-elasticsearch', function (req, res) {

    function addOrUpdateNodeInList(nodeIPAddress, listOfNodes) {
        existing_node = listOfNodes.find((n) => { return n.name == nodeIPAddress; })
        if (existing_node === undefined) {
            new_node = {
                name: nodeIPAddress,
                metadata: {},
                renderer: "focusedChild"
            };
            listOfNodes.push(new_node);
        }
    }

    ipsToTags = {};

    nodes = [{
        name: 'INTERNET' // Required... this is the entry node
    }];
    connections = [];

    tagsWithIPs = getTagsWithIPs(elastic_ip);
    tagsWithIPs.then(function (response) {
        // console.log(util.inspect(response, { depth: null }));
        response.aggregations.nodes.buckets.forEach(function (node) {
            ipsToTags[node.ipAddresses.buckets[0].key] = node.key;
        }, this);
        // console.log(util.inspect(ipsToTags, { depth: null }));
    }).catch(console.trace);;

    requestsByNode = getRequestsByNode(elastic_ip);
    requestsByNode.then(function (response) {
        request_nodes = response.aggregations.nodes.buckets;
        // console.log(util.inspect(request_nodes, { depth: null }));
        request_nodes.forEach(function (node) {
            // add source node to the node list if not already in it
            nodeIPAddress = node.ipAddresses.buckets[0].key;
            addOrUpdateNodeInList(nodeIPAddress, nodes);

            //add incoming http connections
            node.http_in.sources.buckets.forEach(function (connection) {
                connectionMetrics = connection.transactionStatus.buckets.reduce((metrics, status) => {
                    if (status.key == "OK") {
                        metrics.normal += status.doc_count;
                    } else if (status.key == "Error") {
                        metrics.error += status.doc_count;
                    } else {
                        console.log(`found unhandled transaction status '${status.key}'`)
                    }
                    return metrics;
                }, { normal: 0, error: 0 });
                connections.push({ source: connection.key, target: nodeIPAddress, metrics: connectionMetrics, metadata: { request_type: "http" } });
                // add source node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            //add outgoing http connections
            node.http_out.destinations.buckets.forEach(function (connection) {
                connectionMetrics = connection.transactionStatus.buckets.reduce((metrics, status) => {
                    if (status.key == "OK") {
                        metrics.normal += status.doc_count;
                    } else if (status.key == "Error") {
                        metrics.error += status.doc_count;
                    } else {
                        console.log(`found unhandled transaction status '${status.key}'`)
                    }
                    return metrics;
                }, { normal: 0, error: 0 });
                existingConnection = connections.find((c) => { return (c.source == connection.key && c.target == nodeIPAddress); });
                if (existingConnection === undefined) {
                connections.push({ source: nodeIPAddress, target: connection.key, metrics: connectionMetrics, metadata: { request_type: "http" } });
                }
                // add destination node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            // add incoming generic tcp flow connections
            node.flows.sources.buckets.forEach(function (connection) {
                existingConnection = connections.find((c) => { return (c.source == connection.key && c.target == nodeIPAddress); });
                if (existingConnection === undefined) {
                    connections.push({ source: connection.key, target: nodeIPAddress, metrics: { normal: connection.doc_count }, metadata: { request_type: "flow" } });
                } else {
                    existingConnection.metrics.normal += connection.doc_count;
                }
                // add source node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

            // add outgoing generic tcp flow connections
            node.flows.destinations.buckets.forEach(function (connection) {
                existingConnection = connections.find((c) => { return (c.source == nodeIPAddress && c.target == connection.key); });
                if (existingConnection === undefined) {
                    connections.push({ source: nodeIPAddress, target: connection.key, metrics: { normal: connection.doc_count }, metadata: { request_type: "flow" } });
                } else {
                    existingConnection.metrics.normal += connection.doc_count;
                }
                // add destination node to the node list if not already in it
                addOrUpdateNodeInList(connection.key, nodes);
            }, this);

        }, this);

        addTagsToNodeList(nodes, ipsToTags);

        vizceral_data = {
            // Which graph renderer to use for this graph (currently only 'global' and 'region')
            renderer: 'global',
            // since the root object is a node, it has a name too.
            name: 'edge',
            // OPTIONAL: The maximum volume seen recently to relatively measure particle density. This 'global' maxVolume is optional because it can be calculated by using all of the required sub-node maxVolumes.
            maxVolume: 100000,
            // list of nodes for this graph
            nodes: [
                {
                    renderer: 'region',
                    layout: 'ltrTree',
                    // OPTIONAL Override the default layout used for the renderer.
                    name: 'bosh-region',
                    // Unix timestamp. Only checked at this level of nodes. Last time the data was updated (Needed because the client could be passed stale data when loaded)
                    updated: Date.now(),
                    metadata: {},
                    // The maximum volume seen recently to relatively measure particle density
                    maxVolume: 100000,
                    nodes: nodes,
                    connections: connections
                }
            ]
        };
        console.log(`data that will be sent to the user '${util.inspect(vizceral_data, { depth: null })}'`);
        res.send(vizceral_data);

    }).catch(console.trace);
});

app.listen(8081, function () {
    console.log('Server started');
});

function addTagsToNodeList(nodes, ipsToTags) {
    nodes.forEach(function (node) {
        tagForIP = ipsToTags[node.name];
        if (tagForIP !== undefined) {
            node.displayName = tagForIP;
        }
    }, this);
}

function getTagsWithIPs(elastic_ip) {
    var client = elasticsearch.Client({ host: `${elastic_ip}:9200`, apiVersion: "2.3" });

    return client.search({
        index: 'packetbeat-*',
        body: {
            "size": 0,
            "query": {
                "match_all": {}
            },
            "aggregations": {
                "nodes": {
                    "terms": { "field": "tags" },
                    "aggregations": {
                        "ipAddresses": {
                            "terms": { "field": "fields.ip" }
                        }
                    }
                }
            }
        }
    });
}

function getRequestsByNode(elastic_ip) {
    var client = elasticsearch.Client({ host: `${elastic_ip}:9200`, apiVersion: "2.3" });

    return client.search({
        index: 'packetbeat-*',
        body: {
            "size": 0,
            "query": {
                "bool": {
                    "filter": {
                        "bool": {
                            "must": [
                                {
                                    "range": {
                                        "@timestamp": { "gt": "now-1h" }
                                    }
                                }
                            ]
                        }
                    }
                }
            },
            "aggregations": {
                "nodes": {
                    "terms": { "field": "tags" },
                    "aggregations": {
                        "ipAddresses": {
                            "terms": { "field": "fields.ip" }
                        },
                        "flows": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "exists": { "field": "dest.ip" }
                                        },
                                        {
                                            "term": { "type": "flow" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "dest.port": "9200" }
                                        },
                                        {
                                            "term": { "dest.ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "sources": {
                                    "terms": { "field": "source.ip" }
                                },
                                "destinations": {
                                    "terms": { "field": "dest.ip" }
                                }
                            }
                        },
                        "http_in": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "term": { "type": "http" }
                                        },
                                        {
                                            "term": { "direction": "in" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "sources": {
                                    "terms": {
                                        "field": "client_ip"
                                    },
                                    "aggregations": {
                                        "transactionStatus": {
                                            "terms": { "field": "status" }
                                        }
                                    }
                                }
                            }
                        },
                        "http_out": {
                            "filter": {
                                "bool": {
                                    "must": [
                                        {
                                            "term": { "type": "http" }
                                        },
                                        {
                                            "term": { "direction": "out" }
                                        }
                                    ],
                                    "must_not": [
                                        {
                                            "term": { "ip": "127.0.0.1" }
                                        }
                                    ]
                                }
                            },
                            "aggregations": {
                                "destinations": {
                                    "terms": {
                                        "field": "ip"
                                    },
                                    "aggregations": {
                                        "transactionStatus": {
                                            "terms": { "field": "status" }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    });
}
<|endoftext|>


<commit_before>/*!
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* global window, dagTZ, moment, convertSecsToHumanReadable */

// We don't re-import moment again, otherwise webpack will include it twice in the bundle!
import { escapeHtml } from './main';
import { defaultFormat, formatDateTime } from './datetime-utils';

function makeDateTimeHTML(start, end) {
  // check task ended or not
  if (end && end instanceof moment) {
    return (
      `Started: ${start.format(defaultFormat)} <br> Ended: ${end.format(defaultFormat)} <br>`
    );
  }
  return (
    `Started: ${start.format(defaultFormat)} <br> Ended: Not ended yet <br>`
  );
}

function generateTooltipDateTimes(startDate, endDate, dagTZ) {
  if (!startDate) {
    return '
Not yet started';
  }

  const tzFormat = 'z (Z)';
  const localTZ = moment.defaultZone.name;
  startDate = moment.utc(startDate);
  endDate = moment.utc(endDate);
  dagTZ = dagTZ.toUpperCase();

  // Generate UTC Start and End Date
  let tooltipHTML = '
UTC:
';
  tooltipHTML += makeDateTimeHTML(startDate, endDate);

  // Generate User's Local Start and End Date
  startDate.tz(localTZ);
  tooltipHTML += `<br><strong>Local: ${startDate.format(tzFormat)}</strong><br>`;
  const localEndDate = endDate && endDate instanceof moment ? endDate.tz(localTZ) : endDate;
  tooltipHTML += makeDateTimeHTML(startDate, localEndDate);

  // Generate DAG's Start and End Date
  if (dagTZ !== 'UTC' && dagTZ !== localTZ) {
    startDate.tz(dagTZ);
    tooltipHTML += `<br><strong>DAG's TZ: ${startDate.format(tzFormat)}</strong><br>`;
    const dagTZEndDate = endDate && endDate instanceof moment ? endDate.tz(dagTZ) : endDate;
    tooltipHTML += makeDateTimeHTML(startDate, dagTZEndDate);
  }

  return tooltipHTML;
}

export default function tiTooltip(ti, { includeTryNumber = false } = {}) {
  let tt = '';
  if (ti.state !== undefined) {
    tt += `<strong>Status:</strong> ${escapeHtml(ti.state)}<br><br>`;
  }
  if (ti.task_id !== undefined) {
    tt += `Task_id: ${escapeHtml(ti.task_id)}<br>`;
  }
  tt += `Run: ${formatDateTime(ti.execution_date)}<br>`;
  if (ti.run_id !== undefined) {
    tt += `Run Id: <nobr>${escapeHtml(ti.run_id)}</nobr><br>`;
  }
  if (ti.operator !== undefined) {
    tt += `Operator: ${escapeHtml(ti.operator)}<br>`;
  }
  // Don't translate/format this, keep it as the full ISO8601 date
  if (ti.start_date instanceof moment) {
    tt += `Started: ${escapeHtml(ti.start_date.toISOString())}<br>`;
  } else {
    tt += `Started: ${escapeHtml(ti.start_date)}<br>`;
  }
  // Calculate duration on the fly if task instance is still running
  if (ti.state === 'running') {
    const startDate = ti.start_date instanceof moment ? ti.start_date : moment(ti.start_date);
    ti.duration = moment().diff(startDate, 'second');
  }

  tt += `Duration: ${escapeHtml(convertSecsToHumanReadable(ti.duration))}<br>`;

  if (includeTryNumber) {
    tt += `Try Number: ${escapeHtml(ti.try_number)}<br>`;
  }
  // dagTZ has been defined in dag.html
  tt += generateTooltipDateTimes(ti.start_date, ti.end_date, dagTZ);
  return tt;
}

window.tiTooltip = tiTooltip;
<commit_msg>ensure Moment date is valid before attempting to render it (#12492)<commit_after>/*!
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/* global window, dagTZ, moment, convertSecsToHumanReadable */

// We don't re-import moment again, otherwise webpack will include it twice in the bundle!
import { escapeHtml } from './main';
import { defaultFormat, formatDateTime } from './datetime-utils';

function makeDateTimeHTML(start, end) {
  // check task ended or not
  const isEnded = end && end instanceof moment && end.isValid();
  return `Started: ${start.format(defaultFormat)}<br>Ended: ${isEnded ? end.format(defaultFormat) : 'Not ended yet'}<br>`;
}

function generateTooltipDateTimes(startDate, endDate, dagTZ) {
  if (!startDate) {
    return '
Not yet started';
  }

  const tzFormat = 'z (Z)';
  const localTZ = moment.defaultZone.name;
  startDate = moment.utc(startDate);
  endDate = moment.utc(endDate);
  dagTZ = dagTZ.toUpperCase();

  // Generate UTC Start and End Date
  let tooltipHTML = '
UTC:
';
  tooltipHTML += makeDateTimeHTML(startDate, endDate);

  // Generate User's Local Start and End Date
  startDate.tz(localTZ);
  tooltipHTML += `<br><strong>Local: ${startDate.format(tzFormat)}</strong><br>`;
  const localEndDate = endDate && endDate instanceof moment ? endDate.tz(localTZ) : endDate;
  tooltipHTML += makeDateTimeHTML(startDate, localEndDate);

  // Generate DAG's Start and End Date
  if (dagTZ !== 'UTC' && dagTZ !== localTZ) {
    startDate.tz(dagTZ);
    tooltipHTML += `<br><strong>DAG's TZ: ${startDate.format(tzFormat)}</strong><br>`;
    const dagTZEndDate = endDate && endDate instanceof moment ? endDate.tz(dagTZ) : endDate;
    tooltipHTML += makeDateTimeHTML(startDate, dagTZEndDate);
  }

  return tooltipHTML;
}

export default function tiTooltip(ti, { includeTryNumber = false } = {}) {
  let tt = '';
  if (ti.state !== undefined) {
    tt += `<strong>Status:</strong> ${escapeHtml(ti.state)}<br><br>`;
  }
  if (ti.task_id !== undefined) {
    tt += `Task_id: ${escapeHtml(ti.task_id)}<br>`;
  }
  tt += `Run: ${formatDateTime(ti.execution_date)}<br>`;
  if (ti.run_id !== undefined) {
    tt += `Run Id: <nobr>${escapeHtml(ti.run_id)}</nobr><br>`;
  }
  if (ti.operator !== undefined) {
    tt += `Operator: ${escapeHtml(ti.operator)}<br>`;
  }
  // Don't translate/format this, keep it as the full ISO8601 date
  if (ti.start_date instanceof moment) {
    tt += `Started: ${escapeHtml(ti.start_date.toISOString())}<br>`;
  } else {
    tt += `Started: ${escapeHtml(ti.start_date)}<br>`;
  }
  // Calculate duration on the fly if task instance is still running
  if (ti.state === 'running') {
    const startDate = ti.start_date instanceof moment ? ti.start_date : moment(ti.start_date);
    ti.duration = moment().diff(startDate, 'second');
  }

  tt += `Duration: ${escapeHtml(convertSecsToHumanReadable(ti.duration))}<br>`;

  if (includeTryNumber) {
    tt += `Try Number: ${escapeHtml(ti.try_number)}<br>`;
  }
  // dagTZ has been defined in dag.html
  tt += generateTooltipDateTimes(ti.start_date, ti.end_date, dagTZ);
  return tt;
}

window.tiTooltip = tiTooltip;
<|endoftext|>


<commit_before>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var MultiPartParser = util.MultiPartParser;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	var _params = params;
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (util.isMultipart(req) &&
			/boundary=(?:"([^"]+)"|([^;]+))/i.test(req.headers['content-type'])) {
		delete req.headers['content-length'];
		var boundaryStr = '--' + (RegExp.$1 || RegExp.$2);
		var startBoundary = new Buffer(boundaryStr + '\r\n');
		var boundary = new Buffer('\r\n' + boundaryStr);
		var sepBoundary = new Buffer('\r\n' + boundaryStr + '\r\n');
		var endBoudary = new Buffer('\r\n' + boundaryStr + '--');
		var length = startBoundary.length;
		var sepLength = sepBoundary.length;
		var transform = new Transform();

		transform.parse = function(chunk) {
			var index, result, sep, data;
			while((index = util.indexOfList(chunk, boundary)) != -1
						&& ((sep = util.startWithList(chunk, sepBoundary, index))
								|| util.startWithList(chunk, endBoudary, index))) {
				data = this.parser.transform(chunk.slice(0, index));
				result = result && data ? Buffer.concat([result, data]) : (result || data);
				if (!sep) {
					data = util.toMultiparts(_params, boundaryStr);
					result = result ? Buffer.concat([result, data]) : data;
				}
				sep = sep ? sepBoundary : endBoudary;
				result = result ? Buffer.concat([result, sep]) : sep;
				chunk = chunk.slice(index + sepLength);
				this.parser = new MultiPartParser(_params);
			}

			var len = chunk.length;
			if (len >= sepLength) {
				var lastIndex = len - sepLength + 1;
				data = chunk.slice(0, lastIndex);
				chunk = chunk.slice(lastIndex);
				result = result ? Buffer.concat([result, data]) : data;
			}
			this.buffer = chunk;

			return result;
		};
		transform._transform = function(chunk, encoding, callback) {
			if (this.badMultipart) {
				return callback(null, chunk);
			}

			var end;
			if (chunk) {
				chunk = this.buffer ? Buffer.concat([this.buffer, chunk]) : chunk;
			} else {
				end = true;
				chunk = this.buffer;
			}

			if (chunk) {
				this.buffer = null;
				var len = chunk.length;
				if (!this.parser) {
					if (util.startWithList(chunk, startBoundary)) {
						this.parser = new MultiPartParser(_params);
						chunk = this.parse(chunk.slice(length));
						chunk = chunk ? Buffer.concat([startBoundary, chunk]) : startBoundary;
					} else if(end || chunk.length >= length) {
						this.badMultipart = true;
					} else {
						this.buffer = chunk
						chunk = null;
					}
				} else {
					chunk = this.parse(chunk);
				}
			}

			if (end && this.buffer) {
				chunk = chunk ? Buffer.concat([chunk, this.buffer]) : this.buffer;
			}
			callback(null, chunk);
		};
		req.add(transform);
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!util.hasRequestBody(req) || !replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriters([util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, util.getRuleFile(reqRules.reqWriteRaw)], function(writer, rawWriter) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}

						if (rawWriter) {
							req.add(new FileWriterTransform(rawWriter, req, true));
						}

						next();
					});
				});
		});
	});
};<commit_msg>parse form<commit_after>var qs = require('querystring');
var iconv = require('iconv-lite');
var vm = require('vm');
var util = require('../util');
var extend = require('util')._extend;
var Transform = require('pipestream').Transform;
var WhistleTransform = util.WhistleTransform;
var ReplacePatternTransform = util.ReplacePatternTransform;
var ReplaceStringTransform = util.ReplaceStringTransform;
var FileWriterTransform = util.FileWriterTransform;
var MultiPartParser = util.MultiPartParser;
var MAX_REQ_SIZE = 256 * 1024;
var REQ_TYPE = {
		urlencoded: 'application/x-www-form-urlencoded',
		form: 'application/x-www-form-urlencoded',
		json: 'application/json',
		xml: 'text/xml',
		text: 'text/plain',
		upload: 'multipart/form-data',
		multipart: 'multipart/form-data',
		defaultType: 'application/octet-stream'
};

/**
 * 处理请求数据
 *
 * @param req：method、body、headers，top，bottom，speed、delay，charset,timeout
 * @param data
 */
function handleReq(req, data) {
	req.method = data.method || req.method;
	req.timeout = parseInt(data.timeout, 10);
	extend(req.headers, data.headers);
	var method = req.method = typeof req.method != 'string' ? 'GET' : req.method.toUpperCase();
	if (!util.hasRequestBody(method)) {
		delete data.top;
		delete data.bottom;
		delete data.body;
	} else if (data.top || data.bottom || data.body) {
		delete req.headers['content-length'];
	}

	!util.isEmptyObject(data) && req.add(new WhistleTransform(data));
}

function handleCors(data, cors) {
	if (!cors) {
		return;
	}

	if (cors.origin !== undefined) {
		util.setHeader(data, 'origin', cors.origin);
	}

	if (cors.method !== undefined) {
		util.setHeader(data, 'access-control-request-method', cors.method);
	}

	if (cors.headers !== undefined) {
		util.setHeader(data, 'access-control-request-headers', cors.headers);
	}
}

function handleAuth(data, auth) {
	if (!auth) {
		return;
	}

	var basic = [];
	auth.username != null && basic.push(auth.username);
	auth.password != null && basic.push(auth.password);
	if (basic = basic.join(':')) {
		util.setHeader(data, 'authorization', 'Basic ' + new Buffer(basic).toString('base64'));
	}
}

function handleCookies(data, cookies, curCookies) {
	var list = cookies && Object.keys(cookies);
	if (list && list.length) {
		var result = {};
		if (curCookies && typeof curCookies == 'string') {
			curCookies.split(/;\s*/g)
					.forEach(function(cookie) {
						var index = cookie.indexOf('=');
						if (index == -1) {
							result[cookie] = null;
						} else {
							result[cookie.substring(0, index)] = cookie.substring(index + 1);
						}
					});
		}

		list.forEach(function(name) {
			var value = cookies[name];
			value = value && typeof value == 'object' ? value.value : value;
			result[util.encodeURIComponent(name)] = value ? util.encodeURIComponent(value) : value;
		});

		cookies = Object.keys(result).map(function(name) {
			var value = result[name];
			return name + (value == null ? '' : '=' + value);
		}).join('; ');
		util.setHeader(data, 'cookie', cookies);
	}
}

function handleParams(req, params) {
	var _params = params;
	if (!(params = params && qs.stringify(params))) {
		return;
	}

	if (!req.method || /^GET$/i.test(req.method)) {
		req.options.search = util.replaceUrlQueryString(req.options.search, params);
		req.options.path = util.replaceUrlQueryString(req.options.path, params);
		req.options.href = util.replaceUrlQueryString(req.options.href, params);
	} else if (util.isUrlEncoded(req)) {
		delete req.headers['content-length'];
		var transform = new Transform();
		var buffer, interrupt;
		transform._transform = function(chunk, encoding, callback) {
			if (chunk) {
				if (!interrupt) {
					buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
					chunk = null;
					if (buffer.length > MAX_REQ_SIZE) {
						interrupt = true;
						chunk = buffer;
						buffer = null;
					}
				}
			} else if (buffer) {
				var body = buffer + '';
				var isGBK = body.indexOf('�') == -1;
				if (isGBK) {
					body = iconv.decode(buffer, 'GB18030');
					body = util.replaceQueryString(body, params);
					chunk = iconv.encode(body, 'GB18030');
				} else {
					chunk = new Buffer(body);
				}
				buffer = null;
			} else {
				chunk = new Buffer(params);
			}

			callback(null, chunk);
		};
		req.add(transform);
	} else if (util.isMultipart(req) &&
			/boundary=(?:"([^"]+)"|([^;]+))/i.test(req.headers['content-type'])) {
		delete req.headers['content-length'];
		var boundaryStr = '--' + (RegExp.$1 || RegExp.$2);
		var startBoundary = new Buffer(boundaryStr + '\r\n');
		var boundary = new Buffer('\r\n' + boundaryStr);
		var sepBoundary = new Buffer('\r\n' + boundaryStr + '\r\n');
		var endBoudary = new Buffer('\r\n' + boundaryStr + '--');
		var length = startBoundary.length;
		var sepLength = sepBoundary.length;
		var transform = new Transform();

		transform.parse = function(chunk) {
			var index, result, sep, data;
			while((index = util.indexOfList(chunk, boundary)) != -1
						&& ((sep = util.startWithList(chunk, sepBoundary, index))
								|| util.startWithList(chunk, endBoudary, index))) {
				data = this.parser.transform(chunk.slice(0, index));
				result = result && data ? Buffer.concat([result, data]) : (result || data);
				if (!sep) {
					data = util.toMultiparts(_params, boundaryStr);
					result = result ? Buffer.concat([result, data]) : data;
				}
				sep = sep ? sepBoundary : endBoudary;
				result = result ? Buffer.concat([result, sep]) : sep;
				chunk = chunk.slice(index + sepLength);
				this.parser = new MultiPartParser(_params);
			}

			var len = chunk.length;
			if (len >= sepLength) {
				var lastIndex = len - sepLength + 1;
				data = this.parser.transform(chunk.slice(0, lastIndex));
				chunk = chunk.slice(lastIndex);
				result = result && data ? Buffer.concat([result, data]) : (result || data);
			}
			this.buffer = chunk;

			return result;
		};
		transform._transform = function(chunk, encoding, callback) {
			if (this.badMultipart) {
				return callback(null, chunk);
			}

			var end;
			if (chunk) {
				chunk = this.buffer ? Buffer.concat([this.buffer, chunk]) : chunk;
			} else {
				end = true;
				chunk = this.buffer;
			}

			if (chunk) {
				this.buffer = null;
				var len = chunk.length;
				if (!this.parser) {
					if (util.startWithList(chunk, startBoundary)) {
						this.parser = new MultiPartParser(_params);
						chunk = this.parse(chunk.slice(length));
						chunk = chunk ? Buffer.concat([startBoundary, chunk]) : startBoundary;
					} else if(end || chunk.length >= length) {
						this.badMultipart = true;
					} else {
						this.buffer = chunk
						chunk = null;
					}
				} else {
					chunk = this.parse(chunk);
				}
			}

			if (end && this.buffer) {
				chunk = chunk ? Buffer.concat([chunk, this.buffer]) : this.buffer;
			}
			callback(null, chunk);
		};
		req.add(transform);
	}
}

function removeDisableProps(req) {
	var disable = req.disable;
	var headers = req.headers;

	if (disable.ua) {
		delete headers['user-agent'];
	}

	if (disable.cookie || disable.cookies ||
			disable.reqCookie || disable.reqCookies) {
		delete headers.cookie;
	}

	if (disable.referer) {
		delete headers.referer;
	}

	if (disable.cache) {
		delete headers['if-modified-since'];
		delete headers['if-none-match'];
		delete headers['last-modified'];
		delete headers.etag;

		headers['pragma'] = 'no-cache';
		headers['cache-control'] = 'no-cache';
	}
}

function handleReplace(req, replacement) {
	if (!util.hasRequestBody(req) || !replacement) {
		return;
	}

	var type = req.headers['content-type'];
	type = util.isUrlEncoded(req) ? 'FORM' : util.getContentType(type);
	if (!type || type == 'IMG') {
		return;
	}

	Object.keys(replacement).forEach(function(pattern) {
		var value = replacement[pattern];
		if (util.isOriginalRegExp(pattern) && (pattern = util.toOriginalRegExp(pattern))) {
			req.addTextTransform(new ReplacePatternTransform(pattern, value));
		} else if (pattern) {
			req.addTextTransform(new ReplaceStringTransform(pattern, value));
		}
	});
}

module.exports = function(req, res, next) {
	var reqRules = req.rules;
	util.parseRuleJson([reqRules.req, reqRules.reqHeaders, reqRules.reqCookies,
	                    reqRules.auth, reqRules.params, reqRules.reqCors, reqRules.reqReplace],
			function(data, headers, cookies, auth, params, cors, replacement) {
		if (reqRules.head && reqRules.head.req) {
			data = extend(reqRules.head.req, data);
		}
		data = data || {};
		if (headers) {
			data.headers =  extend(data.headers || {}, headers);
		}

		if (data.headers) {
			data.headers = util.lowerCaseify(data.headers);
		}

		var method = util.getMatcherValue(reqRules.method);
		if (method) {
			data.method = method;
		}

		var type = util.getMatcherValue(reqRules.reqType);
		if (type) {
			type = type.indexOf('/') != -1 ? type : (REQ_TYPE[type] || REQ_TYPE.defaultType);
			util.setHeader(data, 'content-type', type);
		}

		var referer = util.getMatcherValue(reqRules.referer);
		if (referer) {
			util.setHeader(data, 'referer', referer);
		}

		var ua = util.getMatcherValue(reqRules.ua);
		if (ua) {
			util.setHeader(data, 'user-agent', ua);
		}

		var reqDelay = util.getMatcherValue(reqRules.reqDelay);
		reqDelay = reqDelay && parseInt(reqDelay, 10);
		if (reqDelay > 0) {
			data.delay = reqDelay;
		}

		var reqSpeed = util.getMatcherValue(reqRules.reqSpeed);
		reqSpeed = reqSpeed && parseFloat(reqSpeed);
		if (reqSpeed > 0) {
			data.speed = reqSpeed;
		}

		handleCookies(data, cookies, req.headers.cookie);
		handleAuth(data, auth);
		handleCors(data, cors);

		util.readInjectFiles(data, function(data) {
			util.getRuleValue([reqRules.reqBody, reqRules.reqPrepend, reqRules.reqAppend],
				function(reqBody, reqPrepend, reqAppend) {
					if (reqBody) {
						data.body = reqBody;
					}

					if (reqPrepend) {
						data.top = reqPrepend;
					}

					if (reqAppend) {
						data.append = reqAppend;
					}

					handleReq(req, data);
					handleParams(req, params);
					util.removeUnsupportsHeaders(req.headers);
					removeDisableProps(req);
					handleReplace(req, replacement);
					util.getFileWriters([util.hasRequestBody(req) ? util.getRuleFile(reqRules.reqWrite) : null, util.getRuleFile(reqRules.reqWriteRaw)], function(writer, rawWriter) {
						if (writer) {
							req.add(new FileWriterTransform(writer, req));
						}

						if (rawWriter) {
							req.add(new FileWriterTransform(rawWriter, req, true));
						}

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