<commit_before>var defaultSettings = {
  focusDuration: 25,
  breakDuration: 5,
  showDesktopNotification: true,
  showNewTabNotification: true
};

function Timer(durationSec, tickSec) {
  var self = this;
  var state = 'stopped';

  this.tickInterval = null;
  this.expireTimeout = null;

  this.periodStartTime = null;
  this.remainingSec = null;

  this.start = function() {
    if (state !== 'stopped') {
      return;
    }

    this.expireTimeout = createExpireTimeout(durationSec);
    this.tickInterval = createTickInterval(tickSec);

    this.remainingSec = durationSec;

    state = 'running';
    this.periodStartTime = Date.now();
    this.emitEvent('start', [{
      elapsed: 0,
      remaining: this.remainingSec
    }]);
  };

  this.stop = function() {
    if (state === 'stopped') {
      return;
    }

    clearInterval(this.tickInterval);
    clearTimeout(this.expireTimeout);

    this.tickInterval = null;
    this.expireTimeout = null;
    this.periodStartTime = null;
    this.remainingSec = null;

    state = 'stopped';
    this.emitEvent('stop', [{}]);
  };

  this.pause = function() {
    if (state !== 'running') {
      return;
    }

    clearInterval(this.tickInterval);
    clearTimeout(this.expireTimeout);

    var periodSec = (Date.now() - this.periodStartTime) / 1000;
    this.remainingSec -= periodSec;

    state = 'paused';
    this.periodStartTime = null;
    this.emitEvent('pause', [{
      elapsed: durationSec - this.remainingSec,
      remaining: this.remainingSec
    }]);
  };

  this.resume = function() {
    if (state !== 'paused') {
      return;
    }

    this.expireTimeout = createExpireTimeout(this.remainingSec);
    this.tickInterval = createTickInterval(tickSec);

    state = 'running';
    this.periodStartTime = Date.now();
    this.emitEvent('resume', [{
      elapsed: durationSec - this.remainingSec,
      remaining: this.remainingSec
    }]);
  };

  this.reset = function() {
    this.stop();
    this.start();
  };

  this.state = function() {
    return state;
  };

  function createExpireTimeout(seconds) {
    return setTimeout(function() {
      clearInterval(self.tickInterval);
      clearTimeout(self.expireTimeout);

      self.tickInterval = null;
      self.expireTimeout = null;
      self.periodStartTime = null;
      self.remainingSec = null;

      state = 'stopped';
      self.emitEvent('expire', [{
        elapsed: durationSec,
        remaining: 0
      }]);
    }, seconds * 1000);
  }

  function createTickInterval(seconds) {
    return setInterval(function() {
      var periodSec = (Date.now() - self.periodStartTime) / 1000;
      var remainingSec = self.remainingSec - periodSec;

      self.emitEvent('tick', [{
        elapsed: durationSec - remainingSec,
        remaining: remainingSec
      }]);
    }, seconds * 1000);
  }
}

Timer.prototype = Object.create(EventEmitter.prototype);

function BadgeObserver() {
}

BadgeObserver.observe = function(timer, title, color) {
  timer.addListener('start', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('tick', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('stop', function() {
    removeBadge();
  });

  timer.addListener('pause', function() {
    updateBadge({ text: '—', title: 'Paused' });
  });

  timer.addListener('resume', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('expire', function() {
    removeBadge();
  });

  function updateBadge(options) {
    var minutes = options.minutes;
    if (minutes != null) {
      text = ((minutes == 0) ? '<1' : minutes)  + 'm';
      badgeTitle = title + ': ' + minutes + 'm remaining.';
    } else {
      text = options.text;
      badgeTitle = title + ': ' + options.title;
    }

    chrome.browserAction.setTitle({ title: badgeTitle });
    chrome.browserAction.setBadgeText({ text: text });
    chrome.browserAction.setBadgeBackgroundColor({ color: color });
  };

  function removeBadge() {
    chrome.browserAction.setTitle({ title: '' });
    chrome.browserAction.setBadgeText({ text: '' });
  }
}

function MenuEntry(title, action) {
  this.id = null;

  this.show = function() {
    this.hide();

    if (title === null) {
      this.id = chrome.contextMenus.create({
        type: 'separator',
        contexts: ['browser_action']
      });
    } else {
      this.id = chrome.contextMenus.create({
        title: title,
        contexts: ['browser_action'],
        onclick: action
      });
    }
  };

  this.hide = function() {
    if (this.id === null) {
      return;
    }

    chrome.contextMenus.remove(this.id, function() { });
    this.id = null;
  };
}

MenuEntry.create = function(title, action) {
  var menu = new MenuEntry(title, action);
  menu.show();
};

function ContextMenuObserver() {
}

ContextMenuObserver.observe = function(controller, timer) {
  var stop = new MenuEntry('Stop', controller.stop);
  var pause = new MenuEntry('Pause', controller.pause);
  var resume = new MenuEntry('Resume', controller.resume);

  timer.addListener('start', function() {
    pause.show();
    stop.show();
    resume.hide();
  });

  timer.addListener('pause', function() {
    resume.show();
    stop.show();
    pause.hide();
  });

  timer.addListener('resume', function() {
    pause.show();
    stop.show();
    resume.hide();
  });

  timer.addListener('stop', function() {
    pause.hide();
    stop.hide();
    resume.hide();
  });

  timer.addListener('expire', function() {
    pause.hide();
    stop.hide();
    resume.hide();
  });
};

function Controller() {
  var self = this;
  var focusNext = true;
  var focusTimer;
  var breakTimer;

  this.startSession = function() {
    focusTimer.stop();
    breakTimer.stop();

    if (focusNext) {
      focusTimer.start();
    } else {
      breakTimer.start();
    }
  };

  this.browserAction = function() {
    if (focusTimer.state() === 'running') {
      focusTimer.pause();
    } else if (breakTimer.state() === 'running') {
      breakTimer.pause();
    } else if (focusTimer.state() === 'paused') {
      focusTimer.resume();
    } else if (breakTimer.state() === 'paused') {
      breakTimer.resume();
    } else {
      this.startSession();
    }
  };

  this.pause = function() {
    if (focusTimer.state() === 'running') {
      focusTimer.pause();
    } else if (breakTimer.state() === 'running') {
      breakTimer.pause();
    }
  };

  this.stop = function() {
    focusTimer.stop();
    breakTimer.stop();
  };

  this.resume = function() {
    if (focusTimer.state() === 'paused') {
      focusTimer.resume();
    } else if (breakTimer.state() === 'paused') {
      breakTimer.resume();
    }
  };

  this.startBreak = function() {
    focusTimer.stop();
    breakTimer.stop();
    breakTimer.start();
  };

  this.startFocus = function() {
    focusTimer.stop();
    breakTimer.stop();
    focusTimer.start();
  };

  this.focusNext = function() {
    return focusNext;
  };

  this.getSettings = function(callback) {
    chrome.storage.sync.get(function(result) {
      if (Object.keys(result).length == 0) {
        chrome.storage.sync.set(defaultSettings, function() {
          callback(defaultSettings);
        });
      } else {
        callback(result);
      }
    });
  };

  this.setSettings = function(settings, callback) {
    chrome.storage.sync.set(settings, function() {
      createTimers();
      callback();
    });
  };

  function showExpirePage() {
    chrome.tabs.create({ url: chrome.extension.getURL('expire/expire.html') });
  }

  function notify(title, message) {
    var notification = {
      type: 'basic',
      title: title,
      message: message,
      iconUrl: '../icons/128.png'
    };

    chrome.notifications.create('', notification, function() { });
  }

  function createTimers() {
    self.getSettings(function(settings) {
      if (focusTimer) {
        focusTimer.stop();
      }

      focusTimer = createFocusTimer(settings);

      if (breakTimer) {
        breakTimer.stop();
      }

      breakTimer = createBreakTimer(settings);
    });
  }

  function createFocusTimer(settings) {
    var timer = new Timer(settings.focusDuration * 60, 60);
    BadgeObserver.observe(timer, 'Focus', '#cc0000');
    ContextMenuObserver.observe(self, timer);

    timer.addListener('expire', function() {
      focusNext = false;

      if (settings.showDesktopNotification) {
        notify('Take a break!', "Start your break when you're ready");
      }

      if (settings.showNewTabNotification) {
        showExpirePage();
      }
    });

    timer.addListener('start', closeExtensionTabs);

    return timer;
  }

  function createBreakTimer(settings) {
    var timer = new Timer(settings.breakDuration * 60, 60);
    BadgeObserver.observe(timer, 'Break', '#00cc00');
    ContextMenuObserver.observe(self, timer);

    timer.addListener('expire', function() {
      focusNext = true;

      if (settings.showDesktopNotification) {
        notify('Break finished', "Start your focus session when you're ready");
      }

      if (settings.showNewTabNotification) {
        showExpirePage();
      }
    });

    timer.addListener('start', closeExtensionTabs);

    return timer;
  }

  function closeExtensionTabs() {
    var id = chrome.runtime.id;
    var extensionUrl = 'chrome-extension://' + id;

    chrome.tabs.query({}, function(tabs) {
      var remove = [];
      for (var i = 0; i < tabs.length; ++i) {
        if (tabs[i].url.indexOf(extensionUrl) !== -1) {
          remove.push(tabs[i].id);
        }
      }

      chrome.tabs.remove(remove, function() { });
    });
  }

  createTimers();
}

var controller = new Controller();

chrome.contextMenus.removeAll();

MenuEntry.create('Begin focusing', controller.startFocus);
MenuEntry.create('Begin break', controller.startBreak);
MenuEntry.create(null);

chrome.browserAction.onClicked.addListener(function() {
  controller.browserAction();
});

chrome.runtime.onMessage.addListener(function(request, sender, respond) {
  if (request.command == 'get-session') {
    if (controller.focusNext()) {
      controller.getSettings(function(settings) {
        respond({
          focusNext: true,
          title: 'Break finished',
          subtitle: "Start your " + settings.focusDuration + " minute focus session when you're ready",
          action: 'Start Focusing'
        });
      });
    } else {
      controller.getSettings(function(settings) {
        respond({
          focusNext: false,
          title: 'Take a break!',
          subtitle: "Start your " + settings.breakDuration + " minute break when you're ready",
          action: 'Start Break'
        });
      });
    }
  } else if (request.command == 'start-session') {
    controller.startSession();
    respond({});
  } else if (request.command == 'get-settings') {
    controller.getSettings(respond);
  } else if (request.command == 'set-settings') {
    var newSettings = request.settings;
    var focusDuration = newSettings.focusDuration.trim();
    var breakDuration = newSettings.breakDuration.trim();

    if (!focusDuration) {
      respond({ error: 'Focus duration is required.' });
      return true;
    } else if (!breakDuration) {
      respond({ error: 'Break duration is required.' });
      return true;
    }

    var focusParsed = +focusDuration;
    var breakParsed = +breakDuration;

    if (focusParsed <= 0 || isNaN(focusParsed)) {
      respond({ error: 'Focus duration must be a positive number.' });
      return true;
    } else if (breakParsed <= 0 || isNaN(breakParsed)) {
      respond({ error: 'Break duration must be a positive number.' });
      return true;
    }

    newSettings.focusDuration = focusParsed;
    newSettings.breakDuration = breakParsed;

    controller.setSettings(newSettings, function() {
      respond({});
    });
  }

  return true;
});
<commit_msg>Fix #6. Auto-focus and show expire page if notification is clicked.<commit_after>var defaultSettings = {
  focusDuration: 25,
  breakDuration: 5,
  showDesktopNotification: true,
  showNewTabNotification: true
};

function Timer(durationSec, tickSec) {
  var self = this;
  var state = 'stopped';

  this.tickInterval = null;
  this.expireTimeout = null;

  this.periodStartTime = null;
  this.remainingSec = null;

  this.start = function() {
    if (state !== 'stopped') {
      return;
    }

    this.expireTimeout = createExpireTimeout(durationSec);
    this.tickInterval = createTickInterval(tickSec);

    this.remainingSec = durationSec;

    state = 'running';
    this.periodStartTime = Date.now();
    this.emitEvent('start', [{
      elapsed: 0,
      remaining: this.remainingSec
    }]);
  };

  this.stop = function() {
    if (state === 'stopped') {
      return;
    }

    clearInterval(this.tickInterval);
    clearTimeout(this.expireTimeout);

    this.tickInterval = null;
    this.expireTimeout = null;
    this.periodStartTime = null;
    this.remainingSec = null;

    state = 'stopped';
    this.emitEvent('stop', [{}]);
  };

  this.pause = function() {
    if (state !== 'running') {
      return;
    }

    clearInterval(this.tickInterval);
    clearTimeout(this.expireTimeout);

    var periodSec = (Date.now() - this.periodStartTime) / 1000;
    this.remainingSec -= periodSec;

    state = 'paused';
    this.periodStartTime = null;
    this.emitEvent('pause', [{
      elapsed: durationSec - this.remainingSec,
      remaining: this.remainingSec
    }]);
  };

  this.resume = function() {
    if (state !== 'paused') {
      return;
    }

    this.expireTimeout = createExpireTimeout(this.remainingSec);
    this.tickInterval = createTickInterval(tickSec);

    state = 'running';
    this.periodStartTime = Date.now();
    this.emitEvent('resume', [{
      elapsed: durationSec - this.remainingSec,
      remaining: this.remainingSec
    }]);
  };

  this.reset = function() {
    this.stop();
    this.start();
  };

  this.state = function() {
    return state;
  };

  function createExpireTimeout(seconds) {
    return setTimeout(function() {
      clearInterval(self.tickInterval);
      clearTimeout(self.expireTimeout);

      self.tickInterval = null;
      self.expireTimeout = null;
      self.periodStartTime = null;
      self.remainingSec = null;

      state = 'stopped';
      self.emitEvent('expire', [{
        elapsed: durationSec,
        remaining: 0
      }]);
    }, seconds * 1000);
  }

  function createTickInterval(seconds) {
    return setInterval(function() {
      var periodSec = (Date.now() - self.periodStartTime) / 1000;
      var remainingSec = self.remainingSec - periodSec;

      self.emitEvent('tick', [{
        elapsed: durationSec - remainingSec,
        remaining: remainingSec
      }]);
    }, seconds * 1000);
  }
}

Timer.prototype = Object.create(EventEmitter.prototype);

function BadgeObserver() {
}

BadgeObserver.observe = function(timer, title, color) {
  timer.addListener('start', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('tick', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('stop', function() {
    removeBadge();
  });

  timer.addListener('pause', function() {
    updateBadge({ text: '—', title: 'Paused' });
  });

  timer.addListener('resume', function(state) {
    updateBadge({ minutes: Math.round(state.remaining / 60) });
  });

  timer.addListener('expire', function() {
    removeBadge();
  });

  function updateBadge(options) {
    var minutes = options.minutes;
    if (minutes != null) {
      text = ((minutes == 0) ? '<1' : minutes)  + 'm';
      badgeTitle = title + ': ' + minutes + 'm remaining.';
    } else {
      text = options.text;
      badgeTitle = title + ': ' + options.title;
    }

    chrome.browserAction.setTitle({ title: badgeTitle });
    chrome.browserAction.setBadgeText({ text: text });
    chrome.browserAction.setBadgeBackgroundColor({ color: color });
  };

  function removeBadge() {
    chrome.browserAction.setTitle({ title: '' });
    chrome.browserAction.setBadgeText({ text: '' });
  }
}

function MenuEntry(title, action) {
  this.id = null;

  this.show = function() {
    this.hide();

    if (title === null) {
      this.id = chrome.contextMenus.create({
        type: 'separator',
        contexts: ['browser_action']
      });
    } else {
      this.id = chrome.contextMenus.create({
        title: title,
        contexts: ['browser_action'],
        onclick: action
      });
    }
  };

  this.hide = function() {
    if (this.id === null) {
      return;
    }

    chrome.contextMenus.remove(this.id, function() { });
    this.id = null;
  };
}

MenuEntry.create = function(title, action) {
  var menu = new MenuEntry(title, action);
  menu.show();
};

function ContextMenuObserver() {
}

ContextMenuObserver.observe = function(controller, timer) {
  var stop = new MenuEntry('Stop', controller.stop);
  var pause = new MenuEntry('Pause', controller.pause);
  var resume = new MenuEntry('Resume', controller.resume);

  timer.addListener('start', function() {
    pause.show();
    stop.show();
    resume.hide();
  });

  timer.addListener('pause', function() {
    resume.show();
    stop.show();
    pause.hide();
  });

  timer.addListener('resume', function() {
    pause.show();
    stop.show();
    resume.hide();
  });

  timer.addListener('stop', function() {
    pause.hide();
    stop.hide();
    resume.hide();
  });

  timer.addListener('expire', function() {
    pause.hide();
    stop.hide();
    resume.hide();
  });
};

function Controller() {
  var self = this;
  var focusNext = true;
  var focusTimer;
  var breakTimer;

  this.startSession = function() {
    focusTimer.stop();
    breakTimer.stop();

    if (focusNext) {
      focusTimer.start();
    } else {
      breakTimer.start();
    }
  };

  this.browserAction = function() {
    if (focusTimer.state() === 'running') {
      focusTimer.pause();
    } else if (breakTimer.state() === 'running') {
      breakTimer.pause();
    } else if (focusTimer.state() === 'paused') {
      focusTimer.resume();
    } else if (breakTimer.state() === 'paused') {
      breakTimer.resume();
    } else {
      this.startSession();
    }
  };

  this.pause = function() {
    if (focusTimer.state() === 'running') {
      focusTimer.pause();
    } else if (breakTimer.state() === 'running') {
      breakTimer.pause();
    }
  };

  this.stop = function() {
    focusTimer.stop();
    breakTimer.stop();
  };

  this.resume = function() {
    if (focusTimer.state() === 'paused') {
      focusTimer.resume();
    } else if (breakTimer.state() === 'paused') {
      breakTimer.resume();
    }
  };

  this.startBreak = function() {
    focusTimer.stop();
    breakTimer.stop();
    breakTimer.start();
  };

  this.startFocus = function() {
    focusTimer.stop();
    breakTimer.stop();
    focusTimer.start();
  };

  this.focusNext = function() {
    return focusNext;
  };

  this.getSettings = function(callback) {
    chrome.storage.sync.get(function(result) {
      if (Object.keys(result).length == 0) {
        chrome.storage.sync.set(defaultSettings, function() {
          callback(defaultSettings);
        });
      } else {
        callback(result);
      }
    });
  };

  this.setSettings = function(settings, callback) {
    chrome.storage.sync.set(settings, function() {
      createTimers();
      callback();
    });
  };

  function showExpirePage(callback) {
    if (expirePageTabId !== null) {
      chrome.tabs.update(expirePageTabId, { active: true, highlighted: true }, callback);
    } else {
      chrome.tabs.create({ url: chrome.extension.getURL('expire/expire.html') }, function(tab) {
        expirePageTabId = tab.id;
        callback(tab);
      });
    }
  }

  var expirePageTabId = null;
  chrome.tabs.onRemoved.addListener(function(tabId) {
    if (tabId === expirePageTabId) {
      expirePageTabId = null;
    }
  });

  function notify(title, message) {
    var options = {
      type: 'basic',
      title: title,
      message: message,
      iconUrl: '../icons/128.png',
      isClickable: true
    };

    chrome.notifications.create('', options, function() { });
  }

  chrome.notifications.onClicked.addListener(function() {
    showExpirePage(function(tab) {
      chrome.windows.update(tab.windowId, { focused: true });
    });
  });

  function createTimers() {
    self.getSettings(function(settings) {
      if (focusTimer) {
        focusTimer.stop();
      }

      focusTimer = createFocusTimer(settings);

      if (breakTimer) {
        breakTimer.stop();
      }

      breakTimer = createBreakTimer(settings);
    });
  }

  function createFocusTimer(settings) {
    var timer = new Timer(settings.focusDuration * 60, 60);
    BadgeObserver.observe(timer, 'Focus', '#cc0000');
    ContextMenuObserver.observe(self, timer);

    timer.addListener('expire', function() {
      focusNext = false;

      if (settings.showDesktopNotification) {
        notify('Take a break!', "Start your break when you're ready");
      }

      if (settings.showNewTabNotification) {
        showExpirePage();
      }
    });

    timer.addListener('start', closeExtensionTabs);

    return timer;
  }

  function createBreakTimer(settings) {
    var timer = new Timer(settings.breakDuration * 60, 60);
    BadgeObserver.observe(timer, 'Break', '#00cc00');
    ContextMenuObserver.observe(self, timer);

    timer.addListener('expire', function() {
      focusNext = true;

      if (settings.showDesktopNotification) {
        notify('Break finished', "Start your focus session when you're ready");
      }

      if (settings.showNewTabNotification) {
        showExpirePage();
      }
    });

    timer.addListener('start', closeExtensionTabs);

    return timer;
  }

  function closeExtensionTabs() {
    var id = chrome.runtime.id;
    var extensionUrl = 'chrome-extension://' + id;

    chrome.tabs.query({}, function(tabs) {
      var remove = [];
      for (var i = 0; i < tabs.length; ++i) {
        if (tabs[i].url.indexOf(extensionUrl) !== -1) {
          remove.push(tabs[i].id);
        }
      }

      chrome.tabs.remove(remove, function() { });
    });
  }

  createTimers();
}

var controller = new Controller();

chrome.contextMenus.removeAll();

MenuEntry.create('Begin focusing', controller.startFocus);
MenuEntry.create('Begin break', controller.startBreak);
MenuEntry.create(null);

chrome.browserAction.onClicked.addListener(function() {
  controller.browserAction();
});

chrome.runtime.onMessage.addListener(function(request, sender, respond) {
  if (request.command == 'get-session') {
    if (controller.focusNext()) {
      controller.getSettings(function(settings) {
        respond({
          focusNext: true,
          title: 'Break finished',
          subtitle: "Start your " + settings.focusDuration + " minute focus session when you're ready",
          action: 'Start Focusing'
        });
      });
    } else {
      controller.getSettings(function(settings) {
        respond({
          focusNext: false,
          title: 'Take a break!',
          subtitle: "Start your " + settings.breakDuration + " minute break when you're ready",
          action: 'Start Break'
        });
      });
    }
  } else if (request.command == 'start-session') {
    controller.startSession();
    respond({});
  } else if (request.command == 'get-settings') {
    controller.getSettings(respond);
  } else if (request.command == 'set-settings') {
    var newSettings = request.settings;
    var focusDuration = newSettings.focusDuration.trim();
    var breakDuration = newSettings.breakDuration.trim();

    if (!focusDuration) {
      respond({ error: 'Focus duration is required.' });
      return true;
    } else if (!breakDuration) {
      respond({ error: 'Break duration is required.' });
      return true;
    }

    var focusParsed = +focusDuration;
    var breakParsed = +breakDuration;

    if (focusParsed <= 0 || isNaN(focusParsed)) {
      respond({ error: 'Focus duration must be a positive number.' });
      return true;
    } else if (breakParsed <= 0 || isNaN(breakParsed)) {
      respond({ error: 'Break duration must be a positive number.' });
      return true;
    }

    newSettings.focusDuration = focusParsed;
    newSettings.breakDuration = breakParsed;

    controller.setSettings(newSettings, function() {
      respond({});
    });
  }

  return true;
});
<|endoftext|>


<commit_before>const chalk = require('chalk');
const path = require('path');
const http = require('http');

const TESTPAGE_URL = path.resolve(__dirname, 'testpage.html').replace('\\', '/');

describe('basic tests', () => {

  it('Request is patched', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      const controller = new AbortController();
      const signal = controller.signal;
      let request = new Request('/', {signal});
      if (request.signal != signal) {
        done('FAIL');
      }
      if (!Request.prototype.isPrototypeOf(request)) {
        done('FAIL');
      }
      done('PASS');
    });
    expect(res.value).toBe('PASS');
  });

  it('abort during fetch', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      setTimeout(() => {
        controller.abort();
      }, 500);
      try {
        await fetch('http://httpstat.us/200?sleep=1000', {signal});
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort during fetch when Request has signal', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      setTimeout(() => {
        controller.abort();
      }, 500);
      try {
        let request = new Request('http://httpstat.us/200?sleep=1000', {signal});
        await fetch(request);
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort before fetch started', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.abort();
      const signal = controller.signal;
      try {
        await fetch('http://httpstat.us/200?sleep=1000', {signal});
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort before fetch started, verify no HTTP request is made', () => {
    const server = http.createServer((req, res) => {
      fail('fetch() made an HTTP request despite pre-aborted signal');
    }).listen(0);
    const boundListenPort = server.address().port;
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (boundListenPort, done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.abort();
      const signal = controller.signal;
      try {
        await fetch(`http://127.0.0.1:${boundListenPort}`, {signal});
        done({name: 'fail'});
      } catch (err) {
        done(err);
      }
    }, boundListenPort);
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
    server.close();
  });

  it('fetch without aborting', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      try {
        await fetch('http://httpstat.us/200?sleep=50', {signal});
        done('PASS');
      } catch (err) {
        done(err);
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('fetch without signal set', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      try {
        await fetch('http://httpstat.us/200?sleep=50');
        done('PASS');
      } catch (err) {
        done(err);
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('event listener fires "abort" event', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.signal.addEventListener('abort', () => {
        done('PASS');
      });
      controller.abort();
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('signal.aborted is true after abort', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done('FAIL');
      }, 2000);
      const controller = new AbortController();
      controller.signal.addEventListener('abort', () => {
        if (controller.signal.aborted === true) {
          done('PASS');
        } else {
          done('FAIL');
        }
      });
      controller.abort();
      if (controller.signal.aborted !== true) {
        done('FAIL');
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('event listener doesn\'t fire "abort" event after removeEventListener', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done('PASS');
      }, 200);
      const controller = new AbortController();
      const handlerFunc = () => {
        done('FAIL');
      };
      controller.signal.addEventListener('abort', handlerFunc);
      controller.signal.removeEventListener('abort', handlerFunc);
      controller.abort();
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('toString() output', () => {
    browser.url(TESTPAGE_URL);

    let res;

    res = browser.executeAsync((done) => {
      done(new AbortController().toString());
    });
    expect(res.value).toBe('[object AbortController]');

    res = browser.executeAsync((done) => {
      done(Object.prototype.toString.call(new AbortController()));
    });
    expect(res.value).toBe('[object AbortController]');

    res = browser.executeAsync((done) => {
      done(new AbortController().signal.toString());
    });
    expect(res.value).toBe('[object AbortSignal]');

    res = browser.executeAsync((done) => {
      done(new AbortSignal().toString());
    });
    expect(res.value).toBe('[object AbortSignal]');

    res = browser.executeAsync((done) => {
      done(Object.prototype.toString.call(new AbortSignal()));
    });
    expect(res.value).toBe('[object AbortSignal]');
  });
});

function getJSErrors() {
  if (browser.desiredCapabilities.browserName === 'firefox') {
    console.log('NOTE: cannot get browser log in firefox so cannot verify "no JS errors"');
  } else {
    const allLogEntries = browser.log('browser').value;
    const jsErrors = allLogEntries.filter(logEntry => logEntry.level === 'SEVERE');
    allLogEntries.forEach(error => {
      if (error.level == 'SEVERE') {
        console.log(chalk.red(`[${error.level}] ${error.message}`));
      } else {
        console.log(`[${error.level}] ${error.message}`);
      }
    });
    return jsErrors;
  }
}
<commit_msg>Make sure test page URL has file:// prefix<commit_after>const chalk = require('chalk');
const path = require('path');
const http = require('http');

const TESTPAGE_URL = 'file://' + path.resolve(__dirname, 'testpage.html').replace('\\', '/');

describe('basic tests', () => {

  it('Request is patched', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      const controller = new AbortController();
      const signal = controller.signal;
      let request = new Request('/', {signal});
      if (request.signal != signal) {
        done('FAIL');
      }
      if (!Request.prototype.isPrototypeOf(request)) {
        done('FAIL');
      }
      done('PASS');
    });
    expect(res.value).toBe('PASS');
  });

  it('abort during fetch', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      setTimeout(() => {
        controller.abort();
      }, 500);
      try {
        await fetch('http://httpstat.us/200?sleep=1000', {signal});
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort during fetch when Request has signal', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      setTimeout(() => {
        controller.abort();
      }, 500);
      try {
        let request = new Request('http://httpstat.us/200?sleep=1000', {signal});
        await fetch(request);
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort before fetch started', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.abort();
      const signal = controller.signal;
      try {
        await fetch('http://httpstat.us/200?sleep=1000', {signal});
      } catch (err) {
        done(err);
      }
    });
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
  });

  it('abort before fetch started, verify no HTTP request is made', () => {
    const server = http.createServer((req, res) => {
      fail('fetch() made an HTTP request despite pre-aborted signal');
    }).listen(0);
    const boundListenPort = server.address().port;
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (boundListenPort, done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.abort();
      const signal = controller.signal;
      try {
        await fetch(`http://127.0.0.1:${boundListenPort}`, {signal});
        done({name: 'fail'});
      } catch (err) {
        done(err);
      }
    }, boundListenPort);
    const err = res.value;
    expect(err.name).toBe('AbortError');
    expect(getJSErrors().length).toBe(0);
    server.close();
  });

  it('fetch without aborting', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      const signal = controller.signal;
      try {
        await fetch('http://httpstat.us/200?sleep=50', {signal});
        done('PASS');
      } catch (err) {
        done(err);
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('fetch without signal set', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      try {
        await fetch('http://httpstat.us/200?sleep=50');
        done('PASS');
      } catch (err) {
        done(err);
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('event listener fires "abort" event', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done({name: 'fail'});
      }, 2000);
      const controller = new AbortController();
      controller.signal.addEventListener('abort', () => {
        done('PASS');
      });
      controller.abort();
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('signal.aborted is true after abort', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done('FAIL');
      }, 2000);
      const controller = new AbortController();
      controller.signal.addEventListener('abort', () => {
        if (controller.signal.aborted === true) {
          done('PASS');
        } else {
          done('FAIL');
        }
      });
      controller.abort();
      if (controller.signal.aborted !== true) {
        done('FAIL');
      }
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('event listener doesn\'t fire "abort" event after removeEventListener', () => {
    browser.url(TESTPAGE_URL);
    const res = browser.executeAsync(async (done) => {
      setTimeout(() => {
        done('PASS');
      }, 200);
      const controller = new AbortController();
      const handlerFunc = () => {
        done('FAIL');
      };
      controller.signal.addEventListener('abort', handlerFunc);
      controller.signal.removeEventListener('abort', handlerFunc);
      controller.abort();
    });
    expect(res.value).toBe('PASS');
    expect(getJSErrors().length).toBe(0);
  });

  it('toString() output', () => {
    browser.url(TESTPAGE_URL);

    let res;

    res = browser.executeAsync((done) => {
      done(new AbortController().toString());
    });
    expect(res.value).toBe('[object AbortController]');

    res = browser.executeAsync((done) => {
      done(Object.prototype.toString.call(new AbortController()));
    });
    expect(res.value).toBe('[object AbortController]');

    res = browser.executeAsync((done) => {
      done(new AbortController().signal.toString());
    });
    expect(res.value).toBe('[object AbortSignal]');

    res = browser.executeAsync((done) => {
      done(new AbortSignal().toString());
    });
    expect(res.value).toBe('[object AbortSignal]');

    res = browser.executeAsync((done) => {
      done(Object.prototype.toString.call(new AbortSignal()));
    });
    expect(res.value).toBe('[object AbortSignal]');
  });
});

function getJSErrors() {
  if (browser.desiredCapabilities.browserName === 'firefox') {
    console.log('NOTE: cannot get browser log in firefox so cannot verify "no JS errors"');
  } else {
    const allLogEntries = browser.log('browser').value;
    const jsErrors = allLogEntries.filter(logEntry => logEntry.level === 'SEVERE');
    allLogEntries.forEach(error => {
      if (error.level == 'SEVERE') {
        console.log(chalk.red(`[${error.level}] ${error.message}`));
      } else {
        console.log(`[${error.level}] ${error.message}`);
      }
    });
    return jsErrors;
  }
}
<|endoftext|>


<commit_before>
function htmlentities(str) {
	var div = document.createElement('div');
	div.appendChild(document.createTextNode(str));
	return div.innerHTML;
}

function formatStackForPopup(stack) {
	var lines = [];
	for(var i in stack) {
		var call = stack[i];
		lines.push('#' + call.num + ' ' + call.url + ' ' + call.method);
	}
	return lines.join('
');
}

// Ignore net::ERR_BLOCKED_BY_CLIENT initiated by AdPlus & etc
var ignoredUrlsHashes = {};
var ignoredUrlsSizeLimit = 100;

function isUrlIgnoredByType(url) {
	if(!url.indexOf('chrome-extension://')) { // ignore Google Chrome extensions 404 errors
		return true;
	}
	var ext = url.split('.').pop().split(/\#|\?/)[0].toLowerCase();
	if(ext == 'js') {
		return localStorage['ignore404js'];
	}
	if(ext == 'css') {
		return localStorage['ignore404css'];
	}
	return localStorage['ignore404others'];
}

function getIgnoredUrlHash(url) {
	return url.replace(/\d+/g, '');
}

chrome.webRequest.onErrorOccurred.addListener(function(e) {
	if(e.error == 'net::ERR_BLOCKED_BY_CLIENT') {
		var url = getIgnoredUrlHash(e.url);
		if(!isUrlIgnoredByType(url)) {
			if(ignoredUrlsHashes[url]) { // move url in the end of list
				delete ignoredUrlsHashes[url];
			}
			ignoredUrlsHashes[url] = true;
			var ignoredUrlsArray = Object.keys(ignoredUrlsHashes);
			if(ignoredUrlsArray.length > ignoredUrlsSizeLimit) {
				ignoredUrlsSizeLimit--;
				delete ignoredUrlsHashes[ignoredUrlsArray[0]];
			}
		}
	}
}, {urls: ["<all_urls>"]});

chrome.extension.onRequest.addListener(function(request, sender) {
	var errorsHtml = [];
	var stackLineRegExp = new RegExp('^(.*?)\\(?(https?://.*?)(\\)|$)');

	for(var i in request.errors) {
		var error = request.errors[i];

		if(error.is404) {
			if(ignoredUrlsHashes[getIgnoredUrlHash(error.url)] || isUrlIgnoredByType(error.url)) {
				delete request.errors[i];
				continue;
			}
			error.type = 'File not found';
			error.text = error.url;
			errorsHtml.unshift('File not found: ' + htmlentities(error.url));
		}
		else {
			error.text = error.text.replace(/^Uncaught /g, '');
			var m = new RegExp('^(\\w+):\s*(.+)').exec(error.text);
			error.type = m ? m[1] : 'Uncaught Error';

			if(localStorage['showColumn'] && error.line && error.col) {
				error.line = error.line + ':' + error.col;
			}

			if(localStorage['showTrace'] && error.stack) {
				var lines = error.stack.replace(/\n\s*at\s+/g, '\n').split('\n');
				lines.shift();
				for(var ii in lines) {
					var m = stackLineRegExp.exec(lines[ii]);
					lines[ii] = {
						num: lines.length - ii,
						url: m ? m[2] : lines[ii],
						method: (m && m[1].trim() ? m[1].trim() + '()' : '')
					};
				}
				error.stack = lines;
			}
			else {
				error.stack = null;
			}

			var sourceLink = error.url ? ('
'
				+ (error.stack ? '#' + (lines.length + 1) + ' ' : '')
				+ '' + error.url.replace(/[\/\\]$/g, '') + (error.line ? ':' + error.line : '') + ''
			) : '';

			var errorLink = '' + htmlentities(error.text) + '';

			errorsHtml.push(errorLink + sourceLink + (error.stack ? '
' + formatStackForPopup(error.stack) : ''));
		}
	}

	if(!errorsHtml.length) {
		return;
	}

	chrome.pageAction.setPopup({
		tabId: sender.tab.id,
		popup: 'popup.html?errors=' + encodeURIComponent(errorsHtml.join('

')) + '&host=' + encodeURIComponent(request.host) + '&tabId=' + sender.tab.id
	});
	chrome.pageAction.show(sender.tab.id);

	chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
		if(request['_tabId']) {
			sendResponse({
				'tabId': sender.tab.id
			});
			return true;
		}
	});

	if(localStorage['notify_' + request.host]) {
		notificationsHandler.showErrorsNotifications(request.errors, sender.tab.id);
	}
});

chrome.tabs.onUpdated.addListener(notificationsHandler.closeTabNotifications);
<commit_msg>Fix ignoring dynamic adBlocked 404 urls errors<commit_after>
function htmlentities(str) {
	var div = document.createElement('div');
	div.appendChild(document.createTextNode(str));
	return div.innerHTML;
}

function formatStackForPopup(stack) {
	var lines = [];
	for(var i in stack) {
		var call = stack[i];
		lines.push('#' + call.num + ' ' + call.url + ' ' + call.method);
	}
	return lines.join('
');
}

// Ignore net::ERR_BLOCKED_BY_CLIENT initiated by AdPlus & etc
var ignoredUrlsHashes = {};
var ignoredUrlsLimit = 100;

function isUrlIgnoredByType(url) {
	if(!url.indexOf('chrome-extension://')) { // ignore Google Chrome extensions 404 errors
		return true;
	}
	var ext = url.split('.').pop().split(/\#|\?/)[0].toLowerCase();
	if(ext == 'js') {
		return localStorage['ignore404js'];
	}
	if(ext == 'css') {
		return localStorage['ignore404css'];
	}
	return localStorage['ignore404others'];
}

function getIgnoredUrlHash(url) {
	return url.replace(/\d+/g, '');
}

chrome.webRequest.onErrorOccurred.addListener(function(e) {
	if(e.error == 'net::ERR_BLOCKED_BY_CLIENT') {
		var url = getIgnoredUrlHash(e.url);
		if(!isUrlIgnoredByType(url)) {
			if(ignoredUrlsHashes[url]) { // move url in the end of list
				delete ignoredUrlsHashes[url];
			}
			ignoredUrlsHashes[url] = true;
			var ignoredUrlsArray = Object.keys(ignoredUrlsHashes);
			if(ignoredUrlsArray.length > ignoredUrlsLimit) {
				delete ignoredUrlsHashes[ignoredUrlsArray[0]];
			}
		}
	}
}, {urls: ["<all_urls>"]});

chrome.extension.onRequest.addListener(function(request, sender) {
	var errorsHtml = [];
	var stackLineRegExp = new RegExp('^(.*?)\\(?(https?://.*?)(\\)|$)');

	for(var i in request.errors) {
		var error = request.errors[i];

		if(error.is404) {
			if(ignoredUrlsHashes[getIgnoredUrlHash(error.url)] || isUrlIgnoredByType(error.url)) {
				delete request.errors[i];
				continue;
			}
			error.type = 'File not found';
			error.text = error.url;
			errorsHtml.unshift('File not found: ' + htmlentities(error.url));
		}
		else {
			error.text = error.text.replace(/^Uncaught /g, '');
			var m = new RegExp('^(\\w+):\s*(.+)').exec(error.text);
			error.type = m ? m[1] : 'Uncaught Error';

			if(localStorage['showColumn'] && error.line && error.col) {
				error.line = error.line + ':' + error.col;
			}

			if(localStorage['showTrace'] && error.stack) {
				var lines = error.stack.replace(/\n\s*at\s+/g, '\n').split('\n');
				lines.shift();
				for(var ii in lines) {
					var m = stackLineRegExp.exec(lines[ii]);
					lines[ii] = {
						num: lines.length - ii,
						url: m ? m[2] : lines[ii],
						method: (m && m[1].trim() ? m[1].trim() + '()' : '')
					};
				}
				error.stack = lines;
			}
			else {
				error.stack = null;
			}

			var sourceLink = error.url ? ('
'
				+ (error.stack ? '#' + (lines.length + 1) + ' ' : '')
				+ '' + error.url.replace(/[\/\\]$/g, '') + (error.line ? ':' + error.line : '') + ''
			) : '';

			var errorLink = '' + htmlentities(error.text) + '';

			errorsHtml.push(errorLink + sourceLink + (error.stack ? '
' + formatStackForPopup(error.stack) : ''));
		}
	}

	if(!errorsHtml.length) {
		return;
	}

	chrome.pageAction.setPopup({
		tabId: sender.tab.id,
		popup: 'popup.html?errors=' + encodeURIComponent(errorsHtml.join('

')) + '&host=' + encodeURIComponent(request.host) + '&tabId=' + sender.tab.id
	});
	chrome.pageAction.show(sender.tab.id);

	chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
		if(request['_tabId']) {
			sendResponse({
				'tabId': sender.tab.id
			});
			return true;
		}
	});

	if(localStorage['notify_' + request.host]) {
		notificationsHandler.showErrorsNotifications(request.errors, sender.tab.id);
	}
});

chrome.tabs.onUpdated.addListener(notificationsHandler.closeTabNotifications);
<|endoftext|>


<commit_before>var mapnik = require('mapnik');
var assert = require('assert');

exports['test color creation'] = function(beforeExit) {
    // no 'new' keyword
    assert.throws(function() { mapnik.Color(); });

    // invalid args
    assert.throws(function() { new mapnik.Color(); });
    assert.throws(function() { new mapnik.Color(1); });
    // todo - fix mapnik upstream to actually throw
    //assert.throws(function() { new mapnik.Color('foo'); });

    var c = new mapnik.Color('green');
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

    var c = new mapnik.Color(0, 128, 0);
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

    var c = new mapnik.Color(0, 128, 0, 255);
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

};
<commit_msg>start testing mapnik.Color errors from string after http://trac.mapnik.org/changeset/3002<commit_after>var mapnik = require('mapnik');
var assert = require('assert');

exports['test color creation'] = function(beforeExit) {
    // no 'new' keyword
    assert.throws(function() { mapnik.Color(); });

    // invalid args
    assert.throws(function() { new mapnik.Color(); });
    assert.throws(function() { new mapnik.Color(1); });
    assert.throws(function() { new mapnik.Color('foo'); });

    var c = new mapnik.Color('green');
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

    var c = new mapnik.Color(0, 128, 0);
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

    var c = new mapnik.Color(0, 128, 0, 255);
    assert.equal(c.r, 0);
    assert.equal(c.g, 128);
    assert.equal(c.b, 0);
    assert.equal(c.a, 255);
    assert.equal(c.hex(), '#008000');
    assert.equal(c.toString(), 'rgb(0,128,0)');

};
<|endoftext|>


<commit_before>;(function($$){ 'use strict';

  var CanvasRenderer = $$('renderer', 'canvas');

  // Draw edge text
  CanvasRenderer.prototype.drawEdgeText = function(context, edge) {
    var text = edge._private.style['content'].strValue;

    if( !text || text.match(/^\s+$/) ){
      return;
    }

    if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching

    var computedSize = edge._private.style['font-size'].pxValue * edge.cy().zoom();
    var minSize = edge._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // Calculate text draw position

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // this.recalculateEdgeLabelProjection( edge );

    var rs = edge._private.rscratch;

    var deltaX = rs.endX - rs.startX;
    var deltaY = rs.endY - rs.startY;

    var angleToRotate = Math.atan2(deltaY, deltaX);
    var fmod = function (x, m) { return ((x % m) + m) % m; };
    var newAngle = fmod((angleToRotate + Math.PI / 2), Math.PI) - Math.PI / 2;

    context.translate(rs.labelX, rs.labelY);
    context.rotate(newAngle);
    // Rotate the label to make it align with the edge
    this.drawText(context, edge, 0, -7);
    context.rotate(-newAngle);
    context.translate(-rs.labelX, -rs.labelY);

  };

  // Draw node text
  CanvasRenderer.prototype.drawNodeText = function(context, node) {
    var text = node._private.style['content'].strValue;

    if ( !text || text.match(/^\s+$/) ) {
      return;
    }

    var computedSize = node._private.style['font-size'].pxValue * node.cy().zoom();
    var minSize = node._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // this.recalculateNodeLabelProjection( node );

    var textHalign = node._private.style['text-halign'].strValue;
    var textValign = node._private.style['text-valign'].strValue;
    var rs = node._private.rscratch;

    switch( textHalign ){
      case 'left':
        context.textAlign = 'right';
        break;

      case 'right':
        context.textAlign = 'left';
        break;

      default: // e.g. center
        context.textAlign = 'center';
    }

    switch( textValign ){
      case 'top':
        context.textBaseline = 'bottom';
        break;

      case 'bottom':
        context.textBaseline = 'top';
        break;

      default: // e.g. center
        context.textBaseline = 'middle';
    }

    this.drawText(context, node, rs.labelX, rs.labelY);
  };

  CanvasRenderer.prototype.getFontCache = function(context){
    var cache;

    this.fontCaches = this.fontCaches || [];

    for( var i = 0; i < this.fontCaches.length; i++ ){
      cache = this.fontCaches[i];

      if( cache.context === context ){
        return cache;
      }
    }

    cache = {
      context: context
    };
    this.fontCaches.push(cache);

    return cache;
  };

  // set up canvas context with font
  // returns transformed text string
  CanvasRenderer.prototype.setupTextStyle = function( context, element ){
    // Font style
    var parentOpacity = element.effectiveOpacity();
    var style = element._private.style;
    var labelStyle = style['font-style'].strValue;
    var labelSize = style['font-size'].pxValue + 'px';
    var labelFamily = style['font-family'].strValue;
    var labelWeight = style['font-weight'].strValue;
    var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity;
    var color = style['color'].value;
    var outlineColor = style['text-outline-color'].value;

    var fontCacheKey = element._private.fontKey;
    var cache = this.getFontCache(context);

    if( cache.key !== fontCacheKey ){
      context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;

      cache.key = fontCacheKey;
    }

    var text = String(style['content'].value);
    var textTransform = style['text-transform'].value;

    if (textTransform == 'none') {
    } else if (textTransform == 'uppercase') {
      text = text.toUpperCase();
    } else if (textTransform == 'lowercase') {
      text = text.toLowerCase();
    }

    // Calculate text draw position based on text alignment

    // so text outlines aren't jagged
    context.lineJoin = 'round';

    this.fillStyle(context, color[0], color[1], color[2], opacity);

    this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], opacity);

    return text;
  };

  // Draw text
  CanvasRenderer.prototype.drawText = function(context, element, textX, textY) {
    var style = element._private.style;
    var parentOpacity = element.effectiveOpacity();
    if( parentOpacity === 0 ){ return; }

    var text = this.setupTextStyle( context, element );

    if ( text != null && !isNaN(textX) && !isNaN(textY) ) {

      var lineWidth = 2  * style['text-outline-width'].value; // *2 b/c the stroke is drawn centred on the middle
      if (lineWidth > 0) {
        context.lineWidth = lineWidth;
        context.strokeText(text, textX, textY);
      }

      context.fillText(text, textX, textY);
    }
  };

})( cytoscape );<commit_msg>Fixed the flashing text while animation<commit_after>;(function($$){ 'use strict';

  var CanvasRenderer = $$('renderer', 'canvas');

  // Draw edge text
  CanvasRenderer.prototype.drawEdgeText = function(context, edge) {
    var text = edge._private.style['content'].strValue;

    if( !text || text.match(/^\s+$/) ){
      return;
    }

    if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching

    var computedSize = edge._private.style['font-size'].pxValue * edge.cy().zoom();
    var minSize = edge._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // Calculate text draw position

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // this.recalculateEdgeLabelProjection( edge );

    var rs = edge._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    var deltaX = rs.endX - rs.startX;
    var deltaY = rs.endY - rs.startY;

    var angleToRotate = Math.atan2(deltaY, deltaX);
    var fmod = function (x, m) { return ((x % m) + m) % m; };
    var newAngle = fmod((angleToRotate + Math.PI / 2), Math.PI) - Math.PI / 2;

    context.translate(rs.labelX, rs.labelY);
    context.rotate(newAngle);
    // Rotate the label to make it align with the edge
    this.drawText(context, edge, 0, -4);
    context.rotate(-newAngle);
    context.translate(-rs.labelX, -rs.labelY);

  };

  // Draw node text
  CanvasRenderer.prototype.drawNodeText = function(context, node) {
    var text = node._private.style['content'].strValue;

    if ( !text || text.match(/^\s+$/) ) {
      return;
    }

    var computedSize = node._private.style['font-size'].pxValue * node.cy().zoom();
    var minSize = node._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // this.recalculateNodeLabelProjection( node );

    var textHalign = node._private.style['text-halign'].strValue;
    var textValign = node._private.style['text-valign'].strValue;
    var rs = node._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    switch( textHalign ){
      case 'left':
        context.textAlign = 'right';
        break;

      case 'right':
        context.textAlign = 'left';
        break;

      default: // e.g. center
        context.textAlign = 'center';
    }

    switch( textValign ){
      case 'top':
        context.textBaseline = 'bottom';
        break;

      case 'bottom':
        context.textBaseline = 'top';
        break;

      default: // e.g. center
        context.textBaseline = 'middle';
    }

    this.drawText(context, node, rs.labelX, rs.labelY);
  };

  CanvasRenderer.prototype.getFontCache = function(context){
    var cache;

    this.fontCaches = this.fontCaches || [];

    for( var i = 0; i < this.fontCaches.length; i++ ){
      cache = this.fontCaches[i];

      if( cache.context === context ){
        return cache;
      }
    }

    cache = {
      context: context
    };
    this.fontCaches.push(cache);

    return cache;
  };

  // set up canvas context with font
  // returns transformed text string
  CanvasRenderer.prototype.setupTextStyle = function( context, element ){
    // Font style
    var parentOpacity = element.effectiveOpacity();
    var style = element._private.style;
    var labelStyle = style['font-style'].strValue;
    var labelSize = style['font-size'].pxValue + 'px';
    var labelFamily = style['font-family'].strValue;
    var labelWeight = style['font-weight'].strValue;
    var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity;
    var color = style['color'].value;
    var outlineColor = style['text-outline-color'].value;

    var fontCacheKey = element._private.fontKey;
    var cache = this.getFontCache(context);

    if( cache.key !== fontCacheKey ){
      context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;

      cache.key = fontCacheKey;
    }

    var text = String(style['content'].value);
    var textTransform = style['text-transform'].value;

    if (textTransform == 'none') {
    } else if (textTransform == 'uppercase') {
      text = text.toUpperCase();
    } else if (textTransform == 'lowercase') {
      text = text.toLowerCase();
    }

    // Calculate text draw position based on text alignment

    // so text outlines aren't jagged
    context.lineJoin = 'round';

    this.fillStyle(context, color[0], color[1], color[2], opacity);

    this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], opacity);

    return text;
  };

  // Draw text
  CanvasRenderer.prototype.drawText = function(context, element, textX, textY) {
    var style = element._private.style;
    var parentOpacity = element.effectiveOpacity();
    if( parentOpacity === 0 ){ return; }

    var text = this.setupTextStyle( context, element );

    if ( text != null && !isNaN(textX) && !isNaN(textY) ) {

      var lineWidth = 2  * style['text-outline-width'].value; // *2 b/c the stroke is drawn centred on the middle
      if (lineWidth > 0) {
        context.lineWidth = lineWidth;
        context.strokeText(text, textX, textY);
      }

      context.fillText(text, textX, textY);
    }
  };

})( cytoscape );<|endoftext|>


<commit_before>;(function($$){ 'use strict';

  var CanvasRenderer = $$('renderer', 'canvas');

  // Draw edge text
  CanvasRenderer.prototype.drawEdgeText = function(context, edge) {
    var text = edge._private.style['content'].strValue;

    if( !text || text.match(/^\s+$/) ){
      return;
    }

    if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching

    var computedSize = edge._private.style['font-size'].pxValue * edge.cy().zoom();
    var minSize = edge._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // Calculate text draw position

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // this.recalculateEdgeLabelProjection( edge );

    var rs = edge._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    var style = edge._private.style;
    var autorotate = style['edge-text-rotation'].strValue === 'autorotate';
    var theta, dx, dy;

    if( autorotate ){
      switch( rs.edgeType ){
        case 'haystack':
          dx = rs.haystackPts[2] - rs.haystackPts[0];
          dy = rs.haystackPts[3] - rs.haystackPts[1];
          break;
        default:
          dx = rs.endX - rs.startX;
          dy = rs.endY - rs.startY;
      }

      theta = Math.atan( dy / dx );

      context.translate(rs.labelX, rs.labelY);
      context.rotate(theta);

      this.drawText(context, edge, 0, 0);

      context.rotate(-theta);
      context.translate(-rs.labelX, -rs.labelY);
    } else {
      this.drawText(context, edge, rs.labelX, rs.labelY);
    }

  };

  // Draw node text
  CanvasRenderer.prototype.drawNodeText = function(context, node) {
    var text = node._private.style['content'].strValue;

    if ( !text || text.match(/^\s+$/) ) {
      return;
    }

    var computedSize = node._private.style['font-size'].pxValue * node.cy().zoom();
    var minSize = node._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // this.recalculateNodeLabelProjection( node );

    var textHalign = node._private.style['text-halign'].strValue;
    var textValign = node._private.style['text-valign'].strValue;
    var rs = node._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    switch( textHalign ){
      case 'left':
        context.textAlign = 'right';
        break;

      case 'right':
        context.textAlign = 'left';
        break;

      default: // e.g. center
        context.textAlign = 'center';
    }

    switch( textValign ){
      case 'top':
        context.textBaseline = 'bottom';
        break;

      case 'bottom':
        context.textBaseline = 'top';
        break;

      default: // e.g. center
        context.textBaseline = 'middle';
    }

    this.drawText(context, node, rs.labelX, rs.labelY);
  };

  CanvasRenderer.prototype.getFontCache = function(context){
    var cache;

    this.fontCaches = this.fontCaches || [];

    for( var i = 0; i < this.fontCaches.length; i++ ){
      cache = this.fontCaches[i];

      if( cache.context === context ){
        return cache;
      }
    }

    cache = {
      context: context
    };
    this.fontCaches.push(cache);

    return cache;
  };

  // set up canvas context with font
  // returns transformed text string
  CanvasRenderer.prototype.setupTextStyle = function( context, element ){
    // Font style
    var parentOpacity = element.effectiveOpacity();
    var style = element._private.style;
    var labelStyle = style['font-style'].strValue;
    var labelSize = style['font-size'].pxValue + 'px';
    var labelFamily = style['font-family'].strValue;
    var labelWeight = style['font-weight'].strValue;
    var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity;
    var outlineOpacity = style['text-outline-opacity'].value * opacity;
    var color = style['color'].value;
    var outlineColor = style['text-outline-color'].value;
    var shadowBlur = style['text-shadow-blur'].pxValue;
    var shadowOpacity = style['text-shadow-opacity'].value;
    var shadowColor = style['text-shadow-color'].value;
    var shadowOffsetX = style['text-shadow-offset-x'].pxValue;
    var shadowOffsetY = style['text-shadow-offset-y'].pxValue;

    var fontCacheKey = element._private.fontKey;
    var cache = this.getFontCache(context);

    if( cache.key !== fontCacheKey ){
      context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;

      cache.key = fontCacheKey;
    }

    var text = String(style['content'].value);
    var textTransform = style['text-transform'].value;

    if (textTransform == 'none') {
    } else if (textTransform == 'uppercase') {
      text = text.toUpperCase();
    } else if (textTransform == 'lowercase') {
      text = text.toLowerCase();
    }

    // Calculate text draw position based on text alignment

    // so text outlines aren't jagged
    context.lineJoin = 'round';

    this.fillStyle(context, color[0], color[1], color[2], opacity);

    this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);

    this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);

    return text;
  };

  function roundRect(ctx, x, y, width, height, radius) {
    var radius = radius || 5;
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.lineTo(x + width - radius, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
    ctx.lineTo(x + width, y + height - radius);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
    ctx.lineTo(x + radius, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
    ctx.lineTo(x, y + radius);
    ctx.quadraticCurveTo(x, y, x + radius, y);
    ctx.closePath();
    ctx.fill();
  }

  function wrapText(context, text, x, y, maxWidth, lineHeight) {
    var words = text.split(' ');
    var line = '';

    for(var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = context.measureText(testLine);
      var testWidth = metrics.width;
      testWidth = testWidth / ((lineHeight - 1) / 7);
      if (testWidth > maxWidth && n > 0) {
        context.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }

    context.fillText(line, x, y);
  }

  // Draw text
  CanvasRenderer.prototype.drawText = function(context, element, textX, textY) {
    var style = element._private.style;
    var rstyle = element._private.rstyle;
    var parentOpacity = element.effectiveOpacity();
    if( parentOpacity === 0 || style["text-opacity"].value === 0){ return; }

    var text = this.setupTextStyle( context, element );

    if ( text != null && !isNaN(textX) && !isNaN(textY)) {
      var backgroundOpacity = style["text-background-opacity"].value;
      if ((style["text-background-color"] && style["text-background-color"].value != "none" || style["text-border-width"].pxValue > 0) && backgroundOpacity > 0) {
        var textBorderWidth = style["text-border-width"].pxValue;
        var margin = 4 + textBorderWidth/2;
        var halign = style["text-halign"].value;
        var valign = style["text-valign"].value;

        if (element.isNode()) {
          //Move textX, textY to include the background margins
          if (valign == "top") {
            textY -=margin;
          } else if (valign == "bottom") {
            textY +=margin;
          }
          if (halign == "left") {
            textX -=margin;
          } else if (halign == "right") {
            textX +=margin;
          }
        }

        var bgWidth = rstyle.labelWidth;
        var bgHeight = style['font-size'].pxValue;
        var bgX = textX;

        if (halign) {
          if (halign == "center") {
            bgX = bgX - bgWidth / 2;
          } else if (halign == "left") {
            bgX = bgX- bgWidth;
          }
        }

        var bgY = textY;

        if (element.isNode()) {
          if (valign == "top") {
             bgY = bgY - bgHeight;
          } else if (valign == "center") {
            bgY = bgY- bgHeight / 2;
          }
        } else {
          bgY = bgY - bgHeight / 2;
        }

        if (style['edge-text-rotation'].strValue === 'autorotate') {
          textY = 0;
          bgWidth = 0;
          bgX = textX - bgWidth / 2;
          bgY = textY - bgHeight / 2;
        } else {
          // Adjust with border width & margin
          bgX -= margin;
          bgY -= margin;
          bgHeight += margin*2;
          bgWidth += margin*2;
        }

        if (style["text-background-color"]) {
          var textFill = context.fillStyle;
          var textBackgroundColor = style["text-background-color"].value;

          context.fillStyle = "rgba(" + textBackgroundColor[0] + "," + textBackgroundColor[1] + "," + textBackgroundColor[2] + "," + backgroundOpacity * parentOpacity + ")";
          roundRect(context, bgX, bgY, bgWidth, bgHeight, 2);
          context.fillStyle = textFill;
        }

        if (textBorderWidth > 0) {
          var textStroke = context.strokeStyle;
          var textLineWidth = context.lineWidth;
          var textBorderColor = style["text-border-color"].value;
          var textBorderStyle = style['text-border-style'].value;

          context.strokeStyle = "rgba(" + textBorderColor[0] + "," + textBorderColor[1] + "," + textBorderColor[2] + "," + backgroundOpacity * parentOpacity + ")";
          context.lineWidth = textBorderWidth;

          if( context.setLineDash ){ // for very outofdate browsers
            switch( textBorderStyle ){
              case 'dotted':
                context.setLineDash([ 1, 1 ]);
                break;
              case 'dashed':
                context.setLineDash([ 4, 2 ]);
                break;
              case 'double':
                context.lineWidth = textBorderWidth/4; // 50% reserved for white between the two borders
              case 'solid':
                context.setLineDash([ ]);
                break;
            }
          }

          context.strokeRect(bgX,bgY,bgWidth,bgHeight);

          if( textBorderStyle === 'double' ){
            var whiteWidth = textBorderWidth/2;

            context.strokeRect(bgX+whiteWidth,bgY+whiteWidth,bgWidth-whiteWidth*2,bgHeight-whiteWidth*2);
          }

          if( context.setLineDash ){ // for very outofdate browsers
            context.setLineDash([ ]);
          }
          context.lineWidth = textLineWidth;
          context.strokeStyle = textStroke;
        }

      }

      var lineWidth = 2  * style['text-outline-width'].value; // *2 b/c the stroke is drawn centred on the middle

      if (lineWidth > 0) {
        context.lineWidth = lineWidth;
        context.strokeText(text, textX, textY);
      }

      if (style['text-wrap'].value == 'wrap') {
        var fontSize = style['font-size'].pxValue;
        wrapText(context, text, textX, textY, style['text-max-width'].value, fontSize + 1);
      } else {
        context.fillText(text, textX, textY);
      }

      this.shadowStyle(context, 'transparent', 0); // reset for next guy
    }
  };

})( cytoscape );<commit_msg>fixed a glitch; also the text wrapping should not happen to edge label text<commit_after>;(function($$){ 'use strict';

  var CanvasRenderer = $$('renderer', 'canvas');

  // Draw edge text
  CanvasRenderer.prototype.drawEdgeText = function(context, edge) {
    var text = edge._private.style['content'].strValue;

    if( !text || text.match(/^\s+$/) ){
      return;
    }

    if( this.hideEdgesOnViewport && (this.dragData.didDrag || this.pinching || this.hoverData.dragging || this.data.wheel || this.swipePanning) ){ return; } // save cycles on pinching

    var computedSize = edge._private.style['font-size'].pxValue * edge.cy().zoom();
    var minSize = edge._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // Calculate text draw position

    context.textAlign = 'center';
    context.textBaseline = 'middle';

    // this.recalculateEdgeLabelProjection( edge );

    var rs = edge._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    var style = edge._private.style;
    var autorotate = style['edge-text-rotation'].strValue === 'autorotate';
    var theta, dx, dy;

    if( autorotate ){
      switch( rs.edgeType ){
        case 'haystack':
          dx = rs.haystackPts[2] - rs.haystackPts[0];
          dy = rs.haystackPts[3] - rs.haystackPts[1];
          break;
        default:
          dx = rs.endX - rs.startX;
          dy = rs.endY - rs.startY;
      }

      theta = Math.atan( dy / dx );

      context.translate(rs.labelX, rs.labelY);
      context.rotate(theta);

      this.drawText(context, edge, 0, 0);

      context.rotate(-theta);
      context.translate(-rs.labelX, -rs.labelY);
    } else {
      this.drawText(context, edge, rs.labelX, rs.labelY);
    }

  };

  // Draw node text
  CanvasRenderer.prototype.drawNodeText = function(context, node) {
    var text = node._private.style['content'].strValue;

    if ( !text || text.match(/^\s+$/) ) {
      return;
    }

    var computedSize = node._private.style['font-size'].pxValue * node.cy().zoom();
    var minSize = node._private.style['min-zoomed-font-size'].pxValue;

    if( computedSize < minSize ){
      return;
    }

    // this.recalculateNodeLabelProjection( node );

    var textHalign = node._private.style['text-halign'].strValue;
    var textValign = node._private.style['text-valign'].strValue;
    var rs = node._private.rscratch;
    if( !$$.is.number( rs.labelX ) || !$$.is.number( rs.labelY ) ){ return; } // no pos => label can't be rendered

    switch( textHalign ){
      case 'left':
        context.textAlign = 'right';
        break;

      case 'right':
        context.textAlign = 'left';
        break;

      default: // e.g. center
        context.textAlign = 'center';
    }

    switch( textValign ){
      case 'top':
        context.textBaseline = 'bottom';
        break;

      case 'bottom':
        context.textBaseline = 'top';
        break;

      default: // e.g. center
        context.textBaseline = 'middle';
    }

    this.drawText(context, node, rs.labelX, rs.labelY);
  };

  CanvasRenderer.prototype.getFontCache = function(context){
    var cache;

    this.fontCaches = this.fontCaches || [];

    for( var i = 0; i < this.fontCaches.length; i++ ){
      cache = this.fontCaches[i];

      if( cache.context === context ){
        return cache;
      }
    }

    cache = {
      context: context
    };
    this.fontCaches.push(cache);

    return cache;
  };

  // set up canvas context with font
  // returns transformed text string
  CanvasRenderer.prototype.setupTextStyle = function( context, element ){
    // Font style
    var parentOpacity = element.effectiveOpacity();
    var style = element._private.style;
    var labelStyle = style['font-style'].strValue;
    var labelSize = style['font-size'].pxValue + 'px';
    var labelFamily = style['font-family'].strValue;
    var labelWeight = style['font-weight'].strValue;
    var opacity = style['text-opacity'].value * style['opacity'].value * parentOpacity;
    var outlineOpacity = style['text-outline-opacity'].value * opacity;
    var color = style['color'].value;
    var outlineColor = style['text-outline-color'].value;
    var shadowBlur = style['text-shadow-blur'].pxValue;
    var shadowOpacity = style['text-shadow-opacity'].value;
    var shadowColor = style['text-shadow-color'].value;
    var shadowOffsetX = style['text-shadow-offset-x'].pxValue;
    var shadowOffsetY = style['text-shadow-offset-y'].pxValue;

    var fontCacheKey = element._private.fontKey;
    var cache = this.getFontCache(context);

    if( cache.key !== fontCacheKey ){
      context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;

      cache.key = fontCacheKey;
    }

    var text = String(style['content'].value);
    var textTransform = style['text-transform'].value;

    if (textTransform == 'none') {
    } else if (textTransform == 'uppercase') {
      text = text.toUpperCase();
    } else if (textTransform == 'lowercase') {
      text = text.toLowerCase();
    }

    // Calculate text draw position based on text alignment

    // so text outlines aren't jagged
    context.lineJoin = 'round';

    this.fillStyle(context, color[0], color[1], color[2], opacity);

    this.strokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);

    this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);

    return text;
  };

  function roundRect(ctx, x, y, width, height, radius) {
    var radius = radius || 5;
    ctx.beginPath();
    ctx.moveTo(x + radius, y);
    ctx.lineTo(x + width - radius, y);
    ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
    ctx.lineTo(x + width, y + height - radius);
    ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
    ctx.lineTo(x + radius, y + height);
    ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
    ctx.lineTo(x, y + radius);
    ctx.quadraticCurveTo(x, y, x + radius, y);
    ctx.closePath();
    ctx.fill();
  }

  function wrapText(context, text, x, y, maxWidth, lineHeight) {
    var words = text.split(' ');
    var line = '';

    for(var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = context.measureText(testLine);
      var testWidth = metrics.width;
      testWidth = testWidth / ((lineHeight - 1) / 7);
      if (testWidth > maxWidth && n > 0) {
        context.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }

    context.fillText(line, x, y);
  }

  // Draw text
  CanvasRenderer.prototype.drawText = function(context, element, textX, textY) {
    var style = element._private.style;
    var rstyle = element._private.rstyle;
    var parentOpacity = element.effectiveOpacity();
    if( parentOpacity === 0 || style["text-opacity"].value === 0){ return; }

    var text = this.setupTextStyle( context, element );

    if ( text != null && !isNaN(textX) && !isNaN(textY)) {
      var backgroundOpacity = style["text-background-opacity"].value;
      if ((style["text-background-color"] && style["text-background-color"].value != "none" || style["text-border-width"].pxValue > 0) && backgroundOpacity > 0) {
        var textBorderWidth = style["text-border-width"].pxValue;
        var margin = 4 + textBorderWidth/2;
        var halign = style["text-halign"].value;
        var valign = style["text-valign"].value;

        if (element.isNode()) {
          //Move textX, textY to include the background margins
          if (valign == "top") {
            textY -=margin;
          } else if (valign == "bottom") {
            textY +=margin;
          }
          if (halign == "left") {
            textX -=margin;
          } else if (halign == "right") {
            textX +=margin;
          }
        }

        var bgWidth = rstyle.labelWidth;
        var bgHeight = style['font-size'].pxValue;
        var bgX = textX;

        if (halign) {
          if (halign == "center") {
            bgX = bgX - bgWidth / 2;
          } else if (halign == "left") {
            bgX = bgX- bgWidth;
          }
        }

        var bgY = textY;

        if (element.isNode()) {
          if (valign == "top") {
             bgY = bgY - bgHeight;
          } else if (valign == "center") {
            bgY = bgY- bgHeight / 2;
          }
        } else {
          bgY = bgY - bgHeight / 2;
        }

        if (style['edge-text-rotation'].strValue === 'autorotate') {
          textY = 0;
          bgWidth += 4;
          bgX = textX - bgWidth / 2;
          bgY = textY - bgHeight / 2;
        } else {
          // Adjust with border width & margin
          bgX -= margin;
          bgY -= margin;
          bgHeight += margin*2;
          bgWidth += margin*2;
        }

        if (style["text-background-color"]) {
          var textFill = context.fillStyle;
          var textBackgroundColor = style["text-background-color"].value;

          context.fillStyle = "rgba(" + textBackgroundColor[0] + "," + textBackgroundColor[1] + "," + textBackgroundColor[2] + "," + backgroundOpacity * parentOpacity + ")";
          roundRect(context, bgX, bgY, bgWidth, bgHeight, 2);
          context.fillStyle = textFill;
        }

        if (textBorderWidth > 0) {
          var textStroke = context.strokeStyle;
          var textLineWidth = context.lineWidth;
          var textBorderColor = style["text-border-color"].value;
          var textBorderStyle = style['text-border-style'].value;

          context.strokeStyle = "rgba(" + textBorderColor[0] + "," + textBorderColor[1] + "," + textBorderColor[2] + "," + backgroundOpacity * parentOpacity + ")";
          context.lineWidth = textBorderWidth;

          if( context.setLineDash ){ // for very outofdate browsers
            switch( textBorderStyle ){
              case 'dotted':
                context.setLineDash([ 1, 1 ]);
                break;
              case 'dashed':
                context.setLineDash([ 4, 2 ]);
                break;
              case 'double':
                context.lineWidth = textBorderWidth/4; // 50% reserved for white between the two borders
              case 'solid':
                context.setLineDash([ ]);
                break;
            }
          }

          context.strokeRect(bgX,bgY,bgWidth,bgHeight);

          if( textBorderStyle === 'double' ){
            var whiteWidth = textBorderWidth/2;

            context.strokeRect(bgX+whiteWidth,bgY+whiteWidth,bgWidth-whiteWidth*2,bgHeight-whiteWidth*2);
          }

          if( context.setLineDash ){ // for very outofdate browsers
            context.setLineDash([ ]);
          }
          context.lineWidth = textLineWidth;
          context.strokeStyle = textStroke;
        }

      }

      var lineWidth = 2  * style['text-outline-width'].value; // *2 b/c the stroke is drawn centred on the middle

      if (lineWidth > 0) {
        context.lineWidth = lineWidth;
        context.strokeText(text, textX, textY);
      }

      if (element.isNode() && style['text-wrap'].value == 'wrap') {
        var fontSize = style['font-size'].pxValue;
        wrapText(context, text, textX, textY, style['text-max-width'].value, fontSize + 1);
      } else {
        context.fillText(text, textX, textY);
      }

      this.shadowStyle(context, 'transparent', 0); // reset for next guy
    }
  };

})( cytoscape );<|endoftext|>


<commit_before>require('approvals').configure({
        reporters:  ["visualstudio"]
    }).mocha(__dirname);

var formatException = require("../src/ExceptionMessageBeautifier.js").format;

describe('When prettyifying DotNet Exception Messages', function () {

    it('formats a basic exception', function () {
        var data = "System.Exception: Hello Exception!   at TestExceptionGenerator.Spike.GetException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 22   at TestExceptionGenerator.Spike.<GenericException>b__0() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 13   at TestExceptionGenerator.Extensions.GetExceptionString(Action action) in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 34";

        var result = formatException(data);

        this.verify(result);
    });

    it('formats an exception with an inner exception', function(){
        var data = 'System.Exception: Test outer exception ---> System.Exception: Hello Exception!   at TestExceptionGenerator.Spike.GetException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 29   at TestExceptionGenerator.Spike.GetInnerException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 36   --- End of inner exception stack trace ---   at TestExceptionGenerator.Spike.GetInnerException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 41   at TestExceptionGenerator.Spike.<GenericExceptionWithInnerException>b__1() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 21   at TestExceptionGenerator.Extensions.GetExceptionString(Action action) in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 56'

        var result = formatException(data);

        this.verify(result);
    });

    it('spike', function(){
        var data = "System.ArgumentException: The parameters dictionary contains a null entry for parameter 'planId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Processing(Int32)' in 'Resgrid.Web.Areas.User.Controllers.SubscriptionController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.Parameter name: parameters   at System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo)   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__36(IAsyncResult asyncResult, ActionInvocation innerInvokeState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3c()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass45.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3e()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass30.<BeginInvokeActionMethodWithFilters>b__2f(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<>c__DisplayClass28.<BeginInvokeAction>b__19()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<BeginInvokeAction>b__1b(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)   at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(IAsyncResult asyncResult, ProcessRequestState innerState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)"
        var result = formatException(data);
        this.verify(result);
    });
});
<commit_msg>adding a sample winJS formatter<commit_after>require('approvals').configure({
        reporters:  ["visualstudio"]
    }).mocha(__dirname);

var formatException = require("../src/ExceptionMessageBeautifier.js").format;

describe('When prettyifying DotNet Exception Messages', function () {

    it('formats a basic exception', function () {
        var data = "System.Exception: Hello Exception!   at TestExceptionGenerator.Spike.GetException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 22   at TestExceptionGenerator.Spike.<GenericException>b__0() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 13   at TestExceptionGenerator.Extensions.GetExceptionString(Action action) in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 34";
        var result = formatException(data);
        this.verify(result);
    });

    it('formats an exception with an inner exception', function(){
        var data = 'System.Exception: Test outer exception ---> System.Exception: Hello Exception!   at TestExceptionGenerator.Spike.GetException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 29   at TestExceptionGenerator.Spike.GetInnerException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 36   --- End of inner exception stack trace ---   at TestExceptionGenerator.Spike.GetInnerException() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 41   at TestExceptionGenerator.Spike.<GenericExceptionWithInnerException>b__1() in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 21   at TestExceptionGenerator.Extensions.GetExceptionString(Action action) in c:\\Code\\personal\\DotNetExceptionMessageFormatter\\TestExceptionGenerator\\Spike.cs:line 56'
        var result = formatException(data);
        this.verify(result);
    });

    it('spike', function(){
        var data = "System.ArgumentException: The parameters dictionary contains a null entry for parameter 'planId' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Processing(Int32)' in 'Resgrid.Web.Areas.User.Controllers.SubscriptionController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.Parameter name: parameters   at System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo)   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.ActionInvocation.InvokeSynchronousActionMethod()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<BeginInvokeSynchronousActionMethod>b__36(IAsyncResult asyncResult, ActionInvocation innerInvokeState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`2.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3c()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.AsyncInvocationWithFilters.<>c__DisplayClass45.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3e()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass30.<BeginInvokeActionMethodWithFilters>b__2f(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<>c__DisplayClass28.<BeginInvokeAction>b__19()   at System.Web.Mvc.Async.AsyncControllerActionInvoker.<>c__DisplayClass1e.<BeginInvokeAction>b__1b(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResult`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.<BeginExecute>b__15(IAsyncResult asyncResult, Controller controller)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult)   at System.Web.Mvc.Controller.System.Web.Mvc.Async.IAsyncController.EndExecute(IAsyncResult asyncResult)   at System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__4(IAsyncResult asyncResult, ProcessRequestState innerState)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncVoid`1.CallEndDelegate(IAsyncResult asyncResult)   at System.Web.Mvc.Async.AsyncResultWrapper.WrappedAsyncResultBase`1.End()   at System.Web.Mvc.Async.AsyncResultWrapper.End[TResult](IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag)   at System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult)   at System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result)   at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)"
        var result = formatException(data);
        this.verify(result);
    });
});

//describe("When beautifying WinJS exception messages", function(){
//    it("should format", function(){
//        var data = '{"type":"error","detail":{"description":"Object doesn\'t support this action","number":-2146827843,"stack":"TypeError: Object doesn\'t support this action\n   at foo3 (ms-appx://3fe70ce3-a49b-4236-bb4f-00a8b8a64290/js/default.js:18:9)\n   at foo2 (ms-appx://3fe70ce3-a49b-4236-bb4f-00a8b8a64290/js/default.js:15:9)\n   at foo1 (ms-appx://3fe70ce3-a49b-4236-bb4f-00a8b8a64290/js/default.js:12:9)\n   at onactivated (ms-appx://3fe70ce3-a49b-4236-bb4f-00a8b8a64290/js/default.js:22:9)\n   at wrapper (ms-appx://microsoft.winjs.1.0/js/base.js:549:61)\n   at dispatchOne (ms-appx://microsoft.winjs.1.0/js/base.js:6987:25)\n   at dispatchEvent (ms-appx://microsoft.winjs.1.0/js/base.js:6986:21)\n   at drainQueue (ms-appx://microsoft.winjs.1.0/js/base.js:7038:9)\n   at queueEvent (ms-appx://microsoft.winjs.1.0/js/base.js:7057:13)\n   at Anonymous function (ms-appx://microsoft.winjs.1.0/js/base.js:7110:13)"}}';
//        verifyHelper.call(this, [data]);
//    });
//})
<|endoftext|>