<commit_before>/* global describe, it, beforeEach */

import React from 'react'
import { expect } from 'chai'
import Form from '../../src/components/form'
import { mount } from 'enzyme'
import td from 'testdouble'

describe('<Form />', () => {
  // Despite eslint's pleadings, this can't be a stateless function
  // because <Form /> will tack a `ref` on to it, which is illegal.
  class ThemedForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
    render() { return <form>{this.props.children}</form> } // eslint-disable-line react/prop-types
  }

  const formProps = {
    theme: { Form: ThemedForm },
    data: {},
    onChange: () => {},
    saved: {},
    errors: {},
    layout: 'horizontal',
    align: 'right',
  }
  const form = (
    <Form {...formProps} >

child

    </Form>
  )

  it('fails fast when props.data is not provided', () => {
    const badForm = <Form onChange={formProps.onChange} />
    const wrapperBound = mount.bind(null, badForm)
    expect(wrapperBound).to.throw(Error, /data=/)
  })

  it('fails fast when props.onChange is not provided', () => {
    const badForm = <Form data={formProps.data} />
    const wrapperBound = mount.bind(null, badForm)
    expect(wrapperBound).to.throw(Error, /onChange=/)
  })

  it('renders the theme.Form component', () => {
    const wrapper = mount(form)
    expect(wrapper.find(ThemedForm)).to.have.length(1)
  })

  describe('via AbstractForm', () => {
    describe('context', () => {
      const wrapper = mount(<Form {...formProps} />)
      const context = wrapper.instance().getChildContext()

      it('passes context (from props) down to its children', () => {
        expect(context.frigForm).to.exist()
        expect(context.frigForm.errors).to.equal(formProps.errors)
        expect(context.frigForm.layout).to.equal(formProps.layout)
        expect(context.frigForm.theme).to.equal(formProps.theme)
        expect(context.frigForm.align).to.equal(formProps.align)
        expect(context.frigForm.saved).to.equal(formProps.saved)
        expect(context.frigForm.data).to.equal(formProps.data)
      })

      it('passes context (internal callbacks) down to its children', () => {
        expect(context.frigForm.childComponentWillMount).to.be.a('function')
        expect(context.frigForm.childComponentWillUnmount).to.be.a('function')
      })

      // pending
      it('passes requestChildComponentChange via context')
      it('passes submit via context')
    })

    describe('public API', () => {
      let wrapper
      let instance

      beforeEach(() => {
        wrapper = mount(<Form {...formProps} />)
        instance = wrapper.instance()
      })

      describe('validate()', () => {
        it('should return false if any inputs\' validate() returns false', () => {
          instance.childComponentWillMount('a', { validate: () => false })
          instance.childComponentWillMount('b', { validate: () => true })
          expect(instance.validate()).to.be.false()
        })

        it('should return true if all inputs\' validate() returns true', () => {
          instance.childComponentWillMount('a', { validate: () => true })
          instance.childComponentWillMount('b', { validate: () => true })
          expect(instance.validate()).to.be.true()
        })
      })

      describe('isValid()', () => {
        it('should return false if any inputs\' isValid() returns false', () => {
          instance.childComponentWillMount('a', { isValid: () => false })
          instance.childComponentWillMount('b', { isValid: () => true })
          expect(instance.isValid()).to.be.false()
        })

        it('should return true if all inputs\' isValid() returns true', () => {
          instance.childComponentWillMount('a', { isValid: () => true })
          instance.childComponentWillMount('b', { isValid: () => true })
          expect(instance.isValid()).to.be.true()
        })
      })

      describe('isModified()', () => {
        it('should be false initially', () => {
          expect(instance.isModified()).to.be.false()
        })
        it('should be false if all inputs\' isModified() returns false', () => {
          instance.childComponentWillMount('a', { isModified: () => false })
          instance.childComponentWillMount('b', { isModified: () => false })
          expect(instance.isModified()).to.be.false()
        })
        it('should be false if any inputs\' isModified() returns true', () => {
          instance.childComponentWillMount('a', { isModified: () => false })
          instance.childComponentWillMount('b', { isModified: () => true })
          expect(instance.isModified()).to.be.true()
        })
      })

      describe('resetModified()', () => {
        it('should call resetModified() on each component', () => {
          let hasReset = false
          instance.childComponentWillMount('a', {
            resetModified: () => { hasReset = true },
          })
          instance.resetModified()
          expect(hasReset).to.be.true()
        })
      })

      describe('reset()', () => {
        it('should call reset() on each component', () => {
          let hasReset = false
          instance.childComponentWillMount('a', {
            reset: () => { hasReset = true },
          })
          instance.reset()
          expect(hasReset).to.be.true()
        })
      })

      describe('modifications()', () => {
        describe('when no fields are modified', () => {
          it('should return empty object', () => {
            expect(instance.modifications()).to.deep.equal({})
          })
        })
        describe('when fields are modified', () => {
          it('should return only modified values', () => {
            instance.childComponentWillMount('a', { isModified: () => true })
            instance.childComponentWillMount('b', { isModified: () => false })
            const expected = { a: true }
            expect(instance.modifications()).to.deep.equal(expected)
          })
          it('should return nested modified values', () => {
            instance.childComponentWillMount('c', {
              isModified: () => true,
              modifications: () => ({ d: true, e: true }),
            })
            const expected = { c: { d: true, e: true } }
            expect(instance.modifications()).to.deep.equal(expected)
          })
        })
      })

      describe('formData()', () => {
        // Since the mock form we set up for this test has no
        // <Input> elements, this should return an empty array.
        // TODO: Write an integration test that covers this.
        it('should return an empty array', () => {
          const formData = instance.formData()
          expect(formData.getAll()).to.deep.equal([])
        })
      })
    })

    describe('private', () => {
      // Usually, we wouldn't test private functions so long as they get called
      // via a test exercising the public API. However, these tests were written
      // much later than the code itself. They exist to help new developers
      // explore & understand the code base. They are very brittle (coupled
      // to implementation). Major design changes will probably require removing
      // these tests. They might still have some use in detecting regressions,
      // but that's not their primary purpose.
      it('_themedFormProps()')
      it('_data()')
      describe('childComponentWill[Mount|Unmount]', () => {
        const wrapper = mount(<Form {...formProps} />)
        const instance = wrapper.instance()

        const object = {}
        instance.childComponentWillMount('name', object)
        const mountedComponent = instance._childComponents()[0]

        it('mount adds components to _childComponents backing array', () => {
          expect(mountedComponent).to.equal(object)
        })

        it('unmount removes components from _childComponents backing array', () => {
          instance.childComponentWillUnmount('name')
          const childComponents = instance._childComponents()
          expect(childComponents).to.deep.equal([])
        })
      })

      it('_onSubmit()')
      describe('_onChildRequestChange()', () => {
        it('updates form.data as notified by children', () => {
          // test doubles
          const onChange = td.function.call()

          // mount component
          const finalProps = {
            ...formProps,
            data: {
              some_input: 'some old value',
              some_other_input: 'this value remains the same',
            },
          }
          const wrapper = mount(<Form {...finalProps} onChange={onChange} />)
          const instance = wrapper.instance()

          // act
          instance._onChildRequestChange('some_input', 'some new value')

          // assert
          const expectedData = {
            some_input: 'some new value',
            some_other_input: 'this value remains the same',
          }
          td.verify(onChange(expectedData))
        })
      })
    })
  })
})
<commit_msg>Add tests for Form._onSubmit<commit_after>/* global describe, it, beforeEach */

import React from 'react'
import { expect } from 'chai'
import Form from '../../src/components/form'
import { mount } from 'enzyme'
import td from 'testdouble'

describe('<Form />', () => {
  // Despite eslint's pleadings, this can't be a stateless function
  // because <Form /> will tack a `ref` on to it, which is illegal.
  class ThemedForm extends React.Component { // eslint-disable-line react/prefer-stateless-function
    render() { return <form>{this.props.children}</form> } // eslint-disable-line react/prop-types
  }

  const formProps = {
    theme: { Form: ThemedForm },
    data: {},
    onChange: () => {},
    saved: {},
    errors: {},
    layout: 'horizontal',
    align: 'right',
  }
  const form = (
    <Form {...formProps} >

child

    </Form>
  )

  it('fails fast when props.data is not provided', () => {
    const badForm = <Form onChange={formProps.onChange} />
    const wrapperBound = mount.bind(null, badForm)
    expect(wrapperBound).to.throw(Error, /data=/)
  })

  it('fails fast when props.onChange is not provided', () => {
    const badForm = <Form data={formProps.data} />
    const wrapperBound = mount.bind(null, badForm)
    expect(wrapperBound).to.throw(Error, /onChange=/)
  })

  it('renders the theme.Form component', () => {
    const wrapper = mount(form)
    expect(wrapper.find(ThemedForm)).to.have.length(1)
  })

  describe('via AbstractForm', () => {
    describe('context', () => {
      const wrapper = mount(<Form {...formProps} />)
      const context = wrapper.instance().getChildContext()

      it('passes context (from props) down to its children', () => {
        expect(context.frigForm).to.exist()
        expect(context.frigForm.errors).to.equal(formProps.errors)
        expect(context.frigForm.layout).to.equal(formProps.layout)
        expect(context.frigForm.theme).to.equal(formProps.theme)
        expect(context.frigForm.align).to.equal(formProps.align)
        expect(context.frigForm.saved).to.equal(formProps.saved)
        expect(context.frigForm.data).to.equal(formProps.data)
      })

      it('passes context (internal callbacks) down to its children', () => {
        expect(context.frigForm.childComponentWillMount).to.be.a('function')
        expect(context.frigForm.childComponentWillUnmount).to.be.a('function')
      })

      // pending
      it('passes requestChildComponentChange via context')
      it('passes submit via context')
    })

    describe('public API', () => {
      let wrapper
      let instance

      beforeEach(() => {
        wrapper = mount(<Form {...formProps} />)
        instance = wrapper.instance()
      })

      describe('validate()', () => {
        it('should return false if any inputs\' validate() returns false', () => {
          instance.childComponentWillMount('a', { validate: () => false })
          instance.childComponentWillMount('b', { validate: () => true })
          expect(instance.validate()).to.be.false()
        })

        it('should return true if all inputs\' validate() returns true', () => {
          instance.childComponentWillMount('a', { validate: () => true })
          instance.childComponentWillMount('b', { validate: () => true })
          expect(instance.validate()).to.be.true()
        })
      })

      describe('isValid()', () => {
        it('should return false if any inputs\' isValid() returns false', () => {
          instance.childComponentWillMount('a', { isValid: () => false })
          instance.childComponentWillMount('b', { isValid: () => true })
          expect(instance.isValid()).to.be.false()
        })

        it('should return true if all inputs\' isValid() returns true', () => {
          instance.childComponentWillMount('a', { isValid: () => true })
          instance.childComponentWillMount('b', { isValid: () => true })
          expect(instance.isValid()).to.be.true()
        })
      })

      describe('isModified()', () => {
        it('should be false initially', () => {
          expect(instance.isModified()).to.be.false()
        })
        it('should be false if all inputs\' isModified() returns false', () => {
          instance.childComponentWillMount('a', { isModified: () => false })
          instance.childComponentWillMount('b', { isModified: () => false })
          expect(instance.isModified()).to.be.false()
        })
        it('should be false if any inputs\' isModified() returns true', () => {
          instance.childComponentWillMount('a', { isModified: () => false })
          instance.childComponentWillMount('b', { isModified: () => true })
          expect(instance.isModified()).to.be.true()
        })
      })

      describe('resetModified()', () => {
        it('should call resetModified() on each component', () => {
          let hasReset = false
          instance.childComponentWillMount('a', {
            resetModified: () => { hasReset = true },
          })
          instance.resetModified()
          expect(hasReset).to.be.true()
        })
      })

      describe('reset()', () => {
        it('should call reset() on each component', () => {
          let hasReset = false
          instance.childComponentWillMount('a', {
            reset: () => { hasReset = true },
          })
          instance.reset()
          expect(hasReset).to.be.true()
        })
      })

      describe('modifications()', () => {
        describe('when no fields are modified', () => {
          it('should return empty object', () => {
            expect(instance.modifications()).to.deep.equal({})
          })
        })
        describe('when fields are modified', () => {
          it('should return only modified values', () => {
            instance.childComponentWillMount('a', { isModified: () => true })
            instance.childComponentWillMount('b', { isModified: () => false })
            const expected = { a: true }
            expect(instance.modifications()).to.deep.equal(expected)
          })
          it('should return nested modified values', () => {
            instance.childComponentWillMount('c', {
              isModified: () => true,
              modifications: () => ({ d: true, e: true }),
            })
            const expected = { c: { d: true, e: true } }
            expect(instance.modifications()).to.deep.equal(expected)
          })
        })
      })

      describe('formData()', () => {
        // Since the mock form we set up for this test has no
        // <Input> elements, this should return an empty array.
        // TODO: Write an integration test that covers this.
        it('should return an empty array', () => {
          const formData = instance.formData()
          expect(formData.getAll()).to.deep.equal([])
        })
      })
    })

    describe('private', () => {
      // Usually, we wouldn't test private functions so long as they get called
      // via a test exercising the public API. However, these tests were written
      // much later than the code itself. They exist to help new developers
      // explore & understand the code base. They are very brittle (coupled
      // to implementation). Major design changes will probably require removing
      // these tests. They might still have some use in detecting regressions,
      // but that's not their primary purpose.
      it('_themedFormProps()')
      it('_data()')
      describe('childComponentWill[Mount|Unmount]', () => {
        const wrapper = mount(<Form {...formProps} />)
        const instance = wrapper.instance()

        const object = {}
        instance.childComponentWillMount('name', object)
        const mountedComponent = instance._childComponents()[0]

        it('mount adds components to _childComponents backing array', () => {
          expect(mountedComponent).to.equal(object)
        })

        it('unmount removes components from _childComponents backing array', () => {
          instance.childComponentWillUnmount('name')
          const childComponents = instance._childComponents()
          expect(childComponents).to.deep.equal([])
        })
      })

      describe('_onChildRequestChange()', () => {
        it('updates form.data as notified by children', () => {
          // test doubles
          const onChange = td.function.call()

          // mount component
          const finalProps = {
            ...formProps,
            data: {
              some_input: 'some old value',
              some_other_input: 'this value remains the same',
            },
          }
          const wrapper = mount(<Form {...finalProps} onChange={onChange} />)
          const instance = wrapper.instance()

          // act
          instance._onChildRequestChange('some_input', 'some new value')

          // assert
          const expectedData = {
            some_input: 'some new value',
            some_other_input: 'this value remains the same',
          }
          td.verify(onChange(expectedData))
        })
      })

      describe('_onSubmit()', () => {
        it('when invalid, should call e.preventDefault', () => {
          // test doubles
          const preventDefault = td.function.call()
          const e = { preventDefault }

          // mount component
          const wrapper = mount(<Form {...formProps} />)
          const instance = wrapper.instance()

          // create a fake child component that always fails validation
          instance.childComponentWillMount('a', { validate: () => false })

          // act
          instance._onSubmit(e)

          // assert
          td.verify(preventDefault())
        })

        it('when valid, should call props.onSubmit(e)', () => {
          // test doubles
          const onSubmit = td.function.call()
          const e = { some_event: true }

          // mount component
          const wrapper = mount(<Form {...formProps} onSubmit={onSubmit} />)
          const instance = wrapper.instance()

          // create a fake child component that always passes validation
          instance.childComponentWillMount('a', { validate: () => true })

          // act
          instance._onSubmit(e)

          // assert that onSubmit was called with our `e`
          td.verify(onSubmit(e))
        })
      })
    })
  })
})
<|endoftext|>


<commit_before>((function() {
    var self, oauthSigner;
    self = this;
    window.oauthSigner = oauthSigner = function(parameters) {
        return _.extend({
            token: function() {
                var self;
                self = this;
                return "";
            },
            tokenSecret: function() {
                var self;
                self = this;
                return "";
            },
            version: function() {
                var self;
                self = this;
                return "1.0";
            },
            signatureMethod: function() {
                var self;
                self = this;
                return "HMAC-SHA1";
            },
            method: function() {
                var self;
                self = this;
                return "GET";
            },
            timestamp: function() {
                var self;
                self = this;
                return Math.floor((new Date).getTime() / 1e3);
            },
            fields: function() {
                var self;
                self = this;
                return {};
            },
            oauthParameters: function() {
                var self, queryFields;
                self = this;
                queryFields = {
                    oauth_consumer_key: self.consumerKey(),
                    oauth_nonce: self.nonce(),
                    oauth_timestamp: self.timestamp(),
                    oauth_signature_method: self.signatureMethod()
                };
                if (self.token()) {
                    queryFields["oauth_token"] = self.token();
                }
                if (self.version()) {
                    queryFields["oauth_version"] = self.version();
                }
                return queryFields;
            },
            queryStringFields: function() {
                var self, queryFields, fields;
                self = this;
                queryFields = self.oauthParameters();
                fields = self.fields();
                _.each(_.keys(fields), function(field) {
                    return queryFields[field] = fields[field];
                });
                return queryFields;
            },
            queryString: function() {
                var self, queryArguments, orderedFields;
                self = this;
                queryArguments = self.queryStringFields();
                orderedFields = _.keys(queryArguments).sort();
                return _.map(orderedFields, function(fieldName) {
                    return fieldName + "=" + self.percentEncode(queryArguments[fieldName]);
                }).join("&");
            },
            urlEncoded: function(fields) {
                var self;
                self = this;
                return _.map(_.keys(fields), function(fieldName) {
                    return fieldName + "=" + encodeURIComponent(fields[fieldName]);
                }).join("&");
            },
            headerEncoded: function(fields) {
                var self;
                self = this;
                return _.map(_.keys(fields), function(fieldName) {
                    return fieldName + '="' + encodeURIComponent(fields[fieldName]) + '"';
                }).join(", ");
            },
            urlEncodedFields: function() {
                var self;
                self = this;
                return self.urlEncoded(self.fields());
            },
            authorizationHeader: function() {
                var self, fields;
                self = this;
                fields = self.oauthParameters();
                fields["oauth_signature"] = self.base64Signature();
                return self.headerEncoded(fields);
            },
            urlAndFields: function() {
                var self, encodedFields;
                self = this;
                encodedFields = self.urlEncodedFields();
                if (encodedFields) {
                    return self.url() + "?" + encodedFields;
                } else {
                    return self.url();
                }
            },
            parameterEncoded: function(fields) {
                var self;
                self = this;
                return _.map(fields, function(field) {
                    return self.percentEncode(field);
                }).join("&");
            },
            baseString: function() {
                var self;
                self = this;
                return self.parameterEncoded([ self.method(), self.url(), self.queryString() ]);
            },
            hmacKey: function() {
                var self;
                self = this;
                return self.parameterEncoded([ self.consumerSecret(), self.tokenSecret() ]);
            },
            hmac: function(gen1_options) {
                var encoding, self;
                encoding = gen1_options && gen1_options.hasOwnProperty("encoding") && gen1_options.encoding !== void 0 ? gen1_options.encoding : "binary";
                self = this;
                if (typeof process !== "undefined") {
                    var crypto, h;
                    crypto = require("crypto");
                    h = crypto.createHmac("sha1", self.hmacKey());
                    h.update(self.baseString());
                    return h.digest(encoding);
                } else {
                    var binaryHash;
                    binaryHash = CryptoJS.HmacSHA1(self.baseString(), self.hmacKey());
                    if (encoding === "base64") {
                        return binaryHash.toString(CryptoJS.enc.Base64);
                    } else {
                        return binaryHash;
                    }
                }
            },
            base64Signature: function() {
                var self;
                self = this;
                return self.hmac({
                    encoding: "base64"
                });
            },
            signature: function() {
                var self;
                self = this;
                return self.percentEncode(self.base64Signature());
            },
            signedUrl: function() {
                var self;
                self = this;
                return self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature();
            },
            curl: function() {
                var self;
                self = this;
                if (self.method() === "GET") {
                    return "curl '" + self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                } else if (self.method() === "POST" || self.method() === "PUT") {
                    if (self.body()) {
                        return "curl -X " + self.method() + " '" + self.urlAndFields() + "' -d '" + self.body() + "' -H 'Authorization: " + self.authorizationHeader() + "' -H 'Content-Type: " + self.bodyEncoding() + "'";
                    } else {
                        return "curl -X " + self.method() + " '" + self.url() + "' -d '" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                    }
                } else {
                    return "curl -X DELETE '" + self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                }
            },
            percentEncode: function(s) {
                var self;
                self = this;
                return encodeURIComponent(s).replace(/\*/g, "%2A");
            }
        }, parameters);
    };
})).call(this);<commit_msg>removing redundant code<commit_after>((function() {
    window.oauthSigner = function(parameters) {
	    var oauthSignerOld = oauthSignerOld(parameters);
        return _.extend({
            token: function() {
                return "";
            },
            tokenSecret: function() {
                return "";
            },
            version: function() {
                return "1.0";
            },
            signatureMethod: function() {
                return "HMAC-SHA1";
            },
            method: function() {
                return "GET";
            },
            timestamp: function() {
                return Math.floor((new Date).getTime() / 1e3);
            },
            fields: function() {
                return {};
            },
            oauthParameters: function() {
                var self, queryFields;
                self = this;
                queryFields = {
                    oauth_consumer_key: self.consumerKey(),
                    oauth_nonce: self.nonce(),
                    oauth_timestamp: self.timestamp(),
                    oauth_signature_method: self.signatureMethod()
                };
                if (self.token()) {
                    queryFields["oauth_token"] = self.token();
                }
                if (self.version()) {
                    queryFields["oauth_version"] = self.version();
                }
                return queryFields;
            },
            queryStringFields: function() {
                var self, queryFields, fields;
                self = this;
                queryFields = self.oauthParameters();
                fields = self.fields();
                _.each(_.keys(fields), function(field) {
                    return queryFields[field] = fields[field];
                });
                return queryFields;
            },
            queryString: function() {
                var self, queryArguments, orderedFields;
                self = this;
                queryArguments = self.queryStringFields();
                orderedFields = _.keys(queryArguments).sort();
                return _.map(orderedFields, function(fieldName) {
                    return fieldName + "=" + self.percentEncode(queryArguments[fieldName]);
                }).join("&");
            },
            urlEncoded: function(fields) {
                return _.map(_.keys(fields), function(fieldName) {
                    return fieldName + "=" + encodeURIComponent(fields[fieldName]);
                }).join("&");
            },
            headerEncoded: function(fields) {
                return _.map(_.keys(fields), function(fieldName) {
                    return fieldName + '="' + encodeURIComponent(fields[fieldName]) + '"';
                }).join(", ");
            },
            urlEncodedFields: function() {
                var self;
                self = this;
                return self.urlEncoded(self.fields());
            },
            authorizationHeader: function() {
                var self, fields;
                self = this;
                fields = self.oauthParameters();
                fields["oauth_signature"] = self.base64Signature();
                return self.headerEncoded(fields);
            },
            urlAndFields: function() {
                var self, encodedFields;
                self = this;
                encodedFields = self.urlEncodedFields();
                if (encodedFields) {
                    return self.url() + "?" + encodedFields;
                } else {
                    return self.url();
                }
            },
            parameterEncoded: function(fields) {
                var self;
                self = this;
                return _.map(fields, function(field) {
                    return self.percentEncode(field);
                }).join("&");
            },
            baseString: function() {
                var self;
                self = this;
                return self.parameterEncoded([ self.method(), self.url(), self.queryString() ]);
            },
            hmacKey: function() {
                var self;
                self = this;
                return self.parameterEncoded([ self.consumerSecret(), self.tokenSecret() ]);
            },
            hmac: function(gen1_options) {
                var encoding, self;
                encoding = gen1_options && gen1_options.hasOwnProperty("encoding") && gen1_options.encoding !== void 0 ? gen1_options.encoding : "binary";
                self = this;
                var binaryHash;
                binaryHash = CryptoJS.HmacSHA1(self.baseString(), self.hmacKey());
                if (encoding === "base64") {
                    return binaryHash.toString(CryptoJS.enc.Base64);
                } else {
                    return binaryHash;
                }
            },
            base64Signature: function() {
                var self;
                self = this;
                return self.hmac({
                    encoding: "base64"
                });
            },
            signature: function() {
                var self;
                self = this;

	            // var signatureNew = oauthSignature.generate(self.method(), self.url(), self.queryString(), self.consumerSecret(), self.tokenSecret());
	            var signatureNew = oauthSignature.generate(self.method(), self.url(), self.queryStringFields(), self.consumerSecret(), self.tokenSecret());
	            var signatureOld = oauthSignerOld.percentEncode(oauthSignerOld.base64Signature());

	            if (signatureNew != signatureOld) {
		            alert('The signatures are different');
		            throw new Error('The signatures are different')
	            }

                return signatureNew;
            },
            signedUrl: function() {
                var self;
                self = this;
                return self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature();
            },
            curl: function() {
                var self;
                self = this;
                if (self.method() === "GET") {
                    return "curl '" + self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                } else if (self.method() === "POST" || self.method() === "PUT") {
                    if (self.body()) {
                        return "curl -X " + self.method() + " '" + self.urlAndFields() + "' -d '" + self.body() + "' -H 'Authorization: " + self.authorizationHeader() + "' -H 'Content-Type: " + self.bodyEncoding() + "'";
                    } else {
                        return "curl -X " + self.method() + " '" + self.url() + "' -d '" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                    }
                } else {
                    return "curl -X DELETE '" + self.url() + "?" + self.queryString() + "&oauth_signature=" + self.signature() + "'";
                }
            },
            percentEncode: function(s) {
                return encodeURIComponent(s).replace(/\*/g, "%2A");
            }
        }, parameters);
    };
})).call(this);<|endoftext|>


<commit_before>// game objects
var game;
var banner;
var fps;
var infoScreen;
var credits;

// Game info
var global = {obstaclesLeft: 0, difficulty: 0, score: 0, lastscore: 0, highscore: 0, frameRateModifier: 1};
const MODE_BANNER = 0;
const MODE_GAME = 1;
const MODE_CREDITS = 2;
var appMode = MODE_BANNER;

// Sounds
var music;
var soundEndgame;
var soundJump;
var soundNextlevel;

function preload() {
    music = loadSound("sounds/music.mp3");
    soundEndgame = loadSound("sounds/endgame.wav");
    soundJump = loadSound("sounds/jump.wav");
    soundNextlevel = loadSound("sounds/nextlevel.wav");

    global.highscore = Cookies.get('highscore') || 0;
    global.lastscore = Cookies.get('lastscore') || 0;

}

function setup() {
    createCanvas(windowWidth, windowHeight);
    game = new Game(global, banner);
    infoScreen = new InfoScreen(global);
    fps = new Fps();
}

function draw() {
    background(0, 0, 255);

    switch (appMode) {
        case MODE_BANNER:
            playMusic(false);
            if (!banner.closeBanner) {
                banner.draw()
            } else {
                banner = null;
                appMode = MODE_GAME;
            }
            break;
        case MODE_GAME:
            playMusic(true);
            game.draw();
            break;
        case MODE_CREDITS:
            playMusic(true);
            credits.draw();
            if (!credits.closeCredits) {
                credits.draw()
            } else {
                credits = null;
                appMode = MODE_GAME;
            }
            break;
    }

    infoScreen.draw();
    fps.draw();
    global.frameRateModifier = 60 / fps.avgFrameRate;
}

function playMusic(bool) {
    if (!music.isPlaying() && bool)
        music.play();

    if (music.isPlaying() && !bool)
        music.stop();
}

function saveScores() {
    Cookies.set('highscore', global.highscore);
    Cookies.set('lastscore', global.lastscore);
}

function userInput() {
    switch (appMode) {
        case MODE_BANNER:
            banner.userInput();
            break;
        case MODE_CREDITS:
            credits.userInput();
            break;
        case MODE_GAME:
            infoScreen.userInput();
            if (appMode == MODE_GAME)
                game.userInput();
            break;
    }
}

function mouseIsPressed() {
    userInput();
    return false;
}

function keyPressed() {
    if (key = ' ') userInput();
    return false;
}

function touchStarted() {
    userInput();
    return false;
}<commit_msg>Play music in loop-mode<commit_after>// game objects
var game;
var banner;
var fps;
var infoScreen;
var credits;

// Game info
var global = {obstaclesLeft: 0, difficulty: 0, score: 0, lastscore: 0, highscore: 0, frameRateModifier: 1};
const MODE_BANNER = 0;
const MODE_GAME = 1;
const MODE_CREDITS = 2;
var appMode = MODE_BANNER;

// Sounds
var music;
var soundEndgame;
var soundJump;
var soundNextlevel;

function preload() {
    music = loadSound("sounds/music.mp3");
    soundEndgame = loadSound("sounds/endgame.wav");
    soundJump = loadSound("sounds/jump.wav");
    soundNextlevel = loadSound("sounds/nextlevel.wav");

    global.highscore = Cookies.get('highscore') || 0;
    global.lastscore = Cookies.get('lastscore') || 0;

}

function setup() {
    createCanvas(windowWidth, windowHeight);
    game = new Game(global, banner);
    infoScreen = new InfoScreen(global);
    fps = new Fps();
}

function draw() {
    background(0, 0, 255);

    switch (appMode) {
        case MODE_BANNER:
            playMusic(false);
            if (!banner.closeBanner) {
                banner.draw()
            } else {
                banner = null;
                appMode = MODE_GAME;
            }
            break;
        case MODE_GAME:
            playMusic(true);
            game.draw();
            break;
        case MODE_CREDITS:
            playMusic(true);
            credits.draw();
            if (!credits.closeCredits) {
                credits.draw()
            } else {
                credits = null;
                appMode = MODE_GAME;
            }
            break;
    }

    infoScreen.draw();
    fps.draw();
    global.frameRateModifier = 60 / fps.avgFrameRate;
}

function playMusic(bool) {
    if (!music.isPlaying() && bool)
        music.loop();

    if (music.isPlaying() && !bool)
        music.stop();
}

function saveScores() {
    Cookies.set('highscore', global.highscore);
    Cookies.set('lastscore', global.lastscore);
}

function userInput() {
    switch (appMode) {
        case MODE_BANNER:
            banner.userInput();
            break;
        case MODE_CREDITS:
            credits.userInput();
            break;
        case MODE_GAME:
            infoScreen.userInput();
            if (appMode == MODE_GAME)
                game.userInput();
            break;
    }
}

function mouseIsPressed() {
    userInput();
    return false;
}

function keyPressed() {
    if (key = ' ') userInput();
    return false;
}

function touchStarted() {
    userInput();
    return false;
}<|endoftext|>


<commit_before>import craftai, { errors } from '../src';

describe.only('craftai(<token_or_cfg>)', function() {
  it('should create a valid client given a valid configuration', function() {
    const client = craftai(CRAFT_CFG);
    expect(client.cfg.url).to.be.defined;
    expect(client.cfg.owner).to.be.defined;
    expect(client.cfg.project).to.be.defined;
    expect(client.cfg.token).to.be.equal(CRAFT_CFG.token);
  });
  it('should create a valid client given a valid token', function() {
    const client = craftai(CRAFT_CFG.token);
    expect(client.cfg.url).to.be.defined;
    expect(client.cfg.owner).to.be.defined;
    expect(client.cfg.project).to.be.defined;
    expect(client.cfg.token).to.be.equal(CRAFT_CFG.token);
  });
  it('should fail properly given an invalid token', function() {
    expect(() => craftai('this is an invalid token')).to.throw(errors.CraftAiCredentialsError);
  });
});
<commit_msg>Remove left behind '.only'<commit_after>import craftai, { errors } from '../src';

describe('craftai(<token_or_cfg>)', function() {
  it('should create a valid client given a valid configuration', function() {
    const client = craftai(CRAFT_CFG);
    expect(client.cfg.url).to.be.defined;
    expect(client.cfg.owner).to.be.defined;
    expect(client.cfg.project).to.be.defined;
    expect(client.cfg.token).to.be.equal(CRAFT_CFG.token);
  });
  it('should create a valid client given a valid token', function() {
    const client = craftai(CRAFT_CFG.token);
    expect(client.cfg.url).to.be.defined;
    expect(client.cfg.owner).to.be.defined;
    expect(client.cfg.project).to.be.defined;
    expect(client.cfg.token).to.be.equal(CRAFT_CFG.token);
  });
  it('should fail properly given an invalid token', function() {
    expect(() => craftai('this is an invalid token')).to.throw(errors.CraftAiCredentialsError);
  });
});
<|endoftext|>


<commit_before>// Инициализация класса
var Rtc = function(i2c) {
  this._time = undefined;
  this._i2c = i2c || PrimaryI2C;
  this._address = 0x68;
  this.start();
};

// Метод записывает данные data в регистр reg
Rtc.prototype.write = function(reg, data) {
  this._i2c.writeTo(this._address, [reg, data]);
};

// Метод производит чтение из регистра reg количестов байт count
Rtc.prototype.read = function(reg, count) {
  if (count === undefined) {
    count = 1;
  }
  this._i2c.writeTo(this._address, reg);
  return this._i2c.readFrom(this._address, count);
};

Rtc.prototype._decToBcd = function(val) {
  return Math.floor(val / 10) * 16 + (val % 10);
};

Rtc.prototype._bcdToDec = function(val) {
  return Math.floor(val / 16) * 10 + (val % 16);
};

Rtc.prototype.setTime = function(time) {
  if (time instanceof Date) {
    this._time = time;
  } else if (time instanceof Object){
    this._time = new Date(
      time.year,
      time.month,
      time.day,
      time.hour,
      time.minute,
      time.second
    );
  } else if (typeof time === 'number') {
    this._time = new Date(time * 1000);
  } else if (typeof time === 'string') {
    this._time = new Date(Date.parse(time));
  } else {
    this._time = new Date(0);
  }

  var halt = this.read(0x00, 1)[0] >> 7;
  this.write(0x00, [
    this._decToBcd(this._time.getSeconds()) | (halt << 7),
    this._decToBcd(this._time.getMinutes()),
    this._decToBcd(this._time.getHours()),
    this._decToBcd(this._time.getDay() + 1),
    this._decToBcd(this._time.getDate()),
    this._decToBcd(this._time.getMonth() + 1),
    this._decToBcd(this._time.getFullYear() - 2000)
  ]);
};

Rtc.prototype.getTime = function(unit) {
  var time = this.read(0x00, 7);
  this._time = new Date(
    this._bcdToDec(time[6]) + 2000,
    this._bcdToDec(time[5]) - 1,
    this._bcdToDec(time[4]),
    this._bcdToDec(time[2] & 0x3f),
    this._bcdToDec(time[1]),
    this._bcdToDec(time[0] & 0x7f)
  );

  var res = this._time;
  switch (unit) {
    case 'unixtime':
      res = Math.ceil(res.getTime() / 1000);
      break;
    case 'iso':
      res = res.getFullYear() + '-' + res.getMonth() + 1 +
         '-' + res.getDay() + 'T' + res.getHours() +
         ':' + res.getMinutes() + ':' + res.getSeconds();
      break;
  }
  return res;
};

Rtc.prototype.start = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7) {
    this.write(0x00, byte ^ 0x80);
  }
};

Rtc.prototype.stop = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7 === 0) {
    this.write(0x00, byte ^ 0x80);
  }
};

// Экспортируем класс
exports.connect = function(i2c) {
  return new Rtc(i2c);
};
<commit_msg>setTime - default: now<commit_after>// Инициализация класса
var Rtc = function(i2c) {
  this._time = undefined;
  this._i2c = i2c || PrimaryI2C;
  this._address = 0x68;
  this.start();
};

// Метод записывает данные data в регистр reg
Rtc.prototype.write = function(reg, data) {
  this._i2c.writeTo(this._address, [reg, data]);
};

// Метод производит чтение из регистра reg количестов байт count
Rtc.prototype.read = function(reg, count) {
  if (count === undefined) {
    count = 1;
  }
  this._i2c.writeTo(this._address, reg);
  return this._i2c.readFrom(this._address, count);
};

Rtc.prototype._decToBcd = function(val) {
  return Math.floor(val / 10) * 16 + (val % 10);
};

Rtc.prototype._bcdToDec = function(val) {
  return Math.floor(val / 16) * 10 + (val % 16);
};

Rtc.prototype.setTime = function(time) {
  if (time instanceof Date) {
    this._time = time;
  } else if (time instanceof Object){
    this._time = new Date(
      time.year,
      time.month,
      time.day,
      time.hour,
      time.minute,
      time.second
    );
  } else if (typeof time === 'number') {
    this._time = new Date(time * 1000);
  } else if (typeof time === 'string') {
    this._time = new Date(Date.parse(time));
  } else {
    this._time = new Date(getTime()*1000);
  }

  var halt = this.read(0x00, 1)[0] >> 7;
  this.write(0x00, [
    this._decToBcd(this._time.getSeconds()) | (halt << 7),
    this._decToBcd(this._time.getMinutes()),
    this._decToBcd(this._time.getHours()),
    this._decToBcd(this._time.getDay() + 1),
    this._decToBcd(this._time.getDate()),
    this._decToBcd(this._time.getMonth() + 1),
    this._decToBcd(this._time.getFullYear() - 2000)
  ]);
};

Rtc.prototype.getTime = function(unit) {
  var time = this.read(0x00, 7);
  this._time = new Date(
    this._bcdToDec(time[6]) + 2000,
    this._bcdToDec(time[5]) - 1,
    this._bcdToDec(time[4]),
    this._bcdToDec(time[2] & 0x3f),
    this._bcdToDec(time[1]),
    this._bcdToDec(time[0] & 0x7f)
  );

  var res = this._time;
  switch (unit) {
    case 'unixtime':
      res = Math.ceil(res.getTime() / 1000);
      break;
    case 'iso':
      res = res.getFullYear() + '-' + res.getMonth() + 1 +
         '-' + res.getDay() + 'T' + res.getHours() +
         ':' + res.getMinutes() + ':' + res.getSeconds();
      break;
  }
  return res;
};

Rtc.prototype.start = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7) {
    this.write(0x00, byte ^ 0x80);
  }
};

Rtc.prototype.stop = function() {
  var byte = this.read(0x00, 1)[0];
  if (byte >> 7 === 0) {
    this.write(0x00, byte ^ 0x80);
  }
};

// Экспортируем класс
exports.connect = function(i2c) {
  return new Rtc(i2c);
};
<|endoftext|>


<commit_before>//  Copyright 2002-2014, University of Colorado Boulder

/**
 * This WebGL renderer is used to draw colored triangles.  Vertices are allocated for geometry + colors, and can be updated
 * dynamically.
 * TODO: Can this same pattern be used for interleaved texture coordinates? (Or other interleaved data?)
 * TODO: Work in progress, much to be done here!
 * TODO: Add this file to the list of scenery files (for jshint, etc.)
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Color = require( 'SCENERY/util/Color' );
  var Matrix4 = require( 'DOT/Matrix4' );

  /**
   * TODO - Merge all these individual array into a single structure to improve performance
   * @constructor
   */
  function ColorTriangleBufferData() {

    //TODO: Use Float32Array -- though we will have to account for the fact that they have a fixed size
    this.vertexArray = []; //x,y,z,r,g,b,a

    //TODO: Add these elements to the vertexArray
    // ,m11,m12,m13,m21,m22,m23
  }

  return inherit( Object, ColorTriangleBufferData, {

    /**
     * Add geometry and color for a scenery path using sampling + triangulation.
     * Uses poly2tri for triangulation
     * @param path
     */
    createFromPath: function( path, z ) {
      assert && assert( z !== undefined );

      var matrix4 = path.getLocalToGlobalMatrix().toMatrix4();
      var shape = path.shape;
      var color = new Color( path.fill );
      var linear = shape.toPiecewiseLinear( {} );
      var subpaths = linear.subpaths;

      // Output to a string for ease of debugging within http://r3mi.github.io/poly2tri.js/
      var string = '';

      // Output the contour to an array of poly2tri.Point
      var contour = [];

      for ( var i = 0; i < subpaths.length; i++ ) {
        var subpath = subpaths[i];
        for ( var k = 0; k < subpath.points.length; k++ ) {

          string = string + '' + subpath.points[k].x + ' ' + subpath.points[k].y + '\n';

          //Add the points into the contour, but don't duplicate the last point.
          //TODO: how to handle closed vs open shapes
          if ( k < subpath.points.length - 1 ) {
            contour.push( new poly2tri.Point( subpath.points[k].x, subpath.points[k].y ) );
          }
        }
      }

      // Triangulate using poly2tri
      // Circle linearization is creating some duplicated points, so bail on those for now.
      var triangles;
      try {
        triangles = new poly2tri.SweepContext( contour ).triangulate().getTriangles();
      }
      catch( error ) {
        console.log( 'error in triangulation', error );
        triangles = [];
      }

      // Add the triangulated geometry into the array buffer.
      for ( var k = 0; k < triangles.length; k++ ) {
        var triangle = triangles[k];
        for ( var zz = 0; zz < triangle.points_.length; zz++ ) {
          var pt = triangle.points_[zz];

          // Mutate the vertices a bit to see what is going on.  Or not.
          var randFactor = 0;
          this.vertexArray.push( pt.x + Math.random() * randFactor, pt.y + Math.random() * randFactor, z,
              color.red / 255, color.green / 255, color.blue / 255, color.alpha,
            matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12()
          );
        }
      }
    },
    createFromTriangle: function( x1, y1, x2, y2, x3, y3, color, z, matrix4 ) {
      assert && assert( z !== undefined );

      matrix4 = matrix4 || Matrix4.identity();
      color = new Color( color );
      var r = color.red / 255;
      var g = color.green / 255;
      var b = color.blue / 255;
      var a = color.alpha;

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      colorTriangleBufferData.vertexArray.push(
        // Top left
        x1, y1, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        x2, y2, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        x3, y3, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12()
      );

      //Track the index so it can delete itself, update itself, etc.
      //TODO: Move to a separate class.
      return {
        index: index,
        endIndex: colorTriangleBufferData.vertexArray.length,
        setTriangle: function( x1, y1, x2, y2, x3, y3 ) {
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 0] = x1;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 0] = y1;
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 1] = x2;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 1] = y2;
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 2] = x3;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 2] = y3;
        },
        setZ: function( z ) {
          colorTriangleBufferData.vertexArray[index + 2] = z;
          colorTriangleBufferData.vertexArray[index + 5] = z;
          colorTriangleBufferData.vertexArray[index + 8] = z;
        }
      };
    },
    createFromRectangle: function( rectangle, z ) {
      assert && assert( z !== undefined );

      var color = new Color( rectangle.fill );

      return this.createRectangle( rectangle.rectX, rectangle.rectY, rectangle.rectWidth, rectangle.rectHeight,
          color.red / 255, color.green / 255, color.blue / 255, color.alpha, z,
        rectangle.getLocalToGlobalMatrix().toMatrix4() );
    },
    createRectangle: function( x, y, width, height, r, g, b, a, z, matrix4 ) {
      assert && assert( z !== undefined );

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      this.vertexArray.push(
        // Top left
        //TODO: Maybe should be m03 for last element, see Matrix3.toAffineMatrix4
        x, y, z, r, g, b, a, /*               */matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        (x + width), y, z, r, g, b, a, /*     */matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        x, y + height, z, r, g, b, a, /*      */matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),

        // Bottom right
        (x + width), y + height, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        (x + width), y, z, r, g, b, a, /*     */matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12(),
        x, y + height, z, r, g, b, a, /*      */matrix4.m00(), matrix4.m01(), matrix4.m02(), matrix4.m10(), matrix4.m11(), matrix4.m12()
      );

      //Track the index so it can delete itself, update itself, etc.
      //TODO: Move to a separate class.
      return {
        initialState: {x: x, y: y, width: width, height: height},
        index: index,
        endIndex: colorTriangleBufferData.vertexArray.length,
        setTransform: function( matrix4 ) {
          for ( var i = 0; i < 6; i++ ) {
            colorTriangleBufferData.vertexArray[index + 7 + i * 13] = matrix4.m00();
            colorTriangleBufferData.vertexArray[index + 8 + i * 13] = matrix4.m01();
            colorTriangleBufferData.vertexArray[index + 9 + i * 13] = matrix4.m02();
            colorTriangleBufferData.vertexArray[index + 10 + i * 13] = matrix4.m10();
            colorTriangleBufferData.vertexArray[index + 11 + i * 13] = matrix4.m11();
            colorTriangleBufferData.vertexArray[index + 12 + i * 13] = matrix4.m12();
          }
        },
        setXWidth: function( x, width ) {
          colorTriangleBufferData.vertexArray[index + 13 * 0] = x;
          colorTriangleBufferData.vertexArray[index + 13 * 1] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 2] = x;
          colorTriangleBufferData.vertexArray[index + 13 * 3] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 4] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 5] = x;
        },
        setRect: function( x, y, width, height ) {

          colorTriangleBufferData.vertexArray[index + 0] = x;
          colorTriangleBufferData.vertexArray[index + 1] = y;

          colorTriangleBufferData.vertexArray[index + 7] = x + width;
          colorTriangleBufferData.vertexArray[index + 8] = y;

          colorTriangleBufferData.vertexArray[index + 14] = x;
          colorTriangleBufferData.vertexArray[index + 15] = y + height;

          colorTriangleBufferData.vertexArray[index + 21] = x + width;
          colorTriangleBufferData.vertexArray[index + 22] = y + height;

          colorTriangleBufferData.vertexArray[index + 28] = x + width;
          colorTriangleBufferData.vertexArray[index + 29] = y;

          colorTriangleBufferData.vertexArray[index + 35] = x;
          colorTriangleBufferData.vertexArray[index + 36] = y + height;
        }
      };
    },

    createStar: function( _x, _y, _innerRadius, _outerRadius, _totalAngle, r, g, b, a, z ) {
      assert && assert( z !== undefined );

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      for ( var i = 0; i < 18; i++ ) {
        this.vertexArray.push( 0 );
      }

      // Add the same color for all vertices (solid fill star).
      // TODO: some way to reduce this amount of elements!
//      this.colors.push(
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a,
//
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a,
//
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a
//      );

      //Track the index so it can delete itself, update itself, etc.
      var myStar = {
        initialState: {_x: _x, _y: _y, _innerRadius: _innerRadius, _outerRadius: _outerRadius, _totalAngle: _totalAngle},
        index: index,
        setStar: function( _x, _y, _innerRadius, _outerRadius, _totalAngle ) {

          var points = [];
          //Create the points for a filled-in star, which will be used to compute the geometry of a partial star.
          for ( i = 0; i < 10; i++ ) {

            //Start at the top and proceed clockwise
            var angle = i / 10 * Math.PI * 2 - Math.PI / 2 + _totalAngle;
            var radius = i % 2 === 0 ? _outerRadius : _innerRadius;
            var x = radius * Math.cos( angle ) + _x;
            var y = radius * Math.sin( angle ) + _y;
            points.push( {x: x, y: y} );
          }

          var index = this.index;
          colorTriangleBufferData.vertexArray[index + 0] = points[0].x;
          colorTriangleBufferData.vertexArray[index + 1] = points[0].y;
          colorTriangleBufferData.vertexArray[index + 2] = points[3].x;
          colorTriangleBufferData.vertexArray[index + 3] = points[3].y;
          colorTriangleBufferData.vertexArray[index + 4] = points[6].x;
          colorTriangleBufferData.vertexArray[index + 5] = points[6].y;

          colorTriangleBufferData.vertexArray[index + 6] = points[8].x;
          colorTriangleBufferData.vertexArray[index + 7] = points[8].y;
          colorTriangleBufferData.vertexArray[index + 8] = points[2].x;
          colorTriangleBufferData.vertexArray[index + 9] = points[2].y;
          colorTriangleBufferData.vertexArray[index + 10] = points[5].x;
          colorTriangleBufferData.vertexArray[index + 11] = points[5].y;

          colorTriangleBufferData.vertexArray[index + 12] = points[0].x;
          colorTriangleBufferData.vertexArray[index + 13] = points[0].y;
          colorTriangleBufferData.vertexArray[index + 14] = points[7].x;
          colorTriangleBufferData.vertexArray[index + 15] = points[7].y;
          colorTriangleBufferData.vertexArray[index + 16] = points[4].x;
          colorTriangleBufferData.vertexArray[index + 17] = points[4].y;
        }
      };
      myStar.setStar( _x, _y, _innerRadius, _outerRadius, _totalAngle );
      return myStar;
    }
  } );
} );<commit_msg>use m03 and m13 to match textureBufferData (though still not sure which is right)<commit_after>//  Copyright 2002-2014, University of Colorado Boulder

/**
 * This WebGL renderer is used to draw colored triangles.  Vertices are allocated for geometry + colors, and can be updated
 * dynamically.
 * TODO: Can this same pattern be used for interleaved texture coordinates? (Or other interleaved data?)
 * TODO: Work in progress, much to be done here!
 * TODO: Add this file to the list of scenery files (for jshint, etc.)
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Color = require( 'SCENERY/util/Color' );
  var Matrix4 = require( 'DOT/Matrix4' );

  /**
   * TODO - Merge all these individual array into a single structure to improve performance
   * @constructor
   */
  function ColorTriangleBufferData() {

    //TODO: Use Float32Array -- though we will have to account for the fact that they have a fixed size
    this.vertexArray = []; //x,y,z,r,g,b,a

    //TODO: Add these elements to the vertexArray
    // ,m11,m13,m13,m21,m22,m23
  }

  return inherit( Object, ColorTriangleBufferData, {

    /**
     * Add geometry and color for a scenery path using sampling + triangulation.
     * Uses poly2tri for triangulation
     * @param path
     */
    createFromPath: function( path, z ) {
      assert && assert( z !== undefined );

      var matrix4 = path.getLocalToGlobalMatrix().toMatrix4();
      var shape = path.shape;
      var color = new Color( path.fill );
      var linear = shape.toPiecewiseLinear( {} );
      var subpaths = linear.subpaths;

      // Output to a string for ease of debugging within http://r3mi.github.io/poly2tri.js/
      var string = '';

      // Output the contour to an array of poly2tri.Point
      var contour = [];

      for ( var i = 0; i < subpaths.length; i++ ) {
        var subpath = subpaths[i];
        for ( var k = 0; k < subpath.points.length; k++ ) {

          string = string + '' + subpath.points[k].x + ' ' + subpath.points[k].y + '\n';

          //Add the points into the contour, but don't duplicate the last point.
          //TODO: how to handle closed vs open shapes
          if ( k < subpath.points.length - 1 ) {
            contour.push( new poly2tri.Point( subpath.points[k].x, subpath.points[k].y ) );
          }
        }
      }

      // Triangulate using poly2tri
      // Circle linearization is creating some duplicated points, so bail on those for now.
      var triangles;
      try {
        triangles = new poly2tri.SweepContext( contour ).triangulate().getTriangles();
      }
      catch( error ) {
        console.log( 'error in triangulation', error );
        triangles = [];
      }

      // Add the triangulated geometry into the array buffer.
      for ( var k = 0; k < triangles.length; k++ ) {
        var triangle = triangles[k];
        for ( var zz = 0; zz < triangle.points_.length; zz++ ) {
          var pt = triangle.points_[zz];

          // Mutate the vertices a bit to see what is going on.  Or not.
          var randFactor = 0;
          this.vertexArray.push( pt.x + Math.random() * randFactor, pt.y + Math.random() * randFactor, z,
              color.red / 255, color.green / 255, color.blue / 255, color.alpha,
            matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13()
          );
        }
      }
    },
    createFromTriangle: function( x1, y1, x2, y2, x3, y3, color, z, matrix4 ) {
      assert && assert( z !== undefined );

      matrix4 = matrix4 || Matrix4.identity();
      color = new Color( color );
      var r = color.red / 255;
      var g = color.green / 255;
      var b = color.blue / 255;
      var a = color.alpha;

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      colorTriangleBufferData.vertexArray.push(
        // Top left
        x1, y1, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        x2, y2, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        x3, y3, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13()
      );

      //Track the index so it can delete itself, update itself, etc.
      //TODO: Move to a separate class.
      return {
        index: index,
        endIndex: colorTriangleBufferData.vertexArray.length,
        setTriangle: function( x1, y1, x2, y2, x3, y3 ) {
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 0] = x1;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 0] = y1;
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 1] = x2;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 1] = y2;
          colorTriangleBufferData.vertexArray[index + 0 + 13 * 2] = x3;
          colorTriangleBufferData.vertexArray[index + 1 + 13 * 2] = y3;
        },
        setZ: function( z ) {
          colorTriangleBufferData.vertexArray[index + 2] = z;
          colorTriangleBufferData.vertexArray[index + 5] = z;
          colorTriangleBufferData.vertexArray[index + 8] = z;
        }
      };
    },
    createFromRectangle: function( rectangle, z ) {
      assert && assert( z !== undefined );

      var color = new Color( rectangle.fill );

      return this.createRectangle( rectangle.rectX, rectangle.rectY, rectangle.rectWidth, rectangle.rectHeight,
          color.red / 255, color.green / 255, color.blue / 255, color.alpha, z,
        rectangle.getLocalToGlobalMatrix().toMatrix4() );
    },
    createRectangle: function( x, y, width, height, r, g, b, a, z, matrix4 ) {
      assert && assert( z !== undefined );

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      this.vertexArray.push(
        // Top left
        //TODO: Maybe should be m03 for last element, see Matrix3.toAffineMatrix4
        x, y, z, r, g, b, a, /*               */matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        (x + width), y, z, r, g, b, a, /*     */matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        x, y + height, z, r, g, b, a, /*      */matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),

        // Bottom right
        (x + width), y + height, z, r, g, b, a, matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        (x + width), y, z, r, g, b, a, /*     */matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13(),
        x, y + height, z, r, g, b, a, /*      */matrix4.m00(), matrix4.m01(), matrix4.m03(), matrix4.m10(), matrix4.m11(), matrix4.m13()
      );

      //Track the index so it can delete itself, update itself, etc.
      //TODO: Move to a separate class.
      return {
        initialState: {x: x, y: y, width: width, height: height},
        index: index,
        endIndex: colorTriangleBufferData.vertexArray.length,
        setTransform: function( matrix4 ) {
          for ( var i = 0; i < 6; i++ ) {
            colorTriangleBufferData.vertexArray[index + 7 + i * 13] = matrix4.m00();
            colorTriangleBufferData.vertexArray[index + 8 + i * 13] = matrix4.m01();
            colorTriangleBufferData.vertexArray[index + 9 + i * 13] = matrix4.m03();
            colorTriangleBufferData.vertexArray[index + 10 + i * 13] = matrix4.m10();
            colorTriangleBufferData.vertexArray[index + 11 + i * 13] = matrix4.m11();
            colorTriangleBufferData.vertexArray[index + 12 + i * 13] = matrix4.m13();
          }
        },
        setXWidth: function( x, width ) {
          colorTriangleBufferData.vertexArray[index + 13 * 0] = x;
          colorTriangleBufferData.vertexArray[index + 13 * 1] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 2] = x;
          colorTriangleBufferData.vertexArray[index + 13 * 3] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 4] = x + width;
          colorTriangleBufferData.vertexArray[index + 13 * 5] = x;
        },
        setRect: function( x, y, width, height ) {

          colorTriangleBufferData.vertexArray[index + 0] = x;
          colorTriangleBufferData.vertexArray[index + 1] = y;

          colorTriangleBufferData.vertexArray[index + 7] = x + width;
          colorTriangleBufferData.vertexArray[index + 8] = y;

          colorTriangleBufferData.vertexArray[index + 14] = x;
          colorTriangleBufferData.vertexArray[index + 15] = y + height;

          colorTriangleBufferData.vertexArray[index + 21] = x + width;
          colorTriangleBufferData.vertexArray[index + 22] = y + height;

          colorTriangleBufferData.vertexArray[index + 28] = x + width;
          colorTriangleBufferData.vertexArray[index + 29] = y;

          colorTriangleBufferData.vertexArray[index + 35] = x;
          colorTriangleBufferData.vertexArray[index + 36] = y + height;
        }
      };
    },

    createStar: function( _x, _y, _innerRadius, _outerRadius, _totalAngle, r, g, b, a, z ) {
      assert && assert( z !== undefined );

      var colorTriangleBufferData = this;
      var index = this.vertexArray.length;
      for ( var i = 0; i < 18; i++ ) {
        this.vertexArray.push( 0 );
      }

      // Add the same color for all vertices (solid fill star).
      // TODO: some way to reduce this amount of elements!
//      this.colors.push(
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a,
//
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a,
//
//        r, g, b, a,
//        r, g, b, a,
//        r, g, b, a
//      );

      //Track the index so it can delete itself, update itself, etc.
      var myStar = {
        initialState: {_x: _x, _y: _y, _innerRadius: _innerRadius, _outerRadius: _outerRadius, _totalAngle: _totalAngle},
        index: index,
        setStar: function( _x, _y, _innerRadius, _outerRadius, _totalAngle ) {

          var points = [];
          //Create the points for a filled-in star, which will be used to compute the geometry of a partial star.
          for ( i = 0; i < 10; i++ ) {

            //Start at the top and proceed clockwise
            var angle = i / 10 * Math.PI * 2 - Math.PI / 2 + _totalAngle;
            var radius = i % 2 === 0 ? _outerRadius : _innerRadius;
            var x = radius * Math.cos( angle ) + _x;
            var y = radius * Math.sin( angle ) + _y;
            points.push( {x: x, y: y} );
          }

          var index = this.index;
          colorTriangleBufferData.vertexArray[index + 0] = points[0].x;
          colorTriangleBufferData.vertexArray[index + 1] = points[0].y;
          colorTriangleBufferData.vertexArray[index + 2] = points[3].x;
          colorTriangleBufferData.vertexArray[index + 3] = points[3].y;
          colorTriangleBufferData.vertexArray[index + 4] = points[6].x;
          colorTriangleBufferData.vertexArray[index + 5] = points[6].y;

          colorTriangleBufferData.vertexArray[index + 6] = points[8].x;
          colorTriangleBufferData.vertexArray[index + 7] = points[8].y;
          colorTriangleBufferData.vertexArray[index + 8] = points[2].x;
          colorTriangleBufferData.vertexArray[index + 9] = points[2].y;
          colorTriangleBufferData.vertexArray[index + 10] = points[5].x;
          colorTriangleBufferData.vertexArray[index + 11] = points[5].y;

          colorTriangleBufferData.vertexArray[index + 12] = points[0].x;
          colorTriangleBufferData.vertexArray[index + 13] = points[0].y;
          colorTriangleBufferData.vertexArray[index + 14] = points[7].x;
          colorTriangleBufferData.vertexArray[index + 15] = points[7].y;
          colorTriangleBufferData.vertexArray[index + 16] = points[4].x;
          colorTriangleBufferData.vertexArray[index + 17] = points[4].y;
        }
      };
      myStar.setStar( _x, _y, _innerRadius, _outerRadius, _totalAngle );
      return myStar;
    }
  } );
} );<|endoftext|>


<commit_before>var pg = require('pg');
var step = require('step');
var ltx = require('ltx');
var errors = require('./errors');

/* ready DB connections */
var pool = [];
/* waiting transaction requests */
var queue = [];

/* at start and when connection died */
function connectDB(config) {
    var db = new pg.Client(config);
    db.connect();

    /* Reconnect in up to 5s */
    db.on('error', function(err) {
	console.error('Postgres: ' + err.message);
	setTimeout(function() {
	    connectDB(config);
	}, Math.ceil(Math.random() * 5000));
	try { db.end(); } catch (e) { /* Alright */ }
    });

    /* wait until connected & authed */
    db.connection.once('readyForQuery', function() {
	dbIsAvailable(db);
    });
}

function dbIsAvailable(db) {
    var cb;
    if ((cb = queue.shift())) {
	/* request was waiting in queue */
	cb(db);
    } else {
	/* no request, put into pool */
	pool.push(db);
    }
}

/* config: { user, database, host, port, poolSize: 4 } */
exports.start = function(config) {
    for(var i = 0; i < (config.poolSize || 4); i++) {
	connectDB(config);
    }
};

exports.transaction = function(cb) {
    var db;
    if ((db = pool.shift())) {
	/* Got one from pool */
	new Transaction(db, cb);
    } else {
	/* Pool was empty, waiting... TODO: limit length, shift first */
	queue.push(function(db) {
	    new Transaction(db, cb);
	});
    }
};

/**
 * Wraps the postgres-js transaction with our model operations.
 */
function Transaction(db, cb) {
    var that = this;
    this.db = db;

    db.query("BEGIN", [], function(err, res) {
	cb(err, that);
    });
}

Transaction.prototype.commit = function(cb) {
    var db = this.db;
    db.query("COMMIT", [], function(err, res) {
	process.nextTick(function() {
	    dbIsAvailable(db);
	});

	cb(err);
    });
};

Transaction.prototype.rollback = function(cb) {
    var db = this.db;

    db.query("ROLLBACK", [], function(err, res) {
	process.nextTick(function() {
	    dbIsAvailable(db);
	});

	cb(err);
    });
};

/**
 * Actual data model
 */

/**
 * Can be dropped in a step() sequence to validate presence of a node.
 */
Transaction.prototype.nodeExists = function(node) {
    var db = this.db;

    return function() {
	step(function() {
		 db.query("SELECT node FROM nodes WHERE node=$1",
			  [node], this);
	     }, function(err, res) {
		 if (err) throw err;

		 if (res.rowCount < 1)
		     throw new errors.NotFound('Node does not exist');

		 this();
	     }, this);
    };
};

Transaction.prototype.createNode = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node FROM nodes WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rowCount > 0)
	    throw new errors.Conflict('Node already exists');

	db.query("INSERT INTO nodes (node) VALUES ($1)", [node], this);
    }, cb);
};

/**
 * cb(err, [{ node: String, title: String }])
 */
Transaction.prototype.listNodes = function(cb) {
    var db = this.db;

    step(function() {
	/* TODO: order by COUNT(subscribers) */
	db.query("SELECT node FROM nodes WHERE node IN (SELECT node FROM node_config WHERE \"key\"=\'accessModel\' AND \"value\"=\'open\') " +
		 "ORDER BY node ASC", this);
    }, function(err, res) {
	if (err) throw err;

	var nodes = res.rows.map(function(row) {
	    return { node: row.node,
		     title: row.title
		   };
	});
	this(null, nodes);
    }, cb);
};

/**
 * Subscription management
 */

Transaction.prototype.getSubscription = function(node, user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT subscription FROM subscriptions WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, (res.rows[0] && res.rows[0].subscription) || 'none');
    }, cb);
};

Transaction.prototype.setSubscription = function(node, user, subscription, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT subscription FROM subscriptions WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	var toDelete = !subscription || subscription == 'none';
	if (isSet && !toDelete)
	    db.query("UPDATE subscriptions SET subscription=$1 WHERE node=$2 AND \"user\"=$3",
		     [subscription, node, user], this);
	else if (!isSet && !toDelete)
	    db.query("INSERT INTO subscriptions (node, \"user\", subscription) VALUES ($1, $2, $3)",
		     [node, user, subscription], this);
	else if (isSet && toDelete)
	    db.query("DELETE FROM subscriptions WHERE node=$1 AND \"user\"=$2",
		     [node, user], this);
	else if (!isSet && toDelete)
	    cb(null);  /* do nothing */
    }, cb);
};

Transaction.prototype.getSubscribers = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\", subscription FROM subscriptions WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var subscribers = [];
	res.rows.forEach(function(row) {
	    subscribers.push({ user: row.user,
			       subscription: row.subscription });
	});
	this(null, subscribers);
    }, cb);
};

Transaction.prototype.getSubscriptions = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node, subscription FROM subscriptions WHERE \"user\"=$1",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	var subscriptions = [];
	res.rows.forEach(function(row) {
	    subscriptions.push({ node: row.node,
				 subscription: row.subscription });
	});
	this(null, subscriptions);
    }, cb);
};

Transaction.prototype.getAllSubscribers = function(cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT DISTINCT \"user\" FROM subscriptions",
		 this);
    }, function(err, res) {
	if (err) throw err;

	var subscribers = [];
	res.rows.forEach(function(row) {
	    subscribers.push(row.user);
	});
	this(null, subscribers);
    }, cb);
};

Transaction.prototype.getPendingNodes = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node FROM affiliations WHERE affiliation = 'owner' AND user = $1 AND EXISTS (SELECT user FROM subscriptions WHERE subscription = 'pending' AND node = affiliations.node)",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.node;
	}));
    }, cb);
};

Transaction.prototype.getPending = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT user FROM subscriptions WHERE subscription = 'pending' AND node = $1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.user;
	}));
    }, cb);
};

/**
 * Affiliation management
 */

Transaction.prototype.getAffiliation = function(node, user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT affiliation FROM affiliations WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, (res.rows[0] && res.rows[0].affiliation) || 'none');
    }, cb);
};

Transaction.prototype.setAffiliation = function(node, user, affiliation, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT affiliation FROM affiliations WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	var toDelete = !affiliation || affiliation == 'none';
	if (isSet && !toDelete)
	    db.query("UPDATE affiliations SET affiliation=$1 WHERE node=$2 AND \"user\"=$3",
		     [affiliation, node, user], this);
	else if (!isSet && !toDelete)
	    db.query("INSERT INTO affiliations (node, \"user\", affiliation) VALUES ($1, $2, $3)",
		     [node, user, affiliation], this);
	else if (isSet && toDelete)
	    db.query("DELETE FROM affiliations WHERE node=$1 AND \"user\"=$2",
		     [node, user], this);
	else if (!isSet && toDelete)
	    cb(null);  /* do nothing */
    }, cb);
};

Transaction.prototype.getAffiliations = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node, affiliation FROM affiliations WHERE \"user\"=$1",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	var affiliations = [];
	res.rows.forEach(function(row) {
	    affiliations.push({ node: row.node,
				affiliation: row.affiliation });
	});
	this(null, affiliations);
    }, cb);
};

Transaction.prototype.getAffiliated = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\", affiliation FROM affiliations WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var affiliations = [];
	res.rows.forEach(function(row) {
	    affiliations.push({ user: row.user,
				affiliation: row.affiliation });
	});
	this(null, affiliations);
    }, cb);
};

Transaction.prototype.getOwners = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\" FROM affiliations WHERE node=$1 AND affiliation='owner'",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.user;
	}));
    }, cb);
};

Transaction.prototype.writeItem = function(publisher, node, id, item, cb) {
    var db = this.db;
    var xml = item.join('').toString();

    step(function() {
	db.query("SELECT id FROM items WHERE node=$1 AND id=$2",
		 [node, id], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	if (isSet)
	    db.query("UPDATE items SET xml=$1, published=CURRENT_TIMESTAMP WHERE node=$2 AND id=$3",
		     [xml, node, id], this);
	else if (!isSet)
	    db.query("INSERT INTO items (node, id, xml, published) VALUES ($1, $2, $3, CURRENT_TIMESTAMP)",
		     [node, id, xml], this);
    }, cb);
};

Transaction.prototype.deleteItem = function(node, itemId, cb) {
    var db = this.db;

    step(function() {
	db.query("DELETE FROM items WHERE node=$1 AND id=$2",
		 [node, itemId], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rowCount < 1)
	    throw new errors.NotFound('No such item');

	this(null);
    }, cb);
};

/**
 * sorted by time
 */
Transaction.prototype.getItemIds = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT id FROM items WHERE node=$1 ORDER BY published DESC",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var ids = res.rows.map(function(row) {
	    return row.id;
	});
	this(null, ids);
    }, cb);
};

Transaction.prototype.getItem = function(node, id, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT xml FROM items WHERE node=$1 AND id=$2",
		 [node, id], this);
    }, function(err, res) {
	if (err) throw err;

	if (res && res.rows && res.rows[0]) {
	    var item;
	    try {
		item = ltx.parse('<r>' + res.rows[0].xml + '</r>').children;
	    } catch (e) {
		console.error('Parsing ' + JSON.stringify({node:node,id:id}) + ': ' + e.stack);
		item = [];
	    }
	    this(null, item);
	} else {
	    throw new errors.NotFound('No such item');
	}
    }, cb);
};

/**
 * Config management
 */

Transaction.prototype.getConfig = function(node, cb) {
    var db = this.db;

    step(this.nodeExists(node),
    function(err) {
	if (err) throw err;

	db.query("SELECT \"key\", \"value\" FROM node_config WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rows) {
	    var config = {};
	    res.rows.forEach(function(row) {
		config[row.key] = row.value;
	    });
	    this(null, config);
	} else
	    throw new errors.NotFound('No such node');
    }, cb);
};

Transaction.prototype.setConfig = function(node, config, cb) {
    var db = this.db;

console.log('setConfig ' + node + ': ' + require('util').inspect(config));
    step(this.nodeExists(node),
    function(err) {
	if (err) throw err;

	/* If user supplied only partial information, old/default
	 * values will be added by controller. That way we can just
	 * INSERT later. */
	 db.query("DELETE FROM node_config WHERE node=$1", [node], this);
    }, function(err) {
	if (err) throw err;

	var g = this.group();
	for(var key in config)
	    if (config.hasOwnProperty(key)) {
		var value = config[key];
		db.query("INSERT node_config SET key=$1, value=$2 WHERE node=$3",
		    [key, value, node], g);
	    }
	g();
    }, function(err, res) {
	if (err) throw err;

	this(null);
    }, cb);
};
<commit_msg>model_postgres nodeExists(): indentation<commit_after>var pg = require('pg');
var step = require('step');
var ltx = require('ltx');
var errors = require('./errors');

/* ready DB connections */
var pool = [];
/* waiting transaction requests */
var queue = [];

/* at start and when connection died */
function connectDB(config) {
    var db = new pg.Client(config);
    db.connect();

    /* Reconnect in up to 5s */
    db.on('error', function(err) {
	console.error('Postgres: ' + err.message);
	setTimeout(function() {
	    connectDB(config);
	}, Math.ceil(Math.random() * 5000));
	try { db.end(); } catch (e) { /* Alright */ }
    });

    /* wait until connected & authed */
    db.connection.once('readyForQuery', function() {
	dbIsAvailable(db);
    });
}

function dbIsAvailable(db) {
    var cb;
    if ((cb = queue.shift())) {
	/* request was waiting in queue */
	cb(db);
    } else {
	/* no request, put into pool */
	pool.push(db);
    }
}

/* config: { user, database, host, port, poolSize: 4 } */
exports.start = function(config) {
    for(var i = 0; i < (config.poolSize || 4); i++) {
	connectDB(config);
    }
};

exports.transaction = function(cb) {
    var db;
    if ((db = pool.shift())) {
	/* Got one from pool */
	new Transaction(db, cb);
    } else {
	/* Pool was empty, waiting... TODO: limit length, shift first */
	queue.push(function(db) {
	    new Transaction(db, cb);
	});
    }
};

/**
 * Wraps the postgres-js transaction with our model operations.
 */
function Transaction(db, cb) {
    var that = this;
    this.db = db;

    db.query("BEGIN", [], function(err, res) {
	cb(err, that);
    });
}

Transaction.prototype.commit = function(cb) {
    var db = this.db;
    db.query("COMMIT", [], function(err, res) {
	process.nextTick(function() {
	    dbIsAvailable(db);
	});

	cb(err);
    });
};

Transaction.prototype.rollback = function(cb) {
    var db = this.db;

    db.query("ROLLBACK", [], function(err, res) {
	process.nextTick(function() {
	    dbIsAvailable(db);
	});

	cb(err);
    });
};

/**
 * Actual data model
 */

/**
 * Can be dropped in a step() sequence to validate presence of a node.
 */
Transaction.prototype.nodeExists = function(node) {
    var db = this.db;

    return function() {
	step(function() {
	    db.query("SELECT node FROM nodes WHERE node=$1",
		     [node], this);
	}, function(err, res) {
	    if (err) throw err;

	    if (res.rowCount < 1)
		throw new errors.NotFound('Node does not exist');

	    this();
	}, this);
    };
};

Transaction.prototype.createNode = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node FROM nodes WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rowCount > 0)
	    throw new errors.Conflict('Node already exists');

	db.query("INSERT INTO nodes (node) VALUES ($1)", [node], this);
    }, cb);
};

/**
 * cb(err, [{ node: String, title: String }])
 */
Transaction.prototype.listNodes = function(cb) {
    var db = this.db;

    step(function() {
	/* TODO: order by COUNT(subscribers) */
	db.query("SELECT node FROM nodes WHERE node IN (SELECT node FROM node_config WHERE \"key\"=\'accessModel\' AND \"value\"=\'open\') " +
		 "ORDER BY node ASC", this);
    }, function(err, res) {
	if (err) throw err;

	var nodes = res.rows.map(function(row) {
	    return { node: row.node,
		     title: row.title
		   };
	});
	this(null, nodes);
    }, cb);
};

/**
 * Subscription management
 */

Transaction.prototype.getSubscription = function(node, user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT subscription FROM subscriptions WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, (res.rows[0] && res.rows[0].subscription) || 'none');
    }, cb);
};

Transaction.prototype.setSubscription = function(node, user, subscription, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT subscription FROM subscriptions WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	var toDelete = !subscription || subscription == 'none';
	if (isSet && !toDelete)
	    db.query("UPDATE subscriptions SET subscription=$1 WHERE node=$2 AND \"user\"=$3",
		     [subscription, node, user], this);
	else if (!isSet && !toDelete)
	    db.query("INSERT INTO subscriptions (node, \"user\", subscription) VALUES ($1, $2, $3)",
		     [node, user, subscription], this);
	else if (isSet && toDelete)
	    db.query("DELETE FROM subscriptions WHERE node=$1 AND \"user\"=$2",
		     [node, user], this);
	else if (!isSet && toDelete)
	    cb(null);  /* do nothing */
    }, cb);
};

Transaction.prototype.getSubscribers = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\", subscription FROM subscriptions WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var subscribers = [];
	res.rows.forEach(function(row) {
	    subscribers.push({ user: row.user,
			       subscription: row.subscription });
	});
	this(null, subscribers);
    }, cb);
};

Transaction.prototype.getSubscriptions = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node, subscription FROM subscriptions WHERE \"user\"=$1",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	var subscriptions = [];
	res.rows.forEach(function(row) {
	    subscriptions.push({ node: row.node,
				 subscription: row.subscription });
	});
	this(null, subscriptions);
    }, cb);
};

Transaction.prototype.getAllSubscribers = function(cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT DISTINCT \"user\" FROM subscriptions",
		 this);
    }, function(err, res) {
	if (err) throw err;

	var subscribers = [];
	res.rows.forEach(function(row) {
	    subscribers.push(row.user);
	});
	this(null, subscribers);
    }, cb);
};

Transaction.prototype.getPendingNodes = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node FROM affiliations WHERE affiliation = 'owner' AND user = $1 AND EXISTS (SELECT user FROM subscriptions WHERE subscription = 'pending' AND node = affiliations.node)",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.node;
	}));
    }, cb);
};

Transaction.prototype.getPending = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT user FROM subscriptions WHERE subscription = 'pending' AND node = $1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.user;
	}));
    }, cb);
};

/**
 * Affiliation management
 */

Transaction.prototype.getAffiliation = function(node, user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT affiliation FROM affiliations WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, (res.rows[0] && res.rows[0].affiliation) || 'none');
    }, cb);
};

Transaction.prototype.setAffiliation = function(node, user, affiliation, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT affiliation FROM affiliations WHERE node=$1 AND user=$2",
		 [node, user], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	var toDelete = !affiliation || affiliation == 'none';
	if (isSet && !toDelete)
	    db.query("UPDATE affiliations SET affiliation=$1 WHERE node=$2 AND \"user\"=$3",
		     [affiliation, node, user], this);
	else if (!isSet && !toDelete)
	    db.query("INSERT INTO affiliations (node, \"user\", affiliation) VALUES ($1, $2, $3)",
		     [node, user, affiliation], this);
	else if (isSet && toDelete)
	    db.query("DELETE FROM affiliations WHERE node=$1 AND \"user\"=$2",
		     [node, user], this);
	else if (!isSet && toDelete)
	    cb(null);  /* do nothing */
    }, cb);
};

Transaction.prototype.getAffiliations = function(user, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT node, affiliation FROM affiliations WHERE \"user\"=$1",
		 [user], this);
    }, function(err, res) {
	if (err) throw err;

	var affiliations = [];
	res.rows.forEach(function(row) {
	    affiliations.push({ node: row.node,
				affiliation: row.affiliation });
	});
	this(null, affiliations);
    }, cb);
};

Transaction.prototype.getAffiliated = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\", affiliation FROM affiliations WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var affiliations = [];
	res.rows.forEach(function(row) {
	    affiliations.push({ user: row.user,
				affiliation: row.affiliation });
	});
	this(null, affiliations);
    }, cb);
};

Transaction.prototype.getOwners = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT \"user\" FROM affiliations WHERE node=$1 AND affiliation='owner'",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	this(null, res.rows.map(function(row) {
	    return row.user;
	}));
    }, cb);
};

Transaction.prototype.writeItem = function(publisher, node, id, item, cb) {
    var db = this.db;
    var xml = item.join('').toString();

    step(function() {
	db.query("SELECT id FROM items WHERE node=$1 AND id=$2",
		 [node, id], this);
    }, function(err, res) {
	if (err) throw err;

	var isSet = res && res.rows && res.rows[0];
	if (isSet)
	    db.query("UPDATE items SET xml=$1, published=CURRENT_TIMESTAMP WHERE node=$2 AND id=$3",
		     [xml, node, id], this);
	else if (!isSet)
	    db.query("INSERT INTO items (node, id, xml, published) VALUES ($1, $2, $3, CURRENT_TIMESTAMP)",
		     [node, id, xml], this);
    }, cb);
};

Transaction.prototype.deleteItem = function(node, itemId, cb) {
    var db = this.db;

    step(function() {
	db.query("DELETE FROM items WHERE node=$1 AND id=$2",
		 [node, itemId], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rowCount < 1)
	    throw new errors.NotFound('No such item');

	this(null);
    }, cb);
};

/**
 * sorted by time
 */
Transaction.prototype.getItemIds = function(node, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT id FROM items WHERE node=$1 ORDER BY published DESC",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	var ids = res.rows.map(function(row) {
	    return row.id;
	});
	this(null, ids);
    }, cb);
};

Transaction.prototype.getItem = function(node, id, cb) {
    var db = this.db;

    step(function() {
	db.query("SELECT xml FROM items WHERE node=$1 AND id=$2",
		 [node, id], this);
    }, function(err, res) {
	if (err) throw err;

	if (res && res.rows && res.rows[0]) {
	    var item;
	    try {
		item = ltx.parse('<r>' + res.rows[0].xml + '</r>').children;
	    } catch (e) {
		console.error('Parsing ' + JSON.stringify({node:node,id:id}) + ': ' + e.stack);
		item = [];
	    }
	    this(null, item);
	} else {
	    throw new errors.NotFound('No such item');
	}
    }, cb);
};

/**
 * Config management
 */

Transaction.prototype.getConfig = function(node, cb) {
    var db = this.db;

    step(this.nodeExists(node),
    function(err) {
	if (err) throw err;

	db.query("SELECT \"key\", \"value\" FROM node_config WHERE node=$1",
		 [node], this);
    }, function(err, res) {
	if (err) throw err;

	if (res.rows) {
	    var config = {};
	    res.rows.forEach(function(row) {
		config[row.key] = row.value;
	    });
	    this(null, config);
	} else
	    throw new errors.NotFound('No such node');
    }, cb);
};

Transaction.prototype.setConfig = function(node, config, cb) {
    var db = this.db;

console.log('setConfig ' + node + ': ' + require('util').inspect(config));
    step(this.nodeExists(node),
    function(err) {
	if (err) throw err;

	/* If user supplied only partial information, old/default
	 * values will be added by controller. That way we can just
	 * INSERT later. */
	 db.query("DELETE FROM node_config WHERE node=$1", [node], this);
    }, function(err) {
	if (err) throw err;

	var g = this.group();
	for(var key in config)
	    if (config.hasOwnProperty(key)) {
		var value = config[key];
		db.query("INSERT node_config SET key=$1, value=$2 WHERE node=$3",
		    [key, value, node], g);
	    }
	g();
    }, function(err, res) {
	if (err) throw err;

	this(null);
    }, cb);
};
<|endoftext|>