<commit_before>import assign from 'object-assign';
import element from '../../lib/element';
import properties from '../../../src/api/properties';

function create (prop) {
  return element().skate({
    properties: {
      test: assign({ attribute: true }, prop)
    }
  })();
}

function testTypeValues (type, values) {
  const elem = create(properties[type]());
  values.forEach(function (value) {
    elem.test = value[0];
    expect(elem.test).to.equal(value[1], 'property');
    expect(elem.getAttribute('test')).to.equal(value[2], 'attribute');
  });
}

describe('api/properties', function () {
  describe('property change event', function () {
    it('is not triggered when `opts.event` is not set', function () {
      let elem = create(properties.boolean());
      elem.test = false;

      let calls = 0;
      elem.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(0);
    });

    it('the event name can be changed', function () {
      let elem = create(assign({event: 'foo'}, properties.boolean()));
      elem.test = false;

      let propertyChangeCalled = false;
      let fooCalled = false;
      elem.addEventListener('propertychange', () => propertyChangeCalled = true);
      elem.addEventListener('foo', () => fooCalled = true);
      elem.test = true;

      expect(propertyChangeCalled).to.equal(false);
      expect(fooCalled).to.equal(true);
    });

    it('is triggered only when properties change', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let calls = 0;
      elem.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(1);
      elem.test = true;
      expect(calls).to.equal(1);
      elem.test = false;
      expect(calls).to.equal(2);
    });

    it('does not bubble', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let parent = document.createElement('div');
      parent.appendChild(elem);

      let calls = 0;
      parent.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(0);
    });

    it('can be cancelled', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;
      elem.addEventListener('propertychange', (e) => e.preventDefault());
      elem.test = true;
      expect(elem.test).to.equal(false);
    });

    it.only('after cancelling events, subsequent sets pass through', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let alreadyPrevented = false;
      elem.addEventListener('propertychange', function (e) {
        if (!alreadyPrevented) {
          alreadyPrevented = true;
          e.preventDefault()
        }
      });

      elem.test = true;
      elem.test = true;
      expect(elem.test).to.equal(true);

    });

    it('contains property name, and change details', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      let event = null;
      elem.test = false;

      elem.addEventListener('propertychange', (e) => event = e);
      elem.test = true;

      expect(event.detail).to.be.an('object');
      expect(event.detail.name).to.equal('test');
      expect(event.detail.oldValue).to.equal(false);
      expect(event.detail.newValue).to.equal(true);
    });
  });

  describe('boolean', function () {
    it('initial value', function () {
      let elem = create(properties.boolean());
      expect(elem.test).to.equal(false);
      expect(elem.getAttribute('test')).to.equal(null);
    });

    [undefined, null, false, 0, '', 'something'].forEach(function (value) {
      value = JSON.stringify(value);
      it('setting attribute to ' + value, function () {
        let elem = create(properties.boolean());
        elem.setAttribute('test', value);
        expect(elem.test).to.equal(true, 'property');
        expect(elem.getAttribute('test')).to.equal(String(value), 'attribute');
      });
      it('setting property to ' + value, function () {
        let elem = create(properties.boolean());
        elem.test = value;
        expect(elem.test).to.equal(!!value, 'property');
        expect(elem.getAttribute('test')).to.equal(value ? '' : null, 'attribute');
      });
    });

    it('removing attribute', function () {
      let elem = create(properties.boolean());

      elem.setAttribute('test', '');
      expect(elem.test).to.equal(true);
      expect(elem.getAttribute('test')).to.equal('');

      elem.removeAttribute('test');
      expect(elem.test).to.equal(false);
      expect(elem.getAttribute('test')).to.equal(null);
    });
  });

  describe('number', function () {
    it('values', function () {
      let elem = create(properties.number());
      expect(elem.test).to.equal(undefined);
      expect(elem.getAttribute('test')).to.equal(null);
      testTypeValues('number', [
        [false, 0, '0'],
        [null, undefined, null],
        [undefined, undefined, null],
        [0.1, 0.1, '0.1'],
        ['', 0, '0']
      ]);
    });
  });

  describe('string', function () {
    it('values', function () {
      let elem = create(properties.string());
      expect(elem.test).to.equal(undefined);
      expect(elem.getAttribute('test')).to.equal(null);
      testTypeValues('string', [
        [false, 'false', 'false'],
        [null, undefined, null],
        [undefined, undefined, null],
        [0, '0', '0'],
        ['', '', '']
      ]);
    });
  });

  describe('overriding', function () {
    it('boolean', function () {
      expect(properties.boolean({ default: true }).default).to.equal(true);
    });

    it('number', function () {
      expect(properties.number({ default: 1 }).default).to.equal(1);
    });

    it('string', function () {
      expect(properties.string({ default: 'test' }).default).to.equal('test');
    });
  });
});
<commit_msg>Remove .only<commit_after>import assign from 'object-assign';
import element from '../../lib/element';
import properties from '../../../src/api/properties';

function create (prop) {
  return element().skate({
    properties: {
      test: assign({ attribute: true }, prop)
    }
  })();
}

function testTypeValues (type, values) {
  const elem = create(properties[type]());
  values.forEach(function (value) {
    elem.test = value[0];
    expect(elem.test).to.equal(value[1], 'property');
    expect(elem.getAttribute('test')).to.equal(value[2], 'attribute');
  });
}

describe('api/properties', function () {
  describe('property change event', function () {
    it('is not triggered when `opts.event` is not set', function () {
      let elem = create(properties.boolean());
      elem.test = false;

      let calls = 0;
      elem.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(0);
    });

    it('the event name can be changed', function () {
      let elem = create(assign({event: 'foo'}, properties.boolean()));
      elem.test = false;

      let propertyChangeCalled = false;
      let fooCalled = false;
      elem.addEventListener('propertychange', () => propertyChangeCalled = true);
      elem.addEventListener('foo', () => fooCalled = true);
      elem.test = true;

      expect(propertyChangeCalled).to.equal(false);
      expect(fooCalled).to.equal(true);
    });

    it('is triggered only when properties change', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let calls = 0;
      elem.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(1);
      elem.test = true;
      expect(calls).to.equal(1);
      elem.test = false;
      expect(calls).to.equal(2);
    });

    it('does not bubble', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let parent = document.createElement('div');
      parent.appendChild(elem);

      let calls = 0;
      parent.addEventListener('propertychange', () => calls++);
      elem.test = true;

      expect(calls).to.equal(0);
    });

    it('can be cancelled', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;
      elem.addEventListener('propertychange', (e) => e.preventDefault());
      elem.test = true;
      expect(elem.test).to.equal(false);
    });

    it('after cancelling events, subsequent sets pass through', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      elem.test = false;

      let alreadyPrevented = false;
      elem.addEventListener('propertychange', function (e) {
        if (!alreadyPrevented) {
          alreadyPrevented = true;
          e.preventDefault()
        }
      });

      elem.test = true;
      elem.test = true;
      expect(elem.test).to.equal(true);
    });

    it('contains property name, and change details', function () {
      let elem = create(assign({event: 'propertychange'}, properties.boolean()));
      let event = null;
      elem.test = false;

      elem.addEventListener('propertychange', (e) => event = e);
      elem.test = true;

      expect(event.detail).to.be.an('object');
      expect(event.detail.name).to.equal('test');
      expect(event.detail.oldValue).to.equal(false);
      expect(event.detail.newValue).to.equal(true);
    });
  });

  describe('boolean', function () {
    it('initial value', function () {
      let elem = create(properties.boolean());
      expect(elem.test).to.equal(false);
      expect(elem.getAttribute('test')).to.equal(null);
    });

    [undefined, null, false, 0, '', 'something'].forEach(function (value) {
      value = JSON.stringify(value);
      it('setting attribute to ' + value, function () {
        let elem = create(properties.boolean());
        elem.setAttribute('test', value);
        expect(elem.test).to.equal(true, 'property');
        expect(elem.getAttribute('test')).to.equal(String(value), 'attribute');
      });
      it('setting property to ' + value, function () {
        let elem = create(properties.boolean());
        elem.test = value;
        expect(elem.test).to.equal(!!value, 'property');
        expect(elem.getAttribute('test')).to.equal(value ? '' : null, 'attribute');
      });
    });

    it('removing attribute', function () {
      let elem = create(properties.boolean());

      elem.setAttribute('test', '');
      expect(elem.test).to.equal(true);
      expect(elem.getAttribute('test')).to.equal('');

      elem.removeAttribute('test');
      expect(elem.test).to.equal(false);
      expect(elem.getAttribute('test')).to.equal(null);
    });
  });

  describe('number', function () {
    it('values', function () {
      let elem = create(properties.number());
      expect(elem.test).to.equal(undefined);
      expect(elem.getAttribute('test')).to.equal(null);
      testTypeValues('number', [
        [false, 0, '0'],
        [null, undefined, null],
        [undefined, undefined, null],
        [0.1, 0.1, '0.1'],
        ['', 0, '0']
      ]);
    });
  });

  describe('string', function () {
    it('values', function () {
      let elem = create(properties.string());
      expect(elem.test).to.equal(undefined);
      expect(elem.getAttribute('test')).to.equal(null);
      testTypeValues('string', [
        [false, 'false', 'false'],
        [null, undefined, null],
        [undefined, undefined, null],
        [0, '0', '0'],
        ['', '', '']
      ]);
    });
  });

  describe('overriding', function () {
    it('boolean', function () {
      expect(properties.boolean({ default: true }).default).to.equal(true);
    });

    it('number', function () {
      expect(properties.number({ default: 1 }).default).to.equal(1);
    });

    it('string', function () {
      expect(properties.string({ default: 'test' }).default).to.equal('test');
    });
  });
});
<|endoftext|>


<commit_before>goog.require('treesaver.ui.ArticleManager');
goog.require('treesaver.ui.Article');
goog.require('treesaver.events');

// Run after window loading
$(function () {
  module('articlemanager', {
    setup: function () {
      // Mock out article load function to always be successful
      treesaver.ui.Article.prototype._oldLoad = treesaver.ui.Article.prototype.load;
      treesaver.ui.Article.prototype.load = function () {
        // Do nothing
      };

      // Set the initial url of the article manager
      treesaver.ui.ArticleManager.initialUrl = document.location.href;
    },
    teardown: function () {
      $('.testonly').remove();
      // Restore mock
      treesaver.ui.Article.prototype.load = treesaver.ui.Article.prototype._oldLoad;
      delete treesaver.ui.Article.prototype._oldLoad;
    }
  });

  test('getTOCLocation', function () {
    var root = /http:\/\/[^\/]*\//.exec(document.location.href),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1) ;

    equals(treesaver.ui.ArticleManager.getTOCLocation(), document.location.href, 'Non-existant <link> uses current document');

    // OK, now add a <link> that points here
    $('<link rel="contents" href="http://test.com/" class="testonly" />').appendTo($('head'));
    equals(treesaver.ui.ArticleManager.getTOCLocation(), 'http://test.com/', 'Absolute URL maintained');

    $('link[rel*=contents]').attr('href', '/');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root, 'Root URL');

    $('link[rel*=contents]').attr('href', '/hello');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root + 'hello', 'Absolute path');

    $('link[rel*=contents]').attr('href', 'hello');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root + basename + '/hello', 'Relative path');

    $('link[rel*=contents]').attr('rel', 'self contents');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), document.location.href, 'Self overrides href');
  });

  test('findTOCLinks', function () {
    var root = /http:\/\/[^\/]*\//.exec(document.location.href),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        urls = ['http://example.com/', '/', '/hello', '', '/'],
        html,
        div = document.createElement('div');

    html = '
Fake document title
Non-TOC link.';
    $(urls).each(function () {
      var p = $('

');
          a = $('test').attr('href', this);

      a.appendTo(p);
      p.appendTo(div);
    });
    html += $(div).html();

    // Make sure loaded event fires
    treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.TOCUPDATED, function handler() {
      ok(true, 'TOC Loaded event received');
      treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.TOCUPDATED, handler);
      start();
    });

    treesaver.ui.ArticleManager.load('

hi

');
    treesaver.ui.ArticleManager.findTOCLinks(html);

    // Make sure basic ordering stuff works
    ok(treesaver.ui.ArticleManager.articleOrder, 'Order data structure created');
    ok(treesaver.ui.ArticleManager.articleMap, 'Map data structure created');
    ok(treesaver.ui.ArticleManager.articles, 'Articles data structure created');
    ok(treesaver.ui.ArticleManager.articleOrder.length, 'Article order populated');
    equals(treesaver.ui.ArticleManager.articleOrder.length, 5, 'Duplicate URLs inserted twice');
    equals(treesaver.ui.ArticleManager.currentArticleIndex, 3, 'Current Index');
    same(treesaver.ui.ArticleManager.articleMap['http://example.com/'], [0], 'Absolute URL positions');
    same(treesaver.ui.ArticleManager.articleMap[document.location.href], [3], 'Current URL positions');
    same(treesaver.ui.ArticleManager.articleMap[root], [1, 4], 'Duplicate URLs positions');
    equals(treesaver.ui.ArticleManager.articleOrder[0].url, 'http://example.com/', 'Absolute URL correct');
    equals(treesaver.ui.ArticleManager.articleOrder[1].url, root, 'Root URL correct');

    // Make sure loaded event fires
    stop(4000);
  });

  test('loadingPage', function () {
  });

  test('getArticleIndex / previousArticle / nextArticle', function () {
    var root = /http:\/\/[^\/]*\//.exec(document.location.href),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        urls = ['http://example.com/', '/', '/hello', 'example.html', '/'],
        html,
        div = document.createElement('div'),
        articles = [],
        article;

    html = '
Fake document title
Non-TOC link.';
    $(urls).each(function () {
      var p = $('

');
          a = $('test').attr('href', this);

      a.appendTo(p);
      p.appendTo(div);
    });
    html += $(div).html();

    treesaver.ui.ArticleManager.load('

hi

');

    treesaver.ui.ArticleManager.findTOCLinks(html);

    articles = urls.map(function (url, i) {
      return treesaver.ui.ArticleManager.articleOrder[i];
    });

    // Test getIndex
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[0].url), 0, 'getIndex: First article');
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 1, 'getIndex: Second article');
    // Move the current position
    treesaver.ui.ArticleManager.currentArticleIndex = 10;
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 4, 'getIndex: Second with different current position');
    treesaver.ui.ArticleManager.currentArticleIndex = 2;
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 1, 'getIndex: Second with different current position');
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url, true), 4, 'getIndex: Second with different current position');

    // Restore the normal position
    // TODO: Re-write this given caching
    //treesaver.ui.ArticleManager.currentArticleIndex = 0;
    //ok(!treesaver.ui.ArticleManager.previousArticle(), 'prev: Cannot go before first');
    //article = treesaver.ui.ArticleManager.nextArticle(true);
    //equals(article.url, articles[1].url, 'next: Correct article');
    //treesaver.ui.ArticleManager.currentArticleIndex = 1;
    //article = treesaver.ui.ArticleManager.previousArticle(true);
    //equals(article.url, articles[0].url, 'previous: Correct article');
  });

  //test('setCurrentArticle / setCurrentPosition / events', function () {
    //var root = /http:\/\/[^\/]*\//.exec(document.location.href),
        //basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        //urls = ['http://example.com/', '/', '/hello', 'example.html', '/'],
        //html,
        //div = document.createElement('div'),
        //articles = [],
        //redirectFunction = treesaver.ui.ArticleManager.redirectToCurrentArticle;

    //// Mock out redirect function
    //treesaver.ui.ArticleManager.redirectToCurrentArticle = function () {
      //ok(false, 'Article manager tried to redirect');
    //};

    //html = '
Fake document title
Non-TOC link.';
    //$(urls).each(function () {
      //$('').attr('href', this).appendTo(div);
    //});
    //html += $(div).html();

    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //var current_article = treesaver.ui.ArticleManager.getCurrentArticle() || {};

      //ok(true, 'ArticleChanged received on first load');
      //equals(e.article.url, current_article.url, 'Article parameter passed in');
      //equals(e.article.url, document.location.href, 'Initial article correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //stop(1000);
    //treesaver.ui.ArticleManager.load('

hi

');
    //treesaver.ui.ArticleManager.findTOCLinks(html);
    //articles = urls.map(function (url, i) {
      //return treesaver.ui.ArticleManager.articleOrder[i];
    //});

    //// Now go to next article, and make sure event comes through
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on next article');
      //equals(e.article.url, articles[1].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 1, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
      //start();
    //});

    //// Move to next article
    //treesaver.ui.ArticleManager.nextArticle();

    //// Go to prev article, and make sure event comes through
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on prev article');
      //equals(e.article.url, articles[0].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 0, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //// Move to next article
    //treesaver.ui.ArticleManager.previousArticle();

    //// Move to the last article
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on setCurrentArticle');
      //equals(e.article.url, articles[3].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 3, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //// Move to later article
    //treesaver.ui.ArticleManager.setCurrentArticle(articles[3]);

    //// Make sure article with two positions is reached correctly
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on next page of double listed');
      //equals(e.article.url, articles[4].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 4, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //treesaver.ui.ArticleManager.nextArticle();

    //// Make sure we don't get a bogus move at end
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(false, 'ArticleChanged received when could not go next');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //treesaver.ui.ArticleManager.nextArticle();

    //// That test is our last and shouldn't have an event handler called, so set a time out
    //setTimeout(start, 300);

    //// Restore mock
    //treesaver.ui.ArticleManager.redirectToCurrentArticle = redirectFunction;
  //});
});
<commit_msg>Fix ArticleManager unit tests in Firefox 3.6 (need to strip hash from current URL)<commit_after>goog.require('treesaver.ui.ArticleManager');
goog.require('treesaver.ui.Article');
goog.require('treesaver.events');

// Run after window loading
$(function () {
  module('articlemanager', {
    setup: function () {
      // Mock out article load function to always be successful
      treesaver.ui.Article.prototype._oldLoad = treesaver.ui.Article.prototype.load;
      treesaver.ui.Article.prototype.load = function () {
        // Do nothing
      };

      // Set the initial url of the article manager
      treesaver.ui.ArticleManager.initialUrl = treesaver.network.stripHash(document.location.href);
    },
    teardown: function () {
      $('.testonly').remove();
      // Restore mock
      treesaver.ui.Article.prototype.load = treesaver.ui.Article.prototype._oldLoad;
      delete treesaver.ui.Article.prototype._oldLoad;
    }
  });

  test('getTOCLocation', function () {
    var root = /http:\/\/[^\/]*\//.exec(treesaver.network.stripHash(document.location.href)),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1) ;

    equals(treesaver.ui.ArticleManager.getTOCLocation(), treesaver.network.stripHash(document.location.href), 'Non-existant <link> uses current document');

    // OK, now add a <link> that points here
    $('<link rel="contents" href="http://test.com/" class="testonly" />').appendTo($('head'));
    equals(treesaver.ui.ArticleManager.getTOCLocation(), 'http://test.com/', 'Absolute URL maintained');

    $('link[rel*=contents]').attr('href', '/');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root, 'Root URL');

    $('link[rel*=contents]').attr('href', '/hello');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root + 'hello', 'Absolute path');

    $('link[rel*=contents]').attr('href', 'hello');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), root + basename + '/hello', 'Relative path');

    $('link[rel*=contents]').attr('rel', 'self contents');
    equals(treesaver.ui.ArticleManager.getTOCLocation(), treesaver.network.stripHash(document.location.href), 'Self overrides href');
  });

  test('findTOCLinks', function () {
    var root = /http:\/\/[^\/]*\//.exec(treesaver.network.stripHash(document.location.href)),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        urls = ['http://example.com/', '/', '/hello', '', '/'],
        html,
        div = document.createElement('div');

    html = '
Fake document title
Non-TOC link.';
    $(urls).each(function () {
      var p = $('

');
          a = $('test').attr('href', this);

      a.appendTo(p);
      p.appendTo(div);
    });
    html += $(div).html();

    // Make sure loaded event fires
    treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.TOCUPDATED, function handler() {
      ok(true, 'TOC Loaded event received');
      treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.TOCUPDATED, handler);
      start();
    });

    treesaver.ui.ArticleManager.load('

hi

');
    treesaver.ui.ArticleManager.findTOCLinks(html);

    // Make sure basic ordering stuff works
    ok(treesaver.ui.ArticleManager.articleOrder, 'Order data structure created');
    ok(treesaver.ui.ArticleManager.articleMap, 'Map data structure created');
    ok(treesaver.ui.ArticleManager.articles, 'Articles data structure created');
    ok(treesaver.ui.ArticleManager.articleOrder.length, 'Article order populated');
    equals(treesaver.ui.ArticleManager.articleOrder.length, 5, 'Duplicate URLs inserted twice');
    equals(treesaver.ui.ArticleManager.currentArticleIndex, 3, 'Current Index');
    same(treesaver.ui.ArticleManager.articleMap['http://example.com/'], [0], 'Absolute URL positions');
    same(treesaver.ui.ArticleManager.articleMap[treesaver.network.stripHash(document.location.href)], [3], 'Current URL positions');
    same(treesaver.ui.ArticleManager.articleMap[root], [1, 4], 'Duplicate URLs positions');
    equals(treesaver.ui.ArticleManager.articleOrder[0].url, 'http://example.com/', 'Absolute URL correct');
    equals(treesaver.ui.ArticleManager.articleOrder[1].url, root, 'Root URL correct');

    // Make sure loaded event fires
    stop(4000);
  });

  test('loadingPage', function () {
  });

  test('getArticleIndex / previousArticle / nextArticle', function () {
    var root = /http:\/\/[^\/]*\//.exec(treesaver.network.stripHash(document.location.href)),
        basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        urls = ['http://example.com/', '/', '/hello', 'example.html', '/'],
        html,
        div = document.createElement('div'),
        articles = [],
        article;

    html = '
Fake document title
Non-TOC link.';
    $(urls).each(function () {
      var p = $('

');
          a = $('test').attr('href', this);

      a.appendTo(p);
      p.appendTo(div);
    });
    html += $(div).html();

    treesaver.ui.ArticleManager.load('

hi

');

    treesaver.ui.ArticleManager.findTOCLinks(html);

    articles = urls.map(function (url, i) {
      return treesaver.ui.ArticleManager.articleOrder[i];
    });

    // Test getIndex
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[0].url), 0, 'getIndex: First article');
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 1, 'getIndex: Second article');
    // Move the current position
    treesaver.ui.ArticleManager.currentArticleIndex = 10;
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 4, 'getIndex: Second with different current position');
    treesaver.ui.ArticleManager.currentArticleIndex = 2;
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url), 1, 'getIndex: Second with different current position');
    equals(treesaver.ui.ArticleManager._getArticleIndex(articles[1].url, true), 4, 'getIndex: Second with different current position');

    // Restore the normal position
    // TODO: Re-write this given caching
    //treesaver.ui.ArticleManager.currentArticleIndex = 0;
    //ok(!treesaver.ui.ArticleManager.previousArticle(), 'prev: Cannot go before first');
    //article = treesaver.ui.ArticleManager.nextArticle(true);
    //equals(article.url, articles[1].url, 'next: Correct article');
    //treesaver.ui.ArticleManager.currentArticleIndex = 1;
    //article = treesaver.ui.ArticleManager.previousArticle(true);
    //equals(article.url, articles[0].url, 'previous: Correct article');
  });

  //test('setCurrentArticle / setCurrentPosition / events', function () {
    //var root = /http:\/\/[^\/]*\//.exec(document.location.href),
        //basename = /^(.*)\/[^\/]*$/.exec(document.location.pathname)[1].substr(1),
        //urls = ['http://example.com/', '/', '/hello', 'example.html', '/'],
        //html,
        //div = document.createElement('div'),
        //articles = [],
        //redirectFunction = treesaver.ui.ArticleManager.redirectToCurrentArticle;

    //// Mock out redirect function
    //treesaver.ui.ArticleManager.redirectToCurrentArticle = function () {
      //ok(false, 'Article manager tried to redirect');
    //};

    //html = '
Fake document title
Non-TOC link.';
    //$(urls).each(function () {
      //$('').attr('href', this).appendTo(div);
    //});
    //html += $(div).html();

    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //var current_article = treesaver.ui.ArticleManager.getCurrentArticle() || {};

      //ok(true, 'ArticleChanged received on first load');
      //equals(e.article.url, current_article.url, 'Article parameter passed in');
      //equals(e.article.url, document.location.href, 'Initial article correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //stop(1000);
    //treesaver.ui.ArticleManager.load('

hi

');
    //treesaver.ui.ArticleManager.findTOCLinks(html);
    //articles = urls.map(function (url, i) {
      //return treesaver.ui.ArticleManager.articleOrder[i];
    //});

    //// Now go to next article, and make sure event comes through
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on next article');
      //equals(e.article.url, articles[1].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 1, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
      //start();
    //});

    //// Move to next article
    //treesaver.ui.ArticleManager.nextArticle();

    //// Go to prev article, and make sure event comes through
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on prev article');
      //equals(e.article.url, articles[0].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 0, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //// Move to next article
    //treesaver.ui.ArticleManager.previousArticle();

    //// Move to the last article
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on setCurrentArticle');
      //equals(e.article.url, articles[3].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 3, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //// Move to later article
    //treesaver.ui.ArticleManager.setCurrentArticle(articles[3]);

    //// Make sure article with two positions is reached correctly
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(true, 'ArticleChanged received on next page of double listed');
      //equals(e.article.url, articles[4].url, 'Article parameter passed in and correct');
      //equals(treesaver.ui.ArticleManager.getCurrentIndex(), 4, 'CurrentIndex correct');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //treesaver.ui.ArticleManager.nextArticle();

    //// Make sure we don't get a bogus move at end
    //treesaver.events.addListener(document, treesaver.ui.ArticleManager.events.articleChanged, function handler(e) {
      //ok(false, 'ArticleChanged received when could not go next');
      //treesaver.events.removeListener(document, treesaver.ui.ArticleManager.events.articleChanged, handler);
    //});

    //treesaver.ui.ArticleManager.nextArticle();

    //// That test is our last and shouldn't have an event handler called, so set a time out
    //setTimeout(start, 300);

    //// Restore mock
    //treesaver.ui.ArticleManager.redirectToCurrentArticle = redirectFunction;
  //});
});
<|endoftext|>


<commit_before>var Observable = require('rx').Observable;
var R = require('../../../src/Router');
var noOp = function() {};
var chai = require('chai');
var expect = chai.expect;
var falcor = require('falcor');
var $ref = falcor.Model.ref;
var $atom = falcor.Model.atom;
var errors = require('./../../../src/exceptions');
var types = require('./../../../src/support/types');
var sinon = require("sinon");

describe('Call', function() {
    it('should perform a simple call.', function(done) {
        var onNext = sinon.spy();
        getRouter().
            call(['videos', 1234, 'rating'], [5]).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        videos: {
                            1234: {
                                rating: 5
                            }
                        }
                    },
                    paths: [['videos', 1234, 'rating']]
                });
            }).
            subscribe(noOp, done, done);
    });

    it('should pass the #30 base call test with only suffix.', function(done) {
        var onNext = sinon.spy();
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], [['name']]).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        },
                        listsById: {
                            0: {
                                name: 'Thrillers'
                            }
                        }
                    },
                    paths: [
                        ['lolomo', 'pvAdd'],
                        ['lolomo', 0, 'name']
                    ]
                });
            }).
            subscribe(noOp, done, done);
    });

    it('should pass the #30 base call test with only paths.', function(done) {
        var called = 0;
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], null, [['length']]).
            doAction(function(jsongEnv) {
                expect(jsongEnv).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                length: 1,
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        }
                    },
                    paths: [
                        ['lolomo', ['length', 'pvAdd']],
                        ['lolomos', 123, '0']
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should pass the #30 base call test with both paths and suffixes.', function(done) {
        var called = 0;
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], [['name']], [['length']]).
            doAction(function(jsongEnv) {
                expect(jsongEnv).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                length: 1,
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        },
                        listsById: {
                            0: {
                                name: 'Thrillers'
                            }
                        }
                    },
                    paths: [
                        ['lolomo', ['length', 'pvAdd']],
                        ['lolomo', 0, 'name']
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should completely onError when an error is thrown from call.', function(done) {
        getRouter(true, true).
            call(['videos', 1234, 'rating'], [5]).
            doAction(function() {
                throw new Error('Should not be called.  onNext');
            }, function(x) {
                expect(x.message).to.equal('Oops?');
            }, function() {
                throw new Error('Should not be called.  onCompleted');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'Oops?') {
                    done();
                    return;
                }
                done(e);
            });
    });

    it('should cause the router to on error only.', function(done) {
        getRouter(true).
            call(['videos', 1234, 'rating'], [5]).
            doAction(function() {
                throw new Error('Should not be called.  onNext');
            }, function(x) {
                expect(x.message).to.equal(errors.callJSONGraphWithouPaths);
            }, function() {
                throw new Error('Should not be called.  onCompleted');
            }).
            subscribe(noOp, function(e) {
                if (e.message === errors.callJSONGraphWithouPaths) {
                    done();
                    return;
                }
                done(e);
            });
    });

    it('should allow item to be pushed onto collection.', function(done) {
        var onNext = sinon.spy();
        getCallRouter().
            call(['genrelist', 0, 'titles', 'push'], [{ $type: 'ref', value: ['titlesById', 1] }]).
            doAction(onNext).
            doAction(noOp, noOp, function(x) {
                expect(onNext.called).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        genrelist: {
                            0: {
                                titles: {
                                    2: {
                                        $type: 'ref',
                                        value: ['titlesById', 1]
                                    },
                                    push: {
                                        $type: types.$atom,
                                        $expires: 0
                                    }
                                }
                            }
                        }
                    },
                    paths: [
                        ['genrelist', 0, 'titles', [2, 'push']]
                    ]
                });
            }, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
            }).
            subscribe(noOp, done, done);
    });

    it('should evaluate path suffixes on result of a function that adds an item to a collection.', function(done) {
        var called = 0;
        var onNext = sinon.spy();
        getCallRouter().
            call(['genrelist', 0, 'titles', 'push'],
                 [{ $type: 'ref', value: ['titlesById', 1] }],
                 [['name'], ['rating']]).
            doAction(onNext).
            doAction(noOp, noOp, function(x) {
                expect(onNext.called).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        genrelist: {
                            0: {
                                titles: {
                                    2: {
                                        $type: 'ref',
                                        value: ['titlesById', 1]
                                    },
                                    push: {
                                        $type: types.$atom,
                                        $expires: 0
                                    }
                                }
                            }
                        },
                        titlesById: {
                            1: {
                                name: 'Orange is the new Black',
                                rating: 5
                            }
                        }
                    },
                    paths: [
                        ['genrelist', 0, 'titles', 'push'],
                        ['genrelist', 0, 'titles', 2, ['name', 'rating']]
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should throw when calling a function that does not exist.', function(done) {
        var router = new R([]);
        var onError = sinon.spy();
        router.
            call(['videos', 1234, 'rating'], [5]).
            doAction(noOp, onError).
            doAction(noOp, function() {
                expect(onError.calledOnce).to.be.ok;

                var args = onError.getCall(0).args;
                expect(args[0] instanceof Error).to.be.ok;
                expect(args[0].message).to.deep.equals('function does not exist');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'function does not exist') {
                    return done();
                }
                return done(e);
            }, done);
    });

    it('should throw when calling a function that does not exist, but get handler does.', function(done) {
        var router = new R([{
            route: 'videos[1234].rating',
            get: function() { }
        }]);
        var onError = sinon.spy();
        router.
            call(['videos', 1234, 'rating'], [5]).
            doAction(noOp, onError).
            doAction(noOp, function() {
                expect(onError.calledOnce).to.be.ok;

                var args = onError.getCall(0).args;
                expect(args[0] instanceof Error).to.be.ok;
                expect(args[0].message).to.deep.equals('function does not exist');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'function does not exist') {
                    return done();
                }
                return done(e);
            }, done);
    });

    it('should return path bound invalidations', function(done) {
        var onNext = sinon.spy();
        var baseIds = {0: 'Thriller'};
        getExtendedRouter(baseIds).
            call(['lolomo', 0, 'invalidate']).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                var args = onNext.getCall(0).args;

                expect(args[0]).to.deep.equals({
                    invalidations: [{
                        path: ['listsById', 0, 'name']
                    }],
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]')
                            }
                        },
                        listsById: {
                            0: {
                                invalidate: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        }
                    },
                    paths: [
                        ['lolomo', 0, 'invalidate']
                    ]
                });
            }).
            subscribe(noOp, done, done);
    });

    function getCallRouter() {
        return new R([{
            route: 'genrelist[{integers}].titles.push',
            call: function(callPath, args) {
                return {
                    path: ['genrelist', 0, 'titles', 2],
                    value: {
                        $type: 'ref',
                        value: ['titlesById', 1]
                    }
                };
            }
        },
        {
            route: 'genrelist[{integers}].titles[{integers}]',
            get: function(pathSet) {
                return {
                    path: ['genrelist', 0, 'titles', 1],
                    value: {
                        $type: 'ref',
                        value: ['titlesById', 1]
                    }
                };
            }
        },
        {
            route: 'titlesById[{integers}]["name", "rating"]',
            get: function(callPath, args) {
                return [
                    {
                        path: ['titlesById', 1, 'name'],
                        value: 'Orange is the new Black'
                    },
                    {
                        path: ['titlesById', 1, 'rating'],
                        value: 5
                    }
                ];
            }
        }]);
    }

    function getRouter(noPaths, throwError) {
        return new R([{
            route: 'videos[{integers:id}].rating',
            call: function(callPath, args) {
                if (throwError) {
                    throw new Error('Oops?');
                }
                return {
                    jsonGraph: {
                        videos: {
                            1234: {
                                rating: args[0]
                            }
                        }
                    },
                    paths: !noPaths && [['videos', 1234, 'rating']] || undefined
                };
            }
        }]);
    }

    function getExtendedRouter(initialIdsAndNames) {
        var listsById = {};
        var idsAndNames = initialIdsAndNames || {};
        Object.keys(idsAndNames).reduce(function(acc, id) {
            var name = idsAndNames[id];
            listsById[id] = {name: name, rating: 3};
            return acc;
        }, listsById);

        function listsLength() {
            return Object.keys(listsById).length;
        }

        function addToList(name) {
            var length = listsLength();
            listsById[length] = {
                name: name,
                rating: 5
            };

            return length;
        }
        return new R([{
            route: 'lolomo',
            get: function() {
                return {
                    path: ['lolomo'],
                    value: $ref('lolomos[123]')
                };
            }
        }, {
            route: 'lolomos[{keys:ids}][{integers:indices}]',
            get: function(alais) {
                var id = alais.ids[0];
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['lolomos', id, idx],
                                value: $ref(['listsById', idx])
                            };
                        }
                        return {
                            path: ['lolomos', id],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'lolomos[{keys:ids}].length',
            get: function(alias) {
                var id = alias.ids[0];
                return {
                    path: ['lolomos', id, 'length'],
                    value: listsLength()
                };
            }
        }, {
            route: 'listsById[{integers:indices}].name',
            get: function(alais) {
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['listsById', idx, 'name'],
                                value: listsById[idx].name
                            };
                        }
                        return {
                            path: ['listsById', idx],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'listsById[{integers:indices}].invalidate',
            call: function(alias, args) {
                var indices = alias.indices;
                return indices.map(function(idx) {
                    return {
                        path: ['listsById', idx, 'name']
                    };
                });
            }
        }, {
            route: 'listsById[{integers:indices}].rating',
            get: function(alais) {
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['listsById', idx, 'rating'],
                                value: listsById[idx].rating
                            };
                        }
                        return {
                            path: ['listsById', idx],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'lolomos[{keys:ids}].pvAdd',
            call: function(callPath, args) {
                var id = callPath.ids[0];
                var idx = addToList(args[0]);
                return {
                    path: ['lolomos', id, idx],
                    value: $ref(['listsById', idx])
                };
            }

        }, {
            route: 'lolomos[{keys:ids}].jsongAdd',
            call: function(callPath, args) {
                var id = callPath.ids[0];
                var idx = addToList(args[0]);
                var lolomos = {};
                lolomos[id] = {};
                lolomos[id][idx] = $ref(['listsById', idx]);
                return {
                    jsonGraph: {
                        lolomos: lolomos
                    },
                    paths: [['lolomos', id, idx]]
                };
            }
        }]);
    }
});
<commit_msg>added test: should return paths in jsonGraphEnvelope if array of pathValues is returned from promise<commit_after>var Observable = require('rx').Observable;
var R = require('../../../src/Router');
var noOp = function() {};
var chai = require('chai');
var expect = chai.expect;
var falcor = require('falcor');
var $ref = falcor.Model.ref;
var $atom = falcor.Model.atom;
var errors = require('./../../../src/exceptions');
var types = require('./../../../src/support/types');
var sinon = require("sinon");
var Promise = require("promise");

describe('Call', function() {

    xit('should return paths in jsonGraphEnvelope if array of pathValues is returned from promise.', function(done) {
        var onNext = sinon.spy();

        var router = new R([{
            route: 'genrelist[{integers:indices}].titles.push',
            call: function(callPath, args) {
                return Promise.resolve([
                   {
                      "path": ["genrelist", 0, "titles", 18],
                      "value": {
                         "$type": "ref",
                         "value": ["titlesById", 1]
                      }
                   },
                   {
                      "path": ["genrelist", 0, "titles", "length"],
                      "value": 19
                   }
                ])
            }
        }]);

        router.
            call(['genrelist', 0, 'titles', 'push'], [{$type: "ref", value: ['titlesById', 1]}], [], []).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    "jsonGraph": {
                        "genrelist": {
                            "0": {
                                "titles": {
                                    "18": {
                                        "$type": "ref",
                                        "value": ["titlesById", 1]
                                    },
                                    "length": 19,
                                    "push": {
                                        "$expires": 0,
                                        "$type": "atom"
                                    }
                                }
                            }
                        }
                    },
                    "paths": [
                        ["genrelist", 0, "titles", ["push", "18", "length"]]
                    ]
                });
            }).
            subscribe(noOp, done, done);
    });

    it('should perform a simple call.', function(done) {
        var onNext = sinon.spy();
        getRouter().
            call(['videos', 1234, 'rating'], [5]).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        videos: {
                            1234: {
                                rating: 5
                            }
                        }
                    },
                    paths: [['videos', 1234, 'rating']]
                });
            }).
            subscribe(noOp, done, done);
    });

    it('should pass the #30 base call test with only suffix.', function(done) {
        var onNext = sinon.spy();
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], [['name']]).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        },
                        listsById: {
                            0: {
                                name: 'Thrillers'
                            }
                        }
                    },
                    paths: [
                        ['lolomo', 'pvAdd'],
                        ['lolomo', 0, 'name']
                    ]
                });
            }).
            subscribe(noOp, done, done);
    });

    it('should pass the #30 base call test with only paths.', function(done) {
        var called = 0;
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], null, [['length']]).
            doAction(function(jsongEnv) {
                expect(jsongEnv).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                length: 1,
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        }
                    },
                    paths: [
                        ['lolomo', ['length', 'pvAdd']],
                        ['lolomos', 123, '0']
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should pass the #30 base call test with both paths and suffixes.', function(done) {
        var called = 0;
        getExtendedRouter().
            call(['lolomo', 'pvAdd'], ['Thrillers'], [['name']], [['length']]).
            doAction(function(jsongEnv) {
                expect(jsongEnv).to.deep.equals({
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]'),
                                length: 1,
                                pvAdd: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        },
                        listsById: {
                            0: {
                                name: 'Thrillers'
                            }
                        }
                    },
                    paths: [
                        ['lolomo', ['length', 'pvAdd']],
                        ['lolomo', 0, 'name']
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should completely onError when an error is thrown from call.', function(done) {
        getRouter(true, true).
            call(['videos', 1234, 'rating'], [5]).
            doAction(function() {
                throw new Error('Should not be called.  onNext');
            }, function(x) {
                expect(x.message).to.equal('Oops?');
            }, function() {
                throw new Error('Should not be called.  onCompleted');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'Oops?') {
                    done();
                    return;
                }
                done(e);
            });
    });

    it('should cause the router to on error only.', function(done) {
        getRouter(true).
            call(['videos', 1234, 'rating'], [5]).
            doAction(function() {
                throw new Error('Should not be called.  onNext');
            }, function(x) {
                expect(x.message).to.equal(errors.callJSONGraphWithouPaths);
            }, function() {
                throw new Error('Should not be called.  onCompleted');
            }).
            subscribe(noOp, function(e) {
                if (e.message === errors.callJSONGraphWithouPaths) {
                    done();
                    return;
                }
                done(e);
            });
    });

    it('should allow item to be pushed onto collection.', function(done) {
        var onNext = sinon.spy();
        getCallRouter().
            call(['genrelist', 0, 'titles', 'push'], [{ $type: 'ref', value: ['titlesById', 1] }]).
            doAction(onNext).
            doAction(noOp, noOp, function(x) {
                expect(onNext.called).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        genrelist: {
                            0: {
                                titles: {
                                    2: {
                                        $type: 'ref',
                                        value: ['titlesById', 1]
                                    },
                                    push: {
                                        $type: types.$atom,
                                        $expires: 0
                                    }
                                }
                            }
                        }
                    },
                    paths: [
                        ['genrelist', 0, 'titles', [2, 'push']]
                    ]
                });
            }, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
            }).
            subscribe(noOp, done, done);
    });

    it('should evaluate path suffixes on result of a function that adds an item to a collection.', function(done) {
        var called = 0;
        var onNext = sinon.spy();
        getCallRouter().
            call(['genrelist', 0, 'titles', 'push'],
                 [{ $type: 'ref', value: ['titlesById', 1] }],
                 [['name'], ['rating']]).
            doAction(onNext).
            doAction(noOp, noOp, function(x) {
                expect(onNext.called).to.be.ok;
                expect(onNext.getCall(0).args[0]).to.deep.equals({
                    jsonGraph: {
                        genrelist: {
                            0: {
                                titles: {
                                    2: {
                                        $type: 'ref',
                                        value: ['titlesById', 1]
                                    },
                                    push: {
                                        $type: types.$atom,
                                        $expires: 0
                                    }
                                }
                            }
                        },
                        titlesById: {
                            1: {
                                name: 'Orange is the new Black',
                                rating: 5
                            }
                        }
                    },
                    paths: [
                        ['genrelist', 0, 'titles', 'push'],
                        ['genrelist', 0, 'titles', 2, ['name', 'rating']]
                    ]
                });
                ++called;
            }).
            subscribe(noOp, done, function() {
                expect(called).to.equals(1);
                done();
            });
    });

    it('should throw when calling a function that does not exist.', function(done) {
        var router = new R([]);
        var onError = sinon.spy();
        router.
            call(['videos', 1234, 'rating'], [5]).
            doAction(noOp, onError).
            doAction(noOp, function() {
                expect(onError.calledOnce).to.be.ok;

                var args = onError.getCall(0).args;
                expect(args[0] instanceof Error).to.be.ok;
                expect(args[0].message).to.deep.equals('function does not exist');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'function does not exist') {
                    return done();
                }
                return done(e);
            }, done);
    });

    it('should throw when calling a function that does not exist, but get handler does.', function(done) {
        var router = new R([{
            route: 'videos[1234].rating',
            get: function() { }
        }]);
        var onError = sinon.spy();
        router.
            call(['videos', 1234, 'rating'], [5]).
            doAction(noOp, onError).
            doAction(noOp, function() {
                expect(onError.calledOnce).to.be.ok;

                var args = onError.getCall(0).args;
                expect(args[0] instanceof Error).to.be.ok;
                expect(args[0].message).to.deep.equals('function does not exist');
            }).
            subscribe(noOp, function(e) {
                if (e.message === 'function does not exist') {
                    return done();
                }
                return done(e);
            }, done);
    });

    it('should return path bound invalidations', function(done) {
        var onNext = sinon.spy();
        var baseIds = {0: 'Thriller'};
        getExtendedRouter(baseIds).
            call(['lolomo', 0, 'invalidate']).
            doAction(onNext).
            doAction(noOp, noOp, function() {
                expect(onNext.calledOnce).to.be.ok;
                var args = onNext.getCall(0).args;

                expect(args[0]).to.deep.equals({
                    invalidations: [{
                        path: ['listsById', 0, 'name']
                    }],
                    jsonGraph: {
                        lolomo: $ref('lolomos[123]'),
                        lolomos: {
                            123: {
                                0: $ref('listsById[0]')
                            }
                        },
                        listsById: {
                            0: {
                                invalidate: {
                                    $type: types.$atom,
                                    $expires: 0
                                }
                            }
                        }
                    },
                    paths: [
                        ['lolomo', 0, 'invalidate']
                    ]
                });
            }).
            subscribe(noOp, done, done);
    });

    function getCallRouter() {
        return new R([{
            route: 'genrelist[{integers}].titles.push',
            call: function(callPath, args) {
                return {
                    path: ['genrelist', 0, 'titles', 2],
                    value: {
                        $type: 'ref',
                        value: ['titlesById', 1]
                    }
                };
            }
        },
        {
            route: 'genrelist[{integers}].titles[{integers}]',
            get: function(pathSet) {
                return {
                    path: ['genrelist', 0, 'titles', 1],
                    value: {
                        $type: 'ref',
                        value: ['titlesById', 1]
                    }
                };
            }
        },
        {
            route: 'titlesById[{integers}]["name", "rating"]',
            get: function(callPath, args) {
                return [
                    {
                        path: ['titlesById', 1, 'name'],
                        value: 'Orange is the new Black'
                    },
                    {
                        path: ['titlesById', 1, 'rating'],
                        value: 5
                    }
                ];
            }
        }]);
    }

    function getRouter(noPaths, throwError) {
        return new R([{
            route: 'videos[{integers:id}].rating',
            call: function(callPath, args) {
                if (throwError) {
                    throw new Error('Oops?');
                }
                return {
                    jsonGraph: {
                        videos: {
                            1234: {
                                rating: args[0]
                            }
                        }
                    },
                    paths: !noPaths && [['videos', 1234, 'rating']] || undefined
                };
            }
        }]);
    }

    function getExtendedRouter(initialIdsAndNames) {
        var listsById = {};
        var idsAndNames = initialIdsAndNames || {};
        Object.keys(idsAndNames).reduce(function(acc, id) {
            var name = idsAndNames[id];
            listsById[id] = {name: name, rating: 3};
            return acc;
        }, listsById);

        function listsLength() {
            return Object.keys(listsById).length;
        }

        function addToList(name) {
            var length = listsLength();
            listsById[length] = {
                name: name,
                rating: 5
            };

            return length;
        }
        return new R([{
            route: 'lolomo',
            get: function() {
                return {
                    path: ['lolomo'],
                    value: $ref('lolomos[123]')
                };
            }
        }, {
            route: 'lolomos[{keys:ids}][{integers:indices}]',
            get: function(alais) {
                var id = alais.ids[0];
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['lolomos', id, idx],
                                value: $ref(['listsById', idx])
                            };
                        }
                        return {
                            path: ['lolomos', id],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'lolomos[{keys:ids}].length',
            get: function(alias) {
                var id = alias.ids[0];
                return {
                    path: ['lolomos', id, 'length'],
                    value: listsLength()
                };
            }
        }, {
            route: 'listsById[{integers:indices}].name',
            get: function(alais) {
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['listsById', idx, 'name'],
                                value: listsById[idx].name
                            };
                        }
                        return {
                            path: ['listsById', idx],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'listsById[{integers:indices}].invalidate',
            call: function(alias, args) {
                var indices = alias.indices;
                return indices.map(function(idx) {
                    return {
                        path: ['listsById', idx, 'name']
                    };
                });
            }
        }, {
            route: 'listsById[{integers:indices}].rating',
            get: function(alais) {
                return Observable.
                    from(alais.indices).
                    map(function(idx) {
                        if (listsById[idx]) {
                            return {
                                path: ['listsById', idx, 'rating'],
                                value: listsById[idx].rating
                            };
                        }
                        return {
                            path: ['listsById', idx],
                            value: $atom(undefined)
                        };
                    });
            }
        }, {
            route: 'lolomos[{keys:ids}].pvAdd',
            call: function(callPath, args) {
                var id = callPath.ids[0];
                var idx = addToList(args[0]);
                return {
                    path: ['lolomos', id, idx],
                    value: $ref(['listsById', idx])
                };
            }

        }, {
            route: 'lolomos[{keys:ids}].jsongAdd',
            call: function(callPath, args) {
                var id = callPath.ids[0];
                var idx = addToList(args[0]);
                var lolomos = {};
                lolomos[id] = {};
                lolomos[id][idx] = $ref(['listsById', idx]);
                return {
                    jsonGraph: {
                        lolomos: lolomos
                    },
                    paths: [['lolomos', id, idx]]
                };
            }
        }]);
    }
});
<|endoftext|>


<commit_before>/*
 * Copyright 2014 Open Ag Data Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var expect = require('chai').expect;
var express = require('express');
var request = require('supertest');

var wkj = require('../');

describe('wkj', function() {

    it('should be exported', function() {
        expect(wkj).to.be.a('function');
    });

    it('should pass errors up', function(done) {
        var options = {};
        options.cors = function failingOptionsCallback(req, cb) {
            cb(new Error('Options Failure'));
        };

        var app = express();
        app.use(wkj(options, {foo: {}}));

        app.use(function(err, req, res, next) {
            expect(err).to.be.ok;
            expect(err.message).to.equal('Options Failure');

            next();
        });

        request(app)
            .get('/.well-known/foo')
            .expect(404, done);
    });

    it('should pass unknown requests to next middleware', function(done) {
        var app = express();
        app.use(wkj({}));

        app.use(function(req, res) {
            res.status(200).end();
        });

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should serve resources provided at mount', function(done) {
        var resources = {
            foo: {
                bar: 'hello world',
                baz: 7
            }
        };
        var app = express();
        app.use(wkj({}, resources));

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should serve resources added with addResource', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should merge resources with same URI', function(done) {
        var resources = {
            foo: {
                a: 1
            }
        };
        var resource = {
            b: 2
        };
        var app = express();
        var middleware = wkj({}, resources);
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return !(res.body.a === 1 && res.body.b === 2);
            })
            .end(done);
    });

    it('should return JSON', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect('Content-Type', /json/)
            .expect(200, done);
    });

    it('should return Not Acceptable for non-JSON Accept', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .set('Accept', 'application/xml')
            .expect(406, done);
    });

    it('should resolve relative URIs to absolute', function(done) {
        var resource = {
            uri: './thing'
        };
        var app = express();
        var middleware = wkj({baseUri: 'http://example.com'});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return res.body.uri !== 'http://example.com/thing';
            })
            .end(done);
    });

    it('should work with array properties', function(done) {
        var resource = {
            arr: [1, 2, 3]
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return Object.prototype.toString.call(res.body.arr) !==
                    '[object Array]';
            })
            .end(done);
    });

    it('should evaluate function properties', function(done) {
        var resource = {
            fun: function() {
                return 'FUN_RETURN';
            }
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return res.body.fun !== 'FUN_RETURN';
            })
            .end(done);
    });

    it('should recursively handle nested object properties', function(done) {
        var resource = {
            obj: {
                obj: {
                    uri: './thing',
                    arr: [1, 2, 3],
                    fun: function() {
                        return 'FUN_RETURN';
                    }
                }
            }
        };
        var app = express();
        var middleware = wkj({baseUri: 'http://example.com'});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return !(
                    res.body.obj.obj.uri === 'http://example.com/thing' &&
                    Object.prototype.toString.call(res.body.obj.obj.arr) ===
                        '[object Array]' &&
                    res.body.obj.obj.fun === 'FUN_RETURN');
            })
            .end(done);
    });
});
<commit_msg>Adds test for X-Forwarded-Proto support<commit_after>/*
 * Copyright 2014 Open Ag Data Alliance
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

var expect = require('chai').expect;
var express = require('express');
var request = require('supertest');

var wkj = require('../');

describe('wkj', function() {

    it('should be exported', function() {
        expect(wkj).to.be.a('function');
    });

    it('should pass errors up', function(done) {
        var options = {};
        options.cors = function failingOptionsCallback(req, cb) {
            cb(new Error('Options Failure'));
        };

        var app = express();
        app.use(wkj(options, {foo: {}}));

        app.use(function(err, req, res, next) {
            expect(err).to.be.ok;
            expect(err.message).to.equal('Options Failure');

            next();
        });

        request(app)
            .get('/.well-known/foo')
            .expect(404, done);
    });

    it('should pass unknown requests to next middleware', function(done) {
        var app = express();
        app.use(wkj({}));

        app.use(function(req, res) {
            res.status(200).end();
        });

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should serve resources provided at mount', function(done) {
        var resources = {
            foo: {
                bar: 'hello world',
                baz: 7
            }
        };
        var app = express();
        app.use(wkj({}, resources));

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should serve resources added with addResource', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(200, done);
    });

    it('should merge resources with same URI', function(done) {
        var resources = {
            foo: {
                a: 1
            }
        };
        var resource = {
            b: 2
        };
        var app = express();
        var middleware = wkj({}, resources);
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return !(res.body.a === 1 && res.body.b === 2);
            })
            .end(done);
    });

    it('should return JSON', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect('Content-Type', /json/)
            .expect(200, done);
    });

    it('should return Not Acceptable for non-JSON Accept', function(done) {
        var resource = {
            bar: 'hello world',
            baz: 7
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .set('Accept', 'application/xml')
            .expect(406, done);
    });

    it('should resolve relative URIs to absolute', function(done) {
        var resource = {
            uri: './thing'
        };
        var app = express();
        var middleware = wkj({baseUri: 'http://example.com'});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return res.body.uri !== 'http://example.com/thing';
            })
            .end(done);
    });

    it('should respect X-Forward-Proto', function(done) {
        var resource = {
            uri: './thing'
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .set('X-Forwarded-Proto', 'test')
            .expect(function(res) {
                return res.body.uri !== 'test://' + res.req._headers.host +
                    '/thing';
            })
            .end(done);
    });

    it('should work with array properties', function(done) {
        var resource = {
            arr: [1, 2, 3]
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return Object.prototype.toString.call(res.body.arr) !==
                    '[object Array]';
            })
            .end(done);
    });

    it('should evaluate function properties', function(done) {
        var resource = {
            fun: function() {
                return 'FUN_RETURN';
            }
        };
        var app = express();
        var middleware = wkj({});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return res.body.fun !== 'FUN_RETURN';
            })
            .end(done);
    });

    it('should recursively handle nested object properties', function(done) {
        var resource = {
            obj: {
                obj: {
                    uri: './thing',
                    arr: [1, 2, 3],
                    fun: function() {
                        return 'FUN_RETURN';
                    }
                }
            }
        };
        var app = express();
        var middleware = wkj({baseUri: 'http://example.com'});
        app.use(middleware);

        middleware.addResource('foo', resource);

        request(app)
            .get('/.well-known/foo')
            .expect(function(res) {
                return !(
                    res.body.obj.obj.uri === 'http://example.com/thing' &&
                    Object.prototype.toString.call(res.body.obj.obj.arr) ===
                        '[object Array]' &&
                    res.body.obj.obj.fun === 'FUN_RETURN');
            })
            .end(done);
    });
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name           Display price history on Amazon.co.jp
// @namespace      curipha
// @description    Import price history from Keepa.com
// @include        https://www.amazon.co.jp/gp/product/*
// @include        https://www.amazon.co.jp/dp/*
// @include        https://www.amazon.co.jp/*/dp/*
// @exclude        https://www.amazon.co.jp/ap/*
// @exclude        https://www.amazon.co.jp/mn/*
// @exclude        https://www.amazon.co.jp/clouddrive*
// @version        0.0.2
// @grant          none
// @noframes
// ==/UserScript==

(function() {
  'use strict';

  const width = 580;
  const height = 240;

  const pricediv = document.getElementById('unifiedPrice_feature_div');
  if (pricediv) {
    const asinform = document.getElementById('ASIN'); // or location.pathname.match(/\b[0-9A-Z]{10}\b/)
    if (asinform) {
      const asin = asinform.value;

      const history = document.createElement('div');
      history.className = 'a-section a-spacing-small';

      const anchor = document.createElement('a');
      anchor.href = `https://keepa.com/#!product/5-${asin}`;
      anchor.target = '_blank';
      anchor.rel = 'noopener';
      anchor.referrerPolicy = 'no-referrer';

      const image = document.createElement('img');
      image.src = `https://dyn.keepa.com/pricehistory.png?domain=co.jp&asin=${asin}&width=${width}&height=${height}`;
      image.alt = 'Price history';
      image.width = width;
      image.height = height;
      image.referrerPolicy = 'no-referrer';

      anchor.appendChild(image);
      history.appendChild(anchor);
      pricediv.appendChild(history);
    }
  }
})();
<commit_msg>Capture variation select and update the graph<commit_after>// ==UserScript==
// @name           Display price history on Amazon.co.jp
// @namespace      curipha
// @description    Import price history from Keepa.com
// @include        https://www.amazon.co.jp/gp/product/*
// @include        https://www.amazon.co.jp/dp/*
// @include        https://www.amazon.co.jp/*/dp/*
// @exclude        https://www.amazon.co.jp/ap/*
// @exclude        https://www.amazon.co.jp/mn/*
// @exclude        https://www.amazon.co.jp/clouddrive*
// @version        0.0.2
// @grant          none
// @noframes
// ==/UserScript==

(function() {
  'use strict';

  const width = 580;
  const height = 240;

  let current_asin = '';

  const get_asin = function() {
    const asinform = document.getElementById('ASIN'); // or location.pathname.match(/\b[0-9A-Z]{10}\b/)
    return asinform ? asinform.value : null;
  }

  const insert_graph = function() {
    const pricediv = document.getElementById('unifiedPrice_feature_div');
    if (pricediv) {
      let asin = get_asin();
      if (asin) {
        current_asin = asin; // Update current asin value to avoid consecutive updating of graph

        const history = document.createElement('div');
        history.className = 'a-section a-spacing-small';

        const anchor = document.createElement('a');
        anchor.href = `https://keepa.com/#!product/5-${asin}`;
        anchor.target = '_blank';
        anchor.rel = 'noopener';
        anchor.referrerPolicy = 'no-referrer';

        const image = document.createElement('img');
        image.src = `https://dyn.keepa.com/pricehistory.png?domain=co.jp&asin=${asin}&width=${width}&height=${height}`;
        image.alt = 'Price history';
        image.width = width;
        image.height = height;
        image.referrerPolicy = 'no-referrer';

        anchor.appendChild(image);
        history.appendChild(anchor);
        pricediv.appendChild(history);
      }
    }
  }

  insert_graph();

  // Capture variation select and update the graph
  const target = document.getElementById('desktop_buybox') || document.getElementById('buybox_feature_div');
  if (target) {
    const update_graph = (mutations) => {
      mutations.forEach((mutation) => {
        if (get_asin() !== current_asin && mutation.addedNodes.length > 0) {
          insert_graph();
        }
      });
    };

    const mo = new MutationObserver(update_graph);
    mo.observe(target, { childList: true });
  }
})();
<|endoftext|>


<commit_before>/****************************************************************************
 * Copyright (C) 2015-2016 EPAM Systems
 *
 * This file is part of Indigo-Node binding.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.md  included in the
 * packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

/* declaration of modules  */
var assert = require('assert');
var path = require('path');
var local = path.join.bind(path, __dirname);

var indigo = require("../indigo-node/indigo");

console.log('indigo basic test');
console.log('start:');

var status = indigo.setOption("molfile-saving-skip-date", "1");
console.log("****** Query reload ********")
var q = indigo.loadQueryMoleculeFromFile(local("../indigo-node/molecules/q_atom_list.mol"));
var qmf1 = q.molfile();
console.log(qmf1);
var q2 = indigo.loadQueryMolecule(q.molfile());
var qmf2 = q2.molfile();
if (qmf1 != qmf2) {
	console.log("Error: reloaded query is different:");
	console.log(qmf2);
}

/* Check that queires are equivalent */
var matcher = indigo.substructureMatcher(indigo.loadMolecule("[Sc]CN[He]"));
var none1 = matcher.match(q);
var none2 = matcher.match(q2);
if (none1 || none2)
	console.log("Error: matching results are not None: " + none1 + ' ' + none2);

console.log("****** Remove constraints and reload ********");
var q = indigo.loadQueryMolecule("c1[nH]c2c(c(N)[n+]([O-])c[n]2)[n]1");
var t = indigo.loadMolecule("c1[n]c2c(N)[n+]([O-])c[n]c2[n]1[C@H]1[C@@H](O)[C@H](O)[C@H](CO)O1");

var original_smiles = q.smiles();
console.log(q.smiles());

var matcher = indigo.substructureMatcher(t);
var has_match_orig = (matcher.match(q) != null);
console.log(has_match_orig);

for (var a of q.iterateAtoms())
	a.removeConstraints("hydrogens");
console.log(q.smiles());

var has_match = (matcher.match(q) != null);
console.log(has_match);

var q2 = q.clone();
console.log(q2.smiles());
var has_match2 = (matcher.match(q2) != null)
console.log(has_match2);
if (has_match != has_match2)
	console.error("Error: query molecule match is different after cloning");

/* reload query from original smiles */
var q3 = indigo.loadQueryMolecule(original_smiles)
console.log(q3.smiles());
var has_match3 = (matcher.match(q3) != null)
console.log(has_match3);
if (has_match3 != has_match_orig)
	console.error("Error: query molecule match is different after reloading from SMILES");
/*
console.log("****** Bad valence, smiles and unfold ********");
var m = indigo.loadMolecule("C\C=C(/N(O)=O)N(O)=O");
var sm = m.smiles();
console.log(m.smiles());
console.log(m.canonicalSmiles());
m.unfoldHydrogens();
*/
<commit_msg>add UT to canonicalSmiles, unfoldHydrogens and resetAtom<commit_after>/****************************************************************************
 * Copyright (C) 2015-2016 EPAM Systems
 *
 * This file is part of Indigo-Node binding.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.md  included in the
 * packaging of this file.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

/* declaration of modules  */
var assert = require('assert');
var path = require('path');
var local = path.join.bind(path, __dirname);

var indigo = require("../indigo-node/indigo");

console.log('indigo basic test');
console.log('start:');

var status = indigo.setOption("molfile-saving-skip-date", "1");
console.log("****** Query reload ********")
var q = indigo.loadQueryMoleculeFromFile(local("../indigo-node/molecules/q_atom_list.mol"));
var qmf1 = q.molfile();
console.log(qmf1);
var q2 = indigo.loadQueryMolecule(q.molfile());
var qmf2 = q2.molfile();
if (qmf1 != qmf2) {
	console.log("Error: reloaded query is different:");
	console.log(qmf2);
}

/* Check that queires are equivalent */
var matcher = indigo.substructureMatcher(indigo.loadMolecule("[Sc]CN[He]"));
var none1 = matcher.match(q);
var none2 = matcher.match(q2);
if (none1 || none2)
	console.log("Error: matching results are not None: " + none1 + ' ' + none2);

console.log("****** Remove constraints and reload ********");
var q = indigo.loadQueryMolecule("c1[nH]c2c(c(N)[n+]([O-])c[n]2)[n]1");
var t = indigo.loadMolecule("c1[n]c2c(N)[n+]([O-])c[n]c2[n]1[C@H]1[C@@H](O)[C@H](O)[C@H](CO)O1");

var original_smiles = q.smiles();
console.log(q.smiles());

var matcher = indigo.substructureMatcher(t);
var has_match_orig = (matcher.match(q) != null);
console.log(has_match_orig);

for (var a of q.iterateAtoms())
	a.removeConstraints("hydrogens");
console.log(q.smiles());

var has_match = (matcher.match(q) != null);
console.log(has_match);

var q2 = q.clone();
console.log(q2.smiles());
var has_match2 = (matcher.match(q2) != null)
console.log(has_match2);
if (has_match != has_match2)
	console.error("Error: query molecule match is different after cloning");

/* reload query from original smiles */
var q3 = indigo.loadQueryMolecule(original_smiles)
console.log(q3.smiles());
var has_match3 = (matcher.match(q3) != null)
console.log(has_match3);
if (has_match3 != has_match_orig)
	console.error("Error: query molecule match is different after reloading from SMILES");

console.log("****** Bad valence, smiles and unfold ********");
var m = indigo.loadMolecule("C\C=C(/N(O)=O)N(O)=O");
var sm = m.smiles();
console.log(m.smiles());
console.log(m.canonicalSmiles());
m.unfoldHydrogens();

/* If there was an exception in unfoldHydrogens then molecule should not be changed */
var sm2 = m.smiles();
if (sm2 != sm)
	console.error("Error: " + sm + " != " + sm2);

console.log("****** Serialize and atom changing ********");
var m = indigo.loadMolecule("CC[C@@H](N)\C=C/C");
console.log(m.smiles());
console.log(m.canonicalSmiles());

for (a of m.iterateAtoms())
	a.resetAtom("*");

console.log(m.smiles());
console.log(m.canonicalSmiles());

<|endoftext|>