<commit_before>/** @babel */

import gitCmd from "../../lib/git-cmd";
import {getFilePath, removeGitRoot, createGitRoot, files} from "../mocks";
import {promisify} from "promisificator";
import fs from "fs";

describe("git.pull", function () {

	beforeEach(function () {
		spyOn(gitCmd, "cmd").and.returnValues(
			Promise.resolve("## master"),
			Promise.resolve(),
		);

		this.gitRoot = "root";
	});

	it("should send ['pull', '--quiet'] to cmd", async function () {
		await gitCmd.pull(this.gitRoot);

		expect(gitCmd.cmd.calls.mostRecent().args[1].filter(i => !!i)).toEqual(["pull", "--quiet"]);
	});

	it("should send --rebase to cmd", async function () {
		await gitCmd.pull(this.gitRoot, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--rebase");
	});

	it("should send --force to cmd", async function () {
		await gitCmd.pull(this.gitRoot, false, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--force");
	});

	it("should send --verbose to cmd", async function () {
		await gitCmd.pull(this.gitRoot, false, false, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--verbose");
	});

	describe("integration tests", function () {

		beforeEach(async function () {
			gitCmd.cmd.and.callThrough();
			await atom.packages.activatePackage("git-menu");
			this.gitRoot = await createGitRoot(true, true);
			this.originRoot = await createGitRoot(true, true);
			await gitCmd.cmd(this.gitRoot, ["remote", "add", "origin", this.originRoot]);
		});

		afterEach(async function () {
			await removeGitRoot(this.gitRoot);
		});

		it("should merge", async function () {
			const newBranch = "new-branch";
			await gitCmd.cmd(this.gitRoot, ["checkout", "-b", newBranch]);
			await gitCmd.cmd(this.gitRoot, ["push", "--set-upstream", "origin", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.gitRoot, files.t1), "test1");
			await gitCmd.cmd(this.gitRoot, ["add", "."]);
			await gitCmd.cmd(this.gitRoot, ["commit", "--message=test1"]);

			await gitCmd.cmd(this.originRoot, ["checkout", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.originRoot, files.t2), "test2");
			await gitCmd.cmd(this.originRoot, ["add", "."]);
			await gitCmd.cmd(this.originRoot, ["commit", "--message=test2"]);

			await gitCmd.pull(this.gitRoot);

			const content = await promisify(fs.readFile)(getFilePath(this.gitRoot, files.t2), {encoding: "utf8"});
			expect(content).toBe("test2");

			const log = await gitCmd.cmd(this.gitRoot, ["log", "--format=%s"]);
			expect(log.trim()).toEqual(jasmine.stringMatching(/^Merge branch/));
			expect(log.trim()).toEqual(jasmine.stringMatching("test1\ntest2\ninit commit"));
		});

		it("should rebase", async function () {
			const newBranch = "new-branch";
			await gitCmd.cmd(this.gitRoot, ["checkout", "-b", newBranch]);
			await gitCmd.cmd(this.gitRoot, ["push", "--set-upstream", "origin", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.gitRoot, files.t1), "test1");
			await gitCmd.cmd(this.gitRoot, ["add", "."]);
			await gitCmd.cmd(this.gitRoot, ["commit", "--message=test1"]);

			await gitCmd.cmd(this.originRoot, ["checkout", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.originRoot, files.t2), "test2");
			await gitCmd.cmd(this.originRoot, ["add", "."]);
			await gitCmd.cmd(this.originRoot, ["commit", "--message=test2"]);

			await gitCmd.pull(this.gitRoot, true);

			const content = await promisify(fs.readFile)(getFilePath(this.gitRoot, files.t2), {encoding: "utf8"});
			expect(content).toBe("test2");

			const log = await gitCmd.cmd(this.gitRoot, ["log", "--format=%s"]);
			expect(log.trim()).toBe("test1\ntest2\ninit commit");
		});

	});

});
<commit_msg>test: fix pull test<commit_after>/** @babel */

import gitCmd from "../../lib/git-cmd";
import {getFilePath, removeGitRoot, createGitRoot, files} from "../mocks";
import {promisify} from "promisificator";
import fs from "fs";

describe("git.pull", function () {

	beforeEach(function () {
		spyOn(gitCmd, "cmd").and.returnValues(
			Promise.resolve("## master"),
			Promise.resolve(),
		);

		this.gitRoot = "root";
	});

	it("should send ['pull', '--quiet'] to cmd", async function () {
		await gitCmd.pull(this.gitRoot);

		expect(gitCmd.cmd.calls.mostRecent().args[1].filter(i => !!i)).toEqual(["pull", "--quiet"]);
	});

	it("should send --rebase to cmd", async function () {
		await gitCmd.pull(this.gitRoot, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--rebase");
	});

	it("should send --force to cmd", async function () {
		await gitCmd.pull(this.gitRoot, false, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--force");
	});

	it("should send --verbose to cmd", async function () {
		await gitCmd.pull(this.gitRoot, false, false, true);

		expect(gitCmd.cmd.calls.mostRecent().args[1]).toContain("--verbose");
	});

	describe("integration tests", function () {

		beforeEach(async function () {
			gitCmd.cmd.and.callThrough();
			await atom.packages.activatePackage("git-menu");
			this.gitRoot = await createGitRoot(true, true);
			this.originRoot = await createGitRoot(true, true);
			await gitCmd.cmd(this.gitRoot, ["remote", "add", "origin", this.originRoot]);
		});

		afterEach(async function () {
			await removeGitRoot(this.gitRoot);
		});

		it("should merge", async function () {
			const newBranch = "new-branch";
			await gitCmd.cmd(this.gitRoot, ["checkout", "-b", newBranch]);
			await gitCmd.cmd(this.gitRoot, ["push", "--set-upstream", "origin", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.gitRoot, files.t1), "test1");
			await gitCmd.cmd(this.gitRoot, ["add", "."]);
			await gitCmd.cmd(this.gitRoot, ["commit", "--message=test1"]);

			await gitCmd.cmd(this.originRoot, ["checkout", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.originRoot, files.t2), "test2");
			await gitCmd.cmd(this.originRoot, ["add", "."]);
			await gitCmd.cmd(this.originRoot, ["commit", "--message=test2"]);

			await gitCmd.pull(this.gitRoot);

			const content = await promisify(fs.readFile)(getFilePath(this.gitRoot, files.t2), {encoding: "utf8"});
			expect(content).toBe("test2");

			const log = await gitCmd.cmd(this.gitRoot, ["log", "--format=%s"]);
			expect(log.trim()).toEqual(jasmine.stringMatching(/^Merge branch/));
		});

		it("should rebase", async function () {
			const newBranch = "new-branch";
			await gitCmd.cmd(this.gitRoot, ["checkout", "-b", newBranch]);
			await gitCmd.cmd(this.gitRoot, ["push", "--set-upstream", "origin", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.gitRoot, files.t1), "test1");
			await gitCmd.cmd(this.gitRoot, ["add", "."]);
			await gitCmd.cmd(this.gitRoot, ["commit", "--message=test1"]);

			await gitCmd.cmd(this.originRoot, ["checkout", newBranch]);
			await promisify(fs.writeFile)(getFilePath(this.originRoot, files.t2), "test2");
			await gitCmd.cmd(this.originRoot, ["add", "."]);
			await gitCmd.cmd(this.originRoot, ["commit", "--message=test2"]);

			await gitCmd.pull(this.gitRoot, true);

			const content = await promisify(fs.readFile)(getFilePath(this.gitRoot, files.t2), {encoding: "utf8"});
			expect(content).toBe("test2");

			const log = await gitCmd.cmd(this.gitRoot, ["log", "--format=%s"]);
			expect(log.trim()).toBe("test1\ntest2\ninit commit");
		});

	});

});
<|endoftext|>


<commit_before>/** @jsx h */
import { h, Component } from 'preact';
import './style.scss';

import Room from '../../components/Room';
import Container from '../../components/Container';
import Error from '../../components/Error';
import Align from '../../components/Align';
import Close from '../../components/Close';
import Mute from '../../components/Mute';
import EnterVR from '../../components/EnterVR';
import InboxCounter from '../../components/InboxCounter';
import cms from '../../../../utils/firebase/cms';
import router from '../../../../router';

export default class Inbox extends Component {
  constructor() {
    super();
    this.state = {
      starred: false,
    };

    this.toggleStarred = this.toggleStarred.bind(this);
  }

  async asyncMount() {
    const unread = await cms.getUnmoderatedRecordings();
    if (!this.mounted) return;
    if ((!this.props.recordingId || !this.props.roomId) && unread.length) {
      router.navigate(`/inbox/2/${unread[0].id}`);
      return;
    }

    this.setState({
      unreadCount: unread.length,
    });
  }

  componentDidMount() {
    this.mounted = true;
    this.asyncMount();
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  toggleStarred() {
    this.setState({
      starred: !this.state.starred,
    });
  }

  render({ roomId, recordingId, goHome }, { starred, unreadCount }) {
    return (
      <Container>
        <Align type="top-left row">
          <InboxCounter unreadCount={unreadCount} />
          <EnterVR />
          <Mute />
        </Align>
        <Align type="bottom-right">

            { starred ? 'Unstar' : 'Star' }

          { starred &&
            <input
              autoFocus
              placeHolder="Performance Title"
              type="text"
              className="inbox-title-input"
              id="performanceTitle"
            />
          }

Next →

        </Align>
        <Align type="top-right">
          <Close
            onClick={goHome}
          />
        </Align>
        {
          roomId === undefined
            ? (
              <Align type="center">
                <Error>Loading inbox</Error>
              </Align>
            )
            : <Room roomIndex={roomId} recordingId={recordingId} />
        }
      </Container>
    );
  }
}
<commit_msg>check null<commit_after>/** @jsx h */
import { h, Component } from 'preact';
import './style.scss';

import Room from '../../components/Room';
import Container from '../../components/Container';
import Error from '../../components/Error';
import Align from '../../components/Align';
import Close from '../../components/Close';
import Mute from '../../components/Mute';
import EnterVR from '../../components/EnterVR';
import InboxCounter from '../../components/InboxCounter';
import cms from '../../../../utils/firebase/cms';
import router from '../../../../router';

export default class Inbox extends Component {
  constructor() {
    super();
    this.state = {
      starred: false,
    };

    this.toggleStarred = this.toggleStarred.bind(this);
  }

  async asyncMount() {
    const unread = await cms.getUnmoderatedRecordings();
    if (!this.mounted) return;
    if ((!this.props.recordingId || !this.props.roomId) && unread.length) {
      router.navigate(`/inbox/2/${unread[0].id}`);
      return;
    }

    this.setState({
      unreadCount: unread.length,
    });
  }

  componentDidMount() {
    this.mounted = true;
    this.asyncMount();
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  toggleStarred() {
    this.setState({
      starred: !this.state.starred,
    });
  }

  render({ roomId, recordingId, goHome }, { starred, unreadCount }) {
    return (
      <Container>
        <Align type="top-left row">
          <InboxCounter unreadCount={unreadCount} />
          <EnterVR />
          <Mute />
        </Align>
        <Align type="bottom-right">

            { starred ? 'Unstar' : 'Star' }

          { starred &&
            <input
              autoFocus
              placeHolder="Performance Title"
              type="text"
              className="inbox-title-input"
              id="performanceTitle"
            />
          }

Next →

        </Align>
        <Align type="top-right">
          <Close
            onClick={goHome}
          />
        </Align>
        {
          roomId == null
            ? (
              <Align type="center">
                <Error>Loading inbox</Error>
              </Align>
            )
            : <Room roomIndex={roomId} recordingId={recordingId} />
        }
      </Container>
    );
  }
}
<|endoftext|>


<commit_before>const path = require('path')
const { MarkdownRenderer } = require('../src/renderer')

describe ('Renderer', () => {
  var renderer
  var callback

  beforeEach(() => {
    callback = jasmine.createSpy('callback')
    renderer = new MarkdownRenderer(callback)
  })

  it('converts a markdown file', (done) => {
    const markdownFilePath = path.join(__dirname, 'md', 'header.md')

    renderer.loadFile(markdownFilePath).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hello

'
      )
      done()
    }, done.fail)
  })

  it('converts a markdown string', (done) => {
    var markdown = '## Hi'

    renderer.load(markdown).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hi

'
      )
      done()
    }, done.fail)
  })

  it('preserves new lines in code', (done) => {
    const markdownFilePath = path.join(__dirname, 'md', 'code.md')

    renderer.loadFile(markdownFilePath).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

var x = 3\nvar y = null\n

'
      )
      done()
    }, done.fail)
  })
})
<commit_msg>Change Jasmine import syntax to satisfy Travis<commit_after>const path = require('path')
const MarkdownRenderer = require('../src/renderer').MarkdownRenderer

describe ('Renderer', () => {
  var renderer
  var callback

  beforeEach(() => {
    callback = jasmine.createSpy('callback')
    renderer = new MarkdownRenderer(callback)
  })

  it('converts a markdown file', (done) => {
    const markdownFilePath = path.join(__dirname, 'md', 'header.md')

    renderer.loadFile(markdownFilePath).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hello

'
      )
      done()
    }, done.fail)
  })

  it('converts a markdown string', (done) => {
    var markdown = '## Hi'

    renderer.load(markdown).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

Hi

'
      )
      done()
    }, done.fail)
  })

  it('preserves new lines in code', (done) => {
    const markdownFilePath = path.join(__dirname, 'md', 'code.md')

    renderer.loadFile(markdownFilePath).then(() => {
      expect(callback).toHaveBeenCalledWith(
        '

var x = 3\nvar y = null\n

'
      )
      done()
    }, done.fail)
  })
})
<|endoftext|>


<commit_before>/**
 * @fileoverview Rule to disallow anonymous default exports.
 * @author Duncan Beevers
 */

import docsUrl from '../docsUrl'

const defs = {
  ArrayExpression: {
    option: 'allowArray',
    description: 'If `false`, will report default export of an array',
    message: 'Assign array to a variable before exporting as module default',
  },
  ArrowFunctionExpression: {
    option: 'allowArrowFunction',
    description: 'If `false`, will report default export of an arrow function',
    message: 'Assign arrow function to a variable before exporting as module default',
  },
  CallExpression: {
    option: 'allowCallExpression',
    description: 'If `false`, will report default export of a function call',
    message: 'Assign call result to a variable before exporting as module default',
    default: true,
  },
  ClassDeclaration: {
    option: 'allowAnonymousClass',
    description: 'If `false`, will report default export of an anonymous class',
    message: 'Unexpected default export of anonymous class',
    forbid: (node) => !node.declaration.id,
  },
  FunctionDeclaration: {
    option: 'allowAnonymousFunction',
    description: 'If `false`, will report default export of an anonymous function',
    message: 'Unexpected default export of anonymous function',
    forbid: (node) => !node.declaration.id,
  },
  Literal: {
    option: 'allowLiteral',
    description: 'If `false`, will report default export of a literal',
    message: 'Assign literal to a variable before exporting as module default',
  },
  ObjectExpression: {
    option: 'allowObject',
    description: 'If `false`, will report default export of an object expression',
    message: 'Assign object to a variable before exporting as module default',
  },
  TemplateLiteral: {
    option: 'allowLiteral',
    description: 'If `false`, will report default export of a literal',
    message: 'Assign literal to a variable before exporting as module default',
  },
}

const schemaProperties = Object.keys(defs)
  .map((key) => defs[key])
  .reduce((acc, def) => {
    acc[def.option] = {
      description: def.description,
      type: 'boolean',
    }

    return acc
  }, {})

const defaults = Object.keys(defs)
  .map((key) => defs[key])
  .reduce((acc, def) => {
    acc[def.option] = def.hasOwnProperty('default') ? def.default : false
    return acc
  }, {})

module.exports = {
  meta: {
    docs: {
      url: docsUrl('no-anonymous-default-export'),
    },

    schema: [
      {
        type: 'object',
        properties: schemaProperties,
        'additionalProperties': false,
      },
    ],
  },

  create: function (context) {
    const options = Object.assign({}, defaults, context.options[0])

    return {
      'ExportDefaultDeclaration': (node) => {
        const def = defs[node.declaration.type]

        // Recognized node type and allowed by configuration,
        //   and has no forbid check, or forbid check return value is truthy
        if (def && !options[def.option] && (!def.forbid || def.forbid(node))) {
          context.report({ node, message: def.message })
        }
      },
    }
  },
}
<commit_msg>[Refactor] use "has" instead of ".hasOwnProperty"<commit_after>/**
 * @fileoverview Rule to disallow anonymous default exports.
 * @author Duncan Beevers
 */

import docsUrl from '../docsUrl'
import has from 'has'

const defs = {
  ArrayExpression: {
    option: 'allowArray',
    description: 'If `false`, will report default export of an array',
    message: 'Assign array to a variable before exporting as module default',
  },
  ArrowFunctionExpression: {
    option: 'allowArrowFunction',
    description: 'If `false`, will report default export of an arrow function',
    message: 'Assign arrow function to a variable before exporting as module default',
  },
  CallExpression: {
    option: 'allowCallExpression',
    description: 'If `false`, will report default export of a function call',
    message: 'Assign call result to a variable before exporting as module default',
    default: true,
  },
  ClassDeclaration: {
    option: 'allowAnonymousClass',
    description: 'If `false`, will report default export of an anonymous class',
    message: 'Unexpected default export of anonymous class',
    forbid: (node) => !node.declaration.id,
  },
  FunctionDeclaration: {
    option: 'allowAnonymousFunction',
    description: 'If `false`, will report default export of an anonymous function',
    message: 'Unexpected default export of anonymous function',
    forbid: (node) => !node.declaration.id,
  },
  Literal: {
    option: 'allowLiteral',
    description: 'If `false`, will report default export of a literal',
    message: 'Assign literal to a variable before exporting as module default',
  },
  ObjectExpression: {
    option: 'allowObject',
    description: 'If `false`, will report default export of an object expression',
    message: 'Assign object to a variable before exporting as module default',
  },
  TemplateLiteral: {
    option: 'allowLiteral',
    description: 'If `false`, will report default export of a literal',
    message: 'Assign literal to a variable before exporting as module default',
  },
}

const schemaProperties = Object.keys(defs)
  .map((key) => defs[key])
  .reduce((acc, def) => {
    acc[def.option] = {
      description: def.description,
      type: 'boolean',
    }

    return acc
  }, {})

const defaults = Object.keys(defs)
  .map((key) => defs[key])
  .reduce((acc, def) => {
    acc[def.option] = has(def, 'default') ? def.default : false
    return acc
  }, {})

module.exports = {
  meta: {
    docs: {
      url: docsUrl('no-anonymous-default-export'),
    },

    schema: [
      {
        type: 'object',
        properties: schemaProperties,
        'additionalProperties': false,
      },
    ],
  },

  create: function (context) {
    const options = Object.assign({}, defaults, context.options[0])

    return {
      'ExportDefaultDeclaration': (node) => {
        const def = defs[node.declaration.type]

        // Recognized node type and allowed by configuration,
        //   and has no forbid check, or forbid check return value is truthy
        if (def && !options[def.option] && (!def.forbid || def.forbid(node))) {
          context.report({ node, message: def.message })
        }
      },
    }
  },
}
<|endoftext|>


<commit_before>/**
 * Sample React Native macOS App
 * https://github.com/ptmt/react-native-macos
 */
import React from 'react'
import ReactNative from 'react-native-macos'
const {
  AppRegistry,
  StyleSheet,
  Text,
  Clipboard,
  View,
  NativeAppEventEmitter,
  ListView,
  Dimensions,
  TouchableOpacity,
  ScrollView,
  TextInput
} = ReactNative

class AutoExpandingTextInput extends React.Component {
  state: any

  constructor(props) {
    super(props)
    this.state = {
      text: '',
      height: 0
    }
  }
  render() {
    return (
      <TextInput
        {...this.props}
        multiline={true}
        onChange={event => {
          this.setState({
            text: event.nativeEvent.text,
            height: event.nativeEvent.contentSize.height
          })
        }}
        style={[styles.default, { height: Math.max(35, this.state.height) }]}
        value={this.state.text}
      />
    )
  }
}

var defaultLayout = Dimensions.get('window')

class OrbisMacos extends React.Component {
  processPacket(packet) {
    var payload = packet.payload

    if (typeof payload === 'string' || payload instanceof String) {
    } else {
      payload.selected = false
      var requests = this.state.requests.concat([payload])
      if (requests.length > 200) {
        requests.pop()
      }
      var datasource = this.state.dataSource.cloneWithRows(requests)
      this.setState({
        ...this.state,
        requests: requests,
        dataSource: datasource
      })
    }
  }

  constructor() {
    super()
    this.state = {
      component: Welcome,
      layout: defaultLayout,
      requests: [],
      selectedRequest: '',
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => true
      })
    }

    var that = this
    this.subscription = NativeAppEventEmitter.addListener(
      'peertalk_new_packet',
      packet => {
        that.processPacket(packet)
      }
    )
    this.renderRequestRow = this.renderRequestRow.bind(this)
  }

  componentWillUnmount() {
    this.subscription.remove()
  }

  onSelectRequest(request) {
    if (this.state.selectedRequest !== '') {
      this.state.selectedRequest.selected = false
    }

    request.selected = true
    this.setState({
      ...this.state,
      selectedRequest: request
    })
  }

  clearRequest() {
    var requests = []
    var datasource = this.state.dataSource.cloneWithRows(requests)
    this.setState({
      ...this.state,
      requests: requests,
      dataSource: datasource
    })
  }

  renderRequestRow(request) {
    var that = this
    var rowStyle = styles.touchableRow
    var textStyle = styles.rowText

    var path = request.path
    if (request.base_url) {
      path = path.replace(request.base_url, '')
    }

    if (this.state.selectedRequest.id === request.id) {
      rowStyle = styles.touchedRow
    }

    if (request.error) {
      textStyle = styles.rowFailText
    }

    return (
      <TouchableOpacity
        style={rowStyle}
        onPress={() => that.onSelectRequest(request)}
      >
        <Text style={textStyle} numberOfLines={1}>
          {request.method + ' ' + path}
        </Text>
      </TouchableOpacity>
    )
  }

  render() {
    var detail
    var that = this

    var request = this.state.selectedRequest

    if (this.state.selectedRequest === '') {
      detail = <Welcome />
    } else {
      if (this.state.selectedRequest.error) {
        detail = (
          <ScrollView showsVerticalScrollIndicator={true}>
            <View style={styles.detailContainer}>
              <TouchableOpacity
                onPress={() =>
                  Clipboard.setString(this.state.selectedRequest.path)
                }
              >
                <Text style={styles.welcomeText}>
                  {this.state.selectedRequest.method +
                    ' ' +
                    this.state.selectedRequest.path}
                </Text>
              </TouchableOpacity>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Error</Text>
                <TouchableOpacity
                  onPress={() => {
                    console.log('asdasdasdsd')
                    return Clipboard.setString(request.error)
                  }}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.error}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Parameters</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.params)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.params}
              </Text>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Header</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.header)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.header}
              </Text>
            </View>
          </ScrollView>
        )
      } else {
        detail = (
          <ScrollView showsVerticalScrollIndicator={true}>
            <View style={styles.detailContainer}>
              <TouchableOpacity
                onPress={() =>
                  Clipboard.setString(this.state.selectedRequest.path)
                }
              >
                <Text style={styles.welcomeText}>
                  {this.state.selectedRequest.method +
                    ' ' +
                    this.state.selectedRequest.path}
                </Text>
              </TouchableOpacity>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Response</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.response)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.response}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Parameters</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.params)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.params}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Header</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.header)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.header}
              </Text>
            </View>
          </ScrollView>
        )
      }
    }

    let sideWidth = 250
    return (
      <View style={styles.container}>
        <View
          style={[
            styles.leftPanel,
            {
              width: sideWidth
            }
          ]}
        >
          <TouchableOpacity
            style={styles.touchableRow}
            onPress={() => that.clearRequest()}
          >
            <Text style={styles.clearText}>Clear All</Text>
          </TouchableOpacity>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRequestRow}
            enableEmptySections={true}
          />
        </View>
        <View
          style={[
            styles.rightPanel,
            {
              width: this.state.layout.width - sideWidth
            }
          ]}
        >
          {detail}
        </View>
      </View>
    )
  }
}

class Welcome extends React.Component {
  render() {
    return (
      <View style={styles.welcomeWrapper}>
        <Text style={styles.welcomeText}>Welcome to Orbis!</Text>
      </View>
    )
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row'
  },
  detailContainer: {
    flex: 1,
    margin: 8
  },
  itemWrapper: {
    backgroundColor: '#eaeaea'
  },
  leftPanel: {
    width: 300
  },
  rightPanel: {
    flex: 1,
    backgroundColor: '#fff'
  },
  welcomeWrapper: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  welcomeText: {
    color: '#999',
    fontSize: 20,
    marginTop: 8,
    marginBottom: 8
  },
  rowText: {
    color: '#999',
    fontSize: 15,
    marginTop: 8,
    marginBottom: 8
  },
  rowFailText: {
    color: '#db1414',
    fontSize: 15,
    marginTop: 8,
    marginBottom: 8
  },
  selectableText: {
    height: 260,
    borderWidth: 0.5,
    borderColor: '#0f0f0f',
    flex: 1,
    fontSize: 13,
    padding: 4
  },

  clearText: {
    color: '#999',
    fontSize: 20,
    marginTop: 16,
    marginBottom: 8,
    textAlign: 'center'
  },
  headerText: {
    color: '#6d7cff',
    marginLeft: 8,
    marginRight: 8,
    fontSize: 15,
    paddingTop: 8
  },
  headerCopyText: {
    color: '#4C7D4C',
    marginLeft: 8,
    fontSize: 15,
    paddingTop: 8
  },
  contentText: {
    fontSize: 13
  },
  touchableRow: {
    paddingLeft: 8,
    paddingBottom: 4
  },
  touchedRow: {
    paddingLeft: 8,
    paddingBottom: 4,
    backgroundColor: '#fff'
  }
})

AppRegistry.registerComponent('OrbisMacos', () => OrbisMacos)
<commit_msg>add button<commit_after>/**
 * Sample React Native macOS App
 * https://github.com/ptmt/react-native-macos
 */
import React from 'react'
import ReactNative from 'react-native-macos'
const {
  AppRegistry,
  StyleSheet,
  Text,
  Clipboard,
  View,
  NativeAppEventEmitter,
  ListView,
  Dimensions,
  TouchableOpacity,
  ScrollView,
  TextInput
} = ReactNative

class AutoExpandingTextInput extends React.Component {
  state: any

  constructor(props) {
    super(props)
    this.state = {
      text: '',
      height: 0
    }
  }
  render() {
    return (
      <TextInput
        {...this.props}
        multiline={true}
        onChange={event => {
          this.setState({
            text: event.nativeEvent.text,
            height: event.nativeEvent.contentSize.height
          })
        }}
        style={[styles.default, { height: Math.max(35, this.state.height) }]}
        value={this.state.text}
      />
    )
  }
}

var defaultLayout = Dimensions.get('window')
const formatForPostman = input => {
  let jsonInput = JSON.parse(input)
  return Object.keys(jsonInput).reduce(
    (prev, curr) => prev.concat(curr, ': ', jsonInput[curr], '\n'),
    ''
  )
}

class OrbisMacos extends React.Component {
  processPacket(packet) {
    var payload = packet.payload

    if (typeof payload === 'string' || payload instanceof String) {
    } else {
      payload.selected = false
      var requests = this.state.requests.concat([payload])
      if (requests.length > 200) {
        requests.pop()
      }
      var datasource = this.state.dataSource.cloneWithRows(requests)
      this.setState({
        ...this.state,
        requests: requests,
        dataSource: datasource
      })
    }
  }

  constructor() {
    super()
    this.state = {
      component: Welcome,
      layout: defaultLayout,
      requests: [],
      selectedRequest: '',
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => true
      })
    }

    var that = this
    this.subscription = NativeAppEventEmitter.addListener(
      'peertalk_new_packet',
      packet => {
        that.processPacket(packet)
      }
    )
    this.renderRequestRow = this.renderRequestRow.bind(this)
  }

  componentWillUnmount() {
    this.subscription.remove()
  }

  onSelectRequest(request) {
    if (this.state.selectedRequest !== '') {
      this.state.selectedRequest.selected = false
    }

    request.selected = true
    this.setState({
      ...this.state,
      selectedRequest: request
    })
  }

  clearRequest() {
    var requests = []
    var datasource = this.state.dataSource.cloneWithRows(requests)
    this.setState({
      ...this.state,
      requests: requests,
      dataSource: datasource
    })
  }

  renderRequestRow(request) {
    var that = this
    var rowStyle = styles.touchableRow
    var textStyle = styles.rowText

    var path = request.path
    if (request.base_url) {
      path = path.replace(request.base_url, '')
    }

    if (this.state.selectedRequest.id === request.id) {
      rowStyle = styles.touchedRow
    }

    if (request.error) {
      textStyle = styles.rowFailText
    }

    return (
      <TouchableOpacity
        style={rowStyle}
        onPress={() => that.onSelectRequest(request)}
      >
        <Text style={textStyle} numberOfLines={1}>
          {request.method + ' ' + path}
        </Text>
      </TouchableOpacity>
    )
  }

  render() {
    var detail
    var that = this

    var request = this.state.selectedRequest

    if (this.state.selectedRequest === '') {
      detail = <Welcome />
    } else {
      if (this.state.selectedRequest.error) {
        detail = (
          <ScrollView showsVerticalScrollIndicator={true}>
            <View style={styles.detailContainer}>
              <TouchableOpacity
                onPress={() =>
                  Clipboard.setString(this.state.selectedRequest.path)
                }
              >
                <Text style={styles.welcomeText}>
                  {this.state.selectedRequest.method +
                    ' ' +
                    this.state.selectedRequest.path}
                </Text>
              </TouchableOpacity>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Error</Text>
                <TouchableOpacity
                  onPress={() => {
                    return Clipboard.setString(request.error)
                  }}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.error}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Parameters</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.params)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.params}
              </Text>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Header</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.header)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
                <TouchableOpacity
                  onPress={() =>
                    Clipboard.setString(formatForPostman(request.header))
                  }
                >
                  <Text style={styles.headerCopyTextAlt}>
                    Copy to Clipboard for Postman
                  </Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.header}
              </Text>
            </View>
          </ScrollView>
        )
      } else {
        detail = (
          <ScrollView showsVerticalScrollIndicator={true}>
            <View style={styles.detailContainer}>
              <TouchableOpacity
                onPress={() =>
                  Clipboard.setString(this.state.selectedRequest.path)
                }
              >
                <Text style={styles.welcomeText}>
                  {this.state.selectedRequest.method +
                    ' ' +
                    this.state.selectedRequest.path}
                </Text>
              </TouchableOpacity>
              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Response</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.response)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.response}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Parameters</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.params)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.params}
              </Text>

              <View style={{ flex: 1, flexDirection: 'row' }}>
                <Text style={styles.headerText}>Header</Text>
                <TouchableOpacity
                  onPress={() => Clipboard.setString(request.header)}
                >
                  <Text style={styles.headerCopyText}>Copy to Clipboard</Text>
                </TouchableOpacity>
                <TouchableOpacity
                  onPress={() =>
                    Clipboard.setString(formatForPostman(request.header))
                  }
                >
                  <Text style={styles.headerCopyTextAlt}>
                    Copy to Clipboard for Postman
                  </Text>
                </TouchableOpacity>
              </View>
              <Text style={styles.contentText}>
                {this.state.selectedRequest.header}
              </Text>
            </View>
          </ScrollView>
        )
      }
    }

    let sideWidth = 250
    return (
      <View style={styles.container}>
        <View
          style={[
            styles.leftPanel,
            {
              width: sideWidth
            }
          ]}
        >
          <TouchableOpacity
            style={styles.touchableRow}
            onPress={() => that.clearRequest()}
          >
            <Text style={styles.clearText}>Clear All</Text>
          </TouchableOpacity>
          <ListView
            dataSource={this.state.dataSource}
            renderRow={this.renderRequestRow}
            enableEmptySections={true}
          />
        </View>
        <View
          style={[
            styles.rightPanel,
            {
              width: this.state.layout.width - sideWidth
            }
          ]}
        >
          {detail}
        </View>
      </View>
    )
  }
}

class Welcome extends React.Component {
  render() {
    return (
      <View style={styles.welcomeWrapper}>
        <Text style={styles.welcomeText}>Welcome to Orbis!</Text>
      </View>
    )
  }
}

var styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row'
  },
  detailContainer: {
    flex: 1,
    margin: 8
  },
  itemWrapper: {
    backgroundColor: '#eaeaea'
  },
  leftPanel: {
    width: 300
  },
  rightPanel: {
    flex: 1,
    backgroundColor: '#fff'
  },
  welcomeWrapper: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center'
  },
  welcomeText: {
    color: '#999',
    fontSize: 20,
    marginTop: 8,
    marginBottom: 8
  },
  rowText: {
    color: '#999',
    fontSize: 15,
    marginTop: 8,
    marginBottom: 8
  },
  rowFailText: {
    color: '#db1414',
    fontSize: 15,
    marginTop: 8,
    marginBottom: 8
  },
  selectableText: {
    height: 260,
    borderWidth: 0.5,
    borderColor: '#0f0f0f',
    flex: 1,
    fontSize: 13,
    padding: 4
  },

  clearText: {
    color: '#999',
    fontSize: 20,
    marginTop: 16,
    marginBottom: 8,
    textAlign: 'center'
  },
  headerText: {
    color: '#6d7cff',
    marginLeft: 8,
    marginRight: 8,
    fontSize: 15,
    paddingTop: 8
  },
  headerCopyText: {
    color: '#4C7D4C',
    marginLeft: 8,
    fontSize: 15,
    paddingTop: 8
  },
  headerCopyTextAlt: {
    color: '#1C4D8C',
    marginLeft: 8,
    fontSize: 15,
    paddingTop: 8
  },
  contentText: {
    fontSize: 13
  },
  touchableRow: {
    paddingLeft: 8,
    paddingBottom: 4
  },
  touchedRow: {
    paddingLeft: 8,
    paddingBottom: 4,
    backgroundColor: '#fff'
  }
})

AppRegistry.registerComponent('OrbisMacos', () => OrbisMacos)
<|endoftext|>


<commit_before>/* exported loadFromKinto */
/* exported saveToKinto */
/* exported BrowserStorageCredentials */

let syncDebounce = null;

const cryptographer = new Jose.WebCryptographer();
cryptographer.setKeyEncryptionAlgorithm('A256KW');
cryptographer.setContentEncryptionAlgorithm('A256GCM');

function shared_key(key) {
  return crypto.subtle.importKey(
    'jwk',
    { kty: key.kty, k: key.k.replace(/=/, '') },
    'AES-KW',
    true,
    ['wrapKey', 'unwrapKey']
  );
}

function encrypt(key, content) {
  const encrypter = new JoseJWE.Encrypter(cryptographer, shared_key(key));
  return encrypter.encrypt(JSON.stringify(content));
}

function decrypt(key, encrypted) {
  const decrypter = new JoseJWE.Decrypter(cryptographer, shared_key(key));
  return decrypter.decrypt(encrypted).then(result => {
    return JSON.parse(result);
  });
}

// An "id schema" used to validate Kinto IDs and generate new ones.
const notesIdSchema = {
  // FIXME: Maybe this should generate IDs?
  generate() {
    throw new Error('cannot generate IDs');
  },

  validate() {
    // FIXME: verify that at least this matches Kinto server ID format
    return true;
  },
};

class ServerKeyNewerError extends Error {
  constructor() {
    super('key used to encrypt the record appears to be newer than our key');
  }
}

class ServerKeyOlderError extends Error {
  constructor() {
    super('key used to encrypt the record appears to be older than our key');
  }
}

class JWETransformer {
  constructor(key) {
    this.key = key;
  }

  async encode(record) {
    // FIXME: should we try to obfuscate the record ID?
    const ciphertext = await encrypt(this.key, record);
    // Copy over the _status field, so that we handle concurrency
    // headers (If-Match, If-None-Match) correctly.
    // DON'T copy over "deleted" status, because then we'd leak
    // plaintext deletes.
    const status = record._status && (record._status === 'deleted' ? 'updated' : record._status);
    const encryptedResult = {
      content: ciphertext,
      id: record.id,
      _status: status,
      kid: this.key.kid,
    };
    if (record.hasOwnProperty('last_modified')) {
      encryptedResult.last_modified = record.last_modified;
    }

    return encryptedResult;
  }

  async decode(record) {
    if (!record.content) {
      // This can happen for tombstones if a record is deleted.
      if (record.deleted) {
        return record;
      }
      throw new Error('No ciphertext: nothing to decrypt?');
    }

    if (record.kid.substr(15) !== this.key.kid.substr(15)) {
      if (this.key.kid < record.kid) {
        throw new ServerKeyNewerError();
      } else {
        throw new ServerKeyOlderError();
      }
    }

    // FIXME: Make sure this is backwards compatible??
    let decoded = await decrypt(this.key, record.content);
    if (!decoded.hasOwnProperty('id')) {
      // Old-style encrypted notes aren't true Kinto records --
      // they're just the content field.
      decoded = {
        content: decoded,
        id: 'singleNote',
      };
    }
    if (record.hasOwnProperty('last_modified')) {
      decoded.last_modified = record.last_modified;
    }

    // _status: deleted records were deleted on a client, but
    // uploaded as an encrypted blob so we don't leak deletions.
    // If we get such a record, flag it as deleted.
    if (decoded._status === 'deleted') {
      decoded.deleted = true;
    }
    return decoded;
  }
}

/**
 * Interface describing a mechanism to fetch credentials.
 */
class Credentials {
  async get() {
    return Promise.reject('Implement me');
  }

  /**
   * Call this if, for example, credentials were invalid.
   */
  async clear() {
    return Promise.reject('Implement me');
  }
}

class BrowserStorageCredentials extends Credentials {
  constructor(storage) {
    super();
    this.storage = storage;
  }

  async get() {
    const data = await this.storage.get(['credentials']);
    return data.credentials;
  }

  async clear() {
    return this.storage.remove('credentials');
  }
}

/**
 * Try to sync our data against the Kinto server.
 *
 * Returns a promise. The promise can reject in case of sync failure
 * or any other reason. This is so that programming errors can be
 * caught more easily in testing. Since this application is
 * offline-first, sync failure should not be a failure for callers.
 */
function syncKinto(client, credentials) {
  // Get credentials and lastmodified
  let credential;
  return credentials.get()
    .then(received => {
      credential = received;
      // XXX: Ask for an refresh token
      // Query Kinto with the Bearer Token
      if (!received) return;
      return client
        .collection('notes', {
          idSchema: notesIdSchema,
          remoteTransformers: [new JWETransformer(credential.key)],
        })
        .sync({
          headers: { Authorization: `Bearer ${credential.access_token}` },
          // FIXME: Handle conflicts
          strategy: 'server_wins',
        });
    })
    .then(syncResult => {
      // FIXME: conflicts would happen here.
      // Do we need to do anything with errors, published, updated, etc.?
      return syncResult;
    })
    .catch(error => {
      if (error.response && error.response.status === 401) {
        // In case of 401 log the user out.
        // FIXME: Fetch a new token and retry?
        return credentials.clear();
      } else if (error instanceof ServerKeyNewerError) {
        // If the key date is greater than current one, log the user out.
        console.error(error);
        return credentials.clear();
      } else if (error instanceof ServerKeyOlderError) {
        // If the key date is older than the current one, we can't help
        // because there is no way we get the previous key.
        // Flush the server because whatever was there is wrong.
        // FIXME: need to reset sync status.
        console.error(error);
        const kintoHttp = client.api.remote;
        return kintoHttp.bucket('default').deleteCollection('notes', {
          headers: { Authorization: `Bearer ${credential.access_token}` }
        });
      } else {
        console.error(error);
        return Promise.reject(error);
      }
    });
}

function retrieveNote(client) {
  return client.collection('notes', {
    idSchema: notesIdSchema,
  }).getAny('singleNote');
}

/**
 * Try to sync against the Kinto server, and retrieve the current note
 * contents.
 *
 * On completion, a 'kinto-loaded' event will be fired with the
 * following structure:
 *
 * {
 *   action: 'kinto-loaded',
 *   data: the "content" that was previously saved to Kinto, or null
 *     if nothing was previously saved to Kinto (for example, a new
 *     FxA account, or if syncing failed on a fresh profile)
 *   last_modified: the timestamp of the sync, or null
 * }
 */
function loadFromKinto(client, credentials) {
  return syncKinto(client, credentials)
  // Ignore failure of syncKinto by retrieving note even when promise rejected
    .then(() => retrieveNote(client), () => retrieveNote(client))
    .then(result => {
      console.log('Collection had record', result);
      browser.runtime.sendMessage({
        action: 'kinto-loaded',
        data: result ? result.data.content : null,
        last_modified: result ? result.data.last_modified : null,
      });
    });
}

function saveToKinto(client, credentials, content) {
  let resolve, reject;
  const promise = new Promise((thisResolve, thisReject) => {
    resolve = thisResolve;
    reject = thisReject;
  });

  // XXX: Debounce the call and set the status to Editing
  browser.runtime.sendMessage('notes@mozilla.com', {
    action: 'text-editing'
  });

  const later = function() {
    syncDebounce = null;
    const notes = client.collection('notes', {
      idSchema: notesIdSchema,
    });
    return notes.upsert({ id: 'singleNote', content })
      .then(() => {
        browser.runtime.sendMessage('notes@mozilla.com', {
          action: 'text-saved'
        });
        return syncKinto(client, credentials);
      })
      .then(() => retrieveNote(client), () => retrieveNote(client))
      .then(result => {
        // Set the status to synced
        return browser.runtime.sendMessage('notes@mozilla.com', {
          action: 'text-synced',
          last_modified: result.data.last_modified,
        });
      })
      .then(() => {
        resolve();
      });
  };

  clearTimeout(syncDebounce);
  syncDebounce = setTimeout(later, 1000);
  return promise;
}
<commit_msg>Fix linter<commit_after>/* exported loadFromKinto */
/* exported saveToKinto */
/* exported BrowserStorageCredentials */

let syncDebounce = null;

const cryptographer = new Jose.WebCryptographer();
cryptographer.setKeyEncryptionAlgorithm('A256KW');
cryptographer.setContentEncryptionAlgorithm('A256GCM');

function shared_key(key) {
  return crypto.subtle.importKey(
    'jwk',
    { kty: key.kty, k: key.k.replace(/=/, '') },
    'AES-KW',
    true,
    ['wrapKey', 'unwrapKey']
  );
}

function encrypt(key, content) {
  const encrypter = new JoseJWE.Encrypter(cryptographer, shared_key(key));
  return encrypter.encrypt(JSON.stringify(content));
}

function decrypt(key, encrypted) {
  const decrypter = new JoseJWE.Decrypter(cryptographer, shared_key(key));
  return decrypter.decrypt(encrypted).then(result => {
    return JSON.parse(result);
  });
}

// An "id schema" used to validate Kinto IDs and generate new ones.
const notesIdSchema = {
  // FIXME: Maybe this should generate IDs?
  generate() {
    throw new Error('cannot generate IDs');
  },

  validate() {
    // FIXME: verify that at least this matches Kinto server ID format
    return true;
  },
};

class ServerKeyNewerError extends Error {
  constructor() {
    super('key used to encrypt the record appears to be newer than our key');
  }
}

class ServerKeyOlderError extends Error {
  constructor() {
    super('key used to encrypt the record appears to be older than our key');
  }
}

class JWETransformer {
  constructor(key) {
    this.key = key;
  }

  async encode(record) {
    // FIXME: should we try to obfuscate the record ID?
    const ciphertext = await encrypt(this.key, record);
    // Copy over the _status field, so that we handle concurrency
    // headers (If-Match, If-None-Match) correctly.
    // DON'T copy over "deleted" status, because then we'd leak
    // plaintext deletes.
    const status = record._status && (record._status === 'deleted' ? 'updated' : record._status);
    const encryptedResult = {
      content: ciphertext,
      id: record.id,
      _status: status,
      kid: this.key.kid,
    };
    if (record.hasOwnProperty('last_modified')) {
      encryptedResult.last_modified = record.last_modified;
    }

    return encryptedResult;
  }

  async decode(record) {
    if (!record.content) {
      // This can happen for tombstones if a record is deleted.
      if (record.deleted) {
        return record;
      }
      throw new Error('No ciphertext: nothing to decrypt?');
    }

    if (record.kid.substr(15) !== this.key.kid.substr(15)) {
      if (this.key.kid < record.kid) {
        throw new ServerKeyNewerError();
      } else {
        throw new ServerKeyOlderError();
      }
    }

    // FIXME: Make sure this is backwards compatible??
    let decoded = await decrypt(this.key, record.content);
    if (!decoded.hasOwnProperty('id')) {
      // Old-style encrypted notes aren't true Kinto records --
      // they're just the content field.
      decoded = {
        content: decoded,
        id: 'singleNote',
      };
    }
    if (record.hasOwnProperty('last_modified')) {
      decoded.last_modified = record.last_modified;
    }

    // _status: deleted records were deleted on a client, but
    // uploaded as an encrypted blob so we don't leak deletions.
    // If we get such a record, flag it as deleted.
    if (decoded._status === 'deleted') {
      decoded.deleted = true;
    }
    return decoded;
  }
}

/**
 * Interface describing a mechanism to fetch credentials.
 */
class Credentials {
  async get() {
    return Promise.reject('Implement me');
  }

  /**
   * Call this if, for example, credentials were invalid.
   */
  async clear() {
    return Promise.reject('Implement me');
  }
}

class BrowserStorageCredentials extends Credentials {
  constructor(storage) {
    super();
    this.storage = storage;
  }

  async get() {
    const data = await this.storage.get(['credentials']);
    return data.credentials;
  }

  async clear() {
    return this.storage.remove('credentials');
  }
}

/**
 * Try to sync our data against the Kinto server.
 *
 * Returns a promise. The promise can reject in case of sync failure
 * or any other reason. This is so that programming errors can be
 * caught more easily in testing. Since this application is
 * offline-first, sync failure should not be a failure for callers.
 */
function syncKinto(client, credentials) {
  // Get credentials and lastmodified
  let credential;
  return credentials.get()
    .then(received => {
      credential = received;
      // XXX: Ask for an refresh token
      // Query Kinto with the Bearer Token
      if (!received) return;
      return client
        .collection('notes', {
          idSchema: notesIdSchema,
          remoteTransformers: [new JWETransformer(credential.key)],
        })
        .sync({
          headers: { Authorization: `Bearer ${credential.access_token}` },
          // FIXME: Handle conflicts
          strategy: 'server_wins',
        });
    })
    .then(syncResult => {
      // FIXME: conflicts would happen here.
      // Do we need to do anything with errors, published, updated, etc.?
      return syncResult;
    })
    .catch(error => {
      if (error.response && error.response.status === 401) {
        // In case of 401 log the user out.
        // FIXME: Fetch a new token and retry?
        return credentials.clear();
      } else if (error instanceof ServerKeyNewerError) {
        // If the key date is greater than current one, log the user out.
        console.error(error);
        return credentials.clear();
      } else if (error instanceof ServerKeyOlderError) {
        // If the key date is older than the current one, we can't help
        // because there is no way we get the previous key.
        // Flush the server because whatever was there is wrong.
        // FIXME: need to reset sync status.
        console.error(error);
        const kintoHttp = client.api.remote;
        return kintoHttp.bucket('default').deleteCollection('notes', {
          headers: { Authorization: `Bearer ${credential.access_token}` }
        });
      } else {
        console.error(error);
        return Promise.reject(error);
      }
    });
}

function retrieveNote(client) {
  return client.collection('notes', {
    idSchema: notesIdSchema,
  }).getAny('singleNote');
}

/**
 * Try to sync against the Kinto server, and retrieve the current note
 * contents.
 *
 * On completion, a 'kinto-loaded' event will be fired with the
 * following structure:
 *
 * {
 *   action: 'kinto-loaded',
 *   data: the "content" that was previously saved to Kinto, or null
 *     if nothing was previously saved to Kinto (for example, a new
 *     FxA account, or if syncing failed on a fresh profile)
 *   last_modified: the timestamp of the sync, or null
 * }
 */
function loadFromKinto(client, credentials) {
  return syncKinto(client, credentials)
  // Ignore failure of syncKinto by retrieving note even when promise rejected
    .then(() => retrieveNote(client), () => retrieveNote(client))
    .then(result => {
      console.log('Collection had record', result);
      browser.runtime.sendMessage({
        action: 'kinto-loaded',
        data: result ? result.data.content : null,
        last_modified: result ? result.data.last_modified : null,
      });
    });
}

function saveToKinto(client, credentials, content) {
  let resolve;
  const promise = new Promise(thisResolve => {
    resolve = thisResolve;
  });

  // XXX: Debounce the call and set the status to Editing
  browser.runtime.sendMessage('notes@mozilla.com', {
    action: 'text-editing'
  });

  const later = function() {
    syncDebounce = null;
    const notes = client.collection('notes', {
      idSchema: notesIdSchema,
    });
    return notes.upsert({ id: 'singleNote', content })
      .then(() => {
        browser.runtime.sendMessage('notes@mozilla.com', {
          action: 'text-saved'
        });
        return syncKinto(client, credentials);
      })
      .then(() => retrieveNote(client), () => retrieveNote(client))
      .then(result => {
        // Set the status to synced
        return browser.runtime.sendMessage('notes@mozilla.com', {
          action: 'text-synced',
          last_modified: result.data.last_modified,
        });
      })
      .then(() => {
        resolve();
      });
  };

  clearTimeout(syncDebounce);
  syncDebounce = setTimeout(later, 1000);
  return promise;
}
<|endoftext|>


<commit_before>import { toString } from '../dist/toString.js'

describe('toString', () => {
	it('converts an input to a string', () => {
		expect(toString([1,2,3])).toEqual('1,2,3')
	})

	it('returns an empty string for a null input', () => {
		expect(toString(null)).toEqual('')
	})

	it('returns an empty string for a undefined input', () => {
		expect(toString(undefined)).toEqual('')
	})

	it('returns a string for an input number', () => {
		expect(toString(17302)).toEqual('17302')
	})

	it('returns only the first number in a series of numbers', () => {
		expect(toString(1830,2,3)).toEqual('1830')
	})

})<commit_msg>Update toString_spec.js<commit_after>import { toString } from '../dist/toString.js'

describe('toString', () => {
  it('converts an input to a string', () => {
  expect(toString([1,2,3])).toEqual('1,2,3')
  })

  it('returns an empty string for a null input', () => {
  expect(toString(null)).toEqual('')
  })

  it('returns an empty string for a undefined input', () => {
  expect(toString(undefined)).toEqual('')
  })

  it('returns a string for an input number', () => {
  expect(toString(17302)).toEqual('17302')
  })

  it('returns only the first number in a series of numbers', () => {
  expect(toString(1830,2,3)).toEqual('1830')
  })

})
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styles from './question.css';

class Question extends Component {
  render() {
    return (

        <input className={styles.accordionSingleHidden} type="checkbox" id={this.props.q} />
         {this.props.q}

          {this.props.a}

    );
  }
}

Question.propTypes = {
  q: PropTypes.string.isRequired,
  a: PropTypes.string.isRequired
};

export default Question;
<commit_msg>Fix faq<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styles from './question.css';

class Question extends Component {
  render() {
    return (

        <input className={styles.accordionSingleHidden} type="checkbox" id={this.props.q} />
         {this.props.q}

    );
  }
}

Question.propTypes = {
  q: PropTypes.string.isRequired,
  a: PropTypes.string.isRequired
};

export default Question;
<|endoftext|>


<commit_before>// python Template for the preamble
var preamble = (basis,classname)=>
`"""3D Projective Geometric Algebra.

Written by a generator written by enki.
"""

__author__ = \'Enki\'

import math

class ${classname}:
    def __init__(self, value=0, index=0):
        """Initiate a new ${classname}.

        Optional, the component index can be set with value.
        """
        self.mvec = [0] * ${basis.length}
        self._base = [${basis.map(x=>'"'+x+'"').join(', ')}]
        if (value != 0):
            self.mvec[index] = value

    def __str__(self):
        res = ' + '.join(filter(None, [("%.7f" % x).rstrip("0").rstrip(".")+(["",self._base[i]][i>0]) if math.fabs(x) > 0.000001 else None for i,x in enumerate(self)]))
        if (res == ''):
            return "0"
        return res

    def __getitem__(self, key):
        return self.mvec[key]

    def __setitem__(self, key, value):
        self.mvec[key] = value

    def __len__(self):
        return len(self.mvec)`

// python Template for our binary operators

var binary = (classname, symbol, name, name_a, name_b, name_ret, code, classname_a=classname, classname_b=classname, desc)=>
`    def ${name.match("s")?name:({"+":"__add__","-":"__sub__","*":"__mul__","^":"__xor__","&":"__and__","|":"__or__"}[symbol]||name)}(${name_a},${name_b}):${(name in {Mul:1,Add:1,Sub:1})?`
        """${classname}.${name}

        ${desc}
        """
        if type(b) in (int, float):
            return a.${name.toLowerCase()}s(b)`:``}
        ${name_ret} = ${classname}()
        ${code.replace(/;/g,'').replace(/^ */g,'').replace(/\n */g,'\n        ')}
        return ${name_ret}
${(name in {Mul:1,Add:1,Sub:1})?`    __r${name.toLowerCase()}__=__${name.toLowerCase()}__`:``}`;

// python Template for our unary operators

var unary = (classname, symbol, name, name_a, name_ret, code, classname_a=classname,desc)=>
`    def ${({"~":"__invert__"}[symbol]||name)}(${name_a}):
        """${classname}.${name}

        ${desc}
        """
        ${name_ret} = ${classname}()
        ${code.replace(/;/g,'').replace(/^ */g,'').replace(/\n */g,'\n        ')}
        return ${name_ret}`;

// python template for CGA example
var CGA = (basis,classname)=>({
preamble:
``,
amble:
`
if __name__ == '__main__':
    # CGA is point based. Vectors are points.
    E1 = ${classname}(1.0, 1)
    E2 = ${classname}(1.0, 2)
    E3 = ${classname}(1.0, 3)
    E4 = ${classname}(1.0, 4)
    E5 = ${classname}(1.0, 5)

    EO = E4 + E5
    EI = (E5 - E4) * 0.5

    def up(x, y, z):
	    return x * E1 + y * E2 + z * E3 + 0.5 * (x * x + y * y + z * z) * EI + EO

    PX = up(1, 2, 3)
    LINE = PX ^ EO ^ EI
    SPHERE = (EO - EI).Dual()

    # output some numbers.
    print("a point       :", str(PX))
    print("a line        :", str(LINE))
    print("a sphere      :", str(SPHERE))
`
});

// python template for algebras without example
var GENERIC = (basis,classname)=>({
preamble:`
`,
amble:`
${basis.slice(1).map((x,i)=>`${x} = ${classname}(1.0, ${i+1})`).join('\n')}

if __name__ == '__main__':
    print("${basis[1]}*${basis[1]}         :", str(${basis[1]}*${basis[1]}))
    print("pss           :", str(${basis[basis.length-1]}))
    print("pss*pss       :", str(${basis[basis.length-1]}*${basis[basis.length-1]}))
`
})

// python template for PGA example
var PGA3D = (basis,classname)=>({
preamble:
``,
amble:
`
if __name__ == '__main__':
    # A rotor (Euclidean line) and translator (Ideal line)
    def rotor(angle, line):
	    return math.cos(angle / 2.0) + math.sin(angle / 2.0) * line.normalized()

    def translator(dist, line):
	    return 1.0 + dist / 2.0 * line

    # PGA is plane based. Vectors are planes. (think linear functionals)
    E0 = ${classname}(1.0, 1)           # ideal plane
    E1 = ${classname}(1.0, 2)           # x=0 plane
    E2 = ${classname}(1.0, 3)           # y=0 plane
    E3 = ${classname}(1.0, 4)           # z=0 plane

    # A plane is defined using its homogenous equation ax + by + cz + d = 0
    def PLANE(a, b, c, d):
        return a * E1 + b * E2 + c * E3 + d * E0

    # PGA points are trivectors.
    E123 = E1 ^ E2 ^ E3
    E032 = E0 ^ E3 ^ E2
    E013 = E0 ^ E1 ^ E3
    E021 = E0 ^ E2 ^ E1

    # A point is just a homogeneous point, euclidean coordinates plus the origin
    def POINT(x, y, z):
        return E123 + x * E032 + y * E013 + z * E021

    # for our toy problem (generate points on the surface of a torus)
    # we start with a function that generates motors.
    # circle(t) with t going from 0 to 1.
    def CIRCLE(t, radius, line):
    	return rotor(t * math.pi * 2.0, line) * translator(radius, E1 * E0)

    # a torus is now the product of two circles.
    def TORUS(s, t, r1, l1, r2, l2):
	    return CIRCLE(s, r2, l2)*CIRCLE(t, r1, l1)

    # sample the torus points by sandwich with the origin
    def POINT_ON_TORUS(s, t):
	    to = TORUS(s, t, 0.25, E1 * E2, 0.6, E1 * E3)
	    return to * E123 * ~to

    # Elements of the even subalgebra (scalar + bivector + pss) of unit length are motors
    ROT = rotor(math.pi / 2.0, E1 * E2)

    # The outer product ^ is the MEET. Here we intersect the yz (x=0) and xz (y=0) planes.
    AXZ = E1 ^ E2                # x=0, y=0 -> z-axis line

    # line and plane meet in point. We intersect the line along the z-axis (x=0,y=0) with the xy (z=0) plane.
    ORIG = AXZ ^ E3              # x=0, y=0, z=0 -> origin

    # We can also easily create points and join them into a line using the regressive (vee, &) product.
    PX = POINT(1, 0, 0)
    LINE = ORIG & PX             # & = regressive product, JOIN, here, x-axis line.

    # Lets also create the plane with equation 2x + z - 3 = 0
    P = PLANE(2, 0, 1, -3)

    # rotations work on all elements ..
    ROTATED_LINE = ROT * LINE * ~ROT
    ROTATED_POINT = ROT * PX * ~ROT
    ROTATED_PLANE = ROT * P * ~ROT

    # See the 3D PGA Cheat sheet for a huge collection of useful formulas
    POINT_ON_PLANE = (P | PX) * P

    # output some numbers.
    print("a point       :", str(PX))
    print("a line        :", str(LINE))
    print("a plane       :", str(P))
    print("a rotor       :", str(ROT))
    print("rotated line  :", str(ROTATED_LINE))
    print("rotated point :", str(ROTATED_POINT))
    print("rotated plane :", str(ROTATED_PLANE))
    print("point on plane:", str(POINT_ON_PLANE.normalized()))
    print("point on torus:", str(POINT_ON_TORUS(0.0, 0.0)))
`
})

// python Template for the postamble
var postamble = (basis,classname,example)=>
`    def norm(a):
        return math.sqrt(math.fabs((a * a.Conjugate())[0]))

    def inorm(a):
        return a.Dual().norm()

    def normalized(a):
        return a * (1 / a.norm())
${example.amble}
`;

Object.assign(exports,{preamble,postamble,unary,binary,desc:"python",PGA3D,CGA,GENERIC});
<commit_msg>Alternative constructor to innitiate from array.<commit_after>// python Template for the preamble
var preamble = (basis,classname)=>
`"""3D Projective Geometric Algebra.

Written by a generator written by enki.
"""

__author__ = \'Enki\'

import math

class ${classname}:
    def __init__(self, value=0, index=0):
        """Initiate a new ${classname}.

        Optional, the component index can be set with value.
        """
        self.mvec = [0] * ${basis.length}
        self._base = [${basis.map(x=>'"'+x+'"').join(', ')}]
        if (value != 0):
            self.mvec[index] = value

    @classmethod
    def fromarray(cls, array):
        """Initiate a new ${classname} from an array-like object.

        The first axis of the array is assumed to correspond to the elements
        of the algebra, and needs to have the same length. Any other dimensions
        are left unchanged, and should have simple operations such as addition
        and multiplication defined. NumPy arrays are therefore a perfect
        candidate.

        :param array: array-like object whose length is the dimension of the algebra.
        :return: new instance of ${classname}.
        """
        self = cls()
        if len(array) != len(self):
            raise TypeError('length of array must be identical to the dimension '
                            'of the algebra.')
        self.mvec = array
        return self

    def __str__(self):
        if isinstance(self.mvec, list):
            res = ' + '.join(filter(None, [("%.7f" % x).rstrip("0").rstrip(".")+(["",self._base[i]][i>0]) if abs(x) > 0.000001 else None for i,x in enumerate(self)]))
        else:  # Assume array-like, redirect str conversion
            res = str(self.mvec)
        if (res == ''):
            return "0"
        return res

    def __getitem__(self, key):
        return self.mvec[key]

    def __setitem__(self, key, value):
        self.mvec[key] = value

    def __len__(self):
        return len(self.mvec)`

// python Template for our binary operators

var binary = (classname, symbol, name, name_a, name_b, name_ret, code, classname_a=classname, classname_b=classname, desc)=>
`    def ${name.match("s")?name:({"+":"__add__","-":"__sub__","*":"__mul__","^":"__xor__","&":"__and__","|":"__or__"}[symbol]||name)}(${name_a},${name_b}):${(name in {Mul:1,Add:1,Sub:1})?`
        """${classname}.${name}

        ${desc}
        """
        if type(${name_b}) in (int, float):
            return ${name_a}.${name.toLowerCase()}s(${name_b})`:``}
        ${name_ret} = ${name_a}.mvec.copy()
        ${code.replace(/;/g,'').replace(/^ */g,'').replace(/\n */g,'\n        ')}
        return ${classname}.fromarray(${name_ret})
${(name in {Mul:1,Add:1,Sub:1})?`    __r${name.toLowerCase()}__=__${name.toLowerCase()}__`:``}`;

// python Template for our unary operators

var unary = (classname, symbol, name, name_a, name_ret, code, classname_a=classname,desc)=>
`    def ${({"~":"__invert__"}[symbol]||name)}(${name_a}):
        """${classname}.${name}

        ${desc}
        """
        ${name_ret} = ${name_a}.mvec.copy()
        ${code.replace(/;/g,'').replace(/^ */g,'').replace(/\n */g,'\n        ')}
        return ${classname}.fromarray(${name_ret})`;

// python template for CGA example
var CGA = (basis,classname)=>({
preamble:
``,
amble:
`
if __name__ == '__main__':
    # CGA is point based. Vectors are points.
    E1 = ${classname}(1.0, 1)
    E2 = ${classname}(1.0, 2)
    E3 = ${classname}(1.0, 3)
    E4 = ${classname}(1.0, 4)
    E5 = ${classname}(1.0, 5)

    EO = E4 + E5
    EI = (E5 - E4) * 0.5

    def up(x, y, z):
	    return x * E1 + y * E2 + z * E3 + 0.5 * (x * x + y * y + z * z) * EI + EO

    PX = up(1, 2, 3)
    LINE = PX ^ EO ^ EI
    SPHERE = (EO - EI).Dual()

    # output some numbers.
    print("a point       :", str(PX))
    print("a line        :", str(LINE))
    print("a sphere      :", str(SPHERE))
`
});

// python template for algebras without example
var GENERIC = (basis,classname)=>({
preamble:`
`,
amble:`
${basis.slice(1).map((x,i)=>`${x} = ${classname}(1.0, ${i+1})`).join('\n')}

if __name__ == '__main__':
    print("${basis[1]}*${basis[1]}         :", str(${basis[1]}*${basis[1]}))
    print("pss           :", str(${basis[basis.length-1]}))
    print("pss*pss       :", str(${basis[basis.length-1]}*${basis[basis.length-1]}))
`
})

// python template for PGA example
var PGA3D = (basis,classname)=>({
preamble:
``,
amble:
`
if __name__ == '__main__':
    # A rotor (Euclidean line) and translator (Ideal line)
    def rotor(angle, line):
	    return math.cos(angle / 2.0) + math.sin(angle / 2.0) * line.normalized()

    def translator(dist, line):
	    return 1.0 + dist / 2.0 * line

    # PGA is plane based. Vectors are planes. (think linear functionals)
    E0 = ${classname}(1.0, 1)           # ideal plane
    E1 = ${classname}(1.0, 2)           # x=0 plane
    E2 = ${classname}(1.0, 3)           # y=0 plane
    E3 = ${classname}(1.0, 4)           # z=0 plane

    # A plane is defined using its homogenous equation ax + by + cz + d = 0
    def PLANE(a, b, c, d):
        return a * E1 + b * E2 + c * E3 + d * E0

    # PGA points are trivectors.
    E123 = E1 ^ E2 ^ E3
    E032 = E0 ^ E3 ^ E2
    E013 = E0 ^ E1 ^ E3
    E021 = E0 ^ E2 ^ E1

    # A point is just a homogeneous point, euclidean coordinates plus the origin
    def POINT(x, y, z):
        return E123 + x * E032 + y * E013 + z * E021

    # for our toy problem (generate points on the surface of a torus)
    # we start with a function that generates motors.
    # circle(t) with t going from 0 to 1.
    def CIRCLE(t, radius, line):
    	return rotor(t * math.pi * 2.0, line) * translator(radius, E1 * E0)

    # a torus is now the product of two circles.
    def TORUS(s, t, r1, l1, r2, l2):
	    return CIRCLE(s, r2, l2)*CIRCLE(t, r1, l1)

    # sample the torus points by sandwich with the origin
    def POINT_ON_TORUS(s, t):
	    to = TORUS(s, t, 0.25, E1 * E2, 0.6, E1 * E3)
	    return to * E123 * ~to

    # Elements of the even subalgebra (scalar + bivector + pss) of unit length are motors
    ROT = rotor(math.pi / 2.0, E1 * E2)

    # The outer product ^ is the MEET. Here we intersect the yz (x=0) and xz (y=0) planes.
    AXZ = E1 ^ E2                # x=0, y=0 -> z-axis line

    # line and plane meet in point. We intersect the line along the z-axis (x=0,y=0) with the xy (z=0) plane.
    ORIG = AXZ ^ E3              # x=0, y=0, z=0 -> origin

    # We can also easily create points and join them into a line using the regressive (vee, &) product.
    PX = POINT(1, 0, 0)
    LINE = ORIG & PX             # & = regressive product, JOIN, here, x-axis line.

    # Lets also create the plane with equation 2x + z - 3 = 0
    P = PLANE(2, 0, 1, -3)

    # rotations work on all elements ..
    ROTATED_LINE = ROT * LINE * ~ROT
    ROTATED_POINT = ROT * PX * ~ROT
    ROTATED_PLANE = ROT * P * ~ROT

    # See the 3D PGA Cheat sheet for a huge collection of useful formulas
    POINT_ON_PLANE = (P | PX) * P

    # output some numbers.
    print("a point       :", str(PX))
    print("a line        :", str(LINE))
    print("a plane       :", str(P))
    print("a rotor       :", str(ROT))
    print("rotated line  :", str(ROTATED_LINE))
    print("rotated point :", str(ROTATED_POINT))
    print("rotated plane :", str(ROTATED_PLANE))
    print("point on plane:", str(POINT_ON_PLANE.normalized()))
    print("point on torus:", str(POINT_ON_TORUS(0.0, 0.0)))
`
})

// python Template for the postamble
var postamble = (basis,classname,example)=>
`    def norm(a):
        return math.sqrt(math.fabs((a * a.Conjugate())[0]))

    def inorm(a):
        return a.Dual().norm()

    def normalized(a):
        return a * (1 / a.norm())
${example.amble}
`;

Object.assign(exports,{preamble,postamble,unary,binary,desc:"python",PGA3D,CGA,GENERIC});
<|endoftext|>


<commit_before>/* eslint-disable */

// Google Analytics: change UA-XXXXX-X to be your site's ID.
(function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r;i[r]=i[r]
||function(){(i[r].q=i[r].q||[]).push(arguments);},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m);
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-32351360-5', 'auto');
ga('send', 'pageview');
<commit_msg>Turn back GA code<commit_after>/* eslint-disable */

// Google Analytics: change UA-XXXXX-X to be your site's ID.
(function(i,s,o,g,r,a,m){i.GoogleAnalyticsObject=r;i[r]=i[r]
||function(){(i[r].q=i[r].q||[]).push(arguments);},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m);
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-32351360-4', 'auto');
ga('set', 'page', 'swillboilerplate.rocks');
ga('send', 'pageview');
<|endoftext|>


<commit_before>import { Map } from 'immutable';
import { dataFns } from './utils/data_utils';
const { get, set } = dataFns(["sync"]);

import { getEntity, observe, read, swap, updateEntity } from './store/index';

export default (m, key, opts) => {
  if (get(m, key) !== undefined) return m;

  const status = opts.waitFn
    ? "waiting"
    : !opts.conditionFn || opts.conditionFn(m) ? "pending" : "no";

  return set(m, key, Map({
    recoverResult: opts.recoverResult,
    syncStatus: status,
    successFn: opts.successFn,
    syncFn: opts.syncFn,
    timeout: opts.timeout || 6000,
    waitFn: opts.waitFn
  }));
}

const syncStatusKey = key => (
  (global.Array.isArray(key) ? key : [key]).concat(["syncStatus"])
);
const getStatus = (m, key) => get(m, syncStatusKey(key));
const setStatus = (m, key, str) => set(m, syncStatusKey(key), str);
const getProp = (m, key, name) => get(m, key).get(name);

const findKeys = m => {
  return m.reduce((r, v, k) => {
    const current = Map.isMap(v) && v.has("syncStatus") ? [k] : [];
    const nested = Map.isMap(v)
      ? findKeys(v).map(x => [k].concat(x))
      : [];
    return r.concat(current, ...[nested]);
  }, []);
}

function removeKeys(m, keys) {
  return keys.reduce((r, k) => r.deleteIn(syncStatusKey(k)), m);
}

const process = (m, id) => {
  const keys = findKeys(get(m, [], Map()));
  // TODO timeout
  return keys.reduce((r, k) => {
    if (typeof getProp(r, k, "syncFn") != "function") return r;
    if (getStatus(r, k) === "pending") {
      r = setStatus(r, k, "loading");
      let called = false;
      getProp(r, k, "syncFn")(r, (error, result) => {
        if (called) return;
        called = true;
        setTimeout(() => {
          swap(updateEntity, "lock", id, m => {
            const recoverResult = getProp(m, k, "recoverResult");
            if (error && recoverResult === undefined) {
              return setStatus(m, k, "error");
            } else {
              m = setStatus(m, k, "ok");
              return getProp(m, k, "successFn")(m, error ? recoverResult : result);
            }
          });
        }, 0);
      });
    } else if (getStatus(r, k) === "waiting") {
      if (getProp(r, k, "waitFn")(r)) {
        r = setStatus(r, k, "pending");
      }
    }

    return r;
  }, m);
}

export const go = id => {
  observe("sync", id, m => {
    setTimeout(() => swap(updateEntity, "lock", id, process, id), 0);
  });
}

export function isSuccess(m, key) {
  return getStatus(m, key) === "ok";
}

export function isDone(m) {
  const keys = findKeys(get(m, [], Map()));
  return keys.length > 0 && keys.reduce((r, k) => r && !isLoading(m, k), true);
}

export function hasError(m, excludeKeys = []) {
  const keys = findKeys(removeKeys(get(m, [], Map()), excludeKeys));
  return keys.length > 0 && keys.reduce((r, k) => r || getStatus(m, k) === "error", false);
}

function isLoading(m, key) {
  return ["loading", "pending", "waiting"].indexOf(getStatus(m, key)) > -1;
}
<commit_msg>Stop Lock after a sync error<commit_after>import { Map } from 'immutable';
import { dataFns } from './utils/data_utils';
const { get, set } = dataFns(["sync"]);
import * as l from './core/index';

import { getEntity, observe, read, swap, updateEntity } from './store/index';

export default (m, key, opts) => {
  if (get(m, key) !== undefined) return m;

  const status = opts.waitFn
    ? "waiting"
    : !opts.conditionFn || opts.conditionFn(m) ? "pending" : "no";

  return set(m, key, Map({
    recoverResult: opts.recoverResult,
    syncStatus: status,
    successFn: opts.successFn,
    syncFn: opts.syncFn,
    timeout: opts.timeout || 6000,
    waitFn: opts.waitFn
  }));
}

const syncStatusKey = key => (
  (global.Array.isArray(key) ? key : [key]).concat(["syncStatus"])
);
const getStatus = (m, key) => get(m, syncStatusKey(key));
const setStatus = (m, key, str) => set(m, syncStatusKey(key), str);
const getProp = (m, key, name) => get(m, key).get(name);

const findKeys = m => {
  return m.reduce((r, v, k) => {
    const current = Map.isMap(v) && v.has("syncStatus") ? [k] : [];
    const nested = Map.isMap(v)
      ? findKeys(v).map(x => [k].concat(x))
      : [];
    return r.concat(current, ...[nested]);
  }, []);
}

function removeKeys(m, keys) {
  return keys.reduce((r, k) => r.deleteIn(syncStatusKey(k)), m);
}

const process = (m, id) => {
  const keys = findKeys(get(m, [], Map()));
  // TODO timeout
  return keys.reduce((r, k) => {
    if (typeof getProp(r, k, "syncFn") != "function") return r;
    if (getStatus(r, k) === "pending") {
      r = setStatus(r, k, "loading");
      let called = false;
      getProp(r, k, "syncFn")(r, (error, result) => {
        if (called) return;
        called = true;
        setTimeout(() => {
          swap(updateEntity, "lock", id, m => {
            const recoverResult = getProp(m, k, "recoverResult");
            if (error && recoverResult === undefined) {
              return handleError(m, k, error);
            } else {
              m = setStatus(m, k, "ok");
              return getProp(m, k, "successFn")(m, error ? recoverResult : result);
            }
          });
        }, 0);
      });
    } else if (getStatus(r, k) === "waiting") {
      if (getProp(r, k, "waitFn")(r)) {
        r = setStatus(r, k, "pending");
      }
    }

    return r;
  }, m);
}

export const go = id => {
  observe("sync", id, m => {
    setTimeout(() => swap(updateEntity, "lock", id, process, id), 0);
  });
}

export function isSuccess(m, key) {
  return getStatus(m, key) === "ok";
}

export function isDone(m) {
  const keys = findKeys(get(m, [], Map()));
  return keys.length > 0 && keys.reduce((r, k) => r && !isLoading(m, k), true);
}

export function hasError(m, excludeKeys = []) {
  const keys = findKeys(removeKeys(get(m, [], Map()), excludeKeys));
  return keys.length > 0 && keys.reduce((r, k) => r || getStatus(m, k) === "error", false);
}

function isLoading(m, key) {
  return ["loading", "pending", "waiting"].indexOf(getStatus(m, key)) > -1;
}

function handleError(m, key, error) {
  let result = setStatus(m, key, "error");

  // TODO: this should be configurable for each sync
  if (key !== "sso") {
    result = l.stop(result);
  }

  return result;
}
<|endoftext|>