<commit_before>'use babel';

import {TextEditor} from 'atom';

let BREAKPOINT_CLASS = 'breakpoint';

export default {
  breakpoints: [],
  init(debuggerInstance, state) {
    this.debugger = debuggerInstance;
    this.breakpoints = state;
    this.gutters = [];

    atom.workspace.observeTextEditors((textEditor) => {
      let gutter = textEditor.addGutter({name: 'polybug'});
      let element = document.createElement('div');
      element.className = 'polybug-gutter';
      let path = textEditor.getPath();
      //TODO: do it in proper way
      let lineHeight = textEditor.getLineHeightInPixels() || Math.floor(atom.config.get('editor.fontSize') * atom.config.get('editor.lineHeight'));
      let applyBreakpoint = (breakpoint) => {
        let screenLine = textEditor.bufferPositionForScreenPosition([breakpoint.line, 0]).row;
        let breakpointElement = document.createElement('div');
        breakpointElement.classList.add(BREAKPOINT_CLASS);
        if (breakpoint.condition) {
          breakpointElement.classList.add('conditional');
          breakpointElement.title = breakpoint.condition;
        }
        breakpointElement.style.top = ((screenLine - 1) * lineHeight + (lineHeight - 10) / 2) + 'px';
        breakpointElement.style.left = ((element.clientWidth - 10) / 2) + 'px';
        breakpointElement.dataset.breakpoint = breakpoint.line;
        element.appendChild(breakpointElement);

        breakpointElement.polybugBreakpoint = breakpoint;
        this.addBreakpoint(breakpoint);
      };

      let toggleBreakpoint = (line) => {
        let breakpoint = this.findBreakpoint(path, line);

        if (breakpoint) {
          this.removeBreakpoint(breakpoint, element);
        } else {
          breakpoint = {
            path: path,
            line: line
          };
          applyBreakpoint(breakpoint);
          this.breakpoints.push(breakpoint);
        }
      };

      element.onclick = (event) => {
        let line = Math.floor(event.offsetY / lineHeight);
        let realLine = textEditor.screenPositionForBufferPosition([line, 0]).row + 1;
        toggleBreakpoint(realLine);
      };

      element.oncontextmenu = (event) => {
        event.stopPropagation();
        let line = Math.floor(event.offsetY / lineHeight);
        let realLine = textEditor.screenPositionForBufferPosition([line, 0]).row + 1;
        let breakpoint = this.findBreakpoint(path, realLine);
        let dialog = document.createElement('div');
        dialog.className = 'polybug-condition';
        let input = new TextEditor({mini: true});
        let label = document.createElement('label');
        if (breakpoint && breakpoint.condition) {
          input.setText(breakpoint.condition);
        }
        label.textContent = 'Condition:';
        dialog.appendChild(label);
        dialog.appendChild(input.element);
        panel = atom.workspace.addModalPanel({item: dialog});
        input.element.focus();
        atom.commands.add(dialog, {
          'core:confirm': () => {

            if (breakpoint) {
              this.removeBreakpoint(breakpoint, element);
            }
            breakpoint = {
              path: path,
              line: realLine,
              condition: input.getText()
            };

            applyBreakpoint(breakpoint);
            this.breakpoints.push(breakpoint);
            panel.destroy();
          },
          'core:cancel': () => panel.destroy()
        });
        input.element.onblur = () => panel.destroy();
      };

      let shadowElement = document.createElement('div');
      shadowElement.className = 'breakpoint-shadow';
      shadowElement.style.display = 'none';
      element.appendChild(shadowElement);
      element.onmousemove = (event) => {
        let line = Math.floor(event.offsetY / lineHeight);
        shadowElement.style.top = (line * lineHeight + (lineHeight - 10) / 2) + 'px';
        shadowElement.style.left = ((element.clientWidth - 10) / 2) + 'px';
        shadowElement.style.display = 'block';
      };
      element.onmouseleave = (event) => {
        shadowElement.style.display = 'none';
      };

      // TODO: change it to something sane
      let decoration = gutter.decorateMarker(textEditor.markBufferRange([[0, 0], [100000, 0]]), {
        type: 'gutter',
        class: 'polybug',
        item: element
      });
      this.gutters.push(gutter);

      let mutationObserver = new MutationObserver(() => {
        mutationObserver.disconnect();
        for (let breakpoint of state) {
          if (breakpoint.path == path) {
            applyBreakpoint(breakpoint);
          }
        }
      });

      mutationObserver.observe(element, {attributes: true, subtree: true});
      gutter.onDidDestroy(() => {
        this.gutters.splice(this.gutters.indexOf(gutter), 1);
        decoration.destroy();
      });

      debuggerInstance.on('move', (path, line) => {
        if (textEditor.getPath() === path) {
          line--;
          let screenLine = textEditor.screenPositionForBufferPosition([line, 0]).row;
          for (let oldLine of document.querySelectorAll('.polybug-current')) {
            oldLine.classList.remove('polybug-current');
          }
          let lineElement = document.querySelector('div.line[data-screen-row="' + screenLine + '"]');
          lineElement.classList.add('polybug-current');
        }
      });
      atom.commands.add(textEditor.element, {
        'polybug:breakpoint': () => toggleBreakpoint(textEditor.getCursorBufferPosition().row + 1)
      });
    });
  },

  findBreakpoint(path, line) {
    for (let breakpoint of this.breakpoints) {
      if (breakpoint.path === path && breakpoint.line == line) {
        return breakpoint;
      }
    }
  },

  addBreakpoint(breakpoint) {
    this.debugger.addBreakpoint(breakpoint.path, breakpoint.line, breakpoint.condition);
  },

  destroy() {
    let gutters = this.gutters.slice(0);
    for (let gutter of gutters) {
      gutter.destroy();
    }
  },

  getState() {
    return this.breakpoints;
  },

  removeBreakpoint(breakpoint, element) {
    this.breakpoints.splice(this.breakpoints.indexOf(breakpoint), 1);
    let breakpointElement = element.querySelector('.breakpoint[data-breakpoint="' + breakpoint.line + '"]');
    if (breakpointElement) {
      element.removeChild(breakpointElement);
    }
    this.debugger.removeBreakpoint(breakpoint.path, breakpoint.line);
  }
};
<commit_msg>Fix state error.<commit_after>'use babel';

import {TextEditor} from 'atom';

let BREAKPOINT_CLASS = 'breakpoint';

export default {
  breakpoints: [],
  init(debuggerInstance, state) {
    state = state || []
    this.debugger = debuggerInstance;
    this.breakpoints = state;
    this.gutters = [];

    atom.workspace.observeTextEditors((textEditor) => {
      let gutter = textEditor.addGutter({name: 'polybug'});
      let element = document.createElement('div');
      element.className = 'polybug-gutter';
      let path = textEditor.getPath();
      //TODO: do it in proper way
      let lineHeight = textEditor.getLineHeightInPixels() || Math.floor(atom.config.get('editor.fontSize') * atom.config.get('editor.lineHeight'));
      let applyBreakpoint = (breakpoint) => {
        let screenLine = textEditor.bufferPositionForScreenPosition([breakpoint.line, 0]).row;
        let breakpointElement = document.createElement('div');
        breakpointElement.classList.add(BREAKPOINT_CLASS);
        if (breakpoint.condition) {
          breakpointElement.classList.add('conditional');
          breakpointElement.title = breakpoint.condition;
        }
        breakpointElement.style.top = ((screenLine - 1) * lineHeight + (lineHeight - 10) / 2) + 'px';
        breakpointElement.style.left = ((element.clientWidth - 10) / 2) + 'px';
        breakpointElement.dataset.breakpoint = breakpoint.line;
        element.appendChild(breakpointElement);

        breakpointElement.polybugBreakpoint = breakpoint;
        this.addBreakpoint(breakpoint);
      };

      let toggleBreakpoint = (line) => {
        let breakpoint = this.findBreakpoint(path, line);

        if (breakpoint) {
          this.removeBreakpoint(breakpoint, element);
        } else {
          breakpoint = {
            path: path,
            line: line
          };
          applyBreakpoint(breakpoint);
          this.breakpoints.push(breakpoint);
        }
      };

      element.onclick = (event) => {
        let line = Math.floor(event.offsetY / lineHeight);
        let realLine = textEditor.screenPositionForBufferPosition([line, 0]).row + 1;
        toggleBreakpoint(realLine);
      };

      element.oncontextmenu = (event) => {
        event.stopPropagation();
        let line = Math.floor(event.offsetY / lineHeight);
        let realLine = textEditor.screenPositionForBufferPosition([line, 0]).row + 1;
        let breakpoint = this.findBreakpoint(path, realLine);
        let dialog = document.createElement('div');
        dialog.className = 'polybug-condition';
        let input = new TextEditor({mini: true});
        let label = document.createElement('label');
        if (breakpoint && breakpoint.condition) {
          input.setText(breakpoint.condition);
        }
        label.textContent = 'Condition:';
        dialog.appendChild(label);
        dialog.appendChild(input.element);
        panel = atom.workspace.addModalPanel({item: dialog});
        input.element.focus();
        atom.commands.add(dialog, {
          'core:confirm': () => {

            if (breakpoint) {
              this.removeBreakpoint(breakpoint, element);
            }
            breakpoint = {
              path: path,
              line: realLine,
              condition: input.getText()
            };

            applyBreakpoint(breakpoint);
            this.breakpoints.push(breakpoint);
            panel.destroy();
          },
          'core:cancel': () => panel.destroy()
        });
        input.element.onblur = () => panel.destroy();
      };

      let shadowElement = document.createElement('div');
      shadowElement.className = 'breakpoint-shadow';
      shadowElement.style.display = 'none';
      element.appendChild(shadowElement);
      element.onmousemove = (event) => {
        let line = Math.floor(event.offsetY / lineHeight);
        shadowElement.style.top = (line * lineHeight + (lineHeight - 10) / 2) + 'px';
        shadowElement.style.left = ((element.clientWidth - 10) / 2) + 'px';
        shadowElement.style.display = 'block';
      };
      element.onmouseleave = (event) => {
        shadowElement.style.display = 'none';
      };

      // TODO: change it to something sane
      let decoration = gutter.decorateMarker(textEditor.markBufferRange([[0, 0], [100000, 0]]), {
        type: 'gutter',
        class: 'polybug',
        item: element
      });
      this.gutters.push(gutter);

      let mutationObserver = new MutationObserver(() => {
        mutationObserver.disconnect();
        for (let breakpoint of state) {
          if (breakpoint.path == path) {
            applyBreakpoint(breakpoint);
          }
        }
      });

      mutationObserver.observe(element, {attributes: true, subtree: true});
      gutter.onDidDestroy(() => {
        this.gutters.splice(this.gutters.indexOf(gutter), 1);
        decoration.destroy();
      });

      debuggerInstance.on('move', (path, line) => {
        if (textEditor.getPath() === path) {
          line--;
          let screenLine = textEditor.screenPositionForBufferPosition([line, 0]).row;
          for (let oldLine of document.querySelectorAll('.polybug-current')) {
            oldLine.classList.remove('polybug-current');
          }
          let lineElement = document.querySelector('div.line[data-screen-row="' + screenLine + '"]');
          lineElement.classList.add('polybug-current');
        }
      });
      atom.commands.add(textEditor.element, {
        'polybug:breakpoint': () => toggleBreakpoint(textEditor.getCursorBufferPosition().row + 1)
      });
    });
  },

  findBreakpoint(path, line) {
    for (let breakpoint of this.breakpoints) {
      if (breakpoint.path === path && breakpoint.line == line) {
        return breakpoint;
      }
    }
  },

  addBreakpoint(breakpoint) {
    this.debugger.addBreakpoint(breakpoint.path, breakpoint.line, breakpoint.condition);
  },

  destroy() {
    let gutters = this.gutters.slice(0);
    for (let gutter of gutters) {
      gutter.destroy();
    }
  },

  getState() {
    return this.breakpoints;
  },

  removeBreakpoint(breakpoint, element) {
    this.breakpoints.splice(this.breakpoints.indexOf(breakpoint), 1);
    let breakpointElement = element.querySelector('.breakpoint[data-breakpoint="' + breakpoint.line + '"]');
    if (breakpointElement) {
      element.removeChild(breakpointElement);
    }
    this.debugger.removeBreakpoint(breakpoint.path, breakpoint.line);
  }
};
<|endoftext|>


<commit_before>/*global
  jQuery: false,
  window: false
*/

/*jshint
  bitwise: true,
  browser: true,
  eqeqeq: true,
  indent: 2,
  newcap: true,
  nomen: true,
  regexp: true,
  undef: true,
  white: true
*/

(function ($) {
  /* Console */
  var console;
  if (window.console) {
    console = window.console;
  }
  else {
    console = {
      log: function (message) {
        window.alert(message);
      }
    };
  }

  var $body = $('body'), $title = $('
');

  /* Button */
  var $button = $.ninja().button({
    html: 'Default'
  }),
  $buttonStates = $.ninja().button({
    html: 'Disabled'
  }).deselect(function () {
    console.log('Deselected button.');
  }).enable(function () {
    console.log('Enabled button.');
  }).select(function () {
    console.log('Selected button.');
  }),
  $buttonChangeState = $.ninja().button({
    html: 'Disable/Enable',
    select: true
  }).toggle(function () {
    $buttonStates.enable();
  }, function () {
    $buttonStates.disable();
  }),
  $buttonIcon = $.ninja().button({
    html: $('').append($.ninja().icon({
      name: 'ninja'
    }), ' With Icon')
  });
  $body.append($title.clone().text('Button'), $button, ' ', $buttonIcon, ' ', $buttonStates, ' ', $buttonChangeState);
  $buttonStates.disable().disable(function () {
    console.log('Disabled button.');
  });

  /* Drawer */
  var $drawer = $.ninja().drawer({
    html: 'Content of the drawer',
    title: 'Open Sesame'
  });
  $body.append($title.clone().text('Drawer'), $drawer);

  /* Icon */
  var $icons = $('
');
  $.each($.ninja().icons, function (name, character) {
    var $icon = $.ninja().icon({
      css: {
        fontSize: '2em',
        margin: '0.25em'
      },
      name: name
    }).attr({
      title: name
    }).mouseenter(function () {
      $icon.css({
        color: '#900'
      });
    }).mouseleave(function () {
      $icon.css({
        color: 'black'
      });
    });
    $icons.append($icon);
  });
  $body.append($title.clone().text('Icon'), $icons);

  /* Pop-Up */
  var $popup;
  var $buttonPopup = $.ninja().button({
    html: 'Pop-Up'
  }).append($.ninja().icon({
    name: 'arrowDown'
  })).select(function () {
    $popup = $buttonPopup.popup({
      html: $('
', {
        css: {
          whiteSpace: 'nowrap'
        },
        text: 'Preloaded Content.'
      })
    });
  }).deselect(function () {
    $popup.detach();
  });
  var $popupList;
  var $buttonPopupList = $.ninja().button({
    html: 'Pop-Up List'
  }).append($.ninja().icon({
    name: 'arrowDown'
  })).select(function () {
    $popupList = $buttonPopupList.popup({
      html: $.ninja().list({
        choices: [{
          html: $('
', {
            text: 'Choose me!'
          }),
          select: function () {
            console.log('Local select function called.');
          }
        },
        {
          html: $('
'),
          spacer: true
        },
        {
          html: $('
', {
            text: 'No, choose me!'
          })
        }]
      }).select(function (event) {
        console.log('Global select function called returning: ' + $(event.html).text());
        $popupList.detach();
      })
    });
  }).deselect(function () {
    $popupList.detach();
  });

  var $popupWindow;
  var $buttonPopupWindow = $.ninja().button({
    html: 'Pop-Up Window'
  }).select(function () {
    $popupWindow = $buttonPopupWindow.ninja().popup({
      html: $.ninja().spinner(),
      button: true,
      window: true
    });
    /* Fake asynchronous delay */
    setTimeout(function () {
      $popupWindow.update({
        html: 'Content loaded.'
      });
    }, 2000);
  }).deselect(function () {
    $popupWindow.detach();
  });
  $body.append($title.clone().text('Pop-Up'), $buttonPopup, ' ', $buttonPopupList, ' ', $buttonPopupWindow);

  /* Rating */
  var $rating = $.ninja().rating({
    choices: [{
      html: $('
', {
        text: 'One star.'
      }),
      select: function () {
        console.log('Local select function called.');
      }
    },
    {
      html: $('
', {
        text: 'Two stars.'
      })
    },
    {
      html: $('
', {
        text: 'Three stars.'
      })
    },
    {
      html: $('
', {
        text: 'Four stars.'
      })
    },
    {
      html: $('
', {
        text: 'Five stars.'
      })
    }],
    starsAverage: 3
  }).select(function (event) {
    console.log('Global select function called returning: ' + event.html.text());
  });
  $body.append($title.clone().text('Rating'), $rating);

  /* Slider */
  var $slider = $.ninja().slider({
    choices: [{
      html: '0 dB',
      select: function (event) {
        console.log(event.choice.html);
      }
    },
    {
      html: '10 dB',
      select: function (event) {
        console.log(event.choice.html + ': Light leaf rustling, calm breathing');
      }
    },
    {
      html: '20-30 dB',
      select: function (event) {
        console.log(event.choice.html + ': Very calm room');
      }

    },
    {
      html: '40-60 dB',
      select: function (event) {
        console.log(event.choice.html + ': Normal conversation at 1 m');
      }
    },
    {
      html: '60 dB',
      select: function (event) {
        console.log(event.choice.html + ': TV set at home level at 1 m');
      }
    },
    {
      html: '60-80 dB',
      select: function (event) {
        console.log(event.choice.html + ': Passenger car at 10 m');
      }
    },
    {
      html: '78 dB',
      select: function (event) {
        console.log(event.choice.html + ': Hearing damage over long-term exposure, need not be continuous');
      }
    },
    {
      html: '80-90 dB',
      select: function (event) {
        console.log(event.choice.html + ': Traffic on a busy roadway at 10 m');
      }
    },
    {
      html: '100 dB',
      select: function (event) {
        console.log(event.choice.html + ': Jack hammer at 1 m');
      }
    },
    {
      html: '120 dB',
      select: function (event) {
        console.log(event.choice.html + ': Hearing damage immediately possible');
      }
    },
    {
      html: '130 dB',
      select: function (event) {
        console.log(event.choice.html + ': Threshold of pain');
      }
    },
    {
      html: '150 dB',
      select: function (event) {
        console.log(event.choice.html + ': Jet engine at 30 m');
      }
    },
    {
      html: '168 dB',
      select: function (event) {
        console.log(event.choice.html + ': M1 Garand rifle being fired at 1 m');
      }
    }],
    slot: 3,
    title: 'Volume'
  }).select(function (event) {
    console.log('Slider returns: ' + event.choice.html);
  });
  $body.append($title.clone().text('Slider'), $slider);

  /* Spinner */
  var $spinner = $.ninja().spinner({
    css: {
      fontSize: '1.7em'
    },
    speed: 100
  });
  $body.append($title.clone().text('Spinner'), $spinner);

  /* Suggest */
  var $suggest = $.ninja().suggest({
    css: {
      width: '50%'
    },
    html: $.ninja().icon({
      name: 'search'
    }),
    placeholder: 'Ninja UI Search'
  }).change(function (event) {
    $.ajax({
      url: 'http://clients1.google.com/complete/search',
      dataType: 'jsonp',
      data: {
        q: event.value,
        ds: '',
        nolabels: 't'
      },
      success: function (data, message) {
        $suggest.update({
          choices: $.map(data[1], function (item) {
            return {
              html: item[0]
            };
          })
        });
      },
      error: function (request, status, message) {
        console.error(message);
        $suggest.error(message);
      },
      timeout: 3000
    });
  }).select(function (event) {
    console.log('Global select function called returning: ' + event.html);
  });
  $body.append($title.clone().text('Suggest'), $suggest);

  /* Tabs */
  var $tabs = $.ninja().tabs({
    choices: [{
      html: 'One',
      select: function () {
        console.log('Local tab function called.');
      }
    },
    {
      html: 'Two',
    },
    {
      html: 'Three',
    }],
    choice: 2
  }).select(function (event) {
    console.log('Global tab function called returning: ' + event.html);
  });
  $body.append($title.clone().text('Tabs'), $tabs);

}(jQuery));<commit_msg>Decreases fake window load to 1 second.<commit_after>/*global
  jQuery: false,
  window: false
*/

/*jshint
  bitwise: true,
  browser: true,
  eqeqeq: true,
  indent: 2,
  newcap: true,
  nomen: true,
  regexp: true,
  undef: true,
  white: true
*/

(function ($) {
  /* Console */
  var console;
  if (window.console) {
    console = window.console;
  }
  else {
    console = {
      log: function (message) {
        window.alert(message);
      }
    };
  }

  var $body = $('body'), $title = $('
');

  /* Button */
  var $button = $.ninja().button({
    html: 'Default'
  }),
  $buttonStates = $.ninja().button({
    html: 'Disabled'
  }).deselect(function () {
    console.log('Deselected button.');
  }).enable(function () {
    console.log('Enabled button.');
  }).select(function () {
    console.log('Selected button.');
  }),
  $buttonChangeState = $.ninja().button({
    html: 'Disable/Enable',
    select: true
  }).toggle(function () {
    $buttonStates.enable();
  }, function () {
    $buttonStates.disable();
  }),
  $buttonIcon = $.ninja().button({
    html: $('').append($.ninja().icon({
      name: 'ninja'
    }), ' With Icon')
  });
  $body.append($title.clone().text('Button'), $button, ' ', $buttonIcon, ' ', $buttonStates, ' ', $buttonChangeState);
  $buttonStates.disable().disable(function () {
    console.log('Disabled button.');
  });

  /* Drawer */
  var $drawer = $.ninja().drawer({
    html: 'Content of the drawer',
    title: 'Open Sesame'
  });
  $body.append($title.clone().text('Drawer'), $drawer);

  /* Icon */
  var $icons = $('
');
  $.each($.ninja().icons, function (name, character) {
    var $icon = $.ninja().icon({
      css: {
        fontSize: '2em',
        margin: '0.25em'
      },
      name: name
    }).attr({
      title: name
    }).mouseenter(function () {
      $icon.css({
        color: '#900'
      });
    }).mouseleave(function () {
      $icon.css({
        color: 'black'
      });
    });
    $icons.append($icon);
  });
  $body.append($title.clone().text('Icon'), $icons);

  /* Pop-Up */
  var $popup;
  var $buttonPopup = $.ninja().button({
    html: 'Pop-Up'
  }).append($.ninja().icon({
    name: 'arrowDown'
  })).select(function () {
    $popup = $buttonPopup.popup({
      html: $('
', {
        css: {
          whiteSpace: 'nowrap'
        },
        text: 'Preloaded Content.'
      })
    });
  }).deselect(function () {
    $popup.detach();
  });
  var $popupList;
  var $buttonPopupList = $.ninja().button({
    html: 'Pop-Up List'
  }).append($.ninja().icon({
    name: 'arrowDown'
  })).select(function () {
    $popupList = $buttonPopupList.popup({
      html: $.ninja().list({
        choices: [{
          html: $('
', {
            text: 'Choose me!'
          }),
          select: function () {
            console.log('Local select function called.');
          }
        },
        {
          html: $('
'),
          spacer: true
        },
        {
          html: $('
', {
            text: 'No, choose me!'
          })
        }]
      }).select(function (event) {
        console.log('Global select function called returning: ' + $(event.html).text());
        $popupList.detach();
      })
    });
  }).deselect(function () {
    $popupList.detach();
  });

  var $popupWindow;
  var $buttonPopupWindow = $.ninja().button({
    html: 'Pop-Up Window'
  }).select(function () {
    $popupWindow = $buttonPopupWindow.ninja().popup({
      html: $.ninja().spinner(),
      button: true,
      window: true
    });
    /* Fake asynchronous delay */
    setTimeout(function () {
      $popupWindow.update({
        html: 'Content loaded.'
      });
    }, 1000);
  }).deselect(function () {
    $popupWindow.detach();
  });
  $body.append($title.clone().text('Pop-Up'), $buttonPopup, ' ', $buttonPopupList, ' ', $buttonPopupWindow);

  /* Rating */
  var $rating = $.ninja().rating({
    choices: [{
      html: $('
', {
        text: 'One star.'
      }),
      select: function () {
        console.log('Local select function called.');
      }
    },
    {
      html: $('
', {
        text: 'Two stars.'
      })
    },
    {
      html: $('
', {
        text: 'Three stars.'
      })
    },
    {
      html: $('
', {
        text: 'Four stars.'
      })
    },
    {
      html: $('
', {
        text: 'Five stars.'
      })
    }],
    starsAverage: 3
  }).select(function (event) {
    console.log('Global select function called returning: ' + event.html.text());
  });
  $body.append($title.clone().text('Rating'), $rating);

  /* Slider */
  var $slider = $.ninja().slider({
    choices: [{
      html: '0 dB',
      select: function (event) {
        console.log(event.choice.html);
      }
    },
    {
      html: '10 dB',
      select: function (event) {
        console.log(event.choice.html + ': Light leaf rustling, calm breathing');
      }
    },
    {
      html: '20-30 dB',
      select: function (event) {
        console.log(event.choice.html + ': Very calm room');
      }

    },
    {
      html: '40-60 dB',
      select: function (event) {
        console.log(event.choice.html + ': Normal conversation at 1 m');
      }
    },
    {
      html: '60 dB',
      select: function (event) {
        console.log(event.choice.html + ': TV set at home level at 1 m');
      }
    },
    {
      html: '60-80 dB',
      select: function (event) {
        console.log(event.choice.html + ': Passenger car at 10 m');
      }
    },
    {
      html: '78 dB',
      select: function (event) {
        console.log(event.choice.html + ': Hearing damage over long-term exposure, need not be continuous');
      }
    },
    {
      html: '80-90 dB',
      select: function (event) {
        console.log(event.choice.html + ': Traffic on a busy roadway at 10 m');
      }
    },
    {
      html: '100 dB',
      select: function (event) {
        console.log(event.choice.html + ': Jack hammer at 1 m');
      }
    },
    {
      html: '120 dB',
      select: function (event) {
        console.log(event.choice.html + ': Hearing damage immediately possible');
      }
    },
    {
      html: '130 dB',
      select: function (event) {
        console.log(event.choice.html + ': Threshold of pain');
      }
    },
    {
      html: '150 dB',
      select: function (event) {
        console.log(event.choice.html + ': Jet engine at 30 m');
      }
    },
    {
      html: '168 dB',
      select: function (event) {
        console.log(event.choice.html + ': M1 Garand rifle being fired at 1 m');
      }
    }],
    slot: 3,
    title: 'Volume'
  }).select(function (event) {
    console.log('Slider returns: ' + event.choice.html);
  });
  $body.append($title.clone().text('Slider'), $slider);

  /* Spinner */
  var $spinner = $.ninja().spinner({
    css: {
      fontSize: '1.7em'
    },
    speed: 100
  });
  $body.append($title.clone().text('Spinner'), $spinner);

  /* Suggest */
  var $suggest = $.ninja().suggest({
    css: {
      width: '50%'
    },
    html: $.ninja().icon({
      name: 'search'
    }),
    placeholder: 'Ninja UI Search'
  }).change(function (event) {
    $.ajax({
      url: 'http://clients1.google.com/complete/search',
      dataType: 'jsonp',
      data: {
        q: event.value,
        ds: '',
        nolabels: 't'
      },
      success: function (data, message) {
        $suggest.update({
          choices: $.map(data[1], function (item) {
            return {
              html: item[0]
            };
          })
        });
      },
      error: function (request, status, message) {
        console.error(message);
        $suggest.error(message);
      },
      timeout: 3000
    });
  }).select(function (event) {
    console.log('Global select function called returning: ' + event.html);
  });
  $body.append($title.clone().text('Suggest'), $suggest);

  /* Tabs */
  var $tabs = $.ninja().tabs({
    choices: [{
      html: 'One',
      select: function () {
        console.log('Local tab function called.');
      }
    },
    {
      html: 'Two',
    },
    {
      html: 'Three',
    }],
    choice: 2
  }).select(function (event) {
    console.log('Global tab function called returning: ' + event.html);
  });
  $body.append($title.clone().text('Tabs'), $tabs);

}(jQuery));<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed 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.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

import * as base64 from './base64';
import { keys, objectTypes } from './constants';

const {id: idKey, realm: realmKey} = keys;
const registeredCallbacks = [];
const typeConverters = {};

let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
let sessionHost;
let sessionId;

// Check if XMLHttpRequest has been overridden, and get the native one if that's the case.
if (XMLHttpRequest.__proto__ != global.XMLHttpRequestEventTarget) {
    let fakeXMLHttpRequest = XMLHttpRequest;
    delete global.XMLHttpRequest;
    XMLHttpRequest = global.XMLHttpRequest;
    global.XMLHttpRequest = fakeXMLHttpRequest;
}

registerTypeConverter(objectTypes.DATA, (_, {value}) => base64.decode(value));
registerTypeConverter(objectTypes.DATE, (_, {value}) => new Date(value));
registerTypeConverter(objectTypes.DICT, deserializeDict);

export function registerTypeConverter(type, handler) {
    typeConverters[type] = handler;
}

export function createSession(host) {
    sessionId = sendRequest('create_session', null, host);
    sessionHost = host;

    return sessionId;
}

export function createRealm(args) {
    if (args) {
        args = args.map((arg) => serialize(null, arg));
    }

    return sendRequest('create_realm', {arguments: args});
}

export function callMethod(realmId, id, name, args) {
    if (args) {
        args = args.map((arg) => serialize(realmId, arg));
    }

    let result = sendRequest('call_method', {realmId, id, name, arguments: args});
    return deserialize(realmId, result);
}

export function getProperty(realmId, id, name) {
    let result = sendRequest('get_property', {realmId, id, name});
    return deserialize(realmId, result);
}

export function setProperty(realmId, id, name, value) {
    value = serialize(realmId, value);
    sendRequest('set_property', {realmId, id, name, value});
}

export function clearTestState() {
    sendRequest('clear_test_state');

    // Clear all registered callbacks.
    registeredCallbacks.length = 0;
}

function registerCallback(callback) {
    let key = registeredCallbacks.indexOf(callback);
    return key >= 0 ? key : (registeredCallbacks.push(callback) - 1);
}

function serialize(realmId, value) {
    if (typeof value == 'undefined') {
        return {type: objectTypes.UNDEFINED};
    }
    if (typeof value == 'function') {
        return {type: objectTypes.FUNCTION, value: registerCallback(value)};
    }
    if (!value || typeof value != 'object') {
        return {value: value};
    }

    let id = value[idKey];
    if (id) {
        if (value[realmKey] != realmId) {
            throw new Error('Unable to serialize value from another Realm');
        }

        return {id};
    }

    if (value instanceof Date) {
        return {type: objectTypes.DATE, value: value.getTime()};
    }

    if (Array.isArray(value)) {
        let array = value.map((item) => serialize(realmId, item));
        return {value: array};
    }

    if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
        return {type: objectTypes.DATA, value: base64.encode(value)};
    }

    let keys = Object.keys(value);
    let values = keys.map((key) => serialize(realmId, value[key]));
    return {type: objectTypes.DICT, keys, values};
}

function deserialize(realmId, info) {
    let type = info.type;
    let handler = type && typeConverters[type];
    if (handler) {
        return handler(realmId, info);
    }

    let value = info.value;
    if (value && Array.isArray(value)) {
        return value.map((item) => deserialize(realmId, item));
    }

    return value;
}

function deserializeDict(realmId, info) {
    let {keys, values} = info;
    let object = {};

    for (let i = 0, len = keys.length; i < len; i++) {
        object[keys[i]] = deserialize(realmId, values[i]);
    }

    return object;
}

function sendRequest(command, data, host = sessionHost) {
    if (!host) {
        throw new Error('Must first create RPC session with a valid host');
    }

    data = Object.assign({}, data, sessionId ? {sessionId} : null);

    let body = JSON.stringify(data);
    let request = new XMLHttpRequest();
    let url = 'http://' + host + '/' + command;

    request.open('POST', url, false);
    request.send(body);

    if (request.status != 200) {
        throw new Error(request.responseText);
    }

    let response = JSON.parse(request.responseText);

    if (!response || response.error) {
        let error = response && response.error;

        // Remove the type prefix from the error message (e.g. "Error: ").
        if (error) {
            error = error.replace(/^[a-z]+: /i, '');
        }

        throw new Error(error || `Invalid response for "${command}"`);
    }

    let callback = response.callback;
    if (callback != null) {
        let result;
        let error;
        try {
            let realmId = data.realmId;
            let args = deserialize(realmId, response.arguments);
            result = registeredCallbacks[callback].apply(null, args);
            result = serialize(realmId, result);
        } catch (e) {
            error = e.message || ('' + e);
        }
        return sendRequest('callback_result', {callback, result, error});
    }

    return response.result;
}
<commit_msg>Remove unnecessary cross-Realm check in rpc<commit_after>////////////////////////////////////////////////////////////////////////////
//
// Copyright 2016 Realm Inc.
//
// Licensed 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.
//
////////////////////////////////////////////////////////////////////////////

'use strict';

import * as base64 from './base64';
import { keys, objectTypes } from './constants';

const {id: idKey, realm: realmKey} = keys;
const registeredCallbacks = [];
const typeConverters = {};

let XMLHttpRequest = global.originalXMLHttpRequest || global.XMLHttpRequest;
let sessionHost;
let sessionId;

// Check if XMLHttpRequest has been overridden, and get the native one if that's the case.
if (XMLHttpRequest.__proto__ != global.XMLHttpRequestEventTarget) {
    let fakeXMLHttpRequest = XMLHttpRequest;
    delete global.XMLHttpRequest;
    XMLHttpRequest = global.XMLHttpRequest;
    global.XMLHttpRequest = fakeXMLHttpRequest;
}

registerTypeConverter(objectTypes.DATA, (_, {value}) => base64.decode(value));
registerTypeConverter(objectTypes.DATE, (_, {value}) => new Date(value));
registerTypeConverter(objectTypes.DICT, deserializeDict);

export function registerTypeConverter(type, handler) {
    typeConverters[type] = handler;
}

export function createSession(host) {
    sessionId = sendRequest('create_session', null, host);
    sessionHost = host;

    return sessionId;
}

export function createRealm(args) {
    if (args) {
        args = args.map((arg) => serialize(null, arg));
    }

    return sendRequest('create_realm', {arguments: args});
}

export function callMethod(realmId, id, name, args) {
    if (args) {
        args = args.map((arg) => serialize(realmId, arg));
    }

    let result = sendRequest('call_method', {realmId, id, name, arguments: args});
    return deserialize(realmId, result);
}

export function getProperty(realmId, id, name) {
    let result = sendRequest('get_property', {realmId, id, name});
    return deserialize(realmId, result);
}

export function setProperty(realmId, id, name, value) {
    value = serialize(realmId, value);
    sendRequest('set_property', {realmId, id, name, value});
}

export function clearTestState() {
    sendRequest('clear_test_state');

    // Clear all registered callbacks.
    registeredCallbacks.length = 0;
}

function registerCallback(callback) {
    let key = registeredCallbacks.indexOf(callback);
    return key >= 0 ? key : (registeredCallbacks.push(callback) - 1);
}

function serialize(realmId, value) {
    if (typeof value == 'undefined') {
        return {type: objectTypes.UNDEFINED};
    }
    if (typeof value == 'function') {
        return {type: objectTypes.FUNCTION, value: registerCallback(value)};
    }
    if (!value || typeof value != 'object') {
        return {value: value};
    }

    let id = value[idKey];
    if (id) {
        return {id};
    }

    if (value instanceof Date) {
        return {type: objectTypes.DATE, value: value.getTime()};
    }

    if (Array.isArray(value)) {
        let array = value.map((item) => serialize(realmId, item));
        return {value: array};
    }

    if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
        return {type: objectTypes.DATA, value: base64.encode(value)};
    }

    let keys = Object.keys(value);
    let values = keys.map((key) => serialize(realmId, value[key]));
    return {type: objectTypes.DICT, keys, values};
}

function deserialize(realmId, info) {
    let type = info.type;
    let handler = type && typeConverters[type];
    if (handler) {
        return handler(realmId, info);
    }

    let value = info.value;
    if (value && Array.isArray(value)) {
        return value.map((item) => deserialize(realmId, item));
    }

    return value;
}

function deserializeDict(realmId, info) {
    let {keys, values} = info;
    let object = {};

    for (let i = 0, len = keys.length; i < len; i++) {
        object[keys[i]] = deserialize(realmId, values[i]);
    }

    return object;
}

function sendRequest(command, data, host = sessionHost) {
    if (!host) {
        throw new Error('Must first create RPC session with a valid host');
    }

    data = Object.assign({}, data, sessionId ? {sessionId} : null);

    let body = JSON.stringify(data);
    let request = new XMLHttpRequest();
    let url = 'http://' + host + '/' + command;

    request.open('POST', url, false);
    request.send(body);

    if (request.status != 200) {
        throw new Error(request.responseText);
    }

    let response = JSON.parse(request.responseText);

    if (!response || response.error) {
        let error = response && response.error;

        // Remove the type prefix from the error message (e.g. "Error: ").
        if (error) {
            error = error.replace(/^[a-z]+: /i, '');
        }

        throw new Error(error || `Invalid response for "${command}"`);
    }

    let callback = response.callback;
    if (callback != null) {
        let result;
        let error;
        try {
            let realmId = data.realmId;
            let args = deserialize(realmId, response.arguments);
            result = registeredCallbacks[callback].apply(null, args);
            result = serialize(realmId, result);
        } catch (e) {
            error = e.message || ('' + e);
        }
        return sendRequest('callback_result', {callback, result, error});
    }

    return response.result;
}
<|endoftext|>


<commit_before>var uuid = require('node-uuid');
var now = require("performance-now")

function Stopwatch( autostart) {
    if (autostart) this.start();
}

Stopwatch.prototype.start = function() {
    var self = this;
    self.startTime = now();
    // console.log('starting stopwatch %s at %s ', self.name, self.startTime);
};

Stopwatch.prototype.stop = function() {
    this.stopTime = now();
};

Stopwatch.prototype.read = function() {
    var self = this;
    var startTime = self.startTime;
    var nowTime;
    var delta;

    if (startTime) {
        if (self.stopTime) {
            nowTime = self.stopTime;
        } else {
            nowTime = now();
        }

        delta = calculateDelta(startTime, nowTime);
    } else {
        nowTime = undefined;
        delta = NaN;
    }

    // console.log('reading stopwatch %s at %s for delta reading of %s ', self.name, nowTime, delta);
    return delta;

    function calculateDelta(start, end) {
        return end - start;
    }
};

module.exports = Stopwatch;<commit_msg>formatting<commit_after>var uuid = require('node-uuid');
var now = require("performance-now")

function Stopwatch( autostart) {
    if (autostart) this.start();
}

Stopwatch.prototype.start = function() {
    this.startTime = now();
    // console.log('starting stopwatch %s at %s ', self.name, self.startTime);
};

Stopwatch.prototype.stop = function() {
    this.stopTime = now();
};

Stopwatch.prototype.read = function() {
    var self = this;
    var startTime = self.startTime;
    var nowTime;
    var delta;

    if (startTime) {
        if (self.stopTime) {
            nowTime = self.stopTime;
        } else {
            nowTime = now();
        }

        delta = calculateDelta(startTime, nowTime);
    } else {
        nowTime = undefined;
        delta = NaN;
    }

    // console.log('reading stopwatch %s at %s for delta reading of %s ', self.name, nowTime, delta);
    return delta;

    function calculateDelta(start, end) {
        return end - start;
    }
};

module.exports = Stopwatch;<|endoftext|>


<commit_before>/**
 * Browsertime (http://www.browsertime.net)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';

var stats = require('./statistics'),
	fs = require('fs'),
	async = require('async'),
	path = require('path'),
	logger = require('./logger'),
	helper = require('./helper'),
	url = require('url'),
	util = require('util'),
	EventEmitter = require('async-node-events'),
	webdriver = require('selenium-webdriver');

var defaultWaitScript = 'return window.performance.timing.loadEventEnd>0';

function Browsertime(b) {
	this.log = logger.getLog();
	EventEmitter.call(this);
	this.result = {
		builtin: {
			static: {},
			dynamic: []
		},
		custom: {
			static: {},
			dynamic: []
		}
	};
	this.builtinScripts = {
		static: {},
		dynamic: {}
	};
	this.customScripts = {
		static: {},
		dynamic: {}
	};
	this.browserVersion = '';
	this.os = '';
	this.browserName = '';
	this.browsers = b;
}

util.inherits(Browsertime, EventEmitter);

Browsertime.prototype.fetch = function(options, callback) {

	callback = callback || function() {};

	this._setupDefaults(options);
	this._populateScripts(options);

	var self = this;

	async.series([
		function(cb) {
			self.emit('beforeRun', cb);
		},
		function(cb) {
			self._do(options, cb);
		},
		function(cb) {
			self.emit('afterRun', cb);
		}
	], function(err) {
		callback(err);
	});
};

Browsertime.prototype._do = function(options, cb) {
	var browserRuns = [];
	var self = this;
	for (var i = 1; i <= options.runs; i++) {
		browserRuns.push(self._collectFromTheBrowser(options, i, options.runs));
	}

	async.series(browserRuns,
		function(err) {
			if (err) {
				self.log.error(err.message);
				return cb(err);
			}
			var data = self._getFormattedResult(options);
			self._saveToFile(data, options, cb);
		});
};

Browsertime.prototype._collectFromTheBrowser = function(options, iteration, totalIterations) {
	var self = this,
		log = this.log;
	var pageUrl = options.url,
		pageLoadTimeout = 60000;

	var waitScript = options.waitScript || defaultWaitScript;

	return function(callback) {
		webdriver.promise.controlFlow().on('uncaughtException', function(e) {
			callback(e);
		});

		async.series([
				function(cb) {
					self.emit('callingBrowser', cb);
				}
			],
			function(err) {
				if (err) {
					return callback(err);
				}

				var driver = self.browsers.getBrowser(options.browser).getDriver(options);

				driver.getCapabilities().then(function(cap) {
					self.browserVersion = cap.get('version');
					self.os = cap.get('platform');
					self.browserName = cap.get('browserName');
				});

				// Not supported in Safari:
				// https://code.google.com/p/selenium/issues/detail?id=6015
				if (options.browser !== 'safari') {
					driver.manage().timeouts().pageLoadTimeout(60000);
				}
				driver.manage().timeouts().setScriptTimeout(60000);

				driver.manage().window().setPosition(0, 0);

				var windowSize = helper.parseWindowSize(options.size, log);
				if (windowSize) {
					driver.manage().window().setSize(windowSize.x, windowSize.y);
				}

				// fetch the URL and wait until the load event ends or we get the time out
				log.info('Fetching ' + pageUrl + ' (' + iteration + ' of ' + totalIterations + ')');
				driver.get(pageUrl);

				var afterFetchTime = Date.now();

				driver.wait(function() {
						return driver.executeScript(waitScript)
							.then(function(b) {
								return b;
							});
					},
					pageLoadTimeout
				).then(function() {
						var afterLoadTime = Date.now();

						log.verbose('loading url took %d milliseconds', (afterLoadTime - afterFetchTime));
						// This is needed since the Firefox driver executes the success callback even when driver.wait
						// took too long.
						if ((afterLoadTime - afterFetchTime) > pageLoadTimeout) {
							return callback(new Error('The url ' + pageUrl + ' timed out'));
						}

						// lets run all scripts
						var promises = [];
						Object.keys(self.builtinScripts.dynamic).forEach(function(scriptName) {
							promises.push({
								name: scriptName,
								promise: driver.executeScript(self.builtinScripts.dynamic[scriptName])
							});
						});

						Object.keys(self.customScripts.dynamic).forEach(function(scriptName) {
							promises.push({
								name: scriptName,
								promise: driver.executeScript(self.customScripts.dynamic[scriptName]),
								custom: true
							});
						});

						if (iteration === 1) {
							Object.keys(self.builtinScripts.static).forEach(function(scriptName) {
								promises.push({
									name: scriptName,
									promise: driver.executeScript(self.builtinScripts.static[scriptName]),
									static: true
								});
							});

							Object.keys(self.customScripts.static).forEach(function(scriptName) {
								promises.push({
									name: scriptName,
									promise: driver.executeScript(self.customScripts.static[scriptName]),
									static: true,
									custom: true
								});
							});
						}

						var callbacks = [];
						promises.forEach(function(promise) {
							callbacks.push(function(cb) {
								promise.promise.then(function(value) {
									var result = {
										name: promise.name,
										value: value,
										static: promise.static || false,
										custom: promise.custom || false
									};
									cb(null, result);
								}, function(e) {
									log.error('Error running script \'%s\': %s', promise.name, util.inspect(e));
									cb(e);
								});
							});
						});

						// when we are finished, push the result and stop the browser
						async.series(callbacks,
							function(e, results) {
								if (e) {
									return callback(e);
								}
								var custom = {
									dynamic: {},
									static: {}
								};
								var data = {
									dynamic: {},
									static: {}
								};
								results.forEach(function(metric) {

									if (metric.static && metric.custom) {
  									custom.static[metric.name] = metric.value;
									} else if (metric.static) {
  									data.static[metric.name] = metric.value;
									} else if (metric.custom) {
  									custom.dynamic[metric.name] = metric.value;
  								} else {
  									data.dynamic[metric.name] = metric.value;
									}
								});
								data.dynamic.date = new Date(afterFetchTime);
								self.result.builtin.dynamic.push(data.dynamic);
								self.result.custom.dynamic.push(custom.dynamic);
								if (iteration === 1) {
									self.result.builtin.static = data.static;
									self.result.custom.static = custom.static;
            		}
								driver.quit().thenFinally(function() {
									return callback();
								});
							});
					},
					function() {

						var afterLoadTime = Date.now();

						log.verbose('loading url took %d milliseconds', (afterLoadTime - afterFetchTime));

						driver.quit().thenFinally(function() {
							return callback(new Error('The url ' + pageUrl + ' timed out'));
						});
					});
			});
	};
};

Browsertime.prototype._getFormattedResult = function(options) {
	// fetch timings for each run and make some statistics
	var builtinDynamic = {};
	var customDynamic = {};
	var self = this;

	this.result.builtin.dynamic.forEach(function(run) {
		Object.keys(self.builtinScripts.dynamic).forEach(function(scriptName) {
			stats.setupTimingsStatistics(builtinDynamic, run);
			stats.setupUserTimingsStatistics(builtinDynamic, run);
			// for all scripts that return numbers automatically
			// include them in the statistics
			if (helper.isNumber(run[scriptName])) {
				stats.setupStatistics(builtinDynamic, run, scriptName);
			}
		});

		// Ugly hack: Firefox has an extra toJSON method in the resource timings
		// lets skip that for the result
		if (run.resourceTiming) {
			run.resourceTiming.forEach(function(entry) {
				if (entry.toJSON) {
					entry.toJSON = undefined;
				}
			});
		}

	});

  // right now only add the dynamics as statistics
	this.result.custom.dynamic.forEach(function(run) {
		Object.keys(self.customScripts.dynamic).forEach(function(scriptName) {
			// for all scripts that return numbers automatically
			// include them in the statistics
			if (helper.isNumber(run[scriptName])) {
				stats.setupStatistics(customDynamic, run, scriptName);
			}
		});
	});

	return {
		url: options.url,
		runs: options.runs,
		browserName: this.browserName,
		browserVersion: this.browserVersion,
		platform: this.os,
		browserTimeVersion: require('../package.json').version,
		builtin: {
			statistics: stats.formatStatistics(builtinDynamic),
			data: this.result.builtin
		},
		custom: {
			statistics: stats.formatStatistics(customDynamic),
			data: this.result.custom
		}
	};

};

Browsertime.prototype._saveToFile = function(data, options, cb) {
	var self = this;

	// lets store the files
	async.parallel([
		function(callback) {
			fs.writeFile(options.filename, JSON.stringify(data), function(err) {
				self.log.info('Storing ' + options.filename);
				callback(err);
			});
		},
		function(callback) {
			self.emit('savingResults', {
				data: data
			}, callback);
		}
	], cb);
};

Browsertime.prototype._setupDefaults = function(options) {

	options.runs = options.runs || 3;
	options.filename = options.filename || path.join(process.cwd(), url.parse(options.url).hostname + '.json');
	options.seleniumServer = options.seleniumServer || 'http://localhost:4444/wd/hub';
};

Browsertime.prototype._populateScripts = function(options) {
	var self = this;
	var scriptRoots = path.join(__dirname, '..', 'scripts', 'dynamic');
	var runOnceScriptRoots = path.join(__dirname, '..', 'scripts', 'static');

	if (options.scriptPath) {
		helper.readScripts(path.join(path.resolve(options.scriptPath), 'dynamic'), self.customScripts.dynamic, this.log);
		helper.readScripts(path.join(path.resolve(options.scriptPath), 'static'), self.customScripts.static, this.log);
	}
	helper.readScripts(runOnceScriptRoots, self.builtinScripts.static, this.log);
	helper.readScripts(scriptRoots, self.builtinScripts.dynamic, this.log);
};

module.exports = Browsertime;
<commit_msg>added todo<commit_after>/**
 * Browsertime (http://www.browsertime.net)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
'use strict';

var stats = require('./statistics'),
	fs = require('fs'),
	async = require('async'),
	path = require('path'),
	logger = require('./logger'),
	helper = require('./helper'),
	url = require('url'),
	util = require('util'),
	EventEmitter = require('async-node-events'),
	webdriver = require('selenium-webdriver');

var defaultWaitScript = 'return window.performance.timing.loadEventEnd>0';

function Browsertime(b) {
	this.log = logger.getLog();
	EventEmitter.call(this);
	this.result = {
		builtin: {
			static: {},
			dynamic: []
		},
		custom: {
			static: {},
			dynamic: []
		}
	};
	this.builtinScripts = {
		static: {},
		dynamic: {}
	};
	this.customScripts = {
		static: {},
		dynamic: {}
	};
	this.browserVersion = '';
	this.os = '';
	this.browserName = '';
	this.browsers = b;
}

util.inherits(Browsertime, EventEmitter);

Browsertime.prototype.fetch = function(options, callback) {

	callback = callback || function() {};

	this._setupDefaults(options);
	this._populateScripts(options);

	var self = this;

	async.series([
		function(cb) {
			self.emit('beforeRun', cb);
		},
		function(cb) {
			self._do(options, cb);
		},
		function(cb) {
			self.emit('afterRun', cb);
		}
	], function(err) {
		callback(err);
	});
};

Browsertime.prototype._do = function(options, cb) {
	var browserRuns = [];
	var self = this;
	for (var i = 1; i <= options.runs; i++) {
		browserRuns.push(self._collectFromTheBrowser(options, i, options.runs));
	}

	async.series(browserRuns,
		function(err) {
			if (err) {
				self.log.error(err.message);
				return cb(err);
			}
			var data = self._getFormattedResult(options);
			self._saveToFile(data, options, cb);
		});
};

Browsertime.prototype._collectFromTheBrowser = function(options, iteration, totalIterations) {
	var self = this,
		log = this.log;
	var pageUrl = options.url,
		pageLoadTimeout = 60000;

	var waitScript = options.waitScript || defaultWaitScript;

	return function(callback) {
		webdriver.promise.controlFlow().on('uncaughtException', function(e) {
			callback(e);
		});

		async.series([
				function(cb) {
					self.emit('callingBrowser', cb);
				}
			],
			function(err) {
				if (err) {
					return callback(err);
				}

				var driver = self.browsers.getBrowser(options.browser).getDriver(options);

				driver.getCapabilities().then(function(cap) {
					self.browserVersion = cap.get('version');
					self.os = cap.get('platform');
					self.browserName = cap.get('browserName');
				});

				// Not supported in Safari:
				// https://code.google.com/p/selenium/issues/detail?id=6015
				if (options.browser !== 'safari') {
					driver.manage().timeouts().pageLoadTimeout(60000);
				}
				driver.manage().timeouts().setScriptTimeout(60000);

				driver.manage().window().setPosition(0, 0);

				var windowSize = helper.parseWindowSize(options.size, log);
				if (windowSize) {
					driver.manage().window().setSize(windowSize.x, windowSize.y);
				}

				// fetch the URL and wait until the load event ends or we get the time out
				log.info('Fetching ' + pageUrl + ' (' + iteration + ' of ' + totalIterations + ')');
				driver.get(pageUrl);

				var afterFetchTime = Date.now();

				driver.wait(function() {
						return driver.executeScript(waitScript)
							.then(function(b) {
								return b;
							});
					},
					pageLoadTimeout
				).then(function() {
						var afterLoadTime = Date.now();

						log.verbose('loading url took %d milliseconds', (afterLoadTime - afterFetchTime));
						// This is needed since the Firefox driver executes the success callback even when driver.wait
						// took too long.
						if ((afterLoadTime - afterFetchTime) > pageLoadTimeout) {
							return callback(new Error('The url ' + pageUrl + ' timed out'));
						}

						// lets run all scripts
						var promises = [];
						Object.keys(self.builtinScripts.dynamic).forEach(function(scriptName) {
							promises.push({
								name: scriptName,
								promise: driver.executeScript(self.builtinScripts.dynamic[scriptName])
							});
						});

						Object.keys(self.customScripts.dynamic).forEach(function(scriptName) {
							promises.push({
								name: scriptName,
								promise: driver.executeScript(self.customScripts.dynamic[scriptName]),
								custom: true
							});
						});

						if (iteration === 1) {
							Object.keys(self.builtinScripts.static).forEach(function(scriptName) {
								promises.push({
									name: scriptName,
									promise: driver.executeScript(self.builtinScripts.static[scriptName]),
									static: true
								});
							});

							Object.keys(self.customScripts.static).forEach(function(scriptName) {
								promises.push({
									name: scriptName,
									promise: driver.executeScript(self.customScripts.static[scriptName]),
									static: true,
									custom: true
								});
							});
						}

						var callbacks = [];
						promises.forEach(function(promise) {
							callbacks.push(function(cb) {
								promise.promise.then(function(value) {
									var result = {
										name: promise.name,
										value: value,
										static: promise.static || false,
										custom: promise.custom || false
									};
									cb(null, result);
								}, function(e) {
									log.error('Error running script \'%s\': %s', promise.name, util.inspect(e));
									cb(e);
								});
							});
						});

						// when we are finished, push the result and stop the browser
						async.series(callbacks,
							function(e, results) {
								if (e) {
									return callback(e);
								}
								var custom = {
									dynamic: {},
									static: {}
								};
								var data = {
									dynamic: {},
									static: {}
								};
								results.forEach(function(metric) {

									if (metric.static && metric.custom) {
  									custom.static[metric.name] = metric.value;
									} else if (metric.static) {
  									data.static[metric.name] = metric.value;
									} else if (metric.custom) {
  									custom.dynamic[metric.name] = metric.value;
  								} else {
  									data.dynamic[metric.name] = metric.value;
									}
								});
								data.dynamic.date = new Date(afterFetchTime);
								self.result.builtin.dynamic.push(data.dynamic);
								self.result.custom.dynamic.push(custom.dynamic);
								if (iteration === 1) {
									self.result.builtin.static = data.static;
									self.result.custom.static = custom.static;
            		}
								driver.quit().thenFinally(function() {
									return callback();
								});
							});
					},
					function() {

						var afterLoadTime = Date.now();

						log.verbose('loading url took %d milliseconds', (afterLoadTime - afterFetchTime));

						driver.quit().thenFinally(function() {
							return callback(new Error('The url ' + pageUrl + ' timed out'));
						});
					});
			});
	};
};

Browsertime.prototype._getFormattedResult = function(options) {
	// fetch timings for each run and make some statistics
	var builtinDynamic = {};
	var customDynamic = {};
	var self = this;

	this.result.builtin.dynamic.forEach(function(run) {
		Object.keys(self.builtinScripts.dynamic).forEach(function(scriptName) {
			stats.setupTimingsStatistics(builtinDynamic, run);
			stats.setupUserTimingsStatistics(builtinDynamic, run);
			// for all scripts that return numbers automatically
			// include them in the statistics
			if (helper.isNumber(run[scriptName])) {
				stats.setupStatistics(builtinDynamic, run, scriptName);
			}
		});

		// Ugly hack: Firefox has an extra toJSON method in the resource timings
		// lets skip that for the result
		if (run.resourceTiming) {
			run.resourceTiming.forEach(function(entry) {
				if (entry.toJSON) {
					entry.toJSON = undefined;
				}
			});
		}

	});

  // right now only add the dynamics as statistics
	this.result.custom.dynamic.forEach(function(run) {
		Object.keys(self.customScripts.dynamic).forEach(function(scriptName) {
			// for all scripts that return numbers automatically
			// include them in the statistics
			if (helper.isNumber(run[scriptName])) {
				stats.setupStatistics(customDynamic, run, scriptName);
			}
		});
	});

	return {
		url: options.url,
		runs: options.runs,
		browserName: this.browserName,
		browserVersion: this.browserVersion,
		platform: this.os,
		browserTimeVersion: require('../package.json').version,
		builtin: {
			statistics: stats.formatStatistics(builtinDynamic),
			data: this.result.builtin
		},
		custom: {
			statistics: stats.formatStatistics(customDynamic),
			data: this.result.custom
		}
	};

};

Browsertime.prototype._saveToFile = function(data, options, cb) {
	var self = this;

	// lets store the files
	async.parallel([
		function(callback) {
			fs.writeFile(options.filename, JSON.stringify(data), function(err) {
				self.log.info('Storing ' + options.filename);
				callback(err);
			});
		},
		function(callback) {
			self.emit('savingResults', {
				data: data
			}, callback);
		}
	], cb);
};

Browsertime.prototype._setupDefaults = function(options) {

	options.runs = options.runs || 3;
	options.filename = options.filename || path.join(process.cwd(), url.parse(options.url).hostname + '.json');
	options.seleniumServer = options.seleniumServer || 'http://localhost:4444/wd/hub';
};

Browsertime.prototype._populateScripts = function(options) {
	var self = this;
	var scriptRoots = path.join(__dirname, '..', 'scripts', 'dynamic');
	var runOnceScriptRoots = path.join(__dirname, '..', 'scripts', 'static');

	if (options.scriptPath) {
    // TODO we should do like this: if the options.scriptPath doesn't have any dirs,
    // add scripts as dynamic (or static). If we have the folders, use them!
		helper.readScripts(path.join(path.resolve(options.scriptPath), 'dynamic'), self.customScripts.dynamic, this.log);
		helper.readScripts(path.join(path.resolve(options.scriptPath), 'static'), self.customScripts.static, this.log);
	}
	helper.readScripts(runOnceScriptRoots, self.builtinScripts.static, this.log);
	helper.readScripts(scriptRoots, self.builtinScripts.dynamic, this.log);
};

module.exports = Browsertime;
<|endoftext|>


<commit_before>var express = require('express'),
    _ = require('underscore'),
    util = require('./util.js');

/**
 * Builds a CRUD router for a Model. The heart of the beast...
 * Also contains some closures at the bottom, if you're wondering
 * where some of these functions are coming from...
 *
 * @param {Model} model
 * @param {Object} options same as expose function
 * @return {Router}
 */
module.exports = function(Model, options) {
    var router = express.Router({
            strict: options.strict,
            caseSensitive: options.caseSensitive
        }),
        route = router.route('/:id');

    /**
     * Browse for documents
     * @query {Number} limit defaults to 15
     * @query {Number} offset defaults to 0
     * @query {String} sort path to sort by
     * @query {String|Number} order asc/desc or 1/-1
     */
    if (exposed('browse')) {
        router.get('/',
            options.custom.browse.pre,
            function(req, res, next) {
                var sort = {};
                sort[req.query.sort || '_id'] = req.query.order || 'asc';

                Model.find()
                    .limit(req.query.limit || 15)
                    .skip(req.query.offset || 0)
                    .sort(sort)
                    .lean(true)
                    .exec(function(err, docs) {
                        if (err) return next(err);
                        res.send(docs.map(function(doc) {
                            return omit(doc, 'browse');
                        }));
                    });
            }
        );
    }

    /**
     * Create a document
     */
    if (exposed('create')) {
        router.post('/',
            options.custom.create.pre,
            function(req, res, next) {
                var model = new Model(protect(req.body, 'create'));
                model.save(function(err, doc) {
                    if (err) return next(err);
                    res.send(omit(doc, 'create'));
                });
            }
        );
    }

    /**
     * Retrieve a document
     * Intercept just JSON because that's all we need
     */
    if (exposed('retrieve')) {
        route.get(
            intercept(false),
            options.custom.retrieve.pre,
            function(req, res, next) {
                res.send(omit(req.doc, 'retrieve'));
            }
        );
    }

    /**
     * Update a document
     * Intercept Model because we need to apply validation and update it
     */
    if (exposed('update')) {
        route.put(
            intercept(false),
            options.custom.update.pre,
            function(req, res, next) {
                req.doc.set(protect(req.body, 'update'));
                req.doc.save(function(err, doc) {
                    if (err) return next(err);
                    res.send(omit(doc, 'update'));
                });
            }
        );
    }

    /**
     * Delete a document
     */
    if (exposed('delete')) {
        route.delete(
            intercept(false),
            options.custom.delete.pre,
            function(req, res, next) {
                req.doc.remove(function(err, doc) {
                    res.send(omit(doc), 'delete');
                });
            }
        );
    }

    /**
     * Catch everything else...
     */
    router.use(function(req, res, next) {
        next(new Error("404 not found."));
    });

    return router;

    /* These closures 'like' having access to some of their
     * parent variables like 'Model', and 'options'
     * I could move them elsewhere but, meh..
     */

    /**
     * Reusable middleware to convert the :id param to an object
     * @param {Boolean} lean
     * @return {Function} middleware
     */
    function intercept(lean) {
        return function(req, res, next) {
            Model.findById(req.params.id)
                .lean(lean)
                .exec(function(err, doc) {
                    if (err) return next(err);
                    if (!doc) return next(new Error('Not found.'));
                    req.doc = doc;
                    next();
                });
        }
    }

    /**
     * Remove all protected/private paths from an object
     * before we try to create/update an actual Model.
     * Typically used on req.body.
     *
     * @param {Object} object
     * @param {String} type
     * @return {Object}
     */
    function protect(object, type) {
        var priv = options.custom[type].private.length ? options.custom[type].private : options.private,
            prot = options.custom[type].protected.length ? options.custom[type].protected : options.protected;

        return util.deepOmit(object, priv.concat(prot));
    }

    /**
     * Remove all private paths we don't want to include
     * when we send this object across the interwebs.
     * @param {Model} doc
     * @param {String} type
     * @return {Object}
     */
    function omit(doc, type) {
        var priv = options.custom[type].private.length ? options.custom[type].private: options.private;
        return util.deepOmit(doc.toObject ? doc.toObject() : doc, priv);
    }

    /**
     * A shortcut to see if a given type is allowed
     * @param {String} type
     * @return {Boolean}
     */
    function exposed(type) {
        return options.methods.indexOf(type) >= 0;
    }
}
<commit_msg>Made protection happen before pre<commit_after>var express = require('express'),
    _ = require('underscore'),
    util = require('./util.js');

/**
 * Builds a CRUD router for a Model. The heart of the beast...
 * Also contains some closures at the bottom, if you're wondering
 * where some of these functions are coming from...
 *
 * @param {Model} model
 * @param {Object} options same as expose function
 * @return {Router}
 */
module.exports = function(Model, options) {
    var router = express.Router({
            strict: options.strict,
            caseSensitive: options.caseSensitive
        }),
        route = router.route('/:id');

    /**
     * Browse for documents
     * @query {Number} limit defaults to 15
     * @query {Number} offset defaults to 0
     * @query {String} sort path to sort by
     * @query {String|Number} order asc/desc or 1/-1
     */
    if (exposed('browse')) {
        router.get('/',
            options.custom.browse.pre,
            function(req, res, next) {
                var sort = {};
                sort[req.query.sort || '_id'] = req.query.order || 'asc';

                Model.find()
                    .limit(req.query.limit || 15)
                    .skip(req.query.offset || 0)
                    .sort(sort)
                    .lean(true)
                    .exec(function(err, docs) {
                        if (err) return next(err);
                        res.send(docs.map(function(doc) {
                            return omit(doc, 'browse');
                        }));
                    });
            }
        );
    }

    /**
     * Create a document
     */
    if (exposed('create')) {
        router.post('/',
            protect('create'),
            options.custom.create.pre,
            function(req, res, next) {
                var model = new Model(req.body);
                model.save(function(err, doc) {
                    if (err) return next(err);
                    res.send(omit(doc, 'create'));
                });
            }
        );
    }

    /**
     * Retrieve a document
     * Intercept just JSON because that's all we need
     */
    if (exposed('retrieve')) {
        route.get(
            intercept(false),
            options.custom.retrieve.pre,
            function(req, res, next) {
                res.send(omit(req.doc, 'retrieve'));
            }
        );
    }

    /**
     * Update a document
     * Intercept Model because we need to apply validation and update it
     */
    if (exposed('update')) {
        route.put(
            protect('update'),
            intercept(false),
            options.custom.update.pre,
            function(req, res, next) {
                req.doc.set(req.body);
                req.doc.save(function(err, doc) {
                    if (err) return next(err);
                    res.send(omit(doc, 'update'));
                });
            }
        );
    }

    /**
     * Delete a document
     */
    if (exposed('delete')) {
        route.delete(
            intercept(false),
            options.custom.delete.pre,
            function(req, res, next) {
                req.doc.remove(function(err, doc) {
                    res.send(omit(doc), 'delete');
                });
            }
        );
    }

    /**
     * Catch everything else...
     */
    router.use(function(req, res, next) {
        next(new Error("404 not found."));
    });

    return router;

    /* These closures 'like' having access to some of their
     * parent variables like 'Model', and 'options'
     * I could move them elsewhere but, meh..
     */

    /**
     * Reusable middleware to convert the :id param to an object
     * @param {Boolean} lean
     * @return {Function} middleware
     */
    function intercept(lean) {
        return function(req, res, next) {
            Model.findById(req.params.id)
                .lean(lean)
                .exec(function(err, doc) {
                    if (err) return next(err);
                    if (!doc) return next(new Error('Not found.'));
                    req.doc = doc;
                    next();
                });
        }
    }

    /**
     * Returns a custom middleware function that removes protected
     * and private paths from req.body
     *
     * @param {String} type
     * @return {Function}
     */
    function protect(type) {
        var priv = options.custom[type].private.length ? options.custom[type].private : options.private,
            prot = options.custom[type].protected.length ? options.custom[type].protected : options.protected,
            paths = priv.concat(prot);

        return function(req, res, next) {
            req.body = util.deepOmit(req.body, paths);
            next();
        }
    }

    /**
     * Remove all private paths we don't want to include
     * when we send this object across the interwebs.
     * @param {Model} doc
     * @param {String} type
     * @return {Object}
     */
    function omit(doc, type) {
        var priv = options.custom[type].private.length ? options.custom[type].private: options.private;
        return util.deepOmit(doc.toObject ? doc.toObject() : doc, priv);
    }

    /**
     * A shortcut to see if a given type is allowed
     * @param {String} type
     * @return {Boolean}
     */
    function exposed(type) {
        return options.methods.indexOf(type) >= 0;
    }
}
<|endoftext|>


<commit_before>'use strict';

require('should-http');

const express = require('../support/express');

const app = express();
const request = require('../support/client');
const assert = require('assert');
const should = require('should');
const cookieParser = require('cookie-parser');
const session = require('express-session');
let http = require('http');

if (process.env.HTTP2_TEST) {
  http = require('http2');
}

app.use(cookieParser());
app.use(
  session({
    secret: 'secret',
    resave: true,
    saveUninitialized: true
  })
);

app.post('/signin', (request_, res) => {
  request_.session.user = 'hunter@hunterloftis.com';
  res.redirect('/dashboard');
});

app.post('/setcookie', (request_, res) => {
  res.cookie('cookie', 'jar');
  res.sendStatus(200);
});

app.get('/getcookie', (request_, res) => {
  res.status(200).send(request_.cookies.cookie);
});

app.get('/dashboard', (request_, res) => {
  if (request_.session.user) return res.status(200).send('dashboard');
  res.status(401).send('dashboard');
});

app.all('/signout', (request_, res) => {
  request_.session.regenerate(() => {
    res.status(200).send('signout');
  });
});

app.get('/', (request_, res) => {
  if (request_.session.user) return res.redirect('/dashboard');
  res.status(200).send('home');
});

app.post('/redirect', (request_, res) => {
  res.redirect('/simple');
});

app.get('/simple', (request_, res) => {
  res.status(200).send('simple');
});

let base = 'http://localhost';
let server;
before(function listen(done) {
  server = http.createServer(app);
  server = server.listen(0, function listening() {
    base += `:${server.address().port}`;
    done();
  });
});

describe('request', () => {
  describe('persistent agent', () => {
    const agent1 = request.agent();
    const agent2 = request.agent();
    const agent3 = request.agent();
    const agent4 = request.agent();

    it('should gain a session on POST', () =>
      agent3.post(`${base}/signin`).then((res) => {
        res.should.have.status(200);
        should.not.exist(res.headers['set-cookie']);
        res.text.should.containEql('dashboard');
      }));

    it('should start with empty session (set cookies)', (done) => {
      agent1.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        should.exist(res.headers['set-cookie']);
        done();
      });
    });

    it('should gain a session (cookies already set)', () =>
      agent1.post(`${base}/signin`).then((res) => {
        res.should.have.status(200);
        should.not.exist(res.headers['set-cookie']);
        res.text.should.containEql('dashboard');
      }));

    it('should persist cookies across requests', () =>
      agent1.get(`${base}/dashboard`).then((res) => {
        res.should.have.status(200);
      }));

    it('should have the cookie set in the end callback', () =>
      agent4
        .post(`${base}/setcookie`)
        .then(() => agent4.get(`${base}/getcookie`))
        .then((res) => {
          res.should.have.status(200);
          assert.strictEqual(res.text, 'jar');
        }));

    it('should not share cookies', (done) => {
      agent2.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        done();
      });
    });

    it('should not lose cookies between agents', () =>
      agent1.get(`${base}/dashboard`).then((res) => {
        res.should.have.status(200);
      }));

    it('should be able to follow redirects', () =>
      agent1.get(base).then((res) => {
        res.should.have.status(200);
        res.text.should.containEql('dashboard');
      }));

    it('should be able to post redirects', () =>
      agent1
        .post(`${base}/redirect`)
        .send({ foo: 'bar', baz: 'blaaah' })
        .then((res) => {
          res.should.have.status(200);
          res.text.should.containEql('simple');
          res.redirects.should.eql([`${base}/simple`]);
        }));

    it('should be able to limit redirects', (done) => {
      agent1
        .get(base)
        .redirects(0)
        .end((error, res) => {
          should.exist(error);
          res.should.have.status(302);
          res.redirects.should.eql([]);
          res.header.location.should.equal('/dashboard');
          done();
        });
    });

    it('should be able to create a new session (clear cookie)', () =>
      agent1.post(`${base}/signout`).then((res) => {
        res.should.have.status(200);
        should.exist(res.headers['set-cookie']);
      }));

    it('should regenerate with an empty session', (done) => {
      agent1.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        should.not.exist(res.headers['set-cookie']);
        done();
      });
    });
  });
});
<commit_msg>ci: fix the error of express-session used in http2<commit_after>'use strict';

require('should-http');

const express = require('../support/express');

const app = express();
const request = require('../support/client');
const assert = require('assert');
const should = require('should');
const cookieParser = require('cookie-parser');
const session = require('express-session');
let http = require('http');

if (process.env.HTTP2_TEST) {
  http = require('http2');
  http.Http2ServerResponse.prototype._implicitHeader = function() {
    this.writeHead(this.statusCode);
  }
}

app.use(cookieParser());
app.use(
  session({
    secret: 'secret',
    resave: true,
    saveUninitialized: true
  })
);

app.post('/signin', (request_, res) => {
  request_.session.user = 'hunter@hunterloftis.com';
  res.redirect('/dashboard');
});

app.post('/setcookie', (request_, res) => {
  res.cookie('cookie', 'jar');
  res.sendStatus(200);
});

app.get('/getcookie', (request_, res) => {
  res.status(200).send(request_.cookies.cookie);
});

app.get('/dashboard', (request_, res) => {
  if (request_.session.user) return res.status(200).send('dashboard');
  res.status(401).send('dashboard');
});

app.all('/signout', (request_, res) => {
  request_.session.regenerate(() => {
    res.status(200).send('signout');
  });
});

app.get('/', (request_, res) => {
  if (request_.session.user) return res.redirect('/dashboard');
  res.status(200).send('home');
});

app.post('/redirect', (request_, res) => {
  res.redirect('/simple');
});

app.get('/simple', (request_, res) => {
  res.status(200).send('simple');
});

let base = 'http://localhost';
let server;
before(function listen(done) {
  server = http.createServer(app);
  server = server.listen(0, function listening() {
    base += `:${server.address().port}`;
    done();
  });
});

describe('request', () => {
  describe('persistent agent', () => {
    const agent1 = request.agent();
    const agent2 = request.agent();
    const agent3 = request.agent();
    const agent4 = request.agent();

    it('should gain a session on POST', () =>
      agent3.post(`${base}/signin`).then((res) => {
        res.should.have.status(200);
        should.not.exist(res.headers['set-cookie']);
        res.text.should.containEql('dashboard');
      }));

    it('should start with empty session (set cookies)', (done) => {
      agent1.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        should.exist(res.headers['set-cookie']);
        done();
      });
    });

    it('should gain a session (cookies already set)', () =>
      agent1.post(`${base}/signin`).then((res) => {
        res.should.have.status(200);
        should.not.exist(res.headers['set-cookie']);
        res.text.should.containEql('dashboard');
      }));

    it('should persist cookies across requests', () =>
      agent1.get(`${base}/dashboard`).then((res) => {
        res.should.have.status(200);
      }));

    it('should have the cookie set in the end callback', () =>
      agent4
        .post(`${base}/setcookie`)
        .then(() => agent4.get(`${base}/getcookie`))
        .then((res) => {
          res.should.have.status(200);
          assert.strictEqual(res.text, 'jar');
        }));

    it('should not share cookies', (done) => {
      agent2.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        done();
      });
    });

    it('should not lose cookies between agents', () =>
      agent1.get(`${base}/dashboard`).then((res) => {
        res.should.have.status(200);
      }));

    it('should be able to follow redirects', () =>
      agent1.get(base).then((res) => {
        res.should.have.status(200);
        res.text.should.containEql('dashboard');
      }));

    it('should be able to post redirects', () =>
      agent1
        .post(`${base}/redirect`)
        .send({ foo: 'bar', baz: 'blaaah' })
        .then((res) => {
          res.should.have.status(200);
          res.text.should.containEql('simple');
          res.redirects.should.eql([`${base}/simple`]);
        }));

    it('should be able to limit redirects', (done) => {
      agent1
        .get(base)
        .redirects(0)
        .end((error, res) => {
          should.exist(error);
          res.should.have.status(302);
          res.redirects.should.eql([]);
          res.header.location.should.equal('/dashboard');
          done();
        });
    });

    it('should be able to create a new session (clear cookie)', () =>
      agent1.post(`${base}/signout`).then((res) => {
        res.should.have.status(200);
        should.exist(res.headers['set-cookie']);
      }));

    it('should regenerate with an empty session', (done) => {
      agent1.get(`${base}/dashboard`).end((error, res) => {
        should.exist(error);
        res.should.have.status(401);
        should.not.exist(res.headers['set-cookie']);
        done();
      });
    });
  });
});
<|endoftext|>