<commit_before>
'use strict';

var qs    = require('querystring');
var url   = require('url');
var debug = require('debug')('proxive:post');

module.exports = function(config) {
  function handleLoginCallback(err, user, req, res) {
    if(err) {
      if(err === 'no-user') {
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=user-not-found'
        });
        res.end();
      } else {
        console.error(err);
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=error-occured'
        });
        res.end();
      }
      return;
    }
    config.users.createSession(user.userId, function(err, sessionKey) {
      if(err) {
        console.error(err);
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=error-occured'
        });
        return res.end();
      }
      // Set cookie for account security for thirty days
      var thirtyDays = Math.floor(Date.now()/1000) + 2592000;
      var fourHours  = Math.floor(Date.now()/1000) + 14400;
      req.cookies.set('login.sec', config.users.hmac(user.userId, thirtyDays, 'secure:'+sessionKey), {
        expires: thirtyDays,
        maxAge: 2592000000,
        domain: '.' + req.headers.host,
        httpOnly: true,
        secure: config.https
      });
      // Set cookie for browsing for 4 hours
      req.cookies.set('login', config.users.hmac(user.userId, fourHours, sessionKey), {
        expires: fourHours,
        maxAge: 14400000,
        domain: '.' + req.headers.host,
        httpOnly: true
      });
      res.writeHead(302, {
        location: 'http://'+req.headers.host+'/'
      });
      res.end();
    });
  }

  return function(req, res) {
    var body = '';

    req.on('data', function (data) {
      body += data;

      // Too much POST data, kill the connection!
      // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
      if (body.length > 1e6) {
        req.connection.destroy();
      }
    });

    req.on('end', function () {
      var post = qs.parse(body);

      if(req.url === '/loginWithUsername') {
        debug('/loginWithUsername');
        if(!post.organization) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-organization'
          });
          res.end();
        } else if(!post.username) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-username'
          });
          res.end();
        } else if(!post.password) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-password'
          });
          res.end();
        } else {
          config.users.loginUsername(post.organization, post.username, post.password, function(err, user) {
            handleLoginCallback(err, user, req, res);
          });
        }
      } else if(req.url === '/loginWithEmail') {
        debug('/loginWithEmail');
        if(!post.email) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-email'
          });
          res.end();
        } else if(!post.password) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-password'
          });
          res.end();
        } else {
          config.users.loginEmail(post.email, post.password, function(err, user) {
            handleLoginCallback(err, user, req, res);
          });
        }
      } else if(req.url === '/admin/userData') {
        // We pass true as the third parameter to ensure that the response will be in json if the user isn't authenticated
        config.users.validateAdmin(req, res, true, function(userInfo) {
          config.users.getDetails(parseInt(post.id, 10), function(err, user) {
            if(err) {
              console.error(err);
              if(err === 'no-user-found') {
                res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
                return res.end('{"error": "User is gone!"}');
              }
              res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
              return res.end('{"error": "Something went wrong!"}');
            }
            // Admins shouldn't be able to change a higher admin
            if(userInfo.admin < user.admin) {
              res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
              return res.end('{"error": "Permissions too low!"}');
            }
            config.users.save(post, function(err) {
              if(err) {
                console.error(err);
                if(err === 'missing-input' || err === 'invalid-email') {
                  res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
                  return res.end('{"error": "Invalid inputs!"}');
                }
                res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
                return res.end('{"error": "Something went wrong!"}');
              }
              if((post.admin ? 1 : 0) !== user.admin) {
                config.users.updateAdmin(user.id, (post.admin ? 1 : 0), function(err) {
                  if(err) {
                    console.error(err);
                    res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
                    return res.end('{"error": "Something went wrong!"}');
                  }
                  res.writeHead(200, {
                    'content-type': 'application/json; charset=utf-8'
                  });
                  res.end('{"error": false}');
                });
                return;
              }
              res.writeHead(200, {
                'content-type': 'application/json; charset=utf-8'
              });
              res.end('{"error": false}');
            });
          });
        });
      }
    });
  };
};
<commit_msg>Add 404 for unknown POST paths<commit_after>
'use strict';

var qs    = require('querystring');
var url   = require('url');
var debug = require('debug')('proxive:post');

module.exports = function(config) {
  function handleLoginCallback(err, user, req, res) {
    if(err) {
      if(err === 'no-user') {
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=user-not-found'
        });
        res.end();
      } else {
        console.error(err);
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=error-occured'
        });
        res.end();
      }
      return;
    }
    config.users.createSession(user.userId, function(err, sessionKey) {
      if(err) {
        console.error(err);
        res.writeHead(302, {
          location: 'http://'+req.headers.host+'/?err=error-occured'
        });
        return res.end();
      }
      // Set cookie for account security for thirty days
      var thirtyDays = Math.floor(Date.now()/1000) + 2592000;
      var fourHours  = Math.floor(Date.now()/1000) + 14400;
      req.cookies.set('login.sec', config.users.hmac(user.userId, thirtyDays, 'secure:'+sessionKey), {
        expires: thirtyDays,
        maxAge: 2592000000,
        domain: '.' + req.headers.host,
        httpOnly: true,
        secure: config.https
      });
      // Set cookie for browsing for 4 hours
      req.cookies.set('login', config.users.hmac(user.userId, fourHours, sessionKey), {
        expires: fourHours,
        maxAge: 14400000,
        domain: '.' + req.headers.host,
        httpOnly: true
      });
      res.writeHead(302, {
        location: 'http://'+req.headers.host+'/'
      });
      res.end();
    });
  }

  return function(req, res) {
    var body = '';

    req.on('data', function (data) {
      body += data;

      // Too much POST data, kill the connection!
      // 1e6 === 1 * Math.pow(10, 6) === 1 * 1000000 ~~~ 1MB
      if (body.length > 1e6) {
        req.connection.destroy();
      }
    });

    req.on('end', function () {
      var post = qs.parse(body);

      if(req.url === '/loginWithUsername') {
        debug('/loginWithUsername');
        if(!post.organization) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-organization'
          });
          res.end();
        } else if(!post.username) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-username'
          });
          res.end();
        } else if(!post.password) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-password'
          });
          res.end();
        } else {
          config.users.loginUsername(post.organization, post.username, post.password, function(err, user) {
            handleLoginCallback(err, user, req, res);
          });
        }
      } else if(req.url === '/loginWithEmail') {
        debug('/loginWithEmail');
        if(!post.email) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-email'
          });
          res.end();
        } else if(!post.password) {
          res.writeHead(302, {
            location: 'http://'+req.headers.host+'/?err=missing-password'
          });
          res.end();
        } else {
          config.users.loginEmail(post.email, post.password, function(err, user) {
            handleLoginCallback(err, user, req, res);
          });
        }
      } else if(req.url === '/admin/userData') {
        // We pass true as the third parameter to ensure that the response will be in json if the user isn't authenticated
        config.users.validateAdmin(req, res, true, function(userInfo) {
          config.users.getDetails(parseInt(post.id, 10), function(err, user) {
            if(err) {
              console.error(err);
              if(err === 'no-user-found') {
                res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
                return res.end('{"error": "User is gone!"}');
              }
              res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
              return res.end('{"error": "Something went wrong!"}');
            }
            // Admins shouldn't be able to change a higher admin
            if(userInfo.admin < user.admin) {
              res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
              return res.end('{"error": "Permissions too low!"}');
            }
            config.users.save(post, function(err) {
              if(err) {
                console.error(err);
                if(err === 'missing-input' || err === 'invalid-email') {
                  res.writeHead(400, {'content-type': 'application/json; charset=utf-8'});
                  return res.end('{"error": "Invalid inputs!"}');
                }
                res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
                return res.end('{"error": "Something went wrong!"}');
              }
              if((post.admin ? 1 : 0) !== user.admin) {
                config.users.updateAdmin(user.id, (post.admin ? 1 : 0), function(err) {
                  if(err) {
                    console.error(err);
                    res.writeHead(500, {'content-type': 'application/json; charset=utf-8'});
                    return res.end('{"error": "Something went wrong!"}');
                  }
                  res.writeHead(200, {
                    'content-type': 'application/json; charset=utf-8'
                  });
                  res.end('{"error": false}');
                });
                return;
              }
              res.writeHead(200, {
                'content-type': 'application/json; charset=utf-8'
              });
              res.end('{"error": false}');
            });
          });
        });
      } else {
        res.writeHead(404, {
          'content-type': 'application/json; charset=utf-8'
        });
        res.end('{"error": 404}');
      }
    });
  };
};
<|endoftext|>


<commit_before>'use strict';

var LoginPage = require('../pages/login_page.js');
var LogoutPage = require('../pages/logout_page.js');
var CollectionPage = require('../pages/collections_page.js');
var OrgPage = require('../pages/organization_page');
var data = require('../fixtures/test_data.json');

describe('OCL Collections Page', function () {

    var loginPage;
    var logoutPage;
    var collectionPage;
    var orgPage;
    var id='';

    beforeEach(function () {
        loginPage = new LoginPage();
        logoutPage = new LogoutPage();
        collectionPage = new CollectionPage();
        orgPage = new OrgPage();
    });

    it('should login', function () {
        loginPage.visit();
        loginPage.login(data.username,data.password);

        expect((loginPage.loginStatus).getText()).toEqual('Successfully signed in as awadhwa1.');
    });

    it('should create collection', function () {
        id = orgPage.getRandomString(2);
        collectionPage.createNewUserCollection( data.short_code+id,
            data.col_name,
            data.full_name,
            data.supported_locale
        );

        expect((collectionPage.status).getText()).toEqual('Collection created');
    });

    it('should create collection version', function () {

       collectionPage.createNewCollectionVersion('V1', 'Version 1');

       expect((orgPage.status).getText()).toEqual('Collection version created!');
    });

    it('should release a user collection version', function () {
        orgPage.releaseVersion();
        browser.sleep('500');

        expect(orgPage.releaseLabel.get(1).getText()).toEqual('Released');
        expect(orgPage.notification.getText()).toEqual('Successfully Released.');

        browser.sleep('500');
        orgPage.notification.click();
        browser.sleep('750');
    });

    it('should retire user collection version', function () {
        orgPage.retireVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Retired.');
        expect(orgPage.retireLabel.get(1).getText()).toEqual('Retired');

         browser.sleep('500');
        orgPage.notification.click();
         browser.sleep('500');
    });

    it('should un-retire user collection version', function () {
        orgPage.retireVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Un-Retired.');
        expect(orgPage.releaseLabel.get(1).getText()).toEqual('Released');

         browser.sleep('500');
        orgPage.notification.click();
         browser.sleep('500');
    });

    it('should un-release a user collection version', function () {
        orgPage.releaseVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Un-Released.');

        browser.sleep('500');
        orgPage.notification.click();
        browser.sleep('500');
    });

    it('should delete a user collection version', function () {
       orgPage.deleteCollectionVersion();
       browser.sleep('750');

       expect(orgPage.notification.getText()).toEqual('Successfully removed collection version.');

        browser.sleep('500');
       orgPage.notification.click();
        browser.sleep('500');
    });

    it('should add a reference of concept to a collection', function () {
        var concept_expression = '/orgs/EthiopiaMOH/sources/HSTP-Indicators/concepts/1/';
        orgPage.createNewReference(concept_expression);
    });

    it('should edit collection', function () {
        collectionPage.editCollection(data.col_desc, data.ext_id);

        expect((collectionPage.status).getText()).toEqual('Collection updated');
        expect((collectionPage.updatedDescValue).getText()).toContain('collection description');
        expect((collectionPage.updatedExtIdValue).getText()).toContain('1.1');
    });

    it('should delete collection', function () {
        collectionPage.deleteCollection();

        expect((collectionPage.status).getText()).toEqual('Collection Deleted');
    });

     it('should logout', function () {
         logoutPage.logout();

         expect((loginPage.loginStatus).getText()).toEqual('You have signed out.');
     });

});

<commit_msg>Commented add reference test from user collection as it is covered already<commit_after>'use strict';

var LoginPage = require('../pages/login_page.js');
var LogoutPage = require('../pages/logout_page.js');
var CollectionPage = require('../pages/collections_page.js');
var OrgPage = require('../pages/organization_page');
var data = require('../fixtures/test_data.json');

describe('OCL Collections Page', function () {

    var loginPage;
    var logoutPage;
    var collectionPage;
    var orgPage;
    var id='';

    beforeEach(function () {
        loginPage = new LoginPage();
        logoutPage = new LogoutPage();
        collectionPage = new CollectionPage();
        orgPage = new OrgPage();
    });

    it('should login', function () {
        loginPage.visit();
        loginPage.login(data.username,data.password);

        expect((loginPage.loginStatus).getText()).toEqual('Successfully signed in as awadhwa1.');
    });

    it('should create collection', function () {
        id = orgPage.getRandomString(2);
        collectionPage.createNewUserCollection( data.short_code+id,
            data.col_name,
            data.full_name,
            data.supported_locale
        );

        expect((collectionPage.status).getText()).toEqual('Collection created');
    });

    it('should create collection version', function () {

       collectionPage.createNewCollectionVersion('V1', 'Version 1');

       expect((orgPage.status).getText()).toEqual('Collection version created!');
    });

    it('should release a user collection version', function () {
        orgPage.releaseVersion();
        browser.sleep('500');

        expect(orgPage.releaseLabel.get(1).getText()).toEqual('Released');
        expect(orgPage.notification.getText()).toEqual('Successfully Released.');

        browser.sleep('500');
        orgPage.notification.click();
        browser.sleep('750');
    });

    it('should retire user collection version', function () {
        orgPage.retireVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Retired.');
        expect(orgPage.retireLabel.get(1).getText()).toEqual('Retired');

         browser.sleep('500');
        orgPage.notification.click();
         browser.sleep('500');
    });

    it('should un-retire user collection version', function () {
        orgPage.retireVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Un-Retired.');
        expect(orgPage.releaseLabel.get(1).getText()).toEqual('Released');

         browser.sleep('500');
        orgPage.notification.click();
         browser.sleep('500');
    });

    it('should un-release a user collection version', function () {
        orgPage.releaseVersion();
        browser.sleep('500');

        expect(orgPage.notification.getText()).toEqual('Successfully Un-Released.');

        browser.sleep('500');
        orgPage.notification.click();
        browser.sleep('500');
    });

    it('should delete a user collection version', function () {
       orgPage.deleteCollectionVersion();
       browser.sleep('750');

       expect(orgPage.notification.getText()).toEqual('Successfully removed collection version.');

        browser.sleep('500');
       orgPage.notification.click();
        browser.sleep('500');
    });

    // it('should add a reference of concept to a collection', function () {
    //     var concept_expression = '/orgs/EthiopiaMOH/sources/HSTP-Indicators/concepts/1/';
    //     orgPage.createNewReference(concept_expression);
    // });

    it('should edit collection', function () {
        collectionPage.editCollection(data.col_desc, data.ext_id);

        expect((collectionPage.status).getText()).toEqual('Collection updated');
        expect((collectionPage.updatedDescValue).getText()).toContain('collection description');
        expect((collectionPage.updatedExtIdValue).getText()).toContain('1.1');
    });

    it('should delete collection', function () {
        collectionPage.deleteCollection();

        expect((collectionPage.status).getText()).toEqual('Collection Deleted');
    });

     it('should logout', function () {
         logoutPage.logout();

         expect((loginPage.loginStatus).getText()).toEqual('You have signed out.');
     });

});

<|endoftext|>


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

Ext.namespace('Sage.Platform.Mobile.Controls');

(function() {
    var U = Sage.Platform.Mobile.Utility;

    Sage.Platform.Mobile.Controls.LookupField = Ext.extend(Sage.Platform.Mobile.Controls.Field, {
        template: new Simplate([
            '{%: $.label %}',
            '<button class="button simpleSubHeaderButton">{%: $.lookupText %}</button>',
            '<input type="text" {% if ($.requireSelection) { %}readonly="readonly"{% } %} />'
        ]),
        dependentErrorText: "A value for '{0}' must be selected.",
        view: false,
        keyProperty: '$key',
        textProperty: '$descriptor',
        textTemplate: null,
        textRenderer: null,
        valueKeyProperty: null,
        valueTextProperty: null,
        requireSelection: true,
        emptyText: '',
        completeText: 'Select',
        lookupText: '...',
        init: function() {
            Sage.Platform.Mobile.Controls.LookupField.superclass.init.apply(this, arguments);

            this.containerEl.on('click', this.onClick, this);

            if (this.isReadOnly())
            {
                this.containerEl.addClass('field-disabled');
                this.el.dom.readOnly = true;
            }
            else if (!this.requireSelection)
            {
                this.el
                    .on('keyup', this.onKeyUp, this)
                    .on('blur', this.onBlur, this);
            }
        },
        enable: function() {
            this.containerEl.removeClass('field-disabled');
        },
        disable: function() {
            this.containerEl.addClass('field-disabled');
            this.clearValue(true);
        },
        isDisabled: function() {
            return this.containerEl.hasClass('field-disabled');
        },
        isReadOnly: function() {
            return !this.view;
        },
        getDependentValue: function() {
            if (this.dependsOn && this.owner)
            {
                var field = this.owner.fields[this.dependsOn];
                if (field) return field.getValue();
            }
        },
        getDependentLabel: function() {
            if (this.dependsOn && this.owner)
            {
                var field = this.owner.fields[this.dependsOn];
                if (field) return field.label;
            }
        },
        expandExpression: function(expression) {
            if (typeof expression === 'function')
                return expression.apply(this, Array.prototype.slice.call(arguments, 1));
            else
                return expression;
        },
        createNavigationOptions: function() {
            var options = {
                selectionOnly: true,
                singleSelect: true,
                resourceKind: this.resourceKind,
                resourcePredicate: this.resourcePredicate,
                where: this.where,
                orderBy: this.orderBy,
                tools: {
                    tbar: [{
                        id: 'complete',
                        fn: this.complete,
                        scope: this
                    },{
                        id: 'cancel',
                        side: 'left',
                        fn: ReUI.back,
                        scope: ReUI
                    }]
                }
            };

            var expand = ['resourceKind', 'resourcePredicate', 'where'],
                dependentValue = this.getDependentValue();

            if (this.dependsOn && !dependentValue)
            {
                alert(String.format(this.dependentErrorText, this.getDependentLabel()));
                return false;
            }

            Ext.each(expand, function(item) {
                if (this[item])
                    options[item] = this.dependsOn // only pass dependentValue if there is a dependency
                        ? this.expandExpression(this[item], dependentValue)
                        : this.expandExpression(this[item]);
            }, this);

            options.dependentValue = dependentValue;
            options.title = this.title;

            return options;
        },
        navigateToListView: function() {
            var view = App.getView(this.view),
                options = this.createNavigationOptions();
            if (view && options)
                view.show(options);
        },
        onClick: function(evt, el, o) {
            if (!this.isDisabled() && (evt.getTarget('.button') || this.requireSelection))
            {
                evt.stopEvent();

                this.navigateToListView();
            }
        },
        onKeyUp: function(evt, el, o) {
            if (!this.isDisabled() && this.notificationTrigger == 'keyup')
                this.onNotificationTrigger(evt, el, o);
        },
        onBlur: function(evt, el, o) {
            if (!this.isDisabled() && this.notificationTrigger == 'blur')
                this.onNotificationTrigger(evt, el, o);
        },
        onNotificationTrigger: function(evt, el, o) {
            var currentValue = this.getValue();

            if (this.previousValue != currentValue)
                this.fireEvent('change', currentValue, this);

            this.previousValue = currentValue;
        },
        setText: function(text) {
            this.el.dom.value = text;

            this.previousValue = text;
        },
        getText: function() {
            return this.el.dom.value;
        },
        complete: function() {
            // todo: should there be a better way?
            var view = App.getActiveView();
            if (view && view.selectionModel)
            {
                var selections = view.selectionModel.getSelections();

                for (var selectionKey in selections)
                {
                    var val = selections[selectionKey].data,
                        success = true;

                    this.setSelection(val, selectionKey);
                    break;
                }

                ReUI.back();

                // if the event is fired before the transition, any XMLHttpRequest created in an event handler and
                // executing during the transition can potentially fail (status 0).  this might only be an issue with CORS
                // requests created in this state (the pre-flight request is made, and the request ends with status 0).
                // wrapping thing in a timeout and placing after the transition starts, mitigates this issue.
                if (success) setTimeout(this.onChange.createDelegate(this), 0);
            }
        },
        onChange: function() {
            this.fireEvent('change', this.currentValue, this);
        },
        isDirty: function() {
            if (this.originalValue && this.currentValue)
            {
                if (this.originalValue.key != this.currentValue.key)
                    return true;

                if (this.originalValue.text != this.currentValue.text)
                    return true;

                if (!this.requireSelection && !this.textTemplate)
                    if (this.originalValue.text != this.getText())
                        return true;

                return false;
            }

            if (this.originalValue)
            {
                if (!this.requireSelection && !this.textTemplate)
                    if (this.originalValue.text != this.getText())
                        return true;
            }
            else
            {
                if (!this.requireSelection && !this.textTemplate)
                {
                    var text = this.getText();
                    if (text && text.length > 0)
                        return true;
                }
            }

            return (this.originalValue != this.currentValue);
        },
        getSelection: function() {
            return this.currentSelection;
        },
        getValue: function() {
            if (this.isDisabled()) return false;

            var value = null,
                text = this.getText() || '',
                // if valueKeyProperty or valueTextProperty IS NOT EXPLICITLY set to false
                // and IS NOT defined use keyProperty or textProperty in its place.
                keyProperty = this.valueKeyProperty !== false
                    ? this.valueKeyProperty || this.keyProperty
                    : false,
                textProperty = this.valueTextProperty !== false
                    ? this.valueTextProperty || this.textProperty
                    : false;

            if (keyProperty || textProperty)
            {
                if (this.currentValue)
                {
                    if (keyProperty)
                        value = U.setValue(value || {}, keyProperty, this.currentValue.key);

                    // if a text template has been applied there is no way to guarantee a correct
                    // mapping back to the property
                    if (textProperty && !this.textTemplate)
                        value = U.setValue(value || {}, textProperty, this.requireSelection ? this.currentValue.text : text);
                }
                else if (!this.requireSelection)
                {
                    if (keyProperty && text.length > 0)
                        value = U.setValue(value || {}, keyProperty, text);

                    // if a text template has been applied there is no way to guarantee a correct
                    // mapping back to the property
                    if (textProperty && !this.textTemplate && text.length > 0)
                    {
                        value = U.setValue(value || {}, textProperty, text);
                    }
                }
            }
            else
            {
                if (this.currentValue)
                {
                    value = this.requireSelection
                        ? this.currentValue.key
                        : this.currentValue.text != text && !this.textTemplate
                            ? text
                            : this.currentValue.key;
                }
                else if (!this.requireSelection && text.length > 0)
                {
                    value = text;
                }
            }

            return value;
        },
        setSelection: function(val, key) {
            var key = U.getValue(val, this.keyProperty, val) || key, // if we can extract the key as requested, use it instead of the selection key
                text = U.getValue(val, this.textProperty);

            if (text && this.textTemplate)
                text = this.textTemplate.apply(text, this);
            else if (this.textRenderer)
                text = this.textRenderer.call(this, val, key, text);

            this.currentSelection = val;

            this.currentValue = {
                key: key || text,
                text: text || key
            };

            this.setText(text);
        },
        setValue: function(val, initial) {
            // if valueKeyProperty or valueTextProperty IS NOT EXPLICITLY set to false
            // and IS NOT defined use keyProperty or textProperty in its place.
            var key,
                text,
                keyProperty = this.valueKeyProperty !== false
                    ? this.valueKeyProperty || this.keyProperty
                    : false,
                textProperty = this.valueTextProperty !== false
                    ? this.valueTextProperty || this.textProperty
                    : false;

            if (keyProperty || textProperty)
            {
                if (keyProperty)
                    key = U.getValue(val, keyProperty);

                if (textProperty)
                    text = U.getValue(val, textProperty);

                if (text && this.textTemplate)
                    text = this.textTemplate.apply(text, this);
                else if (this.textRenderer)
                    text = this.textRenderer.call(this, val, key, text);

                if (key || text)
                {
                    this.currentValue = {
                        key: key || text,
                        text: text || key
                    };

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.currentValue.text);
                }
                else
                {
                    this.currentValue = false;

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.requireSelection ? this.emptyText : '');
                }
            }
            else
            {
                if (val !== undefined && val !== null)
                {
                    key = val;
                    text = val;

                    if (text && this.textTemplate)
                        text = this.textTemplate.apply(text, this);
                    else if (this.textRenderer)
                        text = this.textRenderer.call(this, val, key, text);

                    this.currentValue = {
                        key: key,
                        text: text
                    };

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(text);
                }
                else
                {
                    this.currentValue = false;

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.requireSelection ? this.emptyText : '');
                }
            }
        },
        clearValue: function(flag) {
            var initial = flag !== true;

            this.setValue(null, initial);
        }
    });

    Sage.Platform.Mobile.Controls.FieldManager.register('lookup', Sage.Platform.Mobile.Controls.LookupField);
})();<commit_msg>[#1-79035] Made isDirty to return true if lookup is disabled. This will make sure that getValue returns appropriate empty value.<commit_after>/* Copyright (c) 2010, Sage Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

Ext.namespace('Sage.Platform.Mobile.Controls');

(function() {
    var U = Sage.Platform.Mobile.Utility;

    Sage.Platform.Mobile.Controls.LookupField = Ext.extend(Sage.Platform.Mobile.Controls.Field, {
        template: new Simplate([
            '{%: $.label %}',
            '<button class="button simpleSubHeaderButton">{%: $.lookupText %}</button>',
            '<input type="text" {% if ($.requireSelection) { %}readonly="readonly"{% } %} />'
        ]),
        dependentErrorText: "A value for '{0}' must be selected.",
        view: false,
        keyProperty: '$key',
        textProperty: '$descriptor',
        textTemplate: null,
        textRenderer: null,
        valueKeyProperty: null,
        valueTextProperty: null,
        requireSelection: true,
        emptyText: '',
        completeText: 'Select',
        lookupText: '...',
        init: function() {
            Sage.Platform.Mobile.Controls.LookupField.superclass.init.apply(this, arguments);

            this.containerEl.on('click', this.onClick, this);

            if (this.isReadOnly())
            {
                this.containerEl.addClass('field-disabled');
                this.el.dom.readOnly = true;
            }
            else if (!this.requireSelection)
            {
                this.el
                    .on('keyup', this.onKeyUp, this)
                    .on('blur', this.onBlur, this);
            }
        },
        enable: function() {
            this.containerEl.removeClass('field-disabled');
        },
        disable: function() {
            this.containerEl.addClass('field-disabled');
        },
        isDisabled: function() {
            return this.containerEl.hasClass('field-disabled');
        },
        isReadOnly: function() {
            return !this.view;
        },
        getDependentValue: function() {
            if (this.dependsOn && this.owner)
            {
                var field = this.owner.fields[this.dependsOn];
                if (field) return field.getValue();
            }
        },
        getDependentLabel: function() {
            if (this.dependsOn && this.owner)
            {
                var field = this.owner.fields[this.dependsOn];
                if (field) return field.label;
            }
        },
        expandExpression: function(expression) {
            if (typeof expression === 'function')
                return expression.apply(this, Array.prototype.slice.call(arguments, 1));
            else
                return expression;
        },
        createNavigationOptions: function() {
            var options = {
                selectionOnly: true,
                singleSelect: true,
                resourceKind: this.resourceKind,
                resourcePredicate: this.resourcePredicate,
                where: this.where,
                orderBy: this.orderBy,
                tools: {
                    tbar: [{
                        id: 'complete',
                        fn: this.complete,
                        scope: this
                    },{
                        id: 'cancel',
                        side: 'left',
                        fn: ReUI.back,
                        scope: ReUI
                    }]
                }
            };

            var expand = ['resourceKind', 'resourcePredicate', 'where'],
                dependentValue = this.getDependentValue();

            if (this.dependsOn && !dependentValue)
            {
                alert(String.format(this.dependentErrorText, this.getDependentLabel()));
                return false;
            }

            Ext.each(expand, function(item) {
                if (this[item])
                    options[item] = this.dependsOn // only pass dependentValue if there is a dependency
                        ? this.expandExpression(this[item], dependentValue)
                        : this.expandExpression(this[item]);
            }, this);

            options.dependentValue = dependentValue;
            options.title = this.title;

            return options;
        },
        navigateToListView: function() {
            var view = App.getView(this.view),
                options = this.createNavigationOptions();
            if (view && options)
                view.show(options);
        },
        onClick: function(evt, el, o) {
            if (!this.isDisabled() && (evt.getTarget('.button') || this.requireSelection))
            {
                evt.stopEvent();

                this.navigateToListView();
            }
        },
        onKeyUp: function(evt, el, o) {
            if (!this.isDisabled() && this.notificationTrigger == 'keyup')
                this.onNotificationTrigger(evt, el, o);
        },
        onBlur: function(evt, el, o) {
            if (!this.isDisabled() && this.notificationTrigger == 'blur')
                this.onNotificationTrigger(evt, el, o);
        },
        onNotificationTrigger: function(evt, el, o) {
            var currentValue = this.getValue();

            if (this.previousValue != currentValue)
                this.fireEvent('change', currentValue, this);

            this.previousValue = currentValue;
        },
        setText: function(text) {
            this.el.dom.value = text;

            this.previousValue = text;
        },
        getText: function() {
            return this.el.dom.value;
        },
        complete: function() {
            // todo: should there be a better way?
            var view = App.getActiveView();
            if (view && view.selectionModel)
            {
                var selections = view.selectionModel.getSelections();

                for (var selectionKey in selections)
                {
                    var val = selections[selectionKey].data,
                        success = true;

                    this.setSelection(val, selectionKey);
                    break;
                }

                ReUI.back();

                // if the event is fired before the transition, any XMLHttpRequest created in an event handler and
                // executing during the transition can potentially fail (status 0).  this might only be an issue with CORS
                // requests created in this state (the pre-flight request is made, and the request ends with status 0).
                // wrapping thing in a timeout and placing after the transition starts, mitigates this issue.
                if (success) setTimeout(this.onChange.createDelegate(this), 0);
            }
        },
        onChange: function() {
            this.fireEvent('change', this.currentValue, this);
        },
        isDirty: function() {
            if (this.isDisabled()) return true;

            if (this.originalValue && this.currentValue)
            {
                if (this.originalValue.key != this.currentValue.key)
                    return true;

                if (this.originalValue.text != this.currentValue.text)
                    return true;

                if (!this.requireSelection && !this.textTemplate)
                    if (this.originalValue.text != this.getText())
                        return true;

                return false;
            }

            if (this.originalValue)
            {
                if (!this.requireSelection && !this.textTemplate)
                    if (this.originalValue.text != this.getText())
                        return true;
            }
            else
            {
                if (!this.requireSelection && !this.textTemplate)
                {
                    var text = this.getText();
                    if (text && text.length > 0)
                        return true;
                }
            }

            return (this.originalValue != this.currentValue);
        },
        getSelection: function() {
            return this.currentSelection;
        },
        getValue: function() {
            if (this.isDisabled()) return false;

            var value = null,
                text = this.getText() || '',
                // if valueKeyProperty or valueTextProperty IS NOT EXPLICITLY set to false
                // and IS NOT defined use keyProperty or textProperty in its place.
                keyProperty = this.valueKeyProperty !== false
                    ? this.valueKeyProperty || this.keyProperty
                    : false,
                textProperty = this.valueTextProperty !== false
                    ? this.valueTextProperty || this.textProperty
                    : false;

            if (keyProperty || textProperty)
            {
                if (this.currentValue)
                {
                    if (keyProperty)
                        value = U.setValue(value || {}, keyProperty, this.currentValue.key);

                    // if a text template has been applied there is no way to guarantee a correct
                    // mapping back to the property
                    if (textProperty && !this.textTemplate)
                        value = U.setValue(value || {}, textProperty, this.requireSelection ? this.currentValue.text : text);
                }
                else if (!this.requireSelection)
                {
                    if (keyProperty && text.length > 0)
                        value = U.setValue(value || {}, keyProperty, text);

                    // if a text template has been applied there is no way to guarantee a correct
                    // mapping back to the property
                    if (textProperty && !this.textTemplate && text.length > 0)
                    {
                        value = U.setValue(value || {}, textProperty, text);
                    }
                }
            }
            else
            {
                if (this.currentValue)
                {
                    value = this.requireSelection
                        ? this.currentValue.key
                        : this.currentValue.text != text && !this.textTemplate
                            ? text
                            : this.currentValue.key;
                }
                else if (!this.requireSelection && text.length > 0)
                {
                    value = text;
                }
            }

            return value;
        },
        setSelection: function(val, key) {
            var key = U.getValue(val, this.keyProperty, val) || key, // if we can extract the key as requested, use it instead of the selection key
                text = U.getValue(val, this.textProperty);

            if (text && this.textTemplate)
                text = this.textTemplate.apply(text, this);
            else if (this.textRenderer)
                text = this.textRenderer.call(this, val, key, text);

            this.currentSelection = val;

            this.currentValue = {
                key: key || text,
                text: text || key
            };

            this.setText(text);
        },
        setValue: function(val, initial) {
            // if valueKeyProperty or valueTextProperty IS NOT EXPLICITLY set to false
            // and IS NOT defined use keyProperty or textProperty in its place.
            var key,
                text,
                keyProperty = this.valueKeyProperty !== false
                    ? this.valueKeyProperty || this.keyProperty
                    : false,
                textProperty = this.valueTextProperty !== false
                    ? this.valueTextProperty || this.textProperty
                    : false;

            if (keyProperty || textProperty)
            {
                if (keyProperty)
                    key = U.getValue(val, keyProperty);

                if (textProperty)
                    text = U.getValue(val, textProperty);

                if (text && this.textTemplate)
                    text = this.textTemplate.apply(text, this);
                else if (this.textRenderer)
                    text = this.textRenderer.call(this, val, key, text);

                if (key || text)
                {
                    this.currentValue = {
                        key: key || text,
                        text: text || key
                    };

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.currentValue.text);
                }
                else
                {
                    this.currentValue = false;

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.requireSelection ? this.emptyText : '');
                }
            }
            else
            {
                if (val !== undefined && val !== null)
                {
                    key = val;
                    text = val;

                    if (text && this.textTemplate)
                        text = this.textTemplate.apply(text, this);
                    else if (this.textRenderer)
                        text = this.textRenderer.call(this, val, key, text);

                    this.currentValue = {
                        key: key,
                        text: text
                    };

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(text);
                }
                else
                {
                    this.currentValue = false;

                    if (initial) this.originalValue = this.currentValue;

                    this.setText(this.requireSelection ? this.emptyText : '');
                }
            }
        },
        clearValue: function(flag) {
            var initial = flag !== true;

            this.setValue(null, initial);
        }
    });

    Sage.Platform.Mobile.Controls.FieldManager.register('lookup', Sage.Platform.Mobile.Controls.LookupField);
})();<|endoftext|>


<commit_before>if (window.innerWidth && window.innerWidth <= 600) {
    $(document).ready(function(){
        $('#header ul').addClass('hide');
        $('#header').append('
Menu
');
    });
    function toggleMenu() {
        $('#header ul').toggleClass('hide');
        $('#header .leftButton').toggleClass('pressed');
    }
}
<commit_msg>Update android.js<commit_after>if (window.innerWidth && window.innerWidth <= 600) {
    $(document).ready(function(){
        $('#header ul').addClass('hide');
        $('#header').append('
Menu
');
    });
    function toggleMenu() {
        $('#header ul').toggleClass('hide');
        $('#header .leftButton').toggleClass('pressed');
    }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

const Path = require('path')
const Fs = require('fs')
const jsdom = require('jsdom')
const mkdirp = require('mkdirp')
const cmdwatcher = require('./util/cmdwatcher')
const LANGS = Path.join(__dirname, '../languages/languages.json')

function createLangButton(dom, file, lang, langName) {
  var li = dom.createElement('li')
  li.className = 'nav-lang-' + lang
  li.innerHTML = '' + langName + ''
  return li
}

function createSkipButton(dom) {
  var a = dom.createElement('a')
  a.className = 'skip'
  a.href = '#main'
  a.innerHTML = 'Skip to Content'
  return a
}

function addTranslationNav(dom, file, languages) {
  var nav = dom.createElement('ul')
  nav.className = "nav-lang"

  Object.keys(languages).sort(function (a, b) {
  	return languages[a] > languages[b] ? 1 : -1
  }).forEach(function(lang) {
    nav.appendChild(createLangButton(dom, file, lang, languages[lang]))
  })
  var node = dom.querySelector('header > *:first-child')
  if (node) {
  	node.parentNode.insertBefore(createSkipButton(dom), node)
  	node.parentNode.insertBefore(nav, node)
  }
  return nav
}

cmdwatcher('build-html'
	     , '!(node_modules).html'
	     , 'languages/**'
	     , function processFiles(files)
{
	var languages
	try {
		languages = JSON.parse(Fs.readFileSync(LANGS, 'utf8'))
	} catch (e) {
		return console.log('Error while processing %s:\n%s', LANGS, e)
	}
	var translations = {}
	Object.keys(languages).forEach(function (lang) {
		var raw
		  , translationPath = Path.join(__dirname, '../languages/', lang + '.json')
		try {
			raw = Fs.readFileSync(translationPath, 'utf8')
		} catch (e) {
			delete languages[lang]
			console.log('Error while loading language definition %s:\n%s', translationPath, e)
			return
		}
		try {
			translations[lang] = JSON.parse(raw)
		} catch (e) {
			delete languages[lang]
			console.log('Error while parsing language definition %s:\n%s', translationPath, e)
			return
		}
	})
	languages['en'] = 'English'
	files.forEach(function (file) {
		var raw
		  , dom
		  , original
		try {
			raw = Fs.readFileSync(file, 'utf8')
		} catch(e) {
			return console.log('Error while reading %s:\n%s', file, e)
		}
		try {
			dom = jsdom.jsdom(raw)
		} catch(e) {
			return console.log('Error while domify %s:\n%s', file, e)
		}

		var list = dom.querySelectorAll('[data-i18n]')
		if (list) {
			original = {}
			for (var i = 0; i < list.length; i++) {
				var node = list[i]
				var key = node.attributes['data-i18n'].value
				original[key] = node.innerHTML
			}
		}
		translations['en'] = original

		var nav = addTranslationNav(dom, file, languages)

		Object.keys(languages).forEach(function (lang) {
			var translation = translations[lang]
			  , outputPath = Path.join('.build/', (lang === 'en' ? '' : lang + '/'), file)
			  , outputDir = Path.dirname(outputPath)
			  , output
			if (list) {
				try {
					for (var i = 0; i < list.length; i++) {
						var node = list[i]
						var key = node.attributes['data-i18n'].value
						var newValue = translation[key]
						node.innerHTML = typeof newValue === 'string' ? newValue : original[key]
					}
					var langButton;
					for (var i = 0; i < nav.childNodes.length; i++) {
						var node = nav.childNodes[i]
						if (node.firstChild.attributes.lang.nodeValue === lang) {
							langButton = node
							break
						}
					}
					var _className = langButton.className
					var _html = langButton.innerHTML
					langButton.className += 'selected'
					langButton.innerHTML = languages[lang]
					output = jsdom.serializeDocument(dom)
					langButton.className = _className
					langButton.innerHTML = _html
				} catch (e) {
					return console.log('Couldn\'t process the %s [lang %s]:\n%s', file, lang, e)
				}
			} else {
				output = raw
			}
			try {
				mkdirp.sync(outputDir);
			} catch (e) {
				return console.log('Error trying to create directory %s for %s [lang %s]:\n%s', outputDir, outputPath, lang, e)
			}
			try {
				Fs.writeFileSync(outputPath, output)
			} catch (e) {
				return console.log('Couldn\'t write %s -> %s [lang %s]:\n%s', file, outputPath, lang, e)
			}
		});
	})
})<commit_msg>Added language field<commit_after>#!/usr/bin/env node

const Path = require('path')
const Fs = require('fs')
const jsdom = require('jsdom')
const mkdirp = require('mkdirp')
const cmdwatcher = require('./util/cmdwatcher')
const LANGS = Path.join(__dirname, '../languages/languages.json')

function createLangButton(dom, file, lang, langName) {
  var li = dom.createElement('li')
  li.className = 'nav-lang-' + lang
  li.innerHTML = '' + langName + ''
  return li
}

function createSkipButton(dom) {
  var a = dom.createElement('a')
  a.className = 'skip'
  a.href = '#main'
  a.innerHTML = 'Skip to Content'
  return a
}

function addTranslationNav(dom, file, languages) {
  var nav = dom.createElement('ul')
  nav.className = "nav-lang"

  Object.keys(languages).sort(function (a, b) {
  	return languages[a] > languages[b] ? 1 : -1
  }).forEach(function(lang) {
    nav.appendChild(createLangButton(dom, file, lang, languages[lang]))
  })
  var node = dom.querySelector('header > *:first-child')
  if (node) {
  	node.parentNode.insertBefore(createSkipButton(dom), node)
  	node.parentNode.insertBefore(nav, node)
  }
  return nav
}

cmdwatcher('build-html'
	     , '!(node_modules).html'
	     , 'languages/**'
	     , function processFiles(files)
{
	var languages
	try {
		languages = JSON.parse(Fs.readFileSync(LANGS, 'utf8'))
	} catch (e) {
		return console.log('Error while processing %s:\n%s', LANGS, e)
	}
	var translations = {}
	Object.keys(languages).forEach(function (lang) {
		var raw
		  , translationPath = Path.join(__dirname, '../languages/', lang + '.json')
		try {
			raw = Fs.readFileSync(translationPath, 'utf8')
		} catch (e) {
			delete languages[lang]
			console.log('Error while loading language definition %s:\n%s', translationPath, e)
			return
		}
		try {
			translations[lang] = JSON.parse(raw)
		} catch (e) {
			delete languages[lang]
			console.log('Error while parsing language definition %s:\n%s', translationPath, e)
			return
		}
	})
	languages['en'] = 'English'
	files.forEach(function (file) {
		var raw
		  , dom
		  , original
		try {
			raw = Fs.readFileSync(file, 'utf8')
		} catch(e) {
			return console.log('Error while reading %s:\n%s', file, e)
		}
		try {
			dom = jsdom.jsdom(raw)
		} catch(e) {
			return console.log('Error while domify %s:\n%s', file, e)
		}

		var list = dom.querySelectorAll('[data-i18n]')
		if (list) {
			original = {}
			for (var i = 0; i < list.length; i++) {
				var node = list[i]
				var key = node.attributes['data-i18n'].value
				original[key] = node.innerHTML
			}
		}
		translations['en'] = original

		var html = dom.querySelector('html')

		var nav = addTranslationNav(dom, file, languages)

		Object.keys(languages).forEach(function (lang) {
			var translation = translations[lang]
			  , outputPath = Path.join('.build/', (lang === 'en' ? '' : lang + '/'), file)
			  , outputDir = Path.dirname(outputPath)
			  , output
			html.setAttributeNode(dom.createAttribute('lang'))
			html.attributes.lang.value = lang
			if (list) {
				try {
					for (var i = 0; i < list.length; i++) {
						var node = list[i]
						var key = node.attributes['data-i18n'].value
						var newValue = translation[key]
						node.innerHTML = typeof newValue === 'string' ? newValue : original[key]
					}
					var langButton;
					for (var i = 0; i < nav.childNodes.length; i++) {
						var node = nav.childNodes[i]
						if (node.firstChild.attributes.lang.nodeValue === lang) {
							langButton = node
							break
						}
					}
					var _className = langButton.className
					var _html = langButton.innerHTML
					langButton.className += 'selected'
					langButton.innerHTML = languages[lang]
					output = jsdom.serializeDocument(dom)
					langButton.className = _className
					langButton.innerHTML = _html
				} catch (e) {
					return console.log('Couldn\'t process the %s [lang %s]:\n%s', file, lang, e)
				}
			} else {
				output = raw
			}
			try {
				mkdirp.sync(outputDir);
			} catch (e) {
				return console.log('Error trying to create directory %s for %s [lang %s]:\n%s', outputDir, outputPath, lang, e)
			}
			try {
				Fs.writeFileSync(outputPath, output)
			} catch (e) {
				return console.log('Couldn\'t write %s -> %s [lang %s]:\n%s', file, outputPath, lang, e)
			}
		});
	})
})<|endoftext|>


<commit_before>import autobind from "autobind-decorator";
import invariant from "invariant";

import { Store } from "f.lux";

export default class CollectionHandler {
	constructor(container, collectionPropName, page, resync) {
		this.container = container;
		this.collectionPropName = collectionPropName;
		this.page = page;

		this.errorStateName = `${ collectionPropName }Error`
		this.restoredStateName = `${ collectionPropName }Restored`
	}

	get collection() {
		return this.props[this.collectionPropName];
	}

	get endpointId() {
		const { collection } = this;

		return 	collection && collection.endpoint && collection.endpoint.id;
	}

	get error() {
		return this.container.state[this.errorStateName];
	}

	get props() {
		return this.container.props;
	}

	get restored() {
		return this.container.state[this.restoredStateName];
	}

	init() {
		const { collection, resync } = this;

		this.checkForCollectionChange();

		if (collection && collection.isConnected()) {
			if (!this.syncCalled()) {
				this.sync();
			} else if (resync) {
				this.resync();
			}
		}
	}

	checkForCollectionChange(props=this.props) {
		const { collectionPropName, collection: currCollection, endpointId: currEpId, error, restored } = this;
		const nextCollection = props[collectionPropName];
		const nextEpId = nextCollection && nextCollection.endpoint && nextCollection.endpoint.id;

		if (nextCollection === currCollection) { return }

		// NOTE: this.collection is still currCollection NOT nextCollection
		if (!nextCollection) {
			this.setError(null);
		} else {
			 if (currEpId !== nextEpId) {
				this.clearErrorAndResync(nextCollection);
			}

			if (nextCollection.isConnected() && !this.syncCalled(nextCollection) && !error && !restored) {
				this._syncCollection(nextCollection);
			} else if (error && (nextCollection.synced ||
					(currCollection && nextCollection.nextOffset !== currCollection.nextOffset)))
			{
				this.setError(null);
			}
		}
	}

	@autobind
	clearErrorAndResync(resync, collection=this.collection) {
		if (!this.error) { return }

		this.setState(
			{ [this.errorStateName]: null },
			() => {
					if (this.resync) {
						this._syncCollection(collection);
					}
				}
			);
	}

	clearState() {
		const { errorStateName, restoredStateName } = this;

		this.setState({
			[errorStateName]: null,
			[restoredStateName]: false
		})
	}

	@autobind
	fetchError(error) {
		if (this.container.mounted) {
			this.setError(error);
		}
	}

	@autobind
	restoreOnError(error) {
		if (this.collection.restored) {
			if (this.container.mounted) {
				this.setRestored();
			}
		} else {
			return Store.reject(error);
		}
	}

	resync(collection=this.collection) {
		const { collectionPropName, container } = this;

		invariant(collection, `Could not find "${collectionPropName}" in the props of ` +
			`<${container.constructor.displayName}>. Either wrap the root component in a storeContainer(), or ` +
			`explicitly pass "${collectionPropName}" as a prop to <${container.constructor.displayName}>.`
		)

		return collection.resync()
			.catch( error => this.restoreOnError(error) )
			.catch( error => this.fetchError(error) );
	}

	setError(error) {
		const { container, errorStateName } = this;

		this.setState({ [errorStateName]: error });
	}

	setRestored() {
		const { container, restoredStateName } = this;

		this.setState({ [restoredStateName]: true });
	}

	setState(state, cb) {
		const { container } = this;

		if (container.mounted) {
			container.setState(state, cb);
		}
	}

	@autobind
	sync(mergeOp) {
		this._syncCollection(this.collection, mergeOp);
	}

	syncCalled(collection=this.collection) {
		const { page } = this;

		return collection && (
				(collection.fetching || collection.synced) ||
				(page && (collection.paging || collection.nextOffset != 0))
			);
	}

	_syncCollection(collection, mergeOp) {
		const { collectionPropName, container, page } = this;

		if (!collection || !collection.isConnected() || this.syncCalled(collection) || collection.restoring) {
			return
		}

		invariant(collection, `Could not find "${collectionPropName}" in the props of ` +
			`<${container.constructor.displayName}>. Either wrap the root component in a storeContainer(), or ` +
			`explicitly pass "${collectionPropName}" as a prop to <${container.constructor.displayName}>.`
		)

		this.clearState();

		if (page) {
			if (!collection.fetching && !collection.paging) {
				collection.fetchNext(mergeOp)
					.catch( error => this.restoreOnError(error) )
					.catch( error => this.fetchError(error) );
			}
		} else {
			collection.fetch(null, mergeOp)
				.catch( error => this.restoreOnError(error) )
				.catch( error => this.fetchError(error) );
		}
	}
}
<commit_msg>Fix resync instance variable in constructor<commit_after>import autobind from "autobind-decorator";
import invariant from "invariant";

import { Store } from "f.lux";

export default class CollectionHandler {
	constructor(container, collectionPropName, page, resync) {
		this.container = container;
		this.collectionPropName = collectionPropName;
		this.page = page;
		this.resync = resync;

		this.errorStateName = `${ collectionPropName }Error`
		this.restoredStateName = `${ collectionPropName }Restored`
	}

	get collection() {
		return this.props[this.collectionPropName];
	}

	get endpointId() {
		const { collection } = this;

		return 	collection && collection.endpoint && collection.endpoint.id;
	}

	get error() {
		return this.container.state[this.errorStateName];
	}

	get props() {
		return this.container.props;
	}

	get restored() {
		return this.container.state[this.restoredStateName];
	}

	init() {
		const { collection, resync } = this;

		this.checkForCollectionChange();

		if (collection && collection.isConnected()) {
			if (!this.syncCalled()) {
				this.sync();
			} else if (resync) {
				this.resyncNow();
			}
		}
	}

	checkForCollectionChange(props=this.props) {
		const { collectionPropName, collection: currCollection, endpointId: currEpId, error, restored } = this;
		const nextCollection = props[collectionPropName];
		const nextEpId = nextCollection && nextCollection.endpoint && nextCollection.endpoint.id;

		if (nextCollection === currCollection) { return }

		// NOTE: this.collection is still currCollection NOT nextCollection
		if (!nextCollection) {
			this.setError(null);
		} else {
			 if (currEpId !== nextEpId) {
				this.clearErrorAndResync(nextCollection);
			}

			if (nextCollection.isConnected() && !this.syncCalled(nextCollection) && !error && !restored) {
				this._syncCollection(nextCollection);
			} else if (error && (nextCollection.synced ||
					(currCollection && nextCollection.nextOffset !== currCollection.nextOffset)))
			{
				this.setError(null);
			}
		}
	}

	@autobind
	clearErrorAndResync(resync, collection=this.collection) {
		if (!this.error) { return }

		this.setState(
			{ [this.errorStateName]: null },
			() => {
					if (this.resync) {
						this._syncCollection(collection);
					}
				}
			);
	}

	clearState() {
		const { errorStateName, restoredStateName } = this;

		this.setState({
			[errorStateName]: null,
			[restoredStateName]: false
		})
	}

	@autobind
	fetchError(error) {
		if (this.container.mounted) {
			this.setError(error);
		}
	}

	@autobind
	restoreOnError(error) {
		if (this.collection.restored) {
			if (this.container.mounted) {
				this.setRestored();
			}
		} else {
			return Store.reject(error);
		}
	}

	resyncNow(collection=this.collection) {
		const { collectionPropName, container } = this;

		invariant(collection, `Could not find "${collectionPropName}" in the props of ` +
			`<${container.constructor.displayName}>. Either wrap the root component in a storeContainer(), or ` +
			`explicitly pass "${collectionPropName}" as a prop to <${container.constructor.displayName}>.`
		)

		return collection.resync()
			.catch( error => this.restoreOnError(error) )
			.catch( error => this.fetchError(error) );
	}

	setError(error) {
		const { container, errorStateName } = this;

		this.setState({ [errorStateName]: error });
	}

	setRestored() {
		const { container, restoredStateName } = this;

		this.setState({ [restoredStateName]: true });
	}

	setState(state, cb) {
		const { container } = this;

		if (container.mounted) {
			container.setState(state, cb);
		}
	}

	@autobind
	sync(mergeOp) {
		this._syncCollection(this.collection, mergeOp);
	}

	syncCalled(collection=this.collection) {
		const { page } = this;

		return collection && (
				(collection.fetching || collection.synced) ||
				(page && (collection.paging || collection.nextOffset != 0))
			);
	}

	_syncCollection(collection, mergeOp) {
		const { collectionPropName, container, page } = this;

		if (!collection || !collection.isConnected() || this.syncCalled(collection) || collection.restoring) {
			return
		}

		invariant(collection, `Could not find "${collectionPropName}" in the props of ` +
			`<${container.constructor.displayName}>. Either wrap the root component in a storeContainer(), or ` +
			`explicitly pass "${collectionPropName}" as a prop to <${container.constructor.displayName}>.`
		)

		this.clearState();

		if (page) {
			if (!collection.fetching && !collection.paging) {
				collection.fetchNext(mergeOp)
					.catch( error => this.restoreOnError(error) )
					.catch( error => this.fetchError(error) );
			}
		} else {
			collection.fetch(null, mergeOp)
				.catch( error => this.restoreOnError(error) )
				.catch( error => this.fetchError(error) );
		}
	}
}
<|endoftext|>


<commit_before>var util = require('util')
    , EventEmitter = require('events').EventEmitter
    , MongoClient = require('mongodb').MongoClient
    , ObjectID = require('mongodb').ObjectID
    , jwt = require("jsonwebtoken");

function RestAPI() {
    this.routes = [
        { method: 'get', mount: '', handler: this._query },
        { method: 'get', mount: '/:id', handler: this._get },
        { method: 'put', mount: '', handler: this._create },
        { method: 'post', mount: '', handler: this._create },
        { method: 'post', mount: '/:id', handler: this._update },
        { method: 'delete', mount: '/:id', handler: this._remove }
    ];
}
util.inherits(RestAPI, EventEmitter);
RestAPI.prototype.getRoutes = function () {
    return this.routes;
}

RestAPI.prototype._query = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        self.query.call(self, req.user.bucket, exports.responseCallback(res));
    });
}
RestAPI.prototype._get = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else {
            self.get.call( self, req.user.bucket, { _id: ObjectID(req.params.id) }, exports.responseCallback(res));
        }
    });
}
RestAPI.prototype._create = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.body) {
            exports.sendError(res, 400, 'body missing');
        } else {
            self.create.call( self, req.user.bucket, req.body, exports.responseCallback(res));
        }
    });

}
RestAPI.prototype._update = function (req, res) {

    var self = this;

    self.resolveAuthentification(req,  function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else if (!req.body) {
            exports.sendError(res, 400, 'body missing');
        } else {
            self.update.call( self, req.user.bucket, { _id: ObjectID(req.params.id) }, req.body, exports.responseCallback(res));
        }
    });
}
RestAPI.prototype._remove = function (req, res) {

    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }

        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else {
            self.remove.call(self, req.user.bucket, { _id: ObjectID(req.params.id) }, exports.responseCallback(res));
        }
    });
};

function RestMongoAPI(uri, privateKey, collection) {
    this.mongoUri = uri;
    this.connections = {};
    this.privateKey = privateKey;
    this.collection = collection;

    RestAPI.call(this);

}

util.inherits(RestMongoAPI, RestAPI);

RestMongoAPI.prototype.resolveAuthentification = function(req, cb) {

    var token;

    if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
        if (req.headers['access-control-request-headers'].split(', ').indexOf('authorization') != -1) {
            return cb();
        }
    }

    if (req.headers && req.headers.authorization) {
        var parts = req.headers.authorization.split(' ');
        if (parts.length == 2) {
            var scheme = parts[0]
                , credentials = parts[1];

            if (/^Bearer$/i.test(scheme)) {
                token = credentials;
            }
        } else {
            return cb();
        }
    } else {
        return cb();
    }

    jwt.verify(token, this.privateKey, function (err, decoded) {
        if (err) return cb();

        req.user = decoded;

        return cb();
    });
}

RestMongoAPI.prototype._getBucketConnection = function( bucket, callback ) {
    var self = this;

    if( this.connections[bucket] == undefined ) {
        // Instantiate new mongo connection
        MongoClient.connect(this.mongoUri + "/" + bucket, { auto_reconnect: true }, function (err, db) {
            if (err) return self.emit('error', err);
            self.connections[bucket] = {
                db: db,
                connected_at: new Date()
            };
            self.emit('connect', db);
            db.on('error', function (err) {
                self.emit('error', err);
            });

            callback( self.connections[bucket].db );
        });
    }
    else if( callback ) {
        callback( this.connections[bucket].db );
    }
};

RestMongoAPI.prototype._return = function (err, data, isArray, cb) {
    if (!cb) {
        return;
    } else if (err) {
        cb(500, err.message ? err.message : err);
    } else if (!data) {
        cb(404);
    } else if (isArray) {
        cb(null, data);
    } else {
        cb(null, util.isArray(data) ? data[0] : data);
    }
}
RestMongoAPI.prototype.query = function (bucket, criteria, options, cb) {
    if (typeof criteria === 'function') {
        cb = criteria;
        criteria = {};
        options = {};
    } else if (typeof options === 'function') {
        cb = options;
        options = {};
    }

    var self = this;

    if (!options.limit) options.limit = 100;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).find(criteria, options).toArray(function (err, data) {
            self._return(err, data, true, cb);
        });
    });
}
RestMongoAPI.prototype.get = function (bucket, criteria, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;
    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).findOne(criteria, options, function (err, data) {
            self._return(err, data, false, cb);
        });
    });
}
RestMongoAPI.prototype.create = function (bucket, data, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;
    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).insert(data, options, function (err, data) {
            self._return(err, data, false, cb);
        });
    });
}
RestMongoAPI.prototype.update = function (bucket, criteria, data, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = { w: 1 };
    }
    delete data._id;
    var self = this;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).update(criteria, data, options, function (err, _data) {
            if (criteria._id) {
                data._id = criteria._id.toString();
            }
            self._return(err, _data ? data : null, false, cb);
        });
    });
}
RestMongoAPI.prototype.remove = function (bucket, criteria, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).remove(criteria, options, function (err, data) {
            if (err) {
                cb(500, err.message ? err.message : err);
            } else if (!data) {
                cb(400);
            } else {
                cb(null, '');
            }
        });
    });
}

exports.use = function (express, mount, api) {
    var routes = api.getRoutes();

    function getHandler(context, f) {
        return function (req, res, next) {
            f.call(context, req, res, next);
        }
    }

    for (var i = 0; i < routes.length; i++) {
        express[routes[i].method](mount + routes[i].mount, getHandler(api, routes[i].handler));
    }
}
exports.throwError = function (code, message) {
    console.error(code, message);
    var err = new Error(message);
    err.code = code;
    throw err;
}
exports.sendError = function (res, code, message) {
    var data = {
        success: false,
        code: code,
        error: message || ''
    };
    res.jsonp(code, data);
}
exports.responseCallback = function (res) {
    var self = this;
    return function (err, data) {
        if (err) {
            self.sendError(res, err.code || parseInt(err) || 400, err.message || data);
        } else {
            res.jsonp(data);
        }
    }
}
exports.RestAPI = RestAPI;
exports.RestMongoAPI = RestMongoAPI;

<commit_msg>Token sent by query<commit_after>var util = require('util')
    , EventEmitter = require('events').EventEmitter
    , MongoClient = require('mongodb').MongoClient
    , ObjectID = require('mongodb').ObjectID
    , jwt = require("jsonwebtoken");

function RestAPI() {
    this.routes = [
        { method: 'get', mount: '', handler: this._query },
        { method: 'get', mount: '/:id', handler: this._get },
        { method: 'put', mount: '', handler: this._create },
        { method: 'post', mount: '', handler: this._create },
        { method: 'post', mount: '/:id', handler: this._update },
        { method: 'delete', mount: '/:id', handler: this._remove }
    ];
}
util.inherits(RestAPI, EventEmitter);
RestAPI.prototype.getRoutes = function () {
    return this.routes;
}

RestAPI.prototype._query = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        self.query.call(self, req.user.bucket, exports.responseCallback(res));
    });
}
RestAPI.prototype._get = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else {
            self.get.call( self, req.user.bucket, { _id: ObjectID(req.params.id) }, exports.responseCallback(res));
        }
    });
}
RestAPI.prototype._create = function (req, res) {
    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.body) {
            exports.sendError(res, 400, 'body missing');
        } else {
            self.create.call( self, req.user.bucket, req.body, exports.responseCallback(res));
        }
    });

}
RestAPI.prototype._update = function (req, res) {

    var self = this;

    self.resolveAuthentification(req,  function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }
        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else if (!req.body) {
            exports.sendError(res, 400, 'body missing');
        } else {
            self.update.call( self, req.user.bucket, { _id: ObjectID(req.params.id) }, req.body, exports.responseCallback(res));
        }
    });
}
RestAPI.prototype._remove = function (req, res) {

    var self = this;

    self.resolveAuthentification(req, function () {
        if (!req.user || !req.user.bucket) {
            return exports.sendError( res, 401, 'Unauthorized');
        }

        if (!req.params.id) {
            exports.sendError(res, 400, 'id missing');
        } else {
            self.remove.call(self, req.user.bucket, { _id: ObjectID(req.params.id) }, exports.responseCallback(res));
        }
    });
};

function RestMongoAPI(uri, privateKey, collection) {
    this.mongoUri = uri;
    this.connections = {};
    this.privateKey = privateKey;
    this.collection = collection;

    RestAPI.call(this);

}

util.inherits(RestMongoAPI, RestAPI);

RestMongoAPI.prototype.resolveAuthentification = function(req, cb) {

    var token;

    if (req.method === 'OPTIONS' && req.headers.hasOwnProperty('access-control-request-headers')) {
        if (req.headers['access-control-request-headers'].split(', ').indexOf('authorization') != -1) {
            return cb();
        }
    }

    if( req.query.token) {
      token = req.query.token;
    }
    else if (req.headers && req.headers.authorization) {

      var parts = req.headers.authorization.split(' ');
      if (parts.length == 2) {
        var scheme = parts[0]
          , credentials = parts[1];

        if (/^Bearer$/i.test(scheme)) {
          token = credentials;
        }
      } else {
        return cb();
      }

    }
    else {
      return cb();
    }

    jwt.verify(token, this.privateKey, function (err, decoded) {
        if (err) return cb();

        req.user = decoded;

        return cb();
    });
}

RestMongoAPI.prototype._getBucketConnection = function( bucket, callback ) {
    var self = this;

    if( this.connections[bucket] == undefined ) {
        // Instantiate new mongo connection
        MongoClient.connect(this.mongoUri + "/" + bucket, { auto_reconnect: true }, function (err, db) {
            if (err) return self.emit('error', err);
            self.connections[bucket] = {
                db: db,
                connected_at: new Date()
            };
            self.emit('connect', db);
            db.on('error', function (err) {
                self.emit('error', err);
            });

            callback( self.connections[bucket].db );
        });
    }
    else if( callback ) {
        callback( this.connections[bucket].db );
    }
};

RestMongoAPI.prototype._return = function (err, data, isArray, cb) {
    if (!cb) {
        return;
    } else if (err) {
        cb(500, err.message ? err.message : err);
    } else if (!data) {
        cb(404);
    } else if (isArray) {
        cb(null, data);
    } else {
        cb(null, util.isArray(data) ? data[0] : data);
    }
}
RestMongoAPI.prototype.query = function (bucket, criteria, options, cb) {
    if (typeof criteria === 'function') {
        cb = criteria;
        criteria = {};
        options = {};
    } else if (typeof options === 'function') {
        cb = options;
        options = {};
    }

    var self = this;

    if (!options.limit) options.limit = 100;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).find(criteria, options).toArray(function (err, data) {
            self._return(err, data, true, cb);
        });
    });
}
RestMongoAPI.prototype.get = function (bucket, criteria, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;
    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).findOne(criteria, options, function (err, data) {
            self._return(err, data, false, cb);
        });
    });
}
RestMongoAPI.prototype.create = function (bucket, data, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;
    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).insert(data, options, function (err, data) {
            self._return(err, data, false, cb);
        });
    });
}
RestMongoAPI.prototype.update = function (bucket, criteria, data, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = { w: 1 };
    }
    delete data._id;
    var self = this;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).update(criteria, data, options, function (err, _data) {
            if (criteria._id) {
                data._id = criteria._id.toString();
            }
            self._return(err, _data ? data : null, false, cb);
        });
    });
}
RestMongoAPI.prototype.remove = function (bucket, criteria, options, cb) {
    if (typeof options === 'function') {
        cb = options;
        options = {};
    }
    var self = this;

    self._getBucketConnection( bucket, function(db) {
        db.collection(self.collection).remove(criteria, options, function (err, data) {
            if (err) {
                cb(500, err.message ? err.message : err);
            } else if (!data) {
                cb(400);
            } else {
                cb(null, '');
            }
        });
    });
}

exports.use = function (express, mount, api) {
    var routes = api.getRoutes();

    function getHandler(context, f) {
        return function (req, res, next) {
            f.call(context, req, res, next);
        }
    }

    for (var i = 0; i < routes.length; i++) {
        express[routes[i].method](mount + routes[i].mount, getHandler(api, routes[i].handler));
    }
}
exports.throwError = function (code, message) {
    console.error(code, message);
    var err = new Error(message);
    err.code = code;
    throw err;
}
exports.sendError = function (res, code, message) {
    var data = {
        success: false,
        code: code,
        error: message || ''
    };
    res.jsonp(code, data);
}
exports.responseCallback = function (res) {
    var self = this;
    return function (err, data) {
        if (err) {
            self.sendError(res, err.code || parseInt(err) || 400, err.message || data);
        } else {
            res.jsonp(data);
        }
    }
}
exports.RestAPI = RestAPI;
exports.RestMongoAPI = RestMongoAPI;

<|endoftext|>