<commit_before>/**
d2l-icon is effectively a copy of iron-icon with some modification to handle focusable
attribute for IE, and styles to apply default color and dimensions.  The following
license is included for attribution. Integration with iron-iconset-svg is exactly the same
with the exclusion of the theme.

@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
`d2l-icon`
Polymer-based web component for icons

@demo demo/icon.html d2l-icon
*/
/*
  FIXME(polymer-modulizer): the above comments were extracted
  from HTML and may be out of place here. Review them and
  then delete this comment!
*/
import { Base } from '@polymer/polymer/polymer-legacy.js';

import '@polymer/iron-meta/iron-meta.js';
import 'd2l-colors/d2l-colors.js';
import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js';
import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js';
const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = `<dom-module id="d2l-icon">
	<template strip-whitespace="">
		<style>
			:host {
				-ms-flex-align: center;
				-webkit-align-items: center;
				align-items: center;
				color: var(--d2l-color-ferrite);
				display: -ms-inline-flexbox;
				display: -webkit-inline-flex;
				display: inline-flex;
				fill: var(--d2l-icon-fill-color, currentcolor);
				height: var(--d2l-icon-height, 18px);
				-ms-flex-pack: center;
				-webkit-justify-content: center;
				justify-content: center;
				stroke: var(--d2l-icon-stroke-color, none);
				vertical-align: middle;
				width: var(--d2l-icon-width, 18px);
			}
			:host([icon*="d2l-tier2:"]) {
				height: var(--d2l-icon-height, 24px);
				width: var(--d2l-icon-width, 24px);
			}
			:host([icon*="d2l-tier3:"]) {
				height: var(--d2l-icon-height, 30px);
				width: var(--d2l-icon-width, 30px);
			}
			/* required since display of inline-block is non-default */
			:host([hidden]) {
				display: none;
			}
		</style>
	</template>

</dom-module>`;

document.head.appendChild($_documentContainer.content);
Polymer({
	is: 'd2l-icon',

	properties: {

		/**
		 * (Optional) Name of icon (ex. [iconset-name:icon-id]).
		 */
		icon: {
			type: String,
			reflectToAttribute: true
		},
		/**
		 * (Optional) Explicit source of icon.
		 */
		src: String,

		theme: String,

		_meta: {
			value: Base.create('iron-meta', {type: 'iconset'})
		}

	},

	observers: [
		'_updateIcon(_meta, isAttached)',
		'_updateIcon(theme, isAttached)',
		'_srcChanged(src, isAttached)',
		'_iconChanged(icon, isAttached)'
	],

	_DEFAULT_ICONSET: 'icons',

	_iconChanged: function(icon) {
		var parts = (icon || '').split(':');
		this._iconName = parts.pop();
		this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
		this._updateIcon();
	},

	_srcChanged: function() {
		this._updateIcon();
	},

	_updateIcon: function() {
		if (this._usesIconset()) {
			if (this._img && this._img.parentNode) {
				dom(this.root).removeChild(this._img);
			}
			if (this._iconName === '') {
				if (this._iconset) {
					this._iconset.removeIcon(this);
				}
			} else if (this._iconsetName && this._meta) {
				this._iconset = /** @type {?Polymer.Iconset} */ (
					this._meta.byKey(this._iconsetName));
				if (this._iconset) {
					var d2lIcon = this._iconset.innerHTML.indexOf(`d2l-icon-${this._iconName}`) > -1;
					var iconName = d2lIcon ? (`d2l-icon-${this._iconName}`) : this._iconName;
					this._iconset.applyIcon(this, iconName, this.theme);
					this.unlisten(window, 'iron-iconset-added', '_updateIcon');
				} else {
					this.listen(window, 'iron-iconset-added', '_updateIcon');
				}
			}
		} else {
			if (this._iconset) {
				this._iconset.removeIcon(this);
			}
			if (!this._img) {
				this._img = document.createElement('img');
				this._img.style.width = '100%';
				this._img.style.height = '100%';
				this._img.draggable = false;
			}
			this._img.src = this.src;
			dom(this.root).appendChild(this._img);
		}
	},

	_usesIconset: function() {
		return this.icon || !this.src;
	}

});
<commit_msg>querySelector is faster than innerHTML<commit_after>/**
d2l-icon is effectively a copy of iron-icon with some modification to handle focusable
attribute for IE, and styles to apply default color and dimensions.  The following
license is included for attribution. Integration with iron-iconset-svg is exactly the same
with the exclusion of the theme.

@license
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/
/**
`d2l-icon`
Polymer-based web component for icons

@demo demo/icon.html d2l-icon
*/
/*
  FIXME(polymer-modulizer): the above comments were extracted
  from HTML and may be out of place here. Review them and
  then delete this comment!
*/
import { Base } from '@polymer/polymer/polymer-legacy.js';

import '@polymer/iron-meta/iron-meta.js';
import 'd2l-colors/d2l-colors.js';
import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js';
import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js';
const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = `<dom-module id="d2l-icon">
	<template strip-whitespace="">
		<style>
			:host {
				-ms-flex-align: center;
				-webkit-align-items: center;
				align-items: center;
				color: var(--d2l-color-ferrite);
				display: -ms-inline-flexbox;
				display: -webkit-inline-flex;
				display: inline-flex;
				fill: var(--d2l-icon-fill-color, currentcolor);
				height: var(--d2l-icon-height, 18px);
				-ms-flex-pack: center;
				-webkit-justify-content: center;
				justify-content: center;
				stroke: var(--d2l-icon-stroke-color, none);
				vertical-align: middle;
				width: var(--d2l-icon-width, 18px);
			}
			:host([icon*="d2l-tier2:"]) {
				height: var(--d2l-icon-height, 24px);
				width: var(--d2l-icon-width, 24px);
			}
			:host([icon*="d2l-tier3:"]) {
				height: var(--d2l-icon-height, 30px);
				width: var(--d2l-icon-width, 30px);
			}
			/* required since display of inline-block is non-default */
			:host([hidden]) {
				display: none;
			}
		</style>
	</template>

</dom-module>`;

document.head.appendChild($_documentContainer.content);
Polymer({
	is: 'd2l-icon',

	properties: {

		/**
		 * (Optional) Name of icon (ex. [iconset-name:icon-id]).
		 */
		icon: {
			type: String,
			reflectToAttribute: true
		},
		/**
		 * (Optional) Explicit source of icon.
		 */
		src: String,

		theme: String,

		_meta: {
			value: Base.create('iron-meta', {type: 'iconset'})
		}

	},

	observers: [
		'_updateIcon(_meta, isAttached)',
		'_updateIcon(theme, isAttached)',
		'_srcChanged(src, isAttached)',
		'_iconChanged(icon, isAttached)'
	],

	_DEFAULT_ICONSET: 'icons',

	_iconChanged: function(icon) {
		var parts = (icon || '').split(':');
		var iconName = parts.pop();
		if (iconName !== this._iconName) {
			this._d2lIconName = undefined;
		}
		this._iconName = iconName;
		this._iconsetName = parts.pop() || this._DEFAULT_ICONSET;
		this._updateIcon();
	},

	_srcChanged: function() {
		this._updateIcon();
	},

	_updateIcon: function() {
		if (this._usesIconset()) {
			if (this._img && this._img.parentNode) {
				dom(this.root).removeChild(this._img);
			}
			if (this._iconName === '') {
				if (this._iconset) {
					this._iconset.removeIcon(this);
				}
			} else if (this._iconsetName && this._meta) {
				this._iconset = /** @type {?Polymer.Iconset} */ (
					this._meta.byKey(this._iconsetName));
				if (this._iconset) {
					if (!this._d2lIconName) {
						this._d2lIconName = this._iconset.querySelector(`#d2l-icon-${this._iconName}`) ?
							(`d2l-icon-${this._iconName}`) : this._iconName;
					}
					this._iconset.applyIcon(this, this._d2lIconName, this.theme);
					this.unlisten(window, 'iron-iconset-added', '_updateIcon');
				} else {
					this.listen(window, 'iron-iconset-added', '_updateIcon');
				}
			}
		} else {
			if (this._iconset) {
				this._iconset.removeIcon(this);
			}
			if (!this._img) {
				this._img = document.createElement('img');
				this._img.style.width = '100%';
				this._img.style.height = '100%';
				this._img.draggable = false;
			}
			this._img.src = this.src;
			dom(this.root).appendChild(this._img);
		}
	},

	_usesIconset: function() {
		return this.icon || !this.src;
	}

});
<|endoftext|>


<commit_before>import React from 'react';
import { render, mount } from 'enzyme';
import Table from '..';

describe('Table.filter', () => {
  const filterFn = (value, record) => record.name.indexOf(value) !== -1;
  const column = {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      { text: 'Boy', value: 'boy' },
      { text: 'Girl', value: 'girl' },
      {
        text: 'Title',
        value: 'title',
        children: [
          { text: 'Designer', value: 'designer' },
          { text: 'Coder', value: 'coder' },
        ],
      },
    ],
    onFilter: filterFn,
  };

  const data = [
    { key: 0, name: 'Jack' },
    { key: 1, name: 'Lucy' },
    { key: 2, name: 'Tom' },
    { key: 3, name: 'Jerry' },
  ];

  function createTable(props) {
    return (

    );
  }

  function renderedNames(wrapper) {
    return wrapper.find('TableRow').map(row => row.props().record.name);
  }

  it('renders filter correctly', () => {
    const wrapper = render(createTable());

    expect(wrapper).toMatchSnapshot();
  });

  it('renders menu correctly', () => {
    const wrapper = mount(createTable());
    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('renders radio filter correctly', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterMultiple: false,
      }],
    }));
    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('renders custom content correctly', () => {
    const filter = (

        custom filter

    );
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterDropdown: filter,
      }],
    }));

    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('can be controlled by filterDropdownVisible', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterDropdownVisible: true,
      }],
    }));

    let dropdown = wrapper.find('Dropdown').first();
    expect(dropdown.props().visible).toBe(true);

    wrapper.setProps({
      columns: [{
        ...column,
        filterDropdownVisible: false,
      }],
    });

    dropdown = wrapper.find('Dropdown').first();
    expect(dropdown.props().visible).toBe(false);
  });

  it('fires change event when visible change', () => {
    const handleChange = jest.fn();
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        onFilterDropdownVisibleChange: handleChange,
      }],
    }));

    wrapper.find('.ant-dropdown-trigger').first().simulate('click');

    expect(handleChange).toBeCalledWith(true);
  });

  it('can be controlled by filteredValue', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filteredValue: ['Lucy'],
      }],
    }));

    expect(wrapper.find('tbody tr').length).toBe(1);
    wrapper.setProps({
      columns: [{
        ...column,
        filteredValue: [],
      }],
    });
    expect(wrapper.find('tbody tr').length).toBe(4);
  });

  it('can be controlled by filteredValue null', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filteredValue: ['Lucy'],
      }],
    }));

    expect(wrapper.find('tbody tr').length).toBe(1);
    wrapper.setProps({
      columns: [{
        ...column,
        filteredValue: null,
      }],
    });
    expect(wrapper.find('tbody tr').length).toBe(4);
  });

  it('fires change event', () => {
    const handleChange = jest.fn();
    const wrapper = mount(createTable({ onChange: handleChange }));
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());

    dropdownWrapper.find('MenuItem').first().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');

    expect(handleChange).toBeCalledWith({}, { name: ['boy'] }, {});
  });

  it('three levels menu', () => {
    const filters = [
      { text: 'Upper', value: 'Upper' },
      { text: 'Lower', value: 'Lower' },
      {
        text: 'Level2',
        value: 'Level2',
        children: [
          { text: 'Large', value: 'Large' },
          { text: 'Small', value: 'Small' },
          {
            text: 'Level3',
            value: 'Level3',
            children: [
              { text: 'Black', value: 'Black' },
              { text: 'White', value: 'White' },
              { text: 'Jack', value: 'Jack' },
            ],
          },
        ],
      },
    ];
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filters,
      }],
    }));
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    dropdownWrapper.find('.ant-dropdown-menu-submenu-title').at(0).simulate('mouseEnter');
    dropdownWrapper.find('.ant-dropdown-menu-submenu-title').at(1).simulate('mouseEnter');
    dropdownWrapper.find('MenuItem').last().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack']);
  });

  it('works with JSX in controlled mode', () => {
    const { Column } = Table;

    class App extends React.Component {
      state = {
        filters: {},
      }

      handleChange = (pagination, filters) => {
        this.setState({ filters });
      }

      render() {
        return (

            <Column
              title="name"
              dataIndex="name"
              key="name"
              filters={[
                { text: 'Jack', value: 'Jack' },
                { text: 'Lucy', value: 'Lucy' },
              ]}
              filteredValue={this.state.filters.name}
              onFilter={filterFn}
            />

        );
      }
    }

    const wrapper = mount(<App />);
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());

    dropdownWrapper.find('MenuItem').first().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack']);

    dropdownWrapper.find('.clear').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
  });

  it('works with grouping columns in controlled mode', () => {
    const columns = [
      {
        title: 'group',
        key: 'group',
        children: [
          {
            title: 'Name',
            dataIndex: 'name',
            key: 'name',
            filters: [
              { text: 'Jack', value: 'Jack' },
              { text: 'Lucy', value: 'Lucy' },
            ],
            onFilter: filterFn,
            filteredValue: ['Jack'],
          },
          {
            title: 'Age',
            dataIndex: 'age',
            key: 'age',
          },
        ],
      },
    ];
    const testData = [
      { key: 0, name: 'Jack', age: 11 },
      { key: 1, name: 'Lucy', age: 20 },
      { key: 2, name: 'Tom', age: 21 },
      { key: 3, name: 'Jerry', age: 22 },
    ];
    const wrapper = mount(

    );

    expect(renderedNames(wrapper)).toEqual(['Jack']);
  });
});
<commit_msg>Add table filter test<commit_after>import React from 'react';
import { render, mount } from 'enzyme';
import Table from '..';

describe('Table.filter', () => {
  const filterFn = (value, record) => record.name.indexOf(value) !== -1;
  const column = {
    title: 'Name',
    dataIndex: 'name',
    filters: [
      { text: 'Boy', value: 'boy' },
      { text: 'Girl', value: 'girl' },
      {
        text: 'Title',
        value: 'title',
        children: [
          { text: 'Designer', value: 'designer' },
          { text: 'Coder', value: 'coder' },
        ],
      },
    ],
    onFilter: filterFn,
  };

  const data = [
    { key: 0, name: 'Jack' },
    { key: 1, name: 'Lucy' },
    { key: 2, name: 'Tom' },
    { key: 3, name: 'Jerry' },
  ];

  function createTable(props) {
    return (

    );
  }

  function renderedNames(wrapper) {
    return wrapper.find('TableRow').map(row => row.props().record.name);
  }

  it('renders filter correctly', () => {
    const wrapper = render(createTable());

    expect(wrapper).toMatchSnapshot();
  });

  it('renders menu correctly', () => {
    const wrapper = mount(createTable());
    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('renders radio filter correctly', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterMultiple: false,
      }],
    }));
    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('renders custom content correctly', () => {
    const filter = (

        custom filter

    );
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterDropdown: filter,
      }],
    }));

    const dropdownWrapper = render(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper).toMatchSnapshot();
  });

  it('can be controlled by filterDropdownVisible', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filterDropdownVisible: true,
      }],
    }));

    let dropdown = wrapper.find('Dropdown').first();
    expect(dropdown.props().visible).toBe(true);

    wrapper.setProps({
      columns: [{
        ...column,
        filterDropdownVisible: false,
      }],
    });

    dropdown = wrapper.find('Dropdown').first();
    expect(dropdown.props().visible).toBe(false);
  });

  it('fires change event when visible change', () => {
    const handleChange = jest.fn();
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        onFilterDropdownVisibleChange: handleChange,
      }],
    }));

    wrapper.find('.ant-dropdown-trigger').first().simulate('click');

    expect(handleChange).toBeCalledWith(true);
  });

  it('can be controlled by filteredValue', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filteredValue: ['Lucy'],
      }],
    }));

    expect(wrapper.find('tbody tr').length).toBe(1);
    wrapper.setProps({
      columns: [{
        ...column,
        filteredValue: [],
      }],
    });
    expect(wrapper.find('tbody tr').length).toBe(4);
  });

  it('can be controlled by filteredValue null', () => {
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filteredValue: ['Lucy'],
      }],
    }));

    expect(wrapper.find('tbody tr').length).toBe(1);
    wrapper.setProps({
      columns: [{
        ...column,
        filteredValue: null,
      }],
    });
    expect(wrapper.find('tbody tr').length).toBe(4);
  });

  it('fires change event', () => {
    const handleChange = jest.fn();
    const wrapper = mount(createTable({ onChange: handleChange }));
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());

    dropdownWrapper.find('MenuItem').first().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');

    expect(handleChange).toBeCalledWith({}, { name: ['boy'] }, {});
  });

  it('three levels menu', () => {
    const filters = [
      { text: 'Upper', value: 'Upper' },
      { text: 'Lower', value: 'Lower' },
      {
        text: 'Level2',
        value: 'Level2',
        children: [
          { text: 'Large', value: 'Large' },
          { text: 'Small', value: 'Small' },
          {
            text: 'Level3',
            value: 'Level3',
            children: [
              { text: 'Black', value: 'Black' },
              { text: 'White', value: 'White' },
              { text: 'Jack', value: 'Jack' },
            ],
          },
        ],
      },
    ];
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filters,
      }],
    }));
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    dropdownWrapper.find('.ant-dropdown-menu-submenu-title').at(0).simulate('mouseEnter');
    dropdownWrapper.find('.ant-dropdown-menu-submenu-title').at(1).simulate('mouseEnter');
    dropdownWrapper.find('MenuItem').last().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack']);
  });

  it('works with JSX in controlled mode', () => {
    const { Column } = Table;

    class App extends React.Component {
      state = {
        filters: {},
      }

      handleChange = (pagination, filters) => {
        this.setState({ filters });
      }

      render() {
        return (

            <Column
              title="name"
              dataIndex="name"
              key="name"
              filters={[
                { text: 'Jack', value: 'Jack' },
                { text: 'Lucy', value: 'Lucy' },
              ]}
              filteredValue={this.state.filters.name}
              onFilter={filterFn}
            />

        );
      }
    }

    const wrapper = mount(<App />);
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());

    dropdownWrapper.find('MenuItem').first().simulate('click');
    dropdownWrapper.find('.confirm').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack']);

    dropdownWrapper.find('.clear').simulate('click');
    wrapper.update();
    expect(renderedNames(wrapper)).toEqual(['Jack', 'Lucy', 'Tom', 'Jerry']);
  });

  it('works with grouping columns in controlled mode', () => {
    const columns = [
      {
        title: 'group',
        key: 'group',
        children: [
          {
            title: 'Name',
            dataIndex: 'name',
            key: 'name',
            filters: [
              { text: 'Jack', value: 'Jack' },
              { text: 'Lucy', value: 'Lucy' },
            ],
            onFilter: filterFn,
            filteredValue: ['Jack'],
          },
          {
            title: 'Age',
            dataIndex: 'age',
            key: 'age',
          },
        ],
      },
    ];
    const testData = [
      { key: 0, name: 'Jack', age: 11 },
      { key: 1, name: 'Lucy', age: 20 },
      { key: 2, name: 'Tom', age: 21 },
      { key: 3, name: 'Jerry', age: 22 },
    ];
    const wrapper = mount(

    );

    expect(renderedNames(wrapper)).toEqual(['Jack']);
  });

  it('confirm filter when dropdown hidden', () => {
    const handleChange = jest.fn();
    const wrapper = mount(createTable({
      columns: [{
        ...column,
        filters: [
          { text: 'Jack', value: 'Jack' },
          { text: 'Lucy', value: 'Lucy' },
        ],
      }],
      onChange: handleChange,
    }));

    wrapper.find('.ant-dropdown-trigger').first().simulate('click');
    wrapper.find('.ant-dropdown-menu-item').first().simulate('click');
    wrapper.find('.ant-dropdown-trigger').first().simulate('click');

    expect(handleChange).toBeCalled();
  });
});
<|endoftext|>


<commit_before>$(document).ready(function () {
    "use strict";

    $(document).foundation();

    $(".toolbox label input").each(function () {
        var $this = $(this);
        $this.parent().addClass("toolbox-option-{0}".format($this.val()));
    });

});

var app = window.angular.module('labelingApp', ['ngResource', 'ngRoute', 'ngCookies']).run(
    function ($http, $cookies) {
        $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
        // Add the following two lines
        $http.defaults.xsrfCookieName = 'csrftoken';
        $http.defaults.xsrfHeaderName = 'X-CSRFToken';
    });

app.factory('EntityOccurrence', ['$resource',
        function ($resource) {
            return $resource('/corpus/crud/entity_occurrence/', {'pk': '@pk'}, {});
        }]
    );
app.factory('TextSegment', ['$resource',
        function ($resource) {
            return $resource('/corpus/crud/text_segment/', {'pk': '@pk'}, {});
        }]
    );
app.directive('ngRightClick', function ($parse) {
    return function ($scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function (event) {
            $scope.$apply(function () {
                event.preventDefault();
                fn($scope, {$event: event});
            });
        });
    };
});
app.controller('QuestionsController', ['$scope', 'EntityOccurrence', 'TextSegment',
function ($scope, EntityOccurrence, TextSegment) {
    "use strict";
    // ### Attributes ###

    $scope.eo_selected = undefined; // Id of the selected occurrence

    // Publish into the scope the variables defined globally
    $scope.eos = window.eos;
    $scope.relations = window.relations;
    $scope.forms = window.forms;
    $scope.current_tool = window.initial_tool;
    $scope.arrows = {};
    $scope.eo_modal = {};

    $(document).ready(function () {
        $scope.$segments = $(".segments");
        $scope.$svg = $("svg");
        $scope.svg = $scope.$svg[0];

        $(window).resize($scope.update_relations_arrows);

        $scope.update_relations_arrows();
        $scope.create_relations_metadata();

        $(".eo-submenu").on("click", $scope.on_eo_submenu_click);
        $(".eo-submenu").mouseover($scope.highlight_eo_tokens);
        $(".eo-submenu").mouseout($scope.highlight_eo_tokens);
        $(".entity-occurrence").mouseover($scope.highlight_eo_tokens);
        $(".entity-occurrence").mouseout($scope.highlight_eo_tokens);
        $(".prev-relations li").mouseover($scope.highlight_relation);
        $(".prev-relations li").mouseout($scope.highlight_relation);
        $scope.eo_modal.elem = $('#eoModal');
        $scope.eo_modal.elem.find('a.cancel').bind('click', function () {
            $scope.eo_modal.elem.foundation('reveal', 'close');
        });
        $scope.eo_modal.elem.find('a.save').bind('click', function () {
            $scope.eo_modal.submit();
        });
    });

    // ### Methods ###

    $scope.create_relations_metadata = function () {
        // NOTE: update_relations_arrows must be run before

        var $holder = $(".prev-relations");

        for(var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var relation = $scope.relations[i];
                var info = relation.info;
                var form_value = $scope.forms[relation.form_id];

                if (form_value !== null && info !== undefined) {
                    var $element = $("
");
                    var $arrow = $("");
                    var $text = $("");

                    $arrow.addClass("fi-arrow-right");
                    $arrow.addClass("prev-arrow prev-arrow-{0}".format(form_value));
                    $text.text(info);

                    $element.addClass("prev-relation-{0}".format(relation.form_id));
                    $element.data("relation-id", relation.form_id);
                    $element.append($arrow);
                    $element.append($text);
                    $holder.append($element);
                }
            }
        }
    };

    $scope.update_relations_arrows = function () {
        // Update width and height of the svg element
        $scope.$svg.attr("width", $scope.$segments.width());
        $scope.$svg.attr("height", $scope.$segments.height());

        // Remove all arrows before re-drawing
        for (var form_id in $scope.forms) {
            if ($scope.forms.hasOwnProperty(form_id)) {
                var path = $scope.arrows[form_id];
                if (path) {
                    $scope.svg.removeChild(path);
                }
            }
        }

        // Re-draw all the arrows
        for (var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var rel_obj = $scope.relations[i];
                $scope.calculate_arrow_string(
                    rel_obj.relation[0],
                    rel_obj.relation[1],
                    rel_obj.form_id
                );
            }
        }
    };

    // Sets the value for selectable on all entity occurrences
    $scope.set_selectables = function (value) {
        for (var i in $scope.eos) {
            if ($scope.eos.hasOwnProperty(i)) {
                $scope.eos[i].selectable = value;
            }
        }
    };

    $scope.eo_click = function (ids) {
        // Handles only the case of 1 id, if it has
        // more than one, it shows the menu
        if (ids.length === 1) {
            var id = ids[0];
            $scope.handle_click_on_eo(id);
        }
    };

    $scope.on_eo_submenu_click = function (event) {
        event.preventDefault();

        var $this = $(this);
        var eo_id = $this.data("eo-id");
        $scope.handle_click_on_eo(eo_id);
        $scope.$apply();
    };

    $scope.handle_click_on_eo = function (id) {
        var eo = $scope.eos[id];

        // Not selectable
        if (!eo || !eo.selectable) { return; }

        if ($scope.eo_selected === undefined) {
            // Marking as selected
            $scope.eo_first_click(id);
        } else {
            $scope.eo_second_click(id);
        }
    };

    $scope.highlight_eo_tokens = function () {
        var $this = $(this);
        var eo_id = $this.data("eo-id");
        $(".eo-{0}".format(eo_id)).each(function () {
            var $this = $(this);
            $this.toggleClass("highlight");
        });
    };

    $scope.highlight_relation = function () {
        var $this = $(this);
        var toggle = $this.data("toggle");
        var rel_id = $this.data("relation-id");
        var arrow = $scope.arrows[rel_id];

        if (toggle) {
            $this.data("toggle", false);
            arrow.style.strokeWidth = "";
        } else {
            $this.data("toggle", true);
            arrow.style.strokeWidth = "4px";
        }

    };

    $scope.eo_first_click = function (id) {
        var eo = $scope.eos[id];
        $scope.eo_selected = id;

        $scope.set_selectables(false);
        eo.selectable = true;
        eo.selected = true;

        // Calculate wich ones must be selectable
        for (var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var rel = $scope.relations[i];
                var rel_index = rel.relation.indexOf(id);
                if (rel_index >= 0) {
                    var eo_id = $scope.relations[i].relation[(rel_index + 1) % 2];
                    $scope.eos[eo_id].selectable = true;
                }
            }
        }
    };

    $scope.eo_second_click = function (id) {
        if ($scope.eo_selected === id) {
            // You're de-selecting the one you've just selected
            $scope.eos[id].selected = false;
        } else {
            var eo_id1 = $scope.eo_selected;
            var eo_id2 = id;

            for (var i in $scope.relations) {
                if ($scope.relations.hasOwnProperty(i)) {
                    var rel = $scope.relations[i];
                    var eo_rel_index1 = rel.relation.indexOf(eo_id1);
                    var eo_rel_index2 = rel.relation.indexOf(eo_id2);
                    if (eo_rel_index1 >= 0 && eo_rel_index2 >= 0) {
                        var form_value = $scope.forms[rel.form_id];
                        var new_value = form_value ? "": $scope.current_tool;
                        $scope.forms[rel.form_id] = new_value;

                        $scope.calculate_arrow_string(
                            rel.relation[0],
                            rel.relation[1],
                            rel.form_id
                        );

                        $scope.eos[eo_id1].selected = false;
                        $scope.eos[eo_id2].selected = false;
                    }
                }
            }
        }

        $scope.set_selectables(true);
        $scope.eo_selected = undefined;
    };

    $scope.calculate_arrow_string = function (eo_id1, eo_id2, form_id) {
        var path;

        if ($scope.forms[form_id] === null || $scope.forms[form_id] === "") {
            // Remove the arrow
            path = $scope.arrows[form_id];
            var $prev_arrow = $(".prev-relation-{0}".format(form_id));
            if ($prev_arrow.length > 0) {
                $prev_arrow.remove();
            }
            if (path !== undefined) {
                $scope.svg.removeChild(path);
            }
        } else {
            // Curve configuration
            var curve_distance = 25;
            var y_offset = $($scope.svg).position().top;
            var x_offset = -30;

            // Entity occurrences
            var $eo1 = $(".eo-" + eo_id1);
            var $eo2 = $(".eo-" + eo_id2);

            // Positions
            var eo1_pos = $eo1.position();
            var eo2_pos = $eo2.position();

            // Corrected positions
            var eo1_pos_left = eo1_pos.left - x_offset;
            var eo1_pos_top = eo1_pos.top - y_offset - 5;
            var eo2_pos_left = eo2_pos.left - x_offset;
            var eo2_pos_top = eo2_pos.top - y_offset - 10;

            // Form value
            var form_value = $scope.forms[form_id];

            // Format should be:
            // M<x1>,<y1> C<x1>,<y1 + distance> <x2>,<y2 + distance> <x2>,<y2>
            var curve_string = "M{0},{1} C{0},{4} {2},{5} {2},{3}".format(
                Math.round(eo1_pos_left), // {0}
                Math.round(eo1_pos_top),  // {1}
                Math.round(eo2_pos_left), // {2}
                Math.round(eo2_pos_top),  // {3}
                Math.round(eo1_pos_top - curve_distance), // {4}
                Math.round(eo2_pos_top - curve_distance)  // {5}
            );

            path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute("class", 'arrow arrow_{0}'.format(form_value));
            path.setAttribute("style", 'marker-end: url(#arrow-point-{0});'.format(
                form_value
            ));
            path.setAttribute("d", curve_string);

            $scope.svg.appendChild(path);
            $scope.arrows[form_id] = path;
        }
    };

    // ###  Entity Occurrence Modification methods (and modal)  ###

    $scope.manage_eo = function (value, segment_id) {
        EntityOccurrence.get({pk: value}).$promise.then(
            function (eo_obj) {
                var $modal = $scope.eo_modal.elem;
                var marker_html = '
';
                marker_html +=    '
';
                $modal.find('.message').empty();
                $modal.find('.segment').empty();
                TextSegment.get({pk: segment_id}).$promise.then(
                    function (segment) {
                        // store resources on the scope
                        $scope.eo_modal.eo = eo_obj;
                        $scope.eo_modal.segment = segment;
                        var $segment = $modal.find('.segment');
                        for (var i = 0; i < segment.tokens.length; i++) {
                            if (segment.offset + i === eo_obj.offset) {
                                $segment.append(marker_html);
                            }
                            if (segment.offset + i === eo_obj.offset_end) {
                                $segment.append(marker_html);
                            }
                            $segment.append(
                                '
' + segment.tokens[i] + '
'
                            );
                        }
                        $scope.eo_modal.update_selection();
                        $segment.sortable({
                            cancel: ".token",
                            update: $scope.eo_modal.update_selection
                        });
                    },
                    function (response) {
                        $scope.eo_modal.add_msg("Server error " + response);
                    }
                );
                $modal.foundation('reveal', 'open');
            });
    };

    $scope.eo_modal.reset = function () {
        var $elem = $scope.eo_modal.elem;
        $elem.find('.message').empty();
        $elem.find('.segment').empty();
    };

    $scope.eo_modal.add_msg = function (msg) {
        $scope.eo_modal.elem.find('.message').empty().append('
' + msg + '
');
    };

    $scope.eo_modal.update_selection = function (event) {
        var paiting = false;
        var new_offsets = [];
        $scope.eo_modal.elem.find('.segment div').each(function (idx) {
            var className = 'between-markers';
            var $elem = $(this);
            $elem.removeClass(className);
            var is_marker = $elem.hasClass('marker');
            if (is_marker) {
                new_offsets.push(idx);
            }
            if (paiting && is_marker) {
                // stop paiting
                paiting = false;
            } else if (!paiting && is_marker) {
                // start paiting
                paiting = true;
            }
            // now, paint
            if (paiting && !is_marker) {
                $elem.addClass(className);
            }
        });
        var base = $scope.eo_modal.segment.offset;
        $scope.eo_modal.eo.new_offset = base + new_offsets[0];
        $scope.eo_modal.eo.new_offset_end = base + new_offsets[1] - 1;
    };

    $scope.eo_modal.submit = function () {
        var eo = $scope.eo_modal.eo;
        if (eo.new_offset_end - eo.new_offset <= 0) {
            $scope.eo_modal.add_msg("Invalid Entity Occurrence limits. Can't be empty.");
        } else {
            eo.offset = eo.new_offset;
            eo.offset_end = eo.new_offset_end;
            eo.$save().then(
                function () {
                    // SUCCESS
                    $scope.eo_modal.elem.foundation('reveal', 'close');
                },
                function (response) {
                    $scope.eo_modal.add_msg("Not saved. " + response.statusText);
                }
            );
        }
    };
}
]);

String.prototype.format = String.prototype.f = function () {
    var s = this;
    var i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};
<commit_msg>starting to work on after-save refresh<commit_after>$(document).ready(function () {
    "use strict";

    $(document).foundation();

    $(".toolbox label input").each(function () {
        var $this = $(this);
        $this.parent().addClass("toolbox-option-{0}".format($this.val()));
    });

});

var app = window.angular.module('labelingApp', ['ngResource', 'ngRoute', 'ngCookies']).run(
    function ($http, $cookies) {
        $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
        // Add the following two lines
        $http.defaults.xsrfCookieName = 'csrftoken';
        $http.defaults.xsrfHeaderName = 'X-CSRFToken';
    });

app.factory('EntityOccurrence', ['$resource',
        function ($resource) {
            return $resource('/corpus/crud/entity_occurrence/', {'pk': '@pk'}, {});
        }]
    );
app.factory('TextSegment', ['$resource',
        function ($resource) {
            return $resource('/corpus/crud/text_segment/', {'pk': '@pk'}, {});
        }]
    );
app.directive('ngRightClick', function ($parse) {
    return function ($scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function (event) {
            $scope.$apply(function () {
                event.preventDefault();
                fn($scope, {$event: event});
            });
        });
    };
});
app.controller('QuestionsController', ['$scope', 'EntityOccurrence', 'TextSegment',
function ($scope, EntityOccurrence, TextSegment) {
    "use strict";
    // ### Attributes ###

    $scope.eo_selected = undefined; // Id of the selected occurrence

    // Publish into the scope the variables defined globally
    $scope.eos = window.eos;
    $scope.relations = window.relations;
    $scope.forms = window.forms;
    $scope.current_tool = window.initial_tool;
    $scope.arrows = {};
    $scope.eo_modal = {};

    $(document).ready(function () {
        $scope.$segments = $(".segments");
        $scope.$svg = $("svg");
        $scope.svg = $scope.$svg[0];

        $(window).resize($scope.update_relations_arrows);

        $scope.update_relations_arrows();
        $scope.create_relations_metadata();

        $(".eo-submenu").on("click", $scope.on_eo_submenu_click);
        $(".eo-submenu").mouseover($scope.highlight_eo_tokens);
        $(".eo-submenu").mouseout($scope.highlight_eo_tokens);
        $(".entity-occurrence").mouseover($scope.highlight_eo_tokens);
        $(".entity-occurrence").mouseout($scope.highlight_eo_tokens);
        $(".prev-relations li").mouseover($scope.highlight_relation);
        $(".prev-relations li").mouseout($scope.highlight_relation);
        $scope.eo_modal.elem = $('#eoModal');
        $scope.eo_modal.elem.find('a.cancel').bind('click', function () {
            $scope.eo_modal.elem.foundation('reveal', 'close');
        });
        $scope.eo_modal.elem.find('a.save').bind('click', function () {
            $scope.eo_modal.submit();
        });
    });

    // ### Methods ###

    $scope.create_relations_metadata = function () {
        // NOTE: update_relations_arrows must be run before

        var $holder = $(".prev-relations");

        for(var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var relation = $scope.relations[i];
                var info = relation.info;
                var form_value = $scope.forms[relation.form_id];

                if (form_value !== null && info !== undefined) {
                    var $element = $("
");
                    var $arrow = $("");
                    var $text = $("");

                    $arrow.addClass("fi-arrow-right");
                    $arrow.addClass("prev-arrow prev-arrow-{0}".format(form_value));
                    $text.text(info);

                    $element.addClass("prev-relation-{0}".format(relation.form_id));
                    $element.data("relation-id", relation.form_id);
                    $element.append($arrow);
                    $element.append($text);
                    $holder.append($element);
                }
            }
        }
    };

    $scope.update_relations_arrows = function () {
        // Update width and height of the svg element
        $scope.$svg.attr("width", $scope.$segments.width());
        $scope.$svg.attr("height", $scope.$segments.height());

        // Remove all arrows before re-drawing
        for (var form_id in $scope.forms) {
            if ($scope.forms.hasOwnProperty(form_id)) {
                var path = $scope.arrows[form_id];
                if (path) {
                    $scope.svg.removeChild(path);
                }
            }
        }

        // Re-draw all the arrows
        for (var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var rel_obj = $scope.relations[i];
                $scope.calculate_arrow_string(
                    rel_obj.relation[0],
                    rel_obj.relation[1],
                    rel_obj.form_id
                );
            }
        }
    };

    // Sets the value for selectable on all entity occurrences
    $scope.set_selectables = function (value) {
        for (var i in $scope.eos) {
            if ($scope.eos.hasOwnProperty(i)) {
                $scope.eos[i].selectable = value;
            }
        }
    };

    $scope.eo_click = function (ids) {
        // Handles only the case of 1 id, if it has
        // more than one, it shows the menu
        if (ids.length === 1) {
            var id = ids[0];
            $scope.handle_click_on_eo(id);
        }
    };

    $scope.on_eo_submenu_click = function (event) {
        event.preventDefault();

        var $this = $(this);
        var eo_id = $this.data("eo-id");
        $scope.handle_click_on_eo(eo_id);
        $scope.$apply();
    };

    $scope.handle_click_on_eo = function (id) {
        var eo = $scope.eos[id];

        // Not selectable
        if (!eo || !eo.selectable) { return; }

        if ($scope.eo_selected === undefined) {
            // Marking as selected
            $scope.eo_first_click(id);
        } else {
            $scope.eo_second_click(id);
        }
    };

    $scope.highlight_eo_tokens = function () {
        var $this = $(this);
        var eo_id = $this.data("eo-id");
        $(".eo-{0}".format(eo_id)).each(function () {
            var $this = $(this);
            $this.toggleClass("highlight");
        });
    };

    $scope.highlight_relation = function () {
        var $this = $(this);
        var toggle = $this.data("toggle");
        var rel_id = $this.data("relation-id");
        var arrow = $scope.arrows[rel_id];

        if (toggle) {
            $this.data("toggle", false);
            arrow.style.strokeWidth = "";
        } else {
            $this.data("toggle", true);
            arrow.style.strokeWidth = "4px";
        }

    };

    $scope.eo_first_click = function (id) {
        var eo = $scope.eos[id];
        $scope.eo_selected = id;

        $scope.set_selectables(false);
        eo.selectable = true;
        eo.selected = true;

        // Calculate wich ones must be selectable
        for (var i in $scope.relations) {
            if ($scope.relations.hasOwnProperty(i)) {
                var rel = $scope.relations[i];
                var rel_index = rel.relation.indexOf(id);
                if (rel_index >= 0) {
                    var eo_id = $scope.relations[i].relation[(rel_index + 1) % 2];
                    $scope.eos[eo_id].selectable = true;
                }
            }
        }
    };

    $scope.eo_second_click = function (id) {
        if ($scope.eo_selected === id) {
            // You're de-selecting the one you've just selected
            $scope.eos[id].selected = false;
        } else {
            var eo_id1 = $scope.eo_selected;
            var eo_id2 = id;

            for (var i in $scope.relations) {
                if ($scope.relations.hasOwnProperty(i)) {
                    var rel = $scope.relations[i];
                    var eo_rel_index1 = rel.relation.indexOf(eo_id1);
                    var eo_rel_index2 = rel.relation.indexOf(eo_id2);
                    if (eo_rel_index1 >= 0 && eo_rel_index2 >= 0) {
                        var form_value = $scope.forms[rel.form_id];
                        var new_value = form_value ? "": $scope.current_tool;
                        $scope.forms[rel.form_id] = new_value;

                        $scope.calculate_arrow_string(
                            rel.relation[0],
                            rel.relation[1],
                            rel.form_id
                        );

                        $scope.eos[eo_id1].selected = false;
                        $scope.eos[eo_id2].selected = false;
                    }
                }
            }
        }

        $scope.set_selectables(true);
        $scope.eo_selected = undefined;
    };

    $scope.calculate_arrow_string = function (eo_id1, eo_id2, form_id) {
        var path;

        if ($scope.forms[form_id] === null || $scope.forms[form_id] === "") {
            // Remove the arrow
            path = $scope.arrows[form_id];
            var $prev_arrow = $(".prev-relation-{0}".format(form_id));
            if ($prev_arrow.length > 0) {
                $prev_arrow.remove();
            }
            if (path !== undefined) {
                $scope.svg.removeChild(path);
            }
        } else {
            // Curve configuration
            var curve_distance = 25;
            var y_offset = $($scope.svg).position().top;
            var x_offset = -30;

            // Entity occurrences
            var $eo1 = $(".eo-" + eo_id1);
            var $eo2 = $(".eo-" + eo_id2);

            // Positions
            var eo1_pos = $eo1.position();
            var eo2_pos = $eo2.position();

            // Corrected positions
            var eo1_pos_left = eo1_pos.left - x_offset;
            var eo1_pos_top = eo1_pos.top - y_offset - 5;
            var eo2_pos_left = eo2_pos.left - x_offset;
            var eo2_pos_top = eo2_pos.top - y_offset - 10;

            // Form value
            var form_value = $scope.forms[form_id];

            // Format should be:
            // M<x1>,<y1> C<x1>,<y1 + distance> <x2>,<y2 + distance> <x2>,<y2>
            var curve_string = "M{0},{1} C{0},{4} {2},{5} {2},{3}".format(
                Math.round(eo1_pos_left), // {0}
                Math.round(eo1_pos_top),  // {1}
                Math.round(eo2_pos_left), // {2}
                Math.round(eo2_pos_top),  // {3}
                Math.round(eo1_pos_top - curve_distance), // {4}
                Math.round(eo2_pos_top - curve_distance)  // {5}
            );

            path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
            path.setAttribute("class", 'arrow arrow_{0}'.format(form_value));
            path.setAttribute("style", 'marker-end: url(#arrow-point-{0});'.format(
                form_value
            ));
            path.setAttribute("d", curve_string);

            $scope.svg.appendChild(path);
            $scope.arrows[form_id] = path;
        }
    };

    // ###  Entity Occurrence Modification methods (and modal)  ###

    $scope.manage_eo = function (value, segment_id) {
        EntityOccurrence.get({pk: value}).$promise.then(
            function (eo_obj) {
                var $modal = $scope.eo_modal.elem;
                var marker_html = '
';
                marker_html +=    '
';
                $modal.find('.message').empty();
                $modal.find('.segment').empty();
                TextSegment.get({pk: segment_id}).$promise.then(
                    function (segment) {
                        // store resources on the scope
                        $scope.eo_modal.eo = eo_obj;
                        $scope.eo_modal.segment = segment;
                        var $segment = $modal.find('.segment');
                        for (var i = 0; i < segment.tokens.length; i++) {
                            if (segment.offset + i === eo_obj.offset) {
                                $segment.append(marker_html);
                            }
                            if (segment.offset + i === eo_obj.offset_end) {
                                $segment.append(marker_html);
                            }
                            $segment.append(
                                '
' + segment.tokens[i] + '
'
                            );
                        }
                        $scope.eo_modal.update_selection();
                        $segment.sortable({
                            cancel: ".token",
                            update: $scope.eo_modal.update_selection
                        });
                    },
                    function (response) {
                        $scope.eo_modal.add_msg("Server error " + response);
                    }
                );
                $modal.foundation('reveal', 'open');
            });
    };

    $scope.eo_modal.reset = function () {
        var $elem = $scope.eo_modal.elem;
        $elem.find('.message').empty();
        $elem.find('.segment').empty();
    };

    $scope.eo_modal.add_msg = function (msg) {
        $scope.eo_modal.elem.find('.message').empty().append('
' + msg + '
');
    };

    $scope.eo_modal.update_selection = function (event) {
        var paiting = false;
        var new_offsets = [];
        $scope.eo_modal.elem.find('.segment div').each(function (idx) {
            var className = 'between-markers';
            var $elem = $(this);
            $elem.removeClass(className);
            var is_marker = $elem.hasClass('marker');
            if (is_marker) {
                new_offsets.push(idx);
            }
            if (paiting && is_marker) {
                // stop paiting
                paiting = false;
            } else if (!paiting && is_marker) {
                // start paiting
                paiting = true;
            }
            // now, paint
            if (paiting && !is_marker) {
                $elem.addClass(className);
            }
        });
        var base = $scope.eo_modal.segment.offset;
        $scope.eo_modal.eo.new_offset = base + new_offsets[0];
        $scope.eo_modal.eo.new_offset_end = base + new_offsets[1] - 1;
    };

    $scope.eo_modal.submit = function () {
        var eo = $scope.eo_modal.eo;
        if (eo.new_offset_end - eo.new_offset <= 0) {
            $scope.eo_modal.add_msg("Invalid Entity Occurrence limits. Can't be empty.");
        } else {
            eo.offset = eo.new_offset;
            eo.offset_end = eo.new_offset_end;
            eo.$save().then(
                $scope.eo_modal.save_success,
                function (response) {
                    $scope.eo_modal.add_msg("Not saved. " + response.statusText);
                }
            );
        }
    };

    $scope.eo_modal.save_success = function () {
        /* Here is handled not the segment on the modal, but on the actual underlying
         * page */
        var eo = $scope.eo_modal.eo;
        var seg = $scope.eo_modal.segment;
        eo.segment_offset = eo.offset - seg.offset;
        eo.segment_offset_end = eo.offset_end - seg.offset;
        var $segment = $('.segment-id-' + $scope.eo_modal.segment.pk);
        $segment.find('span.rich-token').each(function (index) {
            var $token = $(this);
            if (index >= eo.segment_offset && index < eo.segment_offset_end) {
                $token.addClass('entity-occurrence');
                $token.addClass('eo-' + eo.pk);
            } else {
                $token.removeClass('eo-' + eo.pk);
            }
        });
        $scope.eo_modal.elem.foundation('reveal', 'close');
    };
}
]);

String.prototype.format = String.prototype.f = function () {
    var s = this;
    var i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};
<|endoftext|>


<commit_before>var fs = require('fs');
module.exports = function(gameServer,split) {
var fileone = fs.readFileSync('./console.txt',"utf8");
var filetwo = fs.readFileSync('./translate.txt',"utf8");
var one = fileone.split("\n");
var two = filetwo.split("\n");
var result = "";
for (var i in one) {
 var on = one[i];
  var tw = two[i];
  var s =  "this.lang[" + i + "] = { original:" + on + ",replace:" + tw + "};\n"
  result = result + s;
}
fs.writeFileSync("result.js",result);
};
<commit_msg>Update combine.js<commit_after>var fs = require('fs');
module.exports = function(gameServer,split) {
var fileone = fs.readFileSync('./console.txt',"utf8");
var filetwo = fs.readFileSync('./translate.txt',"utf8");
var one = fileone.split("\n");
var two = filetwo.split("\n");
var result = "this.lang = [];\n";
for (var i in one) {
 var on = one[i];
  var tw = two[i];
  var s =  "this.lang[" + i + "] = { original:" + on + ",replace:" + tw + "};\n"
  result = result + s;
}
result = result + "module.exports = this;";
fs.writeFileSync("result.js",result);
};
<|endoftext|>


<commit_before>import Component from 'ember-component';
import {A as emberA} from 'ember-array/utils';
import run from 'ember-runloop';
import layout from '../templates/components/gh-koenig';
import Mobiledoc from 'mobiledoc-kit';
import {MOBILEDOC_VERSION} from 'mobiledoc-kit/renderers/mobiledoc';
import createCardFactory from '../lib/card-factory';
import defaultCommands from '../options/default-commands';
import editorCards  from '../cards/index';
// import { VALID_MARKUP_SECTION_TAGNAMES } from 'mobiledoc-kit/models/markup-section'; //the block elements supported by mobile-doc

export const BLANK_DOC = {
    version: MOBILEDOC_VERSION,
    atoms: [],
    markups: [],
    cards: [],
    sections: []
};

export default Component.extend({
    layout,
    classNames: ['editor-holder'],
    emberCards: emberA([]),

    init() {
        this._super(...arguments);

        let mobiledoc = this.get('value') || BLANK_DOC;
        let userCards = this.get('cards') || [];

        if (typeof mobiledoc === 'string') {
            mobiledoc = JSON.parse(mobiledoc);
        }

        // if the doc is cached then the editor is loaded and we don't need to continue.
        if (this._cachedDoc && this._cachedDoc === mobiledoc) {
            return;
        }

        let createCard = createCardFactory.apply(this, {}); // need to pass the toolbar

        let options = {
            mobiledoc,
            // temp
            cards: createCard(editorCards.concat(userCards)),
            atoms: [{
                name: 'soft-return',
                type: 'dom',
                render() {
                    return document.createElement('br');
                }
            }],
            spellcheck: true,
            autofocus: this.get('shouldFocusEditor'),
            placeholder: 'Click here to start ...'
        };

        this.editor = new Mobiledoc.Editor(options);
    },

    willRender() {
        if (this._rendered) {
            return;
        }
        let {editor} = this;

        editor.willRender(() => {
            // console.log(Ember.run.currentRunLoop);
            // if (!Ember.run.currentRunLoop) {
            //     this._startedRunLoop = true;
            //     Ember.run.begin();
            // }
        });

        editor.didRender(() => {

            this.sendAction('loaded', editor);
                // Ember.run.end();

        });
        editor.postDidChange(()=> {
            run.join(() => {
                // store a cache of the local doc so that we don't need to reinitialise it.
                this._cachedDoc = editor.serialize(MOBILEDOC_VERSION);
                this.sendAction('onChange', this._cachedDoc);
                if (this._cachedDoc !== BLANK_DOC && !this._firstChange) {
                    this._firstChange = true;
                    this.sendAction('onFirstChange', this._cachedDoc);
                }
            });
        });
    },

    didRender() {
        if (this._rendered) {
            return;
        }
        let [editorDom] = this.$('.surface');
        this.domContainer = editorDom.parentNode.parentNode.parentNode.parentNode; // nasty nasty nasty.
        this.editor.render(editorDom);
        this._rendered = true;

        window.editor = this.editor;
        defaultCommands(this.editor); // initialise the custom text handlers for MD, etc.
        // shouldFocusEditor is only true when transitioning from new to edit, otherwise it's false or undefined.
        // therefore, if it's true it's after the first lot of content is entered and we expect the caret to be at the
        // end of the document.
        if (this.get('shouldFocusEditor')) {
            let range = document.createRange();
            range.selectNodeContents(this.editor.element);
            range.collapse(false);
            let sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
            this.editor._ensureFocus();
        }

        this.editor.cursorDidChange(() => this.cursorMoved());

    },

    // drag and drop images onto the editor
    drop(event) {
        if (event.dataTransfer.files.length) {
            event.preventDefault();
            for (let i = 0; i < event.dataTransfer.files.length; i++) {
                let file = [event.dataTransfer.files[i]];
                this.editor.insertCard('image-card', {pos: 'top', file});
            }
        }
    },

    // makes sure the cursor is on screen.
    cursorMoved() {
        let scrollBuffer = 33; // the extra buffer to scroll.
        let range = window.getSelection().getRangeAt(0); // get the actual range within the DOM.
        let position =  range.getBoundingClientRect();
        let windowHeight = window.innerHeight;

        if (position.bottom > windowHeight) {
            this.domContainer.scrollTop += position.bottom - windowHeight + scrollBuffer;
        } else if (position.top < 0) {
            this.domContainer.scrollTop += position.top - scrollBuffer;
        }
    },

    willDestroy() {
        this.editor.destroy();
    }

});
<commit_msg>🐜  Bulk selecting (#594)<commit_after>import Component from 'ember-component';
import {A as emberA} from 'ember-array/utils';
import run from 'ember-runloop';
import layout from '../templates/components/gh-koenig';
import Mobiledoc from 'mobiledoc-kit';
import {MOBILEDOC_VERSION} from 'mobiledoc-kit/renderers/mobiledoc';
import createCardFactory from '../lib/card-factory';
import defaultCommands from '../options/default-commands';
import editorCards  from '../cards/index';
// import { VALID_MARKUP_SECTION_TAGNAMES } from 'mobiledoc-kit/models/markup-section'; //the block elements supported by mobile-doc

export const BLANK_DOC = {
    version: MOBILEDOC_VERSION,
    atoms: [],
    markups: [],
    cards: [],
    sections: []
};

export default Component.extend({
    layout,
    classNames: ['editor-holder'],
    emberCards: emberA([]),

    init() {
        this._super(...arguments);

        let mobiledoc = this.get('value') || BLANK_DOC;
        let userCards = this.get('cards') || [];

        if (typeof mobiledoc === 'string') {
            mobiledoc = JSON.parse(mobiledoc);
        }

        // if the doc is cached then the editor is loaded and we don't need to continue.
        if (this._cachedDoc && this._cachedDoc === mobiledoc) {
            return;
        }

        let createCard = createCardFactory.apply(this, {}); // need to pass the toolbar

        let options = {
            mobiledoc,
            // temp
            cards: createCard(editorCards.concat(userCards)),
            atoms: [{
                name: 'soft-return',
                type: 'dom',
                render() {
                    return document.createElement('br');
                }
            }],
            spellcheck: true,
            autofocus: this.get('shouldFocusEditor'),
            placeholder: 'Click here to start ...'
        };

        this.editor = new Mobiledoc.Editor(options);
    },

    willRender() {
        if (this._rendered) {
            return;
        }
        let {editor} = this;

        editor.willRender(() => {
            // console.log(Ember.run.currentRunLoop);
            // if (!Ember.run.currentRunLoop) {
            //     this._startedRunLoop = true;
            //     Ember.run.begin();
            // }
        });

        editor.didRender(() => {

            this.sendAction('loaded', editor);
                // Ember.run.end();

        });
        editor.postDidChange(()=> {
            run.join(() => {
                // store a cache of the local doc so that we don't need to reinitialise it.
                this._cachedDoc = editor.serialize(MOBILEDOC_VERSION);
                this.sendAction('onChange', this._cachedDoc);
                if (this._cachedDoc !== BLANK_DOC && !this._firstChange) {
                    this._firstChange = true;
                    this.sendAction('onFirstChange', this._cachedDoc);
                }
            });
        });
    },

    didRender() {
        if (this._rendered) {
            return;
        }
        let [editorDom] = this.$('.surface');
        this.domContainer = editorDom.parentNode.parentNode.parentNode.parentNode; // nasty nasty nasty.
        this.editor.render(editorDom);
        this._rendered = true;

        window.editor = this.editor;
        defaultCommands(this.editor); // initialise the custom text handlers for MD, etc.
        // shouldFocusEditor is only true when transitioning from new to edit, otherwise it's false or undefined.
        // therefore, if it's true it's after the first lot of content is entered and we expect the caret to be at the
        // end of the document.
        if (this.get('shouldFocusEditor')) {
            let range = document.createRange();
            range.selectNodeContents(this.editor.element);
            range.collapse(false);
            let sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
            this.editor._ensureFocus();
        }

        this.editor.cursorDidChange(() => this.cursorMoved());

    },

    // drag and drop images onto the editor
    drop(event) {
        if (event.dataTransfer.files.length) {
            event.preventDefault();
            for (let i = 0; i < event.dataTransfer.files.length; i++) {
                let file = [event.dataTransfer.files[i]];
                this.editor.insertCard('image-card', {pos: 'top', file});
            }
        }
    },

    // makes sure the cursor is on screen except when selection is happening in which case the browser mostly ensures it.
    // there is an issue with keyboard selection on some browsers though so the next step will be to record mouse and touch events.
    cursorMoved() {
        if (this.get('editor').range.isCollapsed) {
            let scrollBuffer = 33; // the extra buffer to scroll.
            let range = window.getSelection().getRangeAt(0); // get the actual range within the DOM.
            let position =  range.getBoundingClientRect();
            let windowHeight = window.innerHeight;

            if (position.bottom > windowHeight) {
                this.domContainer.scrollTop += position.bottom - windowHeight + scrollBuffer;
            } else if (position.top < 0) {
                this.domContainer.scrollTop += position.top - scrollBuffer;
            }
        }
    },

    willDestroy() {
        this.editor.destroy();
    }

});
<|endoftext|>


<commit_before>// Mozilla User Preferences
//dennyhalim.com
//http://kb.mozillazine.org/User.js_file
//http://kb.mozillazine.org/Locking_preferences
//https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences
pref("app.update.channel", "esr");
user_pref("browser.cache.compression_level", 1);
user_pref("browser.sessionhistory.max_entries", 10);
//user_pref("devtools.memory.enabled", false);
user_pref("browser.cache.memory.enable", false); //very usefull if you have < 4GB RAM
user_pref("network.cookie.cookieBehavior", 3);
user_pref("network.cookie.lifetimePolicy", 2);
user_pref("privacy.clearOnShutdown.cache", true);
user_pref("privacy.clearOnShutdown.offlineApps", true);
user_pref("privacy.sanitize.pending", "[{\"id\":\"shutdown\",\"itemsToClear\":[\"cache\",\"offlineApps\"],\"options\":{}}]");
//user_pref("privacy.sanitize.pending", "[{\"id\":\"shutdown\",\"itemsToClear\":[\"cache\",\"cookies\",\"offlineApps\",\"formdata\",\"sessions\",\"siteSettings\"],\"options\":{}}]");
user_pref("privacy.sanitize.sanitizeOnShutdown", true);
user_pref("privacy.donottrackheader.enabled", true);
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);
user_pref("dom.serviceWorkers.enabled", false);
user_pref("browser.urlbar.placeholderName", "DuckDuckGo");
user_pref("geo.enabled", false);
user_pref("geo.provider.ms-windows-location", false);
user_pref("geo.provider.use_corelocation", false);
user_pref("geo.provider.use_gpsd", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("network.trr.mode", 2);
user_pref("network.trr.uri", "https://doh.cleanbrowsing.org/doh/adult-filter/");  //https://cleanbrowsing.org/dnsoverhttps
user_pref("dom.netinfo.enabled", false);
user_pref("media.video_stats.enabled", false);
//user_pref("dom.maxHardwareConcurrency", 1); //faster on slow devices
user_pref("dom.battery.enabled", false);
user_pref("dom.telephony.enabled", false);
user_pref("beacon.enabled", false);
user_pref("device.sensors.enabled", false);
user_pref("dom.vibrator.enabled", false);
user_pref("dom.enable_resource_timing", false);
<commit_msg>Update firefox-user.js<commit_after>// Mozilla User Preferences
//dennyhalim.com
//http://kb.mozillazine.org/User.js_file
//http://kb.mozillazine.org/Locking_preferences
//https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/A_brief_guide_to_Mozilla_preferences
pref("app.update.channel", "esr");
user_pref("browser.cache.compression_level", 1);
user_pref("browser.cache.memory.enable", false); //very usefull if you have < 4GB RAM
user_pref("browser.sessionhistory.max_entries", 10);
user_pref("browser.urlbar.placeholderName", "DuckDuckGo");
user_pref("network.cookie.cookieBehavior", 3);
user_pref("network.cookie.lifetimePolicy", 2);
user_pref("network.trr.mode", 2);
user_pref("network.trr.uri", "https://doh.cleanbrowsing.org/doh/adult-filter/");  //https://cleanbrowsing.org/dnsoverhttps
user_pref("privacy.clearOnShutdown.cache", true);
user_pref("privacy.clearOnShutdown.offlineApps", true);
user_pref("privacy.sanitize.pending", "[{\"id\":\"shutdown\",\"itemsToClear\":[\"cache\",\"offlineApps\"],\"options\":{}}]");
//user_pref("privacy.sanitize.pending", "[{\"id\":\"shutdown\",\"itemsToClear\":[\"cache\",\"cookies\",\"offlineApps\",\"formdata\",\"sessions\",\"siteSettings\"],\"options\":{}}]");
user_pref("privacy.sanitize.sanitizeOnShutdown", true);
user_pref("privacy.donottrackheader.enabled", true);
user_pref("privacy.trackingprotection.enabled", true);
user_pref("privacy.trackingprotection.pbmode.enabled", true);
//user_pref("devtools.memory.enabled", false);
user_pref("dom.battery.enabled", false);
user_pref("dom.telephony.enabled", false);
user_pref("dom.netinfo.enabled", false);
user_pref("dom.webnotifications.enabled", false);
user_pref("dom.webnotifications.serviceworker.enabled", false);
user_pref("dom.serviceWorkers.enabled", false);
user_pref("dom.vibrator.enabled", false);
user_pref("dom.enable_resource_timing", false);
user_pref("geo.enabled", false);
user_pref("geo.provider.ms-windows-location", false);
user_pref("geo.provider.use_corelocation", false);
user_pref("geo.provider.use_gpsd", false);
user_pref("toolkit.telemetry.unified", false);
user_pref("toolkit.telemetry.enabled", false);
user_pref("toolkit.telemetry.archive.enabled", false);
user_pref("media.video_stats.enabled", false);
//user_pref("dom.maxHardwareConcurrency", 1); //faster on slow devices
user_pref("beacon.enabled", false);
user_pref("device.sensors.enabled", false);
<|endoftext|>


<commit_before>'use strict'

const fs = require('fs')
const hashData = require('./hash-data')
const loki = require('lokijs')
const every = require('lodash.every')
const flattenDeep = require('lodash/flattenDeep')
const uniq = require('lodash.uniq')

var DB = function (config) {
  try {
    const dbJson = fs.readFileSync(config.path, 'utf8')
    this.db = new loki(config.path)
    this.db.loadJSON(dbJson)
  } catch (e) {
    this.db = new loki(config.path, { autosave: true, autosaveInterval: 60000 })
  }
}

DB.prototype.cached = function (coll, hash) {
  return this.retrieve(coll, hash) !== null
}

DB.prototype.dump = function () {
  return this.db.serialize()
}

DB.prototype.getAPIsByConsumer = function (consumer) {
  const providers = this.getProvidersByConsumer(consumer)
  const nestedAPIs = providers.map(this.getAPIsByProvider.bind(this))

  return nestedAPIs.length === 0 ? [] : uniq(flattenDeep(nestedAPIs))
}

DB.prototype.getAPIsByConsumerAndProvider = function (consumer, provider) {
  const apis = this.retrieveCollection(provider)
    .filter(dbo => dbo.consumer === consumer)
    .map(dbo => dbo.api)

  return uniq(apis)
}

DB.prototype.getAPIsByProvider = function (provider) {
  return uniq(this.retrieveCollection(provider).map(dbo => dbo.api))
}

DB.prototype.getCollection = function (name) {
  const collection = this.db.getCollection(name)

  return collection === null ? this.db.addCollection(name) : collection
}

DB.prototype.getConsumers = function () {
  return uniq(this.retrieveCollection('contracts').map(dbo => dbo.consumer))
}

DB.prototype.getConsumersByProvider = function (provider) {
  return uniq(this.retrieveCollection(provider).map(dbo => dbo.consumer))
}

DB.prototype.getProviders = function () {
  const providers = this.getConsumers()
    .map(this.getProvidersByConsumer.bind(this))

  return uniq(flattenDeep(providers))
}

DB.prototype.getProvidersByConsumer = function (consumer) {
  const dbo = this.retrieveCollection('contracts')
    .filter(dbo => dbo.consumer === consumer)

  return dbo.length === 0 ? [] : uniq(dbo[0].providers)
}

DB.prototype.insert = function (coll, hash, contracts) {
  return this.insertForConsumer(coll, hash, contracts) && this.insertForProviders(contracts)
}

DB.prototype.insertForConsumer = function (coll, hash, contracts) {
  const collection = this.getCollection(coll)
  const consumer = Object.keys(contracts)[0]
  const providers = Object.keys(contracts[consumer])
  this.removeDuplicateContracts(collection, { 'hash' : { '$eq' : hash } })

  const doc = {
    hash: hash,
    consumer: consumer,
    providers: providers,
    contracts: contracts
  }

  return collection.insertOne(doc) !== undefined
}

DB.prototype.insertForProviders = function (contracts) {
  const consumer = Object.keys(contracts)[0]

  return every(contracts[consumer], (provider, providerName) => {
    return every(provider, (api, apiName) => {
      return every(api, insertProviderContract.bind(this, consumer, providerName, apiName))
    })
  })

  function insertProviderContract (consumer, provider, api, scenario, scenarioName) {
    const name = `${provider}/${api}/${scenarioName}`
    const contract = buildContract(consumer, provider, api, scenarioName, scenario)
    const hash = hashData(JSON.stringify(contract))

    if (!this.cached(provider, hash)) {
      const collection = this.getCollection(provider)
      this.removeDuplicateContracts(collection, { '$and' : [{ 'name' : { '$eq' : name }, 'consumer' : { '$eq' : consumer } }]})

      const doc = {
        hash: hash,
        name: name,
        consumer: consumer,
        provider: provider,
        api: api,
        contract: contract
      }

      return collection.insertOne(doc) !== undefined
    }

    return true
  }
}

DB.prototype.removeDuplicateContracts = function (collection, by_filter) {
  const query = by_filter
  collection.findAndRemove(query)
}

DB.prototype.retrieve = function (coll, hash) {
  const collection = this.getCollection(coll)

  return collection.findOne({ 'hash' : { '$eq' : hash } })
}

DB.prototype.retrieveCollection = function (coll) {
  const collection = this.getCollection(coll)

  /* FILTH */
  return collection.find({ 'hash' : { '$ne' : null } })
}

module.exports = DB

const buildContract = (consumer, provider, api, name, scenario) => {
  const contract  = {}
  contract[consumer] = {}
  contract[consumer][provider] = {}
  contract[consumer][provider][api] = {}
  contract[consumer][provider][api][name] = scenario

  return contract
}
<commit_msg>Refactored DB to avoid duplicate contracts bug<commit_after>'use strict'

const fs = require('fs')
const hashData = require('./hash-data')
const loki = require('lokijs')
const every = require('lodash.every')
const flattenDeep = require('lodash/flattenDeep')
const uniq = require('lodash.uniq')

var DB = function (config) {
  try {
    const dbJson = fs.readFileSync(config.path, 'utf8')
    this.db = new loki(config.path)
    this.db.loadJSON(dbJson)
  } catch (e) {
    this.db = new loki(config.path, { autosave: true, autosaveInterval: 60000 })
  }
}

DB.prototype.cached = function (coll, hash) {
  return this.retrieve(coll, hash) !== null
}

DB.prototype.dump = function () {
  return this.db.serialize()
}

DB.prototype.getAPIsByConsumer = function (consumer) {
  const providers = this.getProvidersByConsumer(consumer)
  const nestedAPIs = providers.map(this.getAPIsByProvider.bind(this))

  return nestedAPIs.length === 0 ? [] : uniq(flattenDeep(nestedAPIs))
}

DB.prototype.getAPIsByConsumerAndProvider = function (consumer, provider) {
  const apis = this.retrieveCollection(provider)
    .filter(dbo => dbo.consumer === consumer)
    .map(dbo => dbo.api)

  return uniq(apis)
}

DB.prototype.getAPIsByProvider = function (provider) {
  return uniq(this.retrieveCollection(provider).map(dbo => dbo.api))
}

DB.prototype.getCollection = function (name) {
  const collection = this.db.getCollection(name)

  return collection === null ? this.db.addCollection(name) : collection
}

DB.prototype.getConsumers = function () {
  return uniq(this.retrieveCollection('contracts').map(dbo => dbo.consumer))
}

DB.prototype.getConsumersByProvider = function (provider) {
  return uniq(this.retrieveCollection(provider).map(dbo => dbo.consumer))
}

DB.prototype.getProviders = function () {
  const providers = this.getConsumers()
    .map(this.getProvidersByConsumer.bind(this))

  return uniq(flattenDeep(providers))
}

DB.prototype.getProvidersByConsumer = function (consumer) {
  const dbo = this.retrieveCollection('contracts')
    .filter(dbo => dbo.consumer === consumer)

  return dbo.length === 0 ? [] : uniq(dbo[0].providers)
}

DB.prototype.insert = function (coll, hash, contracts) {
  return this.insertForConsumer(coll, hash, contracts) && this.insertForProviders(contracts)
}

DB.prototype.insertForConsumer = function (coll, hash, contracts) {
  const collection = this.getCollection(coll)
  const consumer = Object.keys(contracts)[0]
  const providers = Object.keys(contracts[consumer])

  providers.forEach(removeByConsumerForProvider.bind(this, consumer))
  this.removeDuplicateContracts(coll, { 'hash' : { '$eq' : hash } })

  const doc = {
    hash: hash,
    consumer: consumer,
    providers: providers,
    contracts: contracts
  }

  return collection.insertOne(doc) !== undefined

  function removeByConsumerForProvider (consumer, provider) {
    console.error(consumer, provider)

    this.removeDuplicateContracts(provider, { 'consumer' : { '$eq' : consumer } })
  }
}

DB.prototype.insertForProviders = function (contracts) {
  const consumer = Object.keys(contracts)[0]

  return every(contracts[consumer], (provider, providerName) => {
    return every(provider, (api, apiName) => {
      return every(api, insertProviderContract.bind(this, consumer, providerName, apiName))
    })
  })

  function insertProviderContract (consumer, provider, api, scenario, scenarioName) {
    const name = `${provider}/${api}/${scenarioName}`
    const contract = buildContract(consumer, provider, api, scenarioName, scenario)
    const hash = hashData(JSON.stringify(contract))

    if (!this.cached(provider, hash)) {
      const collection = this.getCollection(provider)
      this.removeDuplicateContracts(provider, { '$and' : [{ 'name' : { '$eq' : name }, 'consumer' : { '$eq' : consumer } }]})

      const doc = {
        hash: hash,
        name: name,
        consumer: consumer,
        provider: provider,
        api: api,
        contract: contract
      }

      return collection.insertOne(doc) !== undefined
    }

    return true
  }
}

DB.prototype.removeDuplicateContracts = function (collectionName, by_filter) {
  const collection = this.getCollection(collectionName)
  collection.findAndRemove(by_filter)
}

DB.prototype.retrieve = function (coll, hash) {
  const collection = this.getCollection(coll)

  return collection.findOne({ 'hash' : { '$eq' : hash } })
}

DB.prototype.retrieveCollection = function (coll) {
  const collection = this.getCollection(coll)

  /* FILTH */
  return collection.find({ 'hash' : { '$ne' : null } })
}

module.exports = DB

const buildContract = (consumer, provider, api, name, scenario) => {
  const contract  = {}
  contract[consumer] = {}
  contract[consumer][provider] = {}
  contract[consumer][provider][api] = {}
  contract[consumer][provider][api][name] = scenario

  return contract
}
<|endoftext|>


<commit_before>const _ = require('lodash/fp')
const pify = require('pify')
const fs = pify(require('fs'))
const cp = require('child_process')
const uuid = require('uuid')
const path = require('path')

const BN = require('./bn')

let dbName

module.exports = { save, prune, clean }

function list (dbRoot) {
  return fs.mkdir(dbRoot)
    .catch(() => {})
    .then(() => fs.readdir(dbRoot))
}

function rotate (dbRoot) {
  dbName = 'tx-db-' + uuid.v4() + '.dat'
  return fs.mkdir(dbRoot)
    .catch(() => {})
    .then(() => fs.writeFile(path.resolve(dbRoot, dbName), ''))
}

function save (dbRoot, tx) {
  return fs.appendFile(path.resolve(dbRoot, dbName), JSON.stringify(tx) + '\n')
}

function nuke (dbPath) {
  return fs.unlink(dbPath)
}

function safeJsonParse (txt) {
  try {
    return JSON.parse(txt)
  } catch (_) {

  }
}

function pruneFile (dbRoot, cleanP, _dbName) {
  const dbPath = path.resolve(dbRoot, _dbName)

  return load(dbPath)
    .then(txs => cleanP(txs))
    .then(r => {
      return nuke(dbPath)
        .catch(err => console.log(`Couldn't nuke ${dbPath}: ${err}`))
        .then(() => r)
    })
}

function prune (dbRoot, cleanP) {
  return list(dbRoot)
    .then(files => {
      console.log(`Processing ${files.length} db files`)

      return rotate(dbRoot)
        .then(() => {
          const promises = _.map(file => pruneFile(dbRoot, cleanP, file), files)
          return Promise.all(promises)
            .then(results => {
              const sum = _.sum(results)
              if (sum === 0) return console.log('No pending txs to process.')
              console.log(`Successfully processed ${_.sum(results)} pending txs.`)
            })
            .catch(err => console.log(`Error processing pending txs: ${err.stack}`))
        })
    })
}

function massage (tx) {
  if (!tx) return

  const massagedFields = {
    fiat: _.isNil(tx.fiat) ? undefined : BN(tx.fiat),
    cryptoAtoms: _.isNil(tx.cryptoAtoms) ? undefined : BN(tx.cryptoAtoms)
  }

  return _.assign(tx, massagedFields)
}

function load (dbPath) {
  const txTable = {}

  return fs.readFile(dbPath, {encoding: 'utf8'})
    .then(f => {
      const recs = f.split('\n')
      const parse = _.flow([safeJsonParse, massage])
      const txs = _.remove(_.isEmpty, _.map(parse, recs))
      _.forEach(tx => { txTable[tx.id] = tx }, txs)
      return _.sortBy(tx => tx.deviceTime, _.values(txTable))
    })
}

function clean (dbRoot) {
  if (_.isNil(dbRoot)) return
  const files = fs.readdirSync(dbRoot)
  if (files.length) {
    cp.exec('rm ' + dbRoot + '/*', {}, err => {
      if (err) console.log(err)
    })
  }
}
<commit_msg>fix: read nonexistent tx-db folder<commit_after>const _ = require('lodash/fp')
const pify = require('pify')
const fs = pify(require('fs'))
const cp = require('child_process')
const uuid = require('uuid')
const path = require('path')

const BN = require('./bn')

let dbName

module.exports = { save, prune, clean }

function list (dbRoot) {
  return fs.mkdir(dbRoot)
    .catch(() => {})
    .then(() => fs.readdir(dbRoot))
}

function rotate (dbRoot) {
  dbName = 'tx-db-' + uuid.v4() + '.dat'
  return fs.mkdir(dbRoot)
    .catch(() => {})
    .then(() => fs.writeFile(path.resolve(dbRoot, dbName), ''))
}

function save (dbRoot, tx) {
  return fs.appendFile(path.resolve(dbRoot, dbName), JSON.stringify(tx) + '\n')
}

function nuke (dbPath) {
  return fs.unlink(dbPath)
}

function safeJsonParse (txt) {
  try {
    return JSON.parse(txt)
  } catch (_) {

  }
}

function pruneFile (dbRoot, cleanP, _dbName) {
  const dbPath = path.resolve(dbRoot, _dbName)

  return load(dbPath)
    .then(txs => cleanP(txs))
    .then(r => {
      return nuke(dbPath)
        .catch(err => console.log(`Couldn't nuke ${dbPath}: ${err}`))
        .then(() => r)
    })
}

function prune (dbRoot, cleanP) {
  return list(dbRoot)
    .then(files => {
      console.log(`Processing ${files.length} db files`)

      return rotate(dbRoot)
        .then(() => {
          const promises = _.map(file => pruneFile(dbRoot, cleanP, file), files)
          return Promise.all(promises)
            .then(results => {
              const sum = _.sum(results)
              if (sum === 0) return console.log('No pending txs to process.')
              console.log(`Successfully processed ${_.sum(results)} pending txs.`)
            })
            .catch(err => console.log(`Error processing pending txs: ${err.stack}`))
        })
    })
}

function massage (tx) {
  if (!tx) return

  const massagedFields = {
    fiat: _.isNil(tx.fiat) ? undefined : BN(tx.fiat),
    cryptoAtoms: _.isNil(tx.cryptoAtoms) ? undefined : BN(tx.cryptoAtoms)
  }

  return _.assign(tx, massagedFields)
}

function load (dbPath) {
  const txTable = {}

  return fs.readFile(dbPath, {encoding: 'utf8'})
    .then(f => {
      const recs = f.split('\n')
      const parse = _.flow([safeJsonParse, massage])
      const txs = _.remove(_.isEmpty, _.map(parse, recs))
      _.forEach(tx => { txTable[tx.id] = tx }, txs)
      return _.sortBy(tx => tx.deviceTime, _.values(txTable))
    })
}

function clean (dbRoot) {
  if (_.isNil(dbRoot)) return
  if (!fs.existsSync(dbRoot)) return
  const files = fs.readdirSync(dbRoot)
  if (files.length) {
    cp.exec('rm ' + dbRoot + '/*', {}, err => {
      if (err) console.log(err)
    })
  }
}
<|endoftext|>


<commit_before>module.exports = new (function(){
  var db_filename, db
  var that = this

  var tables = {'tx': [["id", "integer PRIMARY KEY"],
                       ["account_id","integer"],
                       ["address","text"],
                       ["category","text"],
                       ["amount","text"],
                       ["confirmations","integer"],
                       ["txid","text UNIQUE"],
                       ["time","integer"],
                       ["time_received","integer"],
                       ],
                 'singletons': [["key","text PRIMARY KEY"],
                                ["value","text"]
                               ],
                 'accounts': [["id", "integer PRIMARY KEY"],
                              ["name", "text UNIQUE"],
                              ["username", "text UNIQUE"],
                              ["receiving_address", "text UNIQUE"]
                             ]
                }
  this.setup = function(db_engine, filename){
    db_filename = filename
    db = new db_engine.Database(db_filename)
    db.serialize()
    this.create_tables()
    this.connected = true
  }

  this.create_tables = function(){
    var that=this
    db.serialize(function(){
      for(var table in tables){
        that.table_create(table, tables[table])
      }
    })
  }

  this.table_create = function(name, columns, cb){
    var schema = columns.map(function(col_def){return col_def.join(' ')}).join(', ')
    var sql = 'create table if not exists '+name+' ('+schema+')'
    db.run(sql, function(err){
                  if(err){console.error('table create failed '+err)}
                  if(cb){cb()}
                })
  }

  this.transaction = function(cb){
    db.serialize(function(){
      db.run("begin")
      cb()
      db.run("commit")
    })
  }

  this.singleton_get = function(key, cb){
    db.get("SELECT value FROM singletons where key = ?", key,
          function(err, row) {
            if(err) {
              console.error(err)
            }
            if(row) {
              cb(row.value)
            } else {
              cb()
            }
        });
  }

  this.singleton_set = function(key, value, cb){
    db.run("INSERT OR REPLACE INTO singletons (key, value) VALUES (?,?)",
           [key, value], cb);
  }

  this.load_block_hash = function(cb){
    this.singleton_get('block_hash', cb)
  }

  this.save_block_hash = function(value, cb){
    this.singleton_set('block_hash', value, cb)
  }

  this.insert = function(table_name, values, cb){
    var column_names = tables[table_name].map(function(col){return col[0]})
    column_names.shift() // skip 'id'
    var column_holders = column_names.map(function(name){return "$"+name})
    var sql = "INSERT INTO "+table_name+" ("+column_names.join(',')+") "+
              "VALUES ("+column_holders.join(',')+")"
    db.run(sql, values, cb)
  }

  this.add_bitcoin_tx = function(tx){
    this.account(tx.account, function(account){
      tx.account_id = account.id
      this.insert('tx', tx_values(tx))
    })
  }

  this.tx_values = function(tx){
    return { $account_id   : tx.account_id,
             $address      : tx.address,
             $category     : tx.category,
             $amount       : tx.amount,
             $confirmations: tx.confirmations,
             $txid         : tx.txid,
             $time         : tx.time,
             $time_received: tx.timereceived}
  }

  this.account = function(account, cb){
    var sql = "SELECT * FROM accounts where name = $name"
    var qdat = {$name: account}
    console.log(sql+" "+JSON.stringify(qdat))
    db.get(sql, qdat, function(err, row){
      if(err){
        cb()
      }else{
        cb(row)
      }
    })
  }

  this.add_account = function(account, username, address, cb){
    var record = {$name: account,
                  $username: username,
                  $receiving_address: address}
    that.insert('accounts', record, function(err){
      if(err){
        cb()
      } else {
        cb(this)
      }
    })
  }

  this.transactions = function(account, cb){
    that.account(account,function(row){
      if(row){
        var sql = "SELECT * from tx where account_id = $account_id"
        var qdat = {$account_id: row.id}
        console.log(sql+" "+JSON.stringify(qdat))
        //db.each segfaults
        db.all(sql, qdat, cb)
      }
    })
  }
})()
<commit_msg>this/that fix<commit_after>module.exports = new (function(){
  var db_filename, db
  var that = this

  var tables = {'tx': [["id", "integer PRIMARY KEY"],
                       ["account_id","integer"],
                       ["address","text"],
                       ["category","text"],
                       ["amount","text"],
                       ["confirmations","integer"],
                       ["txid","text UNIQUE"],
                       ["time","integer"],
                       ["time_received","integer"],
                       ],
                 'singletons': [["key","text PRIMARY KEY"],
                                ["value","text"]
                               ],
                 'accounts': [["id", "integer PRIMARY KEY"],
                              ["name", "text UNIQUE"],
                              ["username", "text UNIQUE"],
                              ["receiving_address", "text UNIQUE"]
                             ]
                }
  this.setup = function(db_engine, filename){
    db_filename = filename
    db = new db_engine.Database(db_filename)
    db.serialize()
    this.create_tables()
    this.connected = true
  }

  this.create_tables = function(){
    var that=this
    db.serialize(function(){
      for(var table in tables){
        that.table_create(table, tables[table])
      }
    })
  }

  this.table_create = function(name, columns, cb){
    var schema = columns.map(function(col_def){return col_def.join(' ')}).join(', ')
    var sql = 'create table if not exists '+name+' ('+schema+')'
    db.run(sql, function(err){
                  if(err){console.error('table create failed '+err)}
                  if(cb){cb()}
                })
  }

  this.transaction = function(cb){
    db.serialize(function(){
      db.run("begin")
      cb()
      db.run("commit")
    })
  }

  this.singleton_get = function(key, cb){
    db.get("SELECT value FROM singletons where key = ?", key,
          function(err, row) {
            if(err) {
              console.error(err)
            }
            if(row) {
              cb(row.value)
            } else {
              cb()
            }
        });
  }

  this.singleton_set = function(key, value, cb){
    db.run("INSERT OR REPLACE INTO singletons (key, value) VALUES (?,?)",
           [key, value], cb);
  }

  this.load_block_hash = function(cb){
    this.singleton_get('block_hash', cb)
  }

  this.save_block_hash = function(value, cb){
    this.singleton_set('block_hash', value, cb)
  }

  this.insert = function(table_name, values, cb){
    var column_names = tables[table_name].map(function(col){return col[0]})
    column_names.shift() // skip 'id'
    var column_holders = column_names.map(function(name){return "$"+name})
    var sql = "INSERT INTO "+table_name+" ("+column_names.join(',')+") "+
              "VALUES ("+column_holders.join(',')+")"
    db.run(sql, values, cb)
  }

  this.add_bitcoin_tx = function(tx){
    this.account(tx.account, function(account){
      tx.account_id = account.id
      that.insert('tx', that.tx_values(tx))
    })
  }

  this.tx_values = function(tx){
    return { $account_id   : tx.account_id,
             $address      : tx.address,
             $category     : tx.category,
             $amount       : tx.amount,
             $confirmations: tx.confirmations,
             $txid         : tx.txid,
             $time         : tx.time,
             $time_received: tx.timereceived}
  }

  this.account = function(account, cb){
    var sql = "SELECT * FROM accounts where name = $name"
    var qdat = {$name: account}
    console.log(sql+" "+JSON.stringify(qdat))
    db.get(sql, qdat, function(err, row){
      if(err){
        cb()
      }else{
        cb(row)
      }
    })
  }

  this.add_account = function(account, username, address, cb){
    var record = {$name: account,
                  $username: username,
                  $receiving_address: address}
    that.insert('accounts', record, function(err){
      if(err){
        cb()
      } else {
        cb(this)
      }
    })
  }

  this.transactions = function(account, cb){
    that.account(account,function(row){
      if(row){
        var sql = "SELECT * from tx where account_id = $account_id"
        var qdat = {$account_id: row.id}
        console.log(sql+" "+JSON.stringify(qdat))
        //db.each segfaults
        db.all(sql, qdat, cb)
      }
    })
  }
})()
<|endoftext|>