<commit_before>/*global app, client, URL, me*/
"use strict";

var HumanModel = require('human-model');
var getUserMedia = require('getusermedia');
var Contacts = require('./contacts');
var Calls = require('./calls');
var Contact = require('./contact');
var MUCs = require('./mucs');
var MUC = require('./muc');
var ContactRequests = require('./contactRequests');
var avatarHandler = require('../helpers/avatarHandler');
var crypto = require('crypto');

module.exports = HumanModel.define({
    initialize: function (opts) {
        this.setAvatar(opts ? opts.avatarID : null);

        this.bind('change:jid', this.load, this);
        this.bind('change:hasFocus', function () {
            this.setActiveContact(this._activeContact);
        }, this);
        this.calls.bind('add remove reset', this.updateActiveCalls, this);
        this.bind('change:avatarID', this.save, this);
        this.bind('change:status', this.save, this);
        this.bind('change:rosterVer', this.save, this);
        this.bind('change:soundEnabled', this.save, this);
        this.contacts.bind('change:unreadCount', this.updateUnreadCount, this);
        app.state.bind('change:active', this.updateIdlePresence, this);
        app.state.bind('change:deviceIDReady', this.registerDevice, this);
    },
    props: {
        jid: ['object', true],
        status: 'string',
        avatarID: 'string',
        rosterVer: 'string',
        nick: 'string'
    },
    session: {
        avatar: 'string',
        connected: ['bool', false, false],
        shouldAskForAlertsPermission: ['bool', false, false],
        hasFocus: ['bool', false, false],
        _activeContact: 'string',
        stream: 'object',
        soundEnabled: ['bool', false, true],
    },
    collections: {
        contacts: Contacts,
        contactRequests: ContactRequests,
        mucs: MUCs,
        calls: Calls
    },
    derived: {
        displayName: {
            deps: ['nick', 'jid'],
            fn: function () {
                return this.nick || this.jid.bare;
            }
        },
        streamUrl: {
            deps: ['stream'],
            fn: function () {
                if (!this.stream) return '';
                return URL.createObjectURL(this.stream);
            }
        },
        organization: {
            deps: ['orga'],
            fn: function () {
                return app.serverConfig().name || 'Otalk';
            }
        },
        soundEnabledClass: {
            deps: ['soundEnabled'],
            fn: function () {
                return this.soundEnabled ? "primary" : "secondary";
            }
        },
        isAdmin: {
            deps: ['jid'],
            fn: function () {
               return this.jid.local === SERVER_CONFIG.admin ? 'meIsAdmin' : '';
            }
        }
    },
    setActiveContact: function (jid) {
        var prev = this.getContact(this._activeContact);
        if (prev) {
            prev.activeContact = false;
        }
        var curr = this.getContact(jid);
        if (curr) {
            curr.activeContact = true;
            curr.unreadCount = 0;
            this._activeContact = curr.id;
        }
    },
    getName: function () {
        return this.displayName;
    },
    getNickname: function () {
        return this.displayName != this.nick ? this.nick : '';
    },
    getAvatar: function () {
        return this.avatar;
    },
    setAvatar: function (id, type, source) {
        if (!this.avatar) this.avatar = avatarHandler.getGravatar('').uri;

        var self = this;
        avatarHandler.fetch('', id, type, source, function (avatar) {
            self.avatarID = avatar.id;
            self.avatar = avatar.uri;
        });
    },
    publishAvatar: function (data) {
        if (!data) data = this.avatar;
        if (!data || data.indexOf('https://') != -1) return;

        var resampler = new Resample(data, 80, 80, function (data) {
            var b64Data = data.split(',')[1];
            var id = crypto.createHash('sha1').update(atob(b64Data)).digest('hex');
            app.storage.avatars.add({id: id, uri: data});
            client.publishAvatar(id, b64Data, function (err, res) {
                if (err) return;
                client.useAvatars([{
                  id: id,
                  width: 80,
                  height: 80,
                  type: 'image/png',
                  bytes: b64Data.length
                }]);
            });
        });
    },
    hasLdapUsers: function () {
        return app.ldapUsers.length > 0 ? 'hasLdapUsers' : '';
    },
    setSoundNotification: function(enable) {
        this.soundEnabled = enable;
    },
    getContact: function (jid, alt) {
        if (typeof jid === 'string') {
            if (SERVER_CONFIG.domain && jid.indexOf('@') == -1) jid += '@' + SERVER_CONFIG.domain;
            jid = new client.JID(jid);
        }
        if (typeof alt === 'string') alt = new client.JID(alt);

        if (this.isMe(jid)) {
            jid = alt || jid;
        }

        if (!jid) return;

        return this.contacts.get(jid.bare) ||
            this.mucs.get(jid.bare) ||
            this.calls.findWhere('jid', jid);
    },
    setContact: function (data, create) {
        var contact = this.getContact(data.jid);
        data.jid = data.jid.bare;

        if (contact) {
            contact.set(data);
            contact.save();
        } else if (create) {
            contact = new Contact(data);
            contact.inRoster = true;
            contact.owner = this.jid.bare;
            contact.save();
            this.contacts.add(contact);
        }
    },
    removeContact: function (jid) {
        var contact = this.getContact(jid);
        this.contacts.remove(contact.jid);
        app.storage.roster.remove(contact.storageId);
    },
    load: function () {
        if (!this.jid.bare) return;

        var self = this;

        app.storage.profiles.get(this.jid.bare, function (err, profile) {
            if (!err) {
                self.nick = self.jid.local;
                self.status = profile.status;
                self.avatarID = profile.avatarID;
                self.soundEnabled = profile.soundEnabled;
            }
            self.save();
            app.storage.roster.getAll(self.jid.bare, function (err, contacts) {
                if (err) return;

                contacts.forEach(function (contact) {
                    contact = new Contact(contact);
                    contact.owner = self.jid.bare;
                    contact.inRoster = true;
                    if (contact.jid.indexOf("@" + SERVER_CONFIG.domain) > -1)
                      contact.persistent = true;
                    contact.save();
                    self.contacts.add(contact);
                });
            });
        });

        this.mucs.once('loaded', function () {
            self.contacts.trigger('loaded');
        });
    },
    isMe: function (jid) {
        return jid && (jid.bare === this.jid.bare);
    },
    updateJid: function(newJid) {
        if (this.jid.domain && this.isMe(newJid)) {
            this.jid.full = newJid.full;
            this.jid.resource = newJid.resource;
            this.jid.unescapedFull = newJid.unescapedFull;
            this.jid.prepped = newJid.prepped;
        } else {
            this.jid = newJid;
            this.nick = this.jid.local;
        }
    },
    updateIdlePresence: function () {
        var update = {
            status: this.status,
            show: this.show,
            caps: app.api.disco.caps
        };

        if (!app.state.active) {
            update.idle = {since: app.state.idleSince};
        }

        app.api.sendPresence(update);
    },
    updateUnreadCount: function () {
        var unreadCounts = this.contacts.pluck('unreadCount');
        var count = unreadCounts.reduce(function (a, b) { return a + b; });
        if (count === 0) {
            count = '';
        }
        app.state.badge = '' + count;
    },
    updateActiveCalls: function () {
        app.state.hasActiveCall = !!this.calls.length;
    },
    save: function () {
        var data = {
            jid: this.jid.bare,
            avatarID: this.avatarID,
            status: this.status,
            rosterVer: this.rosterVer,
            soundEnabled: this.soundEnabled
        };
        app.storage.profiles.set(data);
    },
    cameraOn: function () {
        var self = this;
        getUserMedia(function (err, stream) {
            if (err) {
                console.error(err);
            } else {
                self.stream = stream;
            }
        });
    },
    cameraOff: function () {
        if (this.stream) {
            this.stream.stop();
            this.stream = null;
        }
    },
    registerDevice: function () {
        var deviceID = app.state.deviceID;
        if (!!deviceID && deviceID !== undefined && deviceID !== 'undefined') {
            client.otalkRegister(deviceID).then(function () {
                client.registerPush('push@push.otalk.im/prod');
            }).catch(function (err) {
                console.log('Could not enable push notifications');
            });
        }
    }
});
<commit_msg>Fix: remove contact<commit_after>/*global app, client, URL, me*/
"use strict";

var HumanModel = require('human-model');
var getUserMedia = require('getusermedia');
var Contacts = require('./contacts');
var Calls = require('./calls');
var Contact = require('./contact');
var MUCs = require('./mucs');
var MUC = require('./muc');
var ContactRequests = require('./contactRequests');
var avatarHandler = require('../helpers/avatarHandler');
var crypto = require('crypto');

module.exports = HumanModel.define({
    initialize: function (opts) {
        this.setAvatar(opts ? opts.avatarID : null);

        this.bind('change:jid', this.load, this);
        this.bind('change:hasFocus', function () {
            this.setActiveContact(this._activeContact);
        }, this);
        this.calls.bind('add remove reset', this.updateActiveCalls, this);
        this.bind('change:avatarID', this.save, this);
        this.bind('change:status', this.save, this);
        this.bind('change:rosterVer', this.save, this);
        this.bind('change:soundEnabled', this.save, this);
        this.contacts.bind('change:unreadCount', this.updateUnreadCount, this);
        app.state.bind('change:active', this.updateIdlePresence, this);
        app.state.bind('change:deviceIDReady', this.registerDevice, this);
    },
    props: {
        jid: ['object', true],
        status: 'string',
        avatarID: 'string',
        rosterVer: 'string',
        nick: 'string'
    },
    session: {
        avatar: 'string',
        connected: ['bool', false, false],
        shouldAskForAlertsPermission: ['bool', false, false],
        hasFocus: ['bool', false, false],
        _activeContact: 'string',
        stream: 'object',
        soundEnabled: ['bool', false, true],
    },
    collections: {
        contacts: Contacts,
        contactRequests: ContactRequests,
        mucs: MUCs,
        calls: Calls
    },
    derived: {
        displayName: {
            deps: ['nick', 'jid'],
            fn: function () {
                return this.nick || this.jid.bare;
            }
        },
        streamUrl: {
            deps: ['stream'],
            fn: function () {
                if (!this.stream) return '';
                return URL.createObjectURL(this.stream);
            }
        },
        organization: {
            deps: ['orga'],
            fn: function () {
                return app.serverConfig().name || 'Otalk';
            }
        },
        soundEnabledClass: {
            deps: ['soundEnabled'],
            fn: function () {
                return this.soundEnabled ? "primary" : "secondary";
            }
        },
        isAdmin: {
            deps: ['jid'],
            fn: function () {
               return this.jid.local === SERVER_CONFIG.admin ? 'meIsAdmin' : '';
            }
        }
    },
    setActiveContact: function (jid) {
        var prev = this.getContact(this._activeContact);
        if (prev) {
            prev.activeContact = false;
        }
        var curr = this.getContact(jid);
        if (curr) {
            curr.activeContact = true;
            curr.unreadCount = 0;
            this._activeContact = curr.id;
        }
    },
    getName: function () {
        return this.displayName;
    },
    getNickname: function () {
        return this.displayName != this.nick ? this.nick : '';
    },
    getAvatar: function () {
        return this.avatar;
    },
    setAvatar: function (id, type, source) {
        if (!this.avatar) this.avatar = avatarHandler.getGravatar('').uri;

        var self = this;
        avatarHandler.fetch('', id, type, source, function (avatar) {
            self.avatarID = avatar.id;
            self.avatar = avatar.uri;
        });
    },
    publishAvatar: function (data) {
        if (!data) data = this.avatar;
        if (!data || data.indexOf('https://') != -1) return;

        var resampler = new Resample(data, 80, 80, function (data) {
            var b64Data = data.split(',')[1];
            var id = crypto.createHash('sha1').update(atob(b64Data)).digest('hex');
            app.storage.avatars.add({id: id, uri: data});
            client.publishAvatar(id, b64Data, function (err, res) {
                if (err) return;
                client.useAvatars([{
                  id: id,
                  width: 80,
                  height: 80,
                  type: 'image/png',
                  bytes: b64Data.length
                }]);
            });
        });
    },
    hasLdapUsers: function () {
        return app.ldapUsers.length > 0 ? 'hasLdapUsers' : '';
    },
    setSoundNotification: function(enable) {
        this.soundEnabled = enable;
    },
    getContact: function (jid, alt) {
        if (typeof jid === 'string') {
            if (SERVER_CONFIG.domain && jid.indexOf('@') == -1) jid += '@' + SERVER_CONFIG.domain;
            jid = new client.JID(jid);
        }
        if (typeof alt === 'string') alt = new client.JID(alt);

        if (this.isMe(jid)) {
            jid = alt || jid;
        }

        if (!jid) return;

        return this.contacts.get(jid.bare) ||
            this.mucs.get(jid.bare) ||
            this.calls.findWhere('jid', jid);
    },
    setContact: function (data, create) {
        var contact = this.getContact(data.jid);
        data.jid = data.jid.bare;

        if (contact) {
            contact.set(data);
            contact.save();
        } else if (create) {
            contact = new Contact(data);
            contact.inRoster = true;
            contact.owner = this.jid.bare;
            contact.save();
            this.contacts.add(contact);
        }
    },
    removeContact: function (jid) {
        var self = this;
        client.removeRosterItem(jid, function(err, res) {
            var contact = self.getContact(jid);
            self.contacts.remove(contact.jid);
            app.storage.roster.remove(contact.storageId);
        });
    },
    load: function () {
        if (!this.jid.bare) return;

        var self = this;

        app.storage.profiles.get(this.jid.bare, function (err, profile) {
            if (!err) {
                self.nick = self.jid.local;
                self.status = profile.status;
                self.avatarID = profile.avatarID;
                self.soundEnabled = profile.soundEnabled;
            }
            self.save();
            app.storage.roster.getAll(self.jid.bare, function (err, contacts) {
                if (err) return;

                contacts.forEach(function (contact) {
                    contact = new Contact(contact);
                    contact.owner = self.jid.bare;
                    contact.inRoster = true;
                    if (contact.jid.indexOf("@" + SERVER_CONFIG.domain) > -1)
                      contact.persistent = true;
                    contact.save();
                    self.contacts.add(contact);
                });
            });
        });

        this.mucs.once('loaded', function () {
            self.contacts.trigger('loaded');
        });
    },
    isMe: function (jid) {
        return jid && (jid.bare === this.jid.bare);
    },
    updateJid: function(newJid) {
        if (this.jid.domain && this.isMe(newJid)) {
            this.jid.full = newJid.full;
            this.jid.resource = newJid.resource;
            this.jid.unescapedFull = newJid.unescapedFull;
            this.jid.prepped = newJid.prepped;
        } else {
            this.jid = newJid;
            this.nick = this.jid.local;
        }
    },
    updateIdlePresence: function () {
        var update = {
            status: this.status,
            show: this.show,
            caps: app.api.disco.caps
        };

        if (!app.state.active) {
            update.idle = {since: app.state.idleSince};
        }

        app.api.sendPresence(update);
    },
    updateUnreadCount: function () {
        var unreadCounts = this.contacts.pluck('unreadCount');
        var count = unreadCounts.reduce(function (a, b) { return a + b; });
        if (count === 0) {
            count = '';
        }
        app.state.badge = '' + count;
    },
    updateActiveCalls: function () {
        app.state.hasActiveCall = !!this.calls.length;
    },
    save: function () {
        var data = {
            jid: this.jid.bare,
            avatarID: this.avatarID,
            status: this.status,
            rosterVer: this.rosterVer,
            soundEnabled: this.soundEnabled
        };
        app.storage.profiles.set(data);
    },
    cameraOn: function () {
        var self = this;
        getUserMedia(function (err, stream) {
            if (err) {
                console.error(err);
            } else {
                self.stream = stream;
            }
        });
    },
    cameraOff: function () {
        if (this.stream) {
            this.stream.stop();
            this.stream = null;
        }
    },
    registerDevice: function () {
        var deviceID = app.state.deviceID;
        if (!!deviceID && deviceID !== undefined && deviceID !== 'undefined') {
            client.otalkRegister(deviceID).then(function () {
                client.registerPush('push@push.otalk.im/prod');
            }).catch(function (err) {
                console.log('Could not enable push notifications');
            });
        }
    }
});
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require turbolinks
//= require_tree .
<commit_msg>Enable rails-ujs to make working REST methods on link by js<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require turbolinks
//= require rails-ujs
//= require_tree .
<|endoftext|>


<commit_before>import React from 'react';
import {ButtonGroup} from 'react-bootstrap';
import UIStore from './../stores/UIStore';
import CreateButton from './CreateButton';
import ReportButton from './ReportButton';
import ExportImportButton from './ExportImportButton';
import ScanCodeButton from './ScanCodeButton';

export default class ContextActions extends React.Component {
  constructor(props) {
    super(props);
    const uiState = UIStore.getState();
    this.state = {
      uiState
    }
  }

  componentDidMount() {
    UIStore.listen(state => this.onChange(state));
  }

  componentWillUnmount() {
    UIStore.unlisten(state => this.onChange(state));
  }

  onChange(state) {
    const uiState = state;
    this.setState({
      uiState
    });
  }

  isAllCollection() {
    const {currentCollection} = this.state.uiState;
    return currentCollection && currentCollection.label == 'All';
  }

  isDisabled() {
    const {currentCollection} = this.state.uiState

    if (currentCollection) {
      if (currentCollection.label == 'All' ||
          (currentCollection.is_shared == true && currentCollection.permission_level < 4))
      return true
    }

    return false
  }

  render() {
    const { updateModalProps } = this.props;
    return (

        <ButtonGroup>
          <ExportImportButton isDisabled={this.isDisabled()}
                              updateModalProps={updateModalProps} />
          <ReportButton />
        </ButtonGroup>
        <ButtonGroup style={{marginLeft: '10px'}}>
          <CreateButton isDisabled={this.isAllCollection()}/>
        </ButtonGroup>
        <ButtonGroup style={{marginLeft: '10px'}}>
          <ScanCodeButton/>
        </ButtonGroup>

    )
  }
}

ContextActions.propTypes = {
  updateModalProps: React.PropTypes.func.isRequired,
};
<commit_msg>add device-button<commit_after>import React from 'react';
import {ButtonGroup, OverlayTrigger, DropdownButton, Button, MenuItem} from 'react-bootstrap';
import UIStore from './../stores/UIStore';
import CreateButton from './CreateButton';
import ReportButton from './ReportButton';
import ExportImportButton from './ExportImportButton';
import ScanCodeButton from './ScanCodeButton';

export default class ContextActions extends React.Component {
  constructor(props) {
    super(props);
    const uiState = UIStore.getState();
    this.state = {
      uiState
    }
  }

  componentDidMount() {
    UIStore.listen(state => this.onChange(state));
  }

  componentWillUnmount() {
    UIStore.unlisten(state => this.onChange(state));
  }

  onChange(state) {
    const uiState = state;
    this.setState({
      uiState
    });
  }

  isAllCollection() {
    const {currentCollection} = this.state.uiState;
    return currentCollection && currentCollection.label == 'All';
  }

  isDisabled() {
    const {currentCollection} = this.state.uiState

    if (currentCollection) {
      if (currentCollection.label == 'All' ||
          (currentCollection.is_shared == true && currentCollection.permission_level < 4))
      return true
    }

    return false
  }

  render() {
    const { updateModalProps } = this.props;
    return (

        <ButtonGroup>
          <ExportImportButton isDisabled={this.isDisabled()}
                              updateModalProps={updateModalProps} />
          <ReportButton />
        </ButtonGroup>
        <ButtonGroup style={{marginLeft: '10px'}}>
          <CreateButton isDisabled={this.isAllCollection()}/>
        </ButtonGroup>
        <ButtonGroup style={{marginLeft: '10px'}}>
          <ScanCodeButton/>
        </ButtonGroup>
        <DeviceButtonSplit/>

    )
  }
}

ContextActions.propTypes = {
  updateModalProps: React.PropTypes.func.isRequired,
};

const DeviceButtonSplit = () => {
  return (
  <ButtonGroup style={{marginLeft: '10px'}}>
    <OverlayTrigger placement="bottom" overlay={<DeviceTooltip/>}>
      <Button
        bsStyle="warning"
        disabled={true}
        onClick={() => {}}
      >
        UI
      </Button>
      </OverlayTrigger>
      <DropdownButton
        bsStyle="warning"
        title={<DropdownButtonTitle/>}
        style={{width: "26px", paddingLeft: "8px"}}
      >
        <MenuItem
          onSelect={() => {}}
        >
          Device 1
        </MenuItem>
        <MenuItem
          onSelect={() => {}}
        >
          Device 2
        </MenuItem>
        <MenuItem
          onSelect={() => {}}
        >
          Device 3
        </MenuItem>
        <MenuItem divider />
        <MenuItem
          onSelect={() => {}}
        >
          Device Management
        </MenuItem>
      </DropdownButton>
    </ButtonGroup>
  )
}

const DeviceTooltip = () =>
  <Tooltip id="create_button">
    Open Device
  </Tooltip>

const DropdownButtonTitle = () =>

<|endoftext|>


<commit_before>import api from '../api';

export const SIGN_IN_SUCCESS = 'SIGN_IN_SUCCESS';
export const SIGN_IN_FAILED = 'SIGN_IN_FAILED';
export const SIGN_OUT_SUCCESS = 'SIGN_OUT_SUCCESS';
export const SIGN_OUT_FAILED = 'SIGN_OUT_FAILED';

function signInSuccess(user) {
  return {
    type: SIGN_IN_SUCCESS,
    user,
  };
}

function signInFailed(error) {
  return {
    type: SIGN_IN_FAILED,
    error,
  };
}

function signOutSuccess() {
  return {
    type: SIGN_OUT_SUCCESS,
  };
}

function signOutFailed(error) {
  return {
    type: SIGN_OUT_FAILED,
    error,
  };
}

export function signIn(googleUser) {
  return dispatch => {
    const info = {
      token: googleUser.getAuthResponse().id_token,
      id: googleUser.getBasicProfile().getEmail().split('@')[0],
    };
    return Promise.all([info, api.Officers.all({ active: new Date() }, true)])
      .then(data => {
        const officers = data[1];
        const oIndex = officers.map(o => o.userDce).indexOf(data[0].id);
        const user = { dce: data[0].id, officer: oIndex !== -1 };

        return Promise.all([
          api.Auth.getToken('google', data[0].id,  data[0].token),
          user,
        ]);
      })
      .then(p => dispatch(signInSuccess(p[1])))
      .catch(error => dispatch(signInFailed(error)));
  };
}

export function checkLogin() {
  return dispatch => {
    return api.Auth.checkToken().then(user => {
      return api.Officers.all({active: new Date() }, true).then(data => {
        const oIndex = data.map(o => o.userDce).indexOf(user.dce);
        if(oIndex !== -1) {
          return Promise.resolve(data[oIndex]);
        }
        return Promise.reject({ message: 'Need to be an officer to log in'});
      });
    })
    .then(p => dispatch(signInSuccess(p)))
    .catch(p => dispatch(signOutSuccess()));
  }
}

export function signOut() {
  return dispatch => {
    return Promise
      .all([api.Auth.signOut(), gapi.auth2.getAuthInstance().signOut()])
      .then(() => dispatch(signOutSuccess()))
      .catch(error => dispatch(signOutFailed(error)));
  };
}
<commit_msg>apease the linting god<commit_after>import api from '../api';

export const SIGN_IN_SUCCESS = 'SIGN_IN_SUCCESS';
export const SIGN_IN_FAILED = 'SIGN_IN_FAILED';
export const SIGN_OUT_SUCCESS = 'SIGN_OUT_SUCCESS';
export const SIGN_OUT_FAILED = 'SIGN_OUT_FAILED';

function signInSuccess(user) {
  return {
    type: SIGN_IN_SUCCESS,
    user,
  };
}

function signInFailed(error) {
  return {
    type: SIGN_IN_FAILED,
    error,
  };
}

function signOutSuccess() {
  return {
    type: SIGN_OUT_SUCCESS,
  };
}

function signOutFailed(error) {
  return {
    type: SIGN_OUT_FAILED,
    error,
  };
}

export function signIn(googleUser) {
  return dispatch => {
    const info = {
      token: googleUser.getAuthResponse().id_token,
      id: googleUser.getBasicProfile().getEmail().split('@')[0],
    };
    return Promise.all([info, api.Officers.all({ active: new Date() }, true)])
      .then(data => {
        const officers = data[1];
        const oIndex = officers.map(o => o.userDce).indexOf(data[0].id);
        const user = { dce: data[0].id, officer: oIndex !== -1 };

        return Promise.all([
          api.Auth.getToken('google', data[0].id,  data[0].token),
          user,
        ]);
      })
      .then(p => dispatch(signInSuccess(p[1])))
      .catch(error => dispatch(signInFailed(error)));
  };
}

export function checkLogin() {
  return dispatch => {
    return api.Auth.checkToken().then(user => {
      return api.Officers.all({ active: new Date() }, true).then(data => {
        const oIndex = data.map(o => o.userDce).indexOf(user.dce);
        if (oIndex !== -1) {
          return Promise.resolve(data[oIndex]);
        }
        return Promise.reject({ message: 'Need to be an officer to log in' });
      });
    })
    .then(p => dispatch(signInSuccess(p)))
    .catch(() => dispatch(signOutSuccess()));
  };
}

export function signOut() {
  return dispatch => {
    return Promise
      .all([api.Auth.signOut(), gapi.auth2.getAuthInstance().signOut()])
      .then(() => dispatch(signOutSuccess()))
      .catch(error => dispatch(signOutFailed(error)));
  };
}
<|endoftext|>


<commit_before>// Vendor assets
//= require jquery2
//= require jquery_ujs
//= require vendor/chosen.jquery
//= require vendor/jquery.tablesorter
//= require raven-3.14.2.min.js

// GOVUK modules
//= require govuk_toolkit
//= require vendor/polyfills/bind
//= require details.polyfill
//= require moj
//= require lodash
//= require jquery-ui-autocomplete
//= require modernizr-custom
//= require dest/respond.min
//= require jquery-ui/datepicker

// Raven / Sentry
//= require modules/moj.sentry

// Candidates for re-usable components
//= require mapshim
//= require filtershim
//= require modules/moj.analytics
//= require modules/moj.AsyncGA
//= require modules/moj.clearRadioButtons
//= require modules/moj.hijacks
//= require modules/moj.submit-once
//= require modules/moj.Conditional
//= require modules/moj.datepicker
//= require modules/moj.emailPreview
//= require modules/moj.multiSelect
//= require modules/moj.tableSorter
//= require modules/moj.BookToNomis
//= require modules/moj.Rejection
//= require modules/moj.restrictionOverrides
//= require modules/moj.matchVisitors
//= require modules/moj.autocomplete
//= require modules/moj.searchPlaceholder
//= require modules/moj.closedRestriction

(function() {
  'use strict';
  delete moj.Modules.devs;
  var selectionButtons = new GOVUK.SelectionButtons("input[type='radio'], input[type='checkbox']");
  var showHideContent = new GOVUK.ShowHideContent();
  showHideContent.init();
  moj.init();
}());
<commit_msg>Remove details polyfill<commit_after>// Vendor assets
//= require jquery2
//= require jquery_ujs
//= require vendor/chosen.jquery
//= require vendor/jquery.tablesorter
//= require raven-3.14.2.min.js

// GOVUK modules
//= require govuk_toolkit
//= require vendor/polyfills/bind
//= require moj
//= require lodash
//= require jquery-ui-autocomplete
//= require modernizr-custom
//= require dest/respond.min
//= require jquery-ui/datepicker

// Raven / Sentry
//= require modules/moj.sentry

// Candidates for re-usable components
//= require mapshim
//= require filtershim
//= require modules/moj.analytics
//= require modules/moj.AsyncGA
//= require modules/moj.clearRadioButtons
//= require modules/moj.hijacks
//= require modules/moj.submit-once
//= require modules/moj.Conditional
//= require modules/moj.datepicker
//= require modules/moj.emailPreview
//= require modules/moj.multiSelect
//= require modules/moj.tableSorter
//= require modules/moj.BookToNomis
//= require modules/moj.Rejection
//= require modules/moj.restrictionOverrides
//= require modules/moj.matchVisitors
//= require modules/moj.autocomplete
//= require modules/moj.searchPlaceholder
//= require modules/moj.closedRestriction

(function() {
  'use strict';
  delete moj.Modules.devs;
  var selectionButtons = new GOVUK.SelectionButtons("input[type='radio'], input[type='checkbox']");
  var showHideContent = new GOVUK.ShowHideContent();
  showHideContent.init();
  moj.init();
}());
<|endoftext|>


<commit_before>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
//
//= require prototype-legacy/prototype
//= require prototype-legacy/effects
//= require prototype-legacy/dragdrop
//= require prototype-legacy/controls
//= require prototype-legacy/builder
//= require prototype-legacy/browser-img-pool
//= require prototype-legacy/browser-post-loading
//= require prototype-legacy/browser-thumb-view
//= require prototype-legacy/browser-view
//= require prototype-legacy/comment
//= require prototype-legacy/common
//= require prototype-legacy/cookie
//= require prototype-legacy/cropper
//= require prototype-legacy/debug-window
//= require prototype-legacy/dmail
//= require prototype-legacy/favorite
//= require prototype-legacy/forum
//= require prototype-legacy/history
//= require prototype-legacy/inline_image
//= require prototype-legacy/main_menu
//= require prototype-legacy/notes
//= require prototype-legacy/pool
//= require prototype-legacy/post-frame-editor
//= require prototype-legacy/post-upload-form
//= require prototype-legacy/post
//= require prototype-legacy/post_mode_menu
//= require prototype-legacy/post_tag_history
//= require prototype-legacy/preload
//= require prototype-legacy/referral
//= require prototype-legacy/related_tags
//= require prototype-legacy/similar-thumbnailing
//= require prototype-legacy/tag_completion
//= require prototype-legacy/touchscreen-web-app-helpers
//= require prototype-legacy/url-hash
//= require prototype-legacy/user
//= require prototype-legacy/user_record
//= require prototype-legacy/vote-widget
//= require jquery
//= require jquery_ujs
//= require jquery.cookie
//= require jquery.ui.autocomplete
//= require compat.jquery
//= require cookie-defaults
//= require_tree .
<commit_msg>Prefix loaded earliest.<commit_after>// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults
//
//= require prefix
//= require prototype-legacy/prototype
//= require prototype-legacy/effects
//= require prototype-legacy/dragdrop
//= require prototype-legacy/controls
//= require prototype-legacy/builder
//= require prototype-legacy/browser-img-pool
//= require prototype-legacy/browser-post-loading
//= require prototype-legacy/browser-thumb-view
//= require prototype-legacy/browser-view
//= require prototype-legacy/comment
//= require prototype-legacy/common
//= require prototype-legacy/cookie
//= require prototype-legacy/cropper
//= require prototype-legacy/debug-window
//= require prototype-legacy/dmail
//= require prototype-legacy/favorite
//= require prototype-legacy/forum
//= require prototype-legacy/history
//= require prototype-legacy/inline_image
//= require prototype-legacy/main_menu
//= require prototype-legacy/notes
//= require prototype-legacy/pool
//= require prototype-legacy/post-frame-editor
//= require prototype-legacy/post-upload-form
//= require prototype-legacy/post
//= require prototype-legacy/post_mode_menu
//= require prototype-legacy/post_tag_history
//= require prototype-legacy/preload
//= require prototype-legacy/referral
//= require prototype-legacy/related_tags
//= require prototype-legacy/similar-thumbnailing
//= require prototype-legacy/tag_completion
//= require prototype-legacy/touchscreen-web-app-helpers
//= require prototype-legacy/url-hash
//= require prototype-legacy/user
//= require prototype-legacy/user_record
//= require prototype-legacy/vote-widget
//= require jquery
//= require jquery_ujs
//= require jquery.cookie
//= require jquery.ui.autocomplete
//= require compat.jquery
//= require cookie-defaults
//= require_tree .
<|endoftext|>


<commit_before>'use strict';
angular.module('cm.services')
  .factory('dom', function() {
    var getTagClass = function(tag) {
      return tag.className;
    }

    var setUniqueClass = function(tag, pattern, c) {
      var regex = new RegExp('(^|\s)' + tag + '-\S+', 'g');
      var strippedClass = document.body.className.replace(regex, '');
      document.body.className = strippedClass + ' ' + pattern + '-' + c;
      return true;
    }

    return {
      setUniqueClass: setUniqueClass
    }
  });
<commit_msg>Fix setUniqueClass RegExp<commit_after>'use strict';
angular.module('cm.services')
  .factory('dom', function() {
    var getTagClass = function(tag) {
      return tag.className;
    }

    var setUniqueClass = function(tag, pattern, c) {
      var regex, strippedClass;
      regex = new RegExp('(^|\\s)' + pattern + '-\\S+', 'g');
      strippedClass = document.body.className.replace(regex, '');
      document.body.className = strippedClass + ' ' + pattern + '-' + c;
      return true;
    }

    return {
      setUniqueClass: setUniqueClass
    }
  });
<|endoftext|>


<commit_before>'use strict';

angular.module('MAGI.controllers', []).
	controller('FormController',['$scope','$location','$anchorScroll','$timeout','filterFilter', 'Application','relationshipCodes','states', function($scope,$location,$anchorScroll,$timeout,filterFilter, Application, relationshipCodes,states){
                Application.resetResults();
                $scope.submitted = false;
                $scope.applicants = Application.applicants;
                $scope.taxReturns = Application.taxReturns;
                $scope.application = Application;

                $scope.addTaxReturn = Application.addTaxReturn;

                $scope.removeApplicant = function(app){
                    $scope.application.removeApplicant(app);
                };

                $scope.removeTaxReturn = Application.removeTaxReturn;
                $scope.relationshipCodes = relationshipCodes;

                $scope.newHousehold = [];

                $scope.exportApplication = function(){
                         $location.path("/exportimport");
                };

                $scope.$watch('newHousehold.length', function(newVal,oldVal){
                    if(newVal > 0){
                        $scope.application.households.push([$scope.newHousehold.pop()]);
                    }
                });

                $scope.rehouseholdingHappened = function(){
                    return _.chain($scope.application.households).
                        map(function(hh){return hh.length;}).
                        reduce(function (m, w) {return m + w;}, 0).
                        value() == $scope.applicants.length;

                };

                $scope.$watch('rehouseholdingHappened()', function(newVal, oldVal){
                    if(newVal){
                        $scope.application.cleanHouseholds();
                    }
                });

                $scope.showNewHousehold = function(){
                    return $scope.application.households.length < $scope.applicants.length;
                };

                $scope.showHouseholds = function(){
                    return $scope.applicants.length > 1;
                };

                $scope.checkEligibility = function(){
                         console.log("Form Valid: " + $scope.applicationForm.$valid);
                         console.log(JSON.stringify($scope.application.serialize()));
                         console.log('CALLIN');
                        if($scope.applicationForm.$valid){
                                var serv = $scope.application.checkEligibility();
                                serv.then(function(resp){
                                        $location.path("/results");
                                });
                        } else {
                                $scope.submitted = true;
                                $timeout(
                                        function(){
                                            angular.element(document.querySelector("input.ng-invalid"))[0].focus( );
                                });
                        }
                };

                $scope.addApplicant = function(){
                        Application.addApplicant("Applicant " + ($scope.applicants.length+1));
                };

                $scope.showFilers = function(){
                        return $scope.applicants.length > 0;
                };

                // We want to initialize the application with an applicant and a tax return
                if($scope.applicants.length === 0){
                        $scope.addApplicant();
                        $scope.addTaxReturn();
                }

		$scope.states = states;
        $scope.appStates = _.filter(states, function(state){return state.inApp;});
	}]).
        controller('ApplicantController',['$scope',function($scope){
                $scope.checkResponsibility = function(){
                        return $scope.applicant.age <= 19;
                };

                $scope.$watch('checkResponsibility()', function(newValue,oldValue){
                        $scope.applicant.nonParentResponsibility = false;
                });

                $scope.$watch('applicant.pregnantThreeMonths', function(newValue,oldValue){
                        if(newValue){
                                $scope.applicant.pregnant = false;
                        }
                });

                $scope.$watch('applicant.pregnant', function(newValue,oldValue){
                        if(newValue){
                                $scope.applicant.pregnantThreeMonths = false;
                        }
                });

                $scope.updateRelationship = function(relationship){
                        relationship.updateOpposite();
                };

                $scope.updateMonthly = function(applicant){
                  applicant.updateMonthly();
                };

                $scope.updateWages = function(applicant){
                  applicant.updateWages();
                };

                $scope.notMe = function(other) {
                        return other !== $scope.applicant;
                };
        }]).
        controller('ResultsController',['$scope','$location','Application', function($scope,$location,Application){
                $scope.applicants = Application.determination['Applicants'];
                $scope.expandByDefault = function(){
                        // return $scope.applicants.length == 1;
                        return true;
                };

                $scope.exportApplication = function(){
                         $location.path("/exportimport");
                };

                $scope.returnToForm = function(){
                        $location.path("/application");
                };
        }]).
        controller('ExportImportController',['$scope','$location','Application', function($scope,$location,Application){
                $scope.applicationJson = angular.toJson(Application.serialize(), true);
                $scope.resultsJson = angular.toJson(Application.determination, true);

                $scope.showResults = Object.keys(Application.determination).length > 0;

                $scope.importApplication = function(){
                        // Note - may want to wrap this in a try/catch loop of some sort
                        var application = angular.fromJson($scope.applicationJson);
                        console.log(application);
                        Application.deserialize(application);
                        // Redirect to application
                        $location.path("/application");
                };

                $scope.returnToForm = function(){
                        $location.path("/application");
                };
        }]).
        controller('TaxReturnController', ['$scope',function($scope){
            $scope.inputs = {newFiler: {}, newDependent: {}};

            $scope.canAddFiler = function(){
                return $scope.taxReturn.canAddFiler($scope.application);
            };

            $scope.showDependents = function(){
                return $scope.taxReturn.dependents.length > 0 || $scope.canAddDependent();
            };

            $scope.canAddDependent = function(){
                return $scope.taxReturn.canAddDependent($scope.application);
            }

            $scope.$watch('inputs.newFiler', function(newFiler, oldVal){
                console.log(newFiler);
                if(newFiler.id){
                    $scope.taxReturn.addFiler(newFiler);
                    $scope.inputs.newFiler = {};
                }
            });

            $scope.$watch('inputs.newDependent', function(newDependent, oldVal){
                if(newDependent.id){
                    $scope.taxReturn.addDependent(newDependent);
                    $scope.inputs.newDependent = {};
                }
            });

            $scope.$watch('taxReturn.filers', function(newVal, old){
                console.log()
            })

        }]);
<commit_msg>Fix Exporter in IE8<commit_after>'use strict';

angular.module('MAGI.controllers', []).
	controller('FormController',['$scope','$location','$anchorScroll','$timeout','filterFilter', 'Application','relationshipCodes','states', function($scope,$location,$anchorScroll,$timeout,filterFilter, Application, relationshipCodes,states){
                Application.resetResults();
                $scope.submitted = false;
                $scope.applicants = Application.applicants;
                $scope.taxReturns = Application.taxReturns;
                $scope.application = Application;

                $scope.addTaxReturn = Application.addTaxReturn;

                $scope.removeApplicant = function(app){
                    $scope.application.removeApplicant(app);
                };

                $scope.removeTaxReturn = Application.removeTaxReturn;
                $scope.relationshipCodes = relationshipCodes;

                $scope.newHousehold = [];

                $scope.exportApplication = function(){
                         $location.path("/exportimport");
                };

                $scope.$watch('newHousehold.length', function(newVal,oldVal){
                    if(newVal > 0){
                        $scope.application.households.push([$scope.newHousehold.pop()]);
                    }
                });

                $scope.rehouseholdingHappened = function(){
                    return _.chain($scope.application.households).
                        map(function(hh){return hh.length;}).
                        reduce(function (m, w) {return m + w;}, 0).
                        value() == $scope.applicants.length;

                };

                $scope.$watch('rehouseholdingHappened()', function(newVal, oldVal){
                    if(newVal){
                        $scope.application.cleanHouseholds();
                    }
                });

                $scope.showNewHousehold = function(){
                    return $scope.application.households.length < $scope.applicants.length;
                };

                $scope.showHouseholds = function(){
                    return $scope.applicants.length > 1;
                };

                $scope.checkEligibility = function(){
                         console.log("Form Valid: " + $scope.applicationForm.$valid);
                         console.log(JSON.stringify($scope.application.serialize()));
                         console.log('CALLIN');
                        if($scope.applicationForm.$valid){
                                var serv = $scope.application.checkEligibility();
                                serv.then(function(resp){
                                        $location.path("/results");
                                });
                        } else {
                                $scope.submitted = true;
                                $timeout(
                                        function(){
                                            angular.element(document.querySelector("input.ng-invalid"))[0].focus( );
                                });
                        }
                };

                $scope.addApplicant = function(){
                        Application.addApplicant("Applicant " + ($scope.applicants.length+1));
                };

                $scope.showFilers = function(){
                        return $scope.applicants.length > 0;
                };

                // We want to initialize the application with an applicant and a tax return
                if($scope.applicants.length === 0){
                        $scope.addApplicant();
                        $scope.addTaxReturn();
                }

		$scope.states = states;
        $scope.appStates = _.filter(states, function(state){return state.inApp;});
	}]).
        controller('ApplicantController',['$scope',function($scope){
                $scope.checkResponsibility = function(){
                        return $scope.applicant.age <= 19;
                };

                $scope.$watch('checkResponsibility()', function(newValue,oldValue){
                        $scope.applicant.nonParentResponsibility = false;
                });

                $scope.$watch('applicant.pregnantThreeMonths', function(newValue,oldValue){
                        if(newValue){
                                $scope.applicant.pregnant = false;
                        }
                });

                $scope.$watch('applicant.pregnant', function(newValue,oldValue){
                        if(newValue){
                                $scope.applicant.pregnantThreeMonths = false;
                        }
                });

                $scope.updateRelationship = function(relationship){
                        relationship.updateOpposite();
                };

                $scope.updateMonthly = function(applicant){
                  applicant.updateMonthly();
                };

                $scope.updateWages = function(applicant){
                  applicant.updateWages();
                };

                $scope.notMe = function(other) {
                        return other !== $scope.applicant;
                };
        }]).
        controller('ResultsController',['$scope','$location','Application', function($scope,$location,Application){
                $scope.applicants = Application.determination['Applicants'];
                $scope.expandByDefault = function(){
                        // return $scope.applicants.length == 1;
                        return true;
                };

                $scope.exportApplication = function(){
                         $location.path("/exportimport");
                };

                $scope.returnToForm = function(){
                        $location.path("/application");
                };
        }]).
        controller('ExportImportController',['$scope','$location','Application', function($scope,$location,Application){
                $scope.applicationJson = angular.toJson(Application.serialize(), true);
                $scope.resultsJson = angular.toJson(Application.determination, true);

                $scope.showResults = _.keys(Application.determination).length > 0;

                $scope.importApplication = function(){
                        // Note - may want to wrap this in a try/catch loop of some sort
                        var application = angular.fromJson($scope.applicationJson);
                        console.log(application);
                        Application.deserialize(application);
                        // Redirect to application
                        $location.path("/application");
                };

                $scope.returnToForm = function(){
                        $location.path("/application");
                };
        }]).
        controller('TaxReturnController', ['$scope',function($scope){
            $scope.inputs = {newFiler: {}, newDependent: {}};

            $scope.canAddFiler = function(){
                return $scope.taxReturn.canAddFiler($scope.application);
            };

            $scope.showDependents = function(){
                return $scope.taxReturn.dependents.length > 0 || $scope.canAddDependent();
            };

            $scope.canAddDependent = function(){
                return $scope.taxReturn.canAddDependent($scope.application);
            }

            $scope.$watch('inputs.newFiler', function(newFiler, oldVal){
                console.log(newFiler);
                if(newFiler.id){
                    $scope.taxReturn.addFiler(newFiler);
                    $scope.inputs.newFiler = {};
                }
            });

            $scope.$watch('inputs.newDependent', function(newDependent, oldVal){
                if(newDependent.id){
                    $scope.taxReturn.addDependent(newDependent);
                    $scope.inputs.newDependent = {};
                }
            });

            $scope.$watch('taxReturn.filers', function(newVal, old){
                console.log()
            })

        }]);
<|endoftext|>


<commit_before>var chaisePage = require('../../../utils/chaise.page.js');
var testParams = {
    table_name: "multi-add-table",
    records: 3,
    max_input_rows: 200,
    initial_int_val: "1",
    initial_text_val: "a",
    input_1_text_val: "asdfg",
    input_2_text_val: "qwerty",
    input_3_int_val: "17"
};

describe('Record Add', function() {

    describe("for when the user adds multiple forms using the multi form input control, ", function() {
        var EC = protractor.ExpectedConditions,
            multiFormOpenButton = chaisePage.recordEditPage.getMultiFormInputOpenButton(),
            multiFormInput = chaisePage.recordEditPage.getMultiFormInput(),
            multiFormSubmitButton = chaisePage.recordEditPage.getMultiFormInputSubmitButton(),
            intVal = testParams.initial_int_val,
            textVal = testParams.initial_text_val,
            intDisplayName = "int",
            textDisplayName = "text";

        var index;

        beforeAll(function () {
            browser.ignoreSynchronization=true;
            browser.get(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/multi-add:" + testParams.table_name);
        });

        it("should click the button and show an input box.", function() {
            browser.wait(EC.elementToBeClickable(multiFormOpenButton), browser.params.defaultTimeout);

            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn);
            }).then(function() {
                return multiFormInput.isDisplayed();
            }).then(function(bool) {
                expect(bool).toBeTruthy();
            });
        });

        it("should click the button a second time to hide the input.", function() {
            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn);
            }).then(function() {
                return multiFormInput.isDisplayed();
            }).then(function(bool) {
                expect(bool).toBeFalsy();
            });
        });

        it("should alert the user when they try to add more forms than the limit of " + testParams.max_input_rows + " allows.", function() {
            var numberGreaterThanMax = 300,
                errorMessage = "Cannot add " + numberGreaterThanMax + " records. Please input a value between 1 and 200, inclusive.";

            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn)
            }).then(function() {
                chaisePage.recordEditPage.clearInput(multiFormInput);
                browser.sleep(10);
                multiFormInput.sendKeys(numberGreaterThanMax);

                return chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript();
            }).then(function(submitBtn) {
                return chaisePage.clickButton(submitBtn);
            }).then(function() {
                return chaisePage.recordEditPage.getAlertError();
            }).then(function(err) {
                return err.getText();
            }).then(function(text) {
                expect(text.indexOf(errorMessage)).toBeGreaterThan(-1);
            });
        });

        it("should fill in the first form add " + (testParams.records-1) + " more forms.", function() {
            index = 0;
            intInput = chaisePage.recordEditPage.getInputById(index, intDisplayName);
            textInput = chaisePage.recordEditPage.getInputById(index, textDisplayName);

            intInput.sendKeys(intVal);
            textInput.sendKeys(textVal);

            chaisePage.recordEditPage.clearInput(multiFormInput);
            browser.sleep(10);
            multiFormInput.sendKeys(testParams.records-1);

            chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript().then(function(submitBtn) {
                return chaisePage.clickButton(submitBtn);
            }).then(function() {
                return chaisePage.recordEditPage.getViewModelRows();
            }).then(function(rows) {
                expect(rows.length).toBe(testParams.records);
            });
        });

        describe("change values in the forms without affecting the other forms, ", function() {
            //added 2 more forms, should be 3 total
            var textInput1 = chaisePage.recordEditPage.getInputById(0, textDisplayName),
                textInput2 = chaisePage.recordEditPage.getInputById(1, textDisplayName),
                textInput3 = chaisePage.recordEditPage.getInputById(2, textDisplayName);

            var intInput1 = chaisePage.recordEditPage.getInputById(0, intDisplayName),
                intInput2 = chaisePage.recordEditPage.getInputById(1, intDisplayName),
                intInput3 = chaisePage.recordEditPage.getInputById(2, intDisplayName);

            //change value in form 1, test others unchanged
            it("should change text input in form 1.", function() {
                chaisePage.recordEditPage.clearInput(textInput1);
                textInput1.sendKeys(testParams.input_1_text_val);

                expect(textInput1.getAttribute("value")).toBe(testParams.input_1_text_val);
                expect(textInput2.getAttribute("value")).toBe(textVal);
                expect(textInput3.getAttribute("value")).toBe(textVal);
            });

            //change value in form n, test others unchanged
            it("should change int input in form 3.", function() {
                chaisePage.recordEditPage.clearInput(intInput3);
                intInput3.sendKeys(testParams.input_3_int_val);

                expect(intInput1.getAttribute("value")).toBe(intVal);
                expect(intInput2.getAttribute("value")).toBe(intVal);
                expect(intInput3.getAttribute("value")).toBe(testParams.input_3_int_val);
            });

            //change value in n/2, test other sunchanged
            it("should change text input in form 2.", function() {
                chaisePage.recordEditPage.clearInput(textInput2);
                textInput2.sendKeys(testParams.input_2_text_val);

                expect(textInput1.getAttribute("value")).toBe(testParams.input_1_text_val);
                expect(textInput2.getAttribute("value")).toBe(testParams.input_2_text_val);
                expect(textInput3.getAttribute("value")).toBe(textVal);
            });
        });

        //test submission
        describe("Submit " + testParams.records + " records", function() {
            beforeAll(function() {
                // Submit the form
                chaisePage.recordEditPage.submitForm();
            });

            it("should change the view to the resultset table and verify the count.", function() {
                // Make sure the table shows up with the expected # of rows
                browser.wait(function() {
                    return chaisePage.recordsetPage.getRows().count().then(function(ct) {
                        return (ct == testParams.records);
                    });
                }, browser.params.defaultTimeout);

                browser.driver.getCurrentUrl().then(function(url) {
                    expect(url.startsWith(process.env.CHAISE_BASE_URL + "/recordedit/")).toBe(true);

                    return chaisePage.recordsetPage.getRows().count();
                }).then(function(ct) {
                    expect(ct).toBe(testParams.records);
                });
            });
        });

        describe("for when the user adds the maximum amount of forms, ", function() {
            var EC = protractor.ExpectedConditions,
                multiFormOpenButton = chaisePage.recordEditPage.getMultiFormInputOpenButton(),
                multiFormInput = chaisePage.recordEditPage.getMultiFormInput(),
                multiFormSubmitButton = chaisePage.recordEditPage.getMultiFormInputSubmitButton();

            beforeAll(function () {
                browser.ignoreSynchronization=true;
                browser.get(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/multi-add:" + testParams.table_name);
            });

            it("should show a resultset table with " + (testParams.max_input_rows+1) + " entities.", function() {
                browser.wait(EC.elementToBeClickable(multiFormOpenButton), browser.params.defaultTimeout);

                var intInput = chaisePage.recordEditPage.getInputById(0, "int");
                intInput.sendKeys("1");

                chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                    return chaisePage.clickButton(openBtn);
                }).then(function() {
                    chaisePage.recordEditPage.clearInput(multiFormInput);
                    browser.sleep(10);
                    multiFormInput.sendKeys(testParams.max_input_rows);

                    return chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript();
                }).then(function(submitBtn) {
                    return chaisePage.clickButton(submitBtn);
                }).then(function() {
                    // wait for dom to finish rendering the forms
                    browser.wait(function() {
                        return chaisePage.recordEditPage.getForms().count().then(function(ct) {
                            return (ct == testParams.max_input_rows+1);
                        });
                    }, browser.params.defaultTimeout);

                    chaisePage.recordEditPage.submitForm();

                    return browser.driver.getCurrentUrl();
                }).then(function(url) {
                    expect(url.startsWith(process.env.CHAISE_BASE_URL + "/recordedit/")).toBe(true);

                    // so DOM can render table
                    browser.wait(function() {
                        return chaisePage.recordsetPage.getRows().count().then(function(ct) {
                            return (ct == testParams.max_input_rows+1);
                        });
                    }, browser.params.defaultTimeout);

                    return chaisePage.recordsetPage.getRows().count();
                }).then(function(ct) {
                    expect(ct).toBe(testParams.max_input_rows+1);
                });
            })
        });
    });
});
<commit_msg>change the failing testcase<commit_after>var chaisePage = require('../../../utils/chaise.page.js');
var testParams = {
    table_name: "multi-add-table",
    records: 3,
    max_input_rows: 200,
    initial_int_val: "1",
    initial_text_val: "a",
    input_1_text_val: "asdfg",
    input_2_text_val: "qwerty",
    input_3_int_val: "17"
};

describe('Record Add', function() {

    describe("for when the user adds multiple forms using the multi form input control, ", function() {
        var EC = protractor.ExpectedConditions,
            multiFormOpenButton = chaisePage.recordEditPage.getMultiFormInputOpenButton(),
            multiFormInput = chaisePage.recordEditPage.getMultiFormInput(),
            multiFormSubmitButton = chaisePage.recordEditPage.getMultiFormInputSubmitButton(),
            intVal = testParams.initial_int_val,
            textVal = testParams.initial_text_val,
            intDisplayName = "int",
            textDisplayName = "text";

        var index;

        beforeAll(function () {
            browser.ignoreSynchronization=true;
            browser.get(browser.params.url + "/recordedit/#" + browser.params.catalogId + "/multi-add:" + testParams.table_name);
        });

        it("should click the button and show an input box.", function() {
            browser.wait(EC.elementToBeClickable(multiFormOpenButton), browser.params.defaultTimeout);

            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn);
            }).then(function() {
                return multiFormInput.isDisplayed();
            }).then(function(bool) {
                expect(bool).toBeTruthy();
            });
        });

        it("should click the button a second time to hide the input.", function() {
            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn);
            }).then(function() {
                return multiFormInput.isDisplayed();
            }).then(function(bool) {
                expect(bool).toBeFalsy();
            });
        });

        it("should alert the user when they try to add more forms than the limit of " + testParams.max_input_rows + " allows.", function() {
            var numberGreaterThanMax = 300,
                errorMessage = "Cannot add " + numberGreaterThanMax + " records. Please input a value between 1 and 200, inclusive.";

            chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                return chaisePage.clickButton(openBtn)
            }).then(function() {
                chaisePage.recordEditPage.clearInput(multiFormInput);
                browser.sleep(10);
                multiFormInput.sendKeys(numberGreaterThanMax);

                return chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript();
            }).then(function(submitBtn) {
                return chaisePage.clickButton(submitBtn);
            }).then(function() {
                return chaisePage.recordEditPage.getAlertError();
            }).then(function(err) {
                return err.getText();
            }).then(function(text) {
                expect(text.indexOf(errorMessage)).toBeGreaterThan(-1);
            });
        });

        it("should fill in the first form add " + (testParams.records-1) + " more forms.", function() {
            index = 0;
            intInput = chaisePage.recordEditPage.getInputById(index, intDisplayName);
            textInput = chaisePage.recordEditPage.getInputById(index, textDisplayName);

            intInput.sendKeys(intVal);
            textInput.sendKeys(textVal);

            chaisePage.recordEditPage.clearInput(multiFormInput);
            browser.sleep(10);
            multiFormInput.sendKeys(testParams.records-1);

            chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript().then(function(submitBtn) {
                return chaisePage.clickButton(submitBtn);
            }).then(function() {
                return chaisePage.recordEditPage.getViewModelRows();
            }).then(function(rows) {
                expect(rows.length).toBe(testParams.records);
            });
        });

        describe("change values in the forms without affecting the other forms, ", function() {
            //added 2 more forms, should be 3 total
            var textInput1 = chaisePage.recordEditPage.getInputById(0, textDisplayName),
                textInput2 = chaisePage.recordEditPage.getInputById(1, textDisplayName),
                textInput3 = chaisePage.recordEditPage.getInputById(2, textDisplayName);

            var intInput1 = chaisePage.recordEditPage.getInputById(0, intDisplayName),
                intInput2 = chaisePage.recordEditPage.getInputById(1, intDisplayName),
                intInput3 = chaisePage.recordEditPage.getInputById(2, intDisplayName);

            //change value in form 1, test others unchanged
            it("should change text input in form 1.", function() {
                chaisePage.recordEditPage.clearInput(textInput1);
                textInput1.sendKeys(testParams.input_1_text_val);

                expect(textInput1.getAttribute("value")).toBe(testParams.input_1_text_val);
                expect(textInput2.getAttribute("value")).toBe(textVal);
                expect(textInput3.getAttribute("value")).toBe(textVal);
            });

            //change value in form n, test others unchanged
            it("should change int input in form 3.", function() {
                chaisePage.recordEditPage.clearInput(intInput3);
                intInput3.sendKeys(testParams.input_3_int_val);

                expect(intInput1.getAttribute("value")).toBe(intVal);
                expect(intInput2.getAttribute("value")).toBe(intVal);
                expect(intInput3.getAttribute("value")).toBe(testParams.input_3_int_val);
            });

            //change value in n/2, test other sunchanged
            it("should change text input in form 2.", function() {
                chaisePage.recordEditPage.clearInput(textInput2);
                textInput2.sendKeys(testParams.input_2_text_val);

                expect(textInput1.getAttribute("value")).toBe(testParams.input_1_text_val);
                expect(textInput2.getAttribute("value")).toBe(testParams.input_2_text_val);
                expect(textInput3.getAttribute("value")).toBe(textVal);
            });
        });

        //test submission
        describe("Submit " + testParams.records + " records", function() {
            beforeAll(function() {
                // Submit the form
                chaisePage.recordEditPage.submitForm();
            });

            it("should change the view to the resultset table and verify the count.", function() {
                // Make sure the table shows up with the expected # of rows
                browser.wait(function() {
                    return chaisePage.recordsetPage.getRows().count().then(function(ct) {
                        return (ct == testParams.records);
                    });
                }, browser.params.defaultTimeout);

                browser.driver.getCurrentUrl().then(function(url) {
                    expect(url.startsWith(process.env.CHAISE_BASE_URL + "/recordedit/")).toBe(true);

                    return chaisePage.recordsetPage.getRows().count();
                }).then(function(ct) {
                    expect(ct).toBe(testParams.records);
                });
            });
        });

        describe("for when the user adds the maximum amount of forms, ", function() {
            var EC = protractor.ExpectedConditions,
                multiFormOpenButton = chaisePage.recordEditPage.getMultiFormInputOpenButton(),
                multiFormInput = chaisePage.recordEditPage.getMultiFormInput(),
                multiFormSubmitButton = chaisePage.recordEditPage.getMultiFormInputSubmitButton();

            beforeAll(function () {
                browser.ignoreSynchronization=true;
                browser.refresh();
            });

            it("should show a resultset table with " + (testParams.max_input_rows+1) + " entities.", function() {
                browser.wait(EC.elementToBeClickable(multiFormOpenButton), browser.params.defaultTimeout);

                var intInput = chaisePage.recordEditPage.getInputById(0, "int");
                intInput.sendKeys("1");

                chaisePage.recordEditPage.getMultiFormInputOpenButtonScript().then(function(openBtn) {
                    return chaisePage.clickButton(openBtn);
                }).then(function() {
                    chaisePage.recordEditPage.clearInput(multiFormInput);
                    browser.sleep(10);
                    multiFormInput.sendKeys(testParams.max_input_rows);

                    return chaisePage.recordEditPage.getMultiFormInputSubmitButtonScript();
                }).then(function(submitBtn) {
                    return chaisePage.clickButton(submitBtn);
                }).then(function() {
                    // wait for dom to finish rendering the forms
                    browser.wait(function() {
                        return chaisePage.recordEditPage.getForms().count().then(function(ct) {
                            return (ct == testParams.max_input_rows+1);
                        });
                    }, browser.params.defaultTimeout);

                    chaisePage.recordEditPage.submitForm();

                    return browser.driver.getCurrentUrl();
                }).then(function(url) {
                    expect(url.startsWith(process.env.CHAISE_BASE_URL + "/recordedit/")).toBe(true);

                    // so DOM can render table
                    browser.wait(function() {
                        return chaisePage.recordsetPage.getRows().count().then(function(ct) {
                            return (ct == testParams.max_input_rows+1);
                        });
                    }, browser.params.defaultTimeout);

                    return chaisePage.recordsetPage.getRows().count();
                }).then(function(ct) {
                    expect(ct).toBe(testParams.max_input_rows+1);
                });
            })
        });
    });
});
<|endoftext|>


<commit_before>var Prometheus = Prometheus || {},
    dashboardData = dashboardData || {},
    servers;

Prometheus.Graph = {
  rangeSteps: [
    '1s', '10s', '1m', '5m', '15m', '30m', '1h', '2h', '6h', '12h', '1d', '2d',
    '1w', '2w', '4w', '8w', '1y', '2y'
  ],

  timeFactors: {
    'y': 60 * 60 * 24 * 365,
    'w': 60 * 60 * 24 * 7,
    'd': 60 * 60 * 24,
    'h': 60 * 60,
    'm': 60,
    's': 1
  },

  parseDuration: function(durationText) {
    if (!durationText) {
      return 60;
    }
    var rangeRE = new RegExp('^([0-9]+)([ywdhms]+)$');
    var matches = durationText.match(rangeRE);
    if (!matches) { return };
    if (matches.length != 3) {
      return 60;
    }
    var value = parseInt(matches[1]);
    var unit = matches[2];
    return value * Prometheus.Graph.timeFactors[unit];
  },

  nextLongerRange: function(currentRange) {
    if (!currentRange) {
      return '1h';
    }
    var rangeSeconds = Prometheus.Graph.parseDuration(currentRange);
    for (var i = 0; i < Prometheus.Graph.rangeSteps.length; i++) {
      if (rangeSeconds < Prometheus.Graph.parseDuration(Prometheus.Graph.rangeSteps[i])) {
        return Prometheus.Graph.rangeSteps[i];
      }
    }
    return currentRange;
  },

  nextShorterRange: function(currentRange) {
    if (!currentRange) {
      return '1h';
    }
    var rangeSeconds = Prometheus.Graph.parseDuration(currentRange);
    for (var i = Prometheus.Graph.rangeSteps.length - 1; i >= 0; i--) {
      if (rangeSeconds > Prometheus.Graph.parseDuration(Prometheus.Graph.rangeSteps[i])) {
        return Prometheus.Graph.rangeSteps[i];
      }
    }
    return currentRange;
  },

  earlierEndTime: function(timestamp, rangeText) {
    var range = Prometheus.Graph.parseDuration(rangeText);
    var date = timestamp ? new Date(timestamp) : new Date();
    return Math.floor(date.getTime() - (range * 1000 / 2));
  },

  laterEndTime: function(timestamp, rangeText) {
    var range = Prometheus.Graph.parseDuration(rangeText);
    var date = timestamp ? new Date(timestamp) : new Date();
    return Math.floor(date.getTime() + (range * 1000 / 2));
  },

  getGraphDefaults: function() {
    return {
      title: "Title",
      range: "1h",
      endTime: null,
      expressions: [],
      type: "graph"
    };
  },

  getAxisDefaults: function() {
    return {
      orientation: 'left',
      renderer: 'line',
      scale: 'linear',
      format: 'kmbt'
    };
  }
};
<commit_msg>Assign default empty array to new graph for tags.<commit_after>var Prometheus = Prometheus || {},
    dashboardData = dashboardData || {},
    servers;

Prometheus.Graph = {
  rangeSteps: [
    '1s', '10s', '1m', '5m', '15m', '30m', '1h', '2h', '6h', '12h', '1d', '2d',
    '1w', '2w', '4w', '8w', '1y', '2y'
  ],

  timeFactors: {
    'y': 60 * 60 * 24 * 365,
    'w': 60 * 60 * 24 * 7,
    'd': 60 * 60 * 24,
    'h': 60 * 60,
    'm': 60,
    's': 1
  },

  parseDuration: function(durationText) {
    if (!durationText) {
      return 60;
    }
    var rangeRE = new RegExp('^([0-9]+)([ywdhms]+)$');
    var matches = durationText.match(rangeRE);
    if (!matches) { return };
    if (matches.length != 3) {
      return 60;
    }
    var value = parseInt(matches[1]);
    var unit = matches[2];
    return value * Prometheus.Graph.timeFactors[unit];
  },

  nextLongerRange: function(currentRange) {
    if (!currentRange) {
      return '1h';
    }
    var rangeSeconds = Prometheus.Graph.parseDuration(currentRange);
    for (var i = 0; i < Prometheus.Graph.rangeSteps.length; i++) {
      if (rangeSeconds < Prometheus.Graph.parseDuration(Prometheus.Graph.rangeSteps[i])) {
        return Prometheus.Graph.rangeSteps[i];
      }
    }
    return currentRange;
  },

  nextShorterRange: function(currentRange) {
    if (!currentRange) {
      return '1h';
    }
    var rangeSeconds = Prometheus.Graph.parseDuration(currentRange);
    for (var i = Prometheus.Graph.rangeSteps.length - 1; i >= 0; i--) {
      if (rangeSeconds > Prometheus.Graph.parseDuration(Prometheus.Graph.rangeSteps[i])) {
        return Prometheus.Graph.rangeSteps[i];
      }
    }
    return currentRange;
  },

  earlierEndTime: function(timestamp, rangeText) {
    var range = Prometheus.Graph.parseDuration(rangeText);
    var date = timestamp ? new Date(timestamp) : new Date();
    return Math.floor(date.getTime() - (range * 1000 / 2));
  },

  laterEndTime: function(timestamp, rangeText) {
    var range = Prometheus.Graph.parseDuration(rangeText);
    var date = timestamp ? new Date(timestamp) : new Date();
    return Math.floor(date.getTime() + (range * 1000 / 2));
  },

  getGraphDefaults: function() {
    return {
      title: "Title",
      range: "1h",
      endTime: null,
      expressions: [],
      tags: [],
      type: "graph"
    };
  },

  getAxisDefaults: function() {
    return {
      orientation: 'left',
      renderer: 'line',
      scale: 'linear',
      format: 'kmbt'
    };
  }
};
<|endoftext|>


<commit_before>// FileLoader - A caching file downloader for Titanium

// TODO: Docs

// Constants {{{1
// Load constants allowing them to be overwriten with configuration.
var HTTP_TIMEOUT    = 10000;
var MAX_ASYNC_TASKS = 10;
var CACHE_METADATA_PROPERTY, EXPIRATION_TIME, CACHE_PATH_PREFIX;
(function() {
  var have_alloy = (typeof Alloy !== 'undefined' && Alloy !== null && Alloy.CFG);

  if (have_alloy && Alloy.CFG.cache_metadata_property) {
    CACHE_METADATA_PROPERTY = Alloy.CFG.caching_property_key;
  }
  else if (typeof caching_property_key !== 'undefined' && caching_property_key !== null) {
    CACHE_METADATA_PROPERTY = CACHING_PROPERTY_KEY;
  }
  else {
    CACHE_METADATA_PROPERTY = "file_loader_cache_metadata";
  }

  if (have_alloy && Alloy.CFG.cache_expiration) {
    EXPIRATION_TIME = Alloy.CFG.cache_expiration;
  }
  else if (typeof cache_expiration !== 'undefined' && cache_expiration !== null) {
    EXPIRATION_TIME = cache_expiration;
  }
  else {
    EXPIRATION_TIME = 3600000; // 60 minutes
  }

  if (have_alloy && Alloy.CFG.cache_directory) {
    CACHE_PATH_PREFIX = Alloy.CFG.cache_directory;
  }
  else if (typeof cache_directory !== 'undefined' && cache_directory !== null) {
    CACHE_PATH_PREFIX = cache_directory;
  }
  else {
    CACHE_PATH_PREFIX = "cached_files";
  }
})();

// Metadata {{{1
var metadata = Ti.App.Properties.getObject(CACHE_METADATA_PROPERTY) || {};

function saveMetaData() {
  Ti.App.Properties.setObject(CACHE_METADATA_PROPERTY, metadata);
}

// Cache path {{{1
// Make sure we have the directory to store files.
var cache_path = (function() {
  var os = Ti.Platform.osname;
  var data_dir = (os === "iphone" || os === "ipad") ?
    Ti.Filesystem.applicationSupportDirectory :
    Ti.Filesystem.applicationDataDirectory;
  var cache_dir = Ti.Filesystem.getFile(data_dir, CACHE_PATH_PREFIX);
  if (!cache_dir.exists()) {
    cache_dir.createDirectory();
  }
  return cache_dir.resolve();
})();

// Class: File {{{1

// Constructor {{{2
function File(id) {
  this.id        = id;
  var cache_data = metadata[this.id];
  this.file_path = Ti.Filesystem.getFile(cache_path, this.id);

  if (cache_data) {
    this.is_cached    = this.exists();
    this.last_used_at = cache_data.last_used_at;
    this.md5          = cache_data.md5;
  }
  else {
    this.is_cached    = false;
    this.last_used_at = 0;
    this.md5          = null;
  }
}

// File::updateLastUsedAt {{{2
File.prototype.updateLastUsedAt = function() {
  this.last_used_at = new Date().getTime();
  return this;
};

// File::save {{{2
File.prototype.save = function() {
  metadata[this.id] = {
    last_used_at: this.last_used_at,
    md5:          this.md5
  };
  saveMetaData();
  this.is_cached = true;
  return this;
};

// File::write {{{2
File.prototype.write = function(data) {
  // A Titanium bug cause this to always return false. We need to manually
  // check it exists. And assume it worked.
  // (https://jira.appcelerator.org/browse/TIMOB-1658)
  this.file_path.write(data);
  this.md5 = File.getMD5(data);
  return this.exists();
};

// File::exists {{{2
File.prototype.exists = function() {
  return this.file_path.exists();
};

// File::expired {{{2
File.prototype.expired = function() {
  return ((new Date().getTime() - this.last_used_at) > EXPIRATION_TIME);
};

// File::expunge {{{2
File.prototype.expunge = function() {
  this.file_path.deleteFile();
  delete metadata[this.id];
  saveMetaData();
  this.is_cached = false;
};

// File::getPath {{{2
File.prototype.getPath = function() {
  return this.file_path.resolve();
};

// File.getFile {{{2
File.prototype.getFile = function() {
  return this.file_path;
};

// File.getMD5 {{{2
File.getMD5 = function(data) {
  return Ti.Utils.md5HexDigest(data);
};

// File.idFromUrl {{{2
File.idFromUrl = function(url) {
  // Insanely simple conversion to keep id unique to the url and prevent
  // possible illegal filesystem characters and removes path seperators.
  // MD5 should be fast enough not that this is repeated so much.
  return Ti.Utils.md5HexDigest(url);
};

// File.fromURL {{{2
File.fromURL = function(url) {
  return new File(File.idFromUrl(url));
};

// FileLoader {{{1
var throttle_warn = false;
var pending_promises;
var FileLoader = {};

// spawnHTTPClient {{{2
function spawnHTTPClient(url, callbacks) {
  var http = Ti.Network.createHTTPClient({
    onload:       callbacks.resolve,
    onerror:      callbacks.reject,
    ondatastream: callbacks.notify,
    timeout:      HTTP_TIMEOUT
  });
  http.open("GET", url);
  http.send();
}

// Exports {{{1
// #download - Attempt to download and cache url {{{2
FileLoader.download = function(url, callbacks) {
  // Promises are better. Why would you not use them?!
  if (!callbacks) { return FileLoader.downloadP(url); }
  if (!throttle_warn) {
    Ti.API.warn(
      "Using callbacks does NOT throttle requests. " +
      "This can be dangerous as all downloads happen at the same time.\n" +
      "It is preferred to use FileLoader.download(url).then(...) after installing q.js"
    );
    throttle_warn = true;
  }

  var file = File.fromURL(url);
  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    // Poor man's nextTick:
    Ti.API.info("Cached " + file.id + ": " + url);
    setTimeout(function() { attemptCallback(file); }, 0);
    return;
  }

  // I hate this callback crap
  function onload() {
    if (!file.write(this.responseData)) {
      if (typeof callbacks.onerror === 'function') {
        callbacks.onerror({message: "Failed to save data from " + url + " to " + file.getPath()});
      }
      return;
    }
    file.downloaded = true;
    file.updateLastUsedAt().save();
    attemptCallback(file);
  }

  function attemptCallback(value) {
    var cb = callbacks.onload || callbacks;
    if (typeof cb === 'function') {
      cb(value);
    }
  }

  spawnHTTPClient(url, {
    resolve: onload,
    reject:  callbacks.onerror,
    notify:  callbacks.ondatastream
  });

  Ti.API.info("Downloading " + file.id + ": " + url);
};

// #downloadP - Same as #download but returns a promise {{{2
FileLoader.downloadP = function(url) {
  var Q;
  try { Q = require("q"); }
  catch (e) {
    throw new Error("Promises are not supported without the Q library. Install q.js into Resources/ or app/lib/");
  }

  if (!pending_promises) { pending_promises = {dispatch_queue: []}; }

  function requestDispatch() {
    var waitForDispatch = Q.defer();
    pending_promises.dispatch_queue.push(waitForDispatch);
    return waitForDispatch.promise;
  }

  function dispatchNextPromise() {
    var defer;
    if (pending_promises.dispatch_queue.length < MAX_ASYNC_TASKS) {
      defer = pending_promises.dispatch_queue.shift();
      if (defer) { defer.resolve(); }
    }
  }

  var file = File.fromURL(url);

  if (pending_promises[file.id]) {
    Ti.API.info("Pending " + file.id + ": " + url);
    return pending_promises[file.id];
  }

  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    Ti.API.info("Cached " + file.id + ": " + url);
    return Q(file);
  }

  var waitingForDownload = requestDispatch()
    .then(function() {
      var waitForHttp = Q.defer();
      spawnHTTPClient(url, waitForHttp);
      Ti.API.info("Downloading " + file.id + ": " + url);
      return waitForHttp.promise;
    })
    .get("source")
    .get("responseData")
    .then(function(data) {
      if (!file.write(data)) {
        throw new Error("Failed to save data from " + url + " to " + file.getPath());
      }
      file.downloaded = true;
      file.updateLastUsedAt().save();
      return file;
    })
    .fin(function() {
      delete pending_promises[file.id];
      dispatchNextPromise();
    });

  pending_promises[file.id] = waitingForDownload;
  dispatchNextPromise();

  return waitingForDownload;
};

// pruneStaleCache - (alias: gc) Remove stale cache files {{{2
FileLoader.pruneStaleCache = FileLoader.gc = function(force) {
  var id, file;
  for (id in metadata) {
    file = new File(id);
    if (force || file.expired()) {
      file.expunge();
    }
  }
};
// }}}1

FileLoader.File = File;
module.exports  = FileLoader;
/* vim:set ts=2 sw=2 et fdm=marker: */
<commit_msg>Refactored promise queueing<commit_after>// FileLoader - A caching file downloader for Titanium

// TODO: Docs

// Constants {{{1
// Load constants allowing them to be overwriten with configuration.
var HTTP_TIMEOUT    = 10000;
var MAX_ASYNC_TASKS = 10;
var CACHE_METADATA_PROPERTY, EXPIRATION_TIME, CACHE_PATH_PREFIX;
(function() {
  var have_alloy = (typeof Alloy !== 'undefined' && Alloy !== null && Alloy.CFG);

  if (have_alloy && Alloy.CFG.cache_metadata_property) {
    CACHE_METADATA_PROPERTY = Alloy.CFG.caching_property_key;
  }
  else if (typeof caching_property_key !== 'undefined' && caching_property_key !== null) {
    CACHE_METADATA_PROPERTY = CACHING_PROPERTY_KEY;
  }
  else {
    CACHE_METADATA_PROPERTY = "file_loader_cache_metadata";
  }

  if (have_alloy && Alloy.CFG.cache_expiration) {
    EXPIRATION_TIME = Alloy.CFG.cache_expiration;
  }
  else if (typeof cache_expiration !== 'undefined' && cache_expiration !== null) {
    EXPIRATION_TIME = cache_expiration;
  }
  else {
    EXPIRATION_TIME = 3600000; // 60 minutes
  }

  if (have_alloy && Alloy.CFG.cache_directory) {
    CACHE_PATH_PREFIX = Alloy.CFG.cache_directory;
  }
  else if (typeof cache_directory !== 'undefined' && cache_directory !== null) {
    CACHE_PATH_PREFIX = cache_directory;
  }
  else {
    CACHE_PATH_PREFIX = "cached_files";
  }
})();

// Metadata {{{1
var metadata = Ti.App.Properties.getObject(CACHE_METADATA_PROPERTY) || {};

function saveMetaData() {
  Ti.App.Properties.setObject(CACHE_METADATA_PROPERTY, metadata);
}

// Cache path {{{1
// Make sure we have the directory to store files.
var cache_path = (function() {
  var os = Ti.Platform.osname;
  var data_dir = (os === "iphone" || os === "ipad") ?
    Ti.Filesystem.applicationSupportDirectory :
    Ti.Filesystem.applicationDataDirectory;
  var cache_dir = Ti.Filesystem.getFile(data_dir, CACHE_PATH_PREFIX);
  if (!cache_dir.exists()) {
    cache_dir.createDirectory();
  }
  return cache_dir.resolve();
})();

// Class: File {{{1

// Constructor {{{2
function File(id) {
  this.id        = id;
  var cache_data = metadata[this.id];
  this.file_path = Ti.Filesystem.getFile(cache_path, this.id);

  if (cache_data) {
    this.is_cached    = this.exists();
    this.last_used_at = cache_data.last_used_at;
    this.md5          = cache_data.md5;
  }
  else {
    this.is_cached    = false;
    this.last_used_at = 0;
    this.md5          = null;
  }
}

// File::updateLastUsedAt {{{2
File.prototype.updateLastUsedAt = function() {
  this.last_used_at = new Date().getTime();
  return this;
};

// File::save {{{2
File.prototype.save = function() {
  metadata[this.id] = {
    last_used_at: this.last_used_at,
    md5:          this.md5
  };
  saveMetaData();
  this.is_cached = true;
  return this;
};

// File::write {{{2
File.prototype.write = function(data) {
  // A Titanium bug cause this to always return false. We need to manually
  // check it exists. And assume it worked.
  // (https://jira.appcelerator.org/browse/TIMOB-1658)
  this.file_path.write(data);
  this.md5 = File.getMD5(data);
  return this.exists();
};

// File::exists {{{2
File.prototype.exists = function() {
  return this.file_path.exists();
};

// File::expired {{{2
File.prototype.expired = function() {
  return ((new Date().getTime() - this.last_used_at) > EXPIRATION_TIME);
};

// File::expunge {{{2
File.prototype.expunge = function() {
  this.file_path.deleteFile();
  delete metadata[this.id];
  saveMetaData();
  this.is_cached = false;
};

// File::getPath {{{2
File.prototype.getPath = function() {
  return this.file_path.resolve();
};

// File.getFile {{{2
File.prototype.getFile = function() {
  return this.file_path;
};

// File.getMD5 {{{2
File.getMD5 = function(data) {
  return Ti.Utils.md5HexDigest(data);
};

// File.idFromUrl {{{2
File.idFromUrl = function(url) {
  // Insanely simple conversion to keep id unique to the url and prevent
  // possible illegal filesystem characters and removes path seperators.
  // MD5 should be fast enough not that this is repeated so much.
  return Ti.Utils.md5HexDigest(url);
};

// File.fromURL {{{2
File.fromURL = function(url) {
  return new File(File.idFromUrl(url));
};

// FileLoader {{{1
var pending_tasks = {dispatch_queue:[]};
var FileLoader = {};

// dispatchNextTask {{{2
function dispatchNextTask() {
  var task;
  if (pending_tasks.dispatch_queue.length < MAX_ASYNC_TASKS) {
    task = pending_tasks.dispatch_queue.shift();
    if (!task) { return; }
    if (task.resolve) { task.resolve(); }
    else { poorMansNextTick(task); }
  }
}

// spawnHTTPClient {{{2
function spawnHTTPClient(url, callbacks) {
  var http = Ti.Network.createHTTPClient({
    onload:       callbacks.resolve,
    onerror:      callbacks.reject,
    ondatastream: callbacks.notify,
    timeout:      HTTP_TIMEOUT
  });
  http.open("GET", url);
  http.send();
}

// Exports {{{1
// #download - Attempt to download and cache url {{{2
FileLoader.download = function(url, callbacks) {
  // Promises are better. Why would you not use them?!
  if (!callbacks) { return FileLoader.downloadP(url); }
  if (!throttle_warn) {
    Ti.API.warn(
      "Using callbacks does NOT throttle requests. " +
      "This can be dangerous as all downloads happen at the same time.\n" +
      "It is preferred to use FileLoader.download(url).then(...) after installing q.js"
    );
    throttle_warn = true;
  }

  var file = File.fromURL(url);
  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    // Poor man's nextTick:
    Ti.API.info("Cached " + file.id + ": " + url);
    setTimeout(function() { attemptCallback(file); }, 0);
    return;
  }

  // I hate this callback crap
  function onload() {
    if (!file.write(this.responseData)) {
      if (typeof callbacks.onerror === 'function') {
        callbacks.onerror({message: "Failed to save data from " + url + " to " + file.getPath()});
      }
      return;
    }
    file.downloaded = true;
    file.updateLastUsedAt().save();
    attemptCallback(file);
  }

  function attemptCallback(value) {
    var cb = callbacks.onload || callbacks;
    if (typeof cb === 'function') {
      cb(value);
    }
  }

  spawnHTTPClient(url, {
    resolve: onload,
    reject:  callbacks.onerror,
    notify:  callbacks.ondatastream
  });

  Ti.API.info("Downloading " + file.id + ": " + url);
};

// #downloadP - Same as #download but returns a promise {{{2
FileLoader.downloadP = function(url) {
  var Q;
  try { Q = require("q"); }
  catch (e) {
    throw new Error("Promises are not supported without the Q library. Install q.js into Resources/ or app/lib/");
  }

  function requestDispatch() {
    var waitForDispatch = Q.defer();
    pending_tasks.dispatch_queue.push(waitForDispatch);
    return waitForDispatch.promise;
  }

  var file = File.fromURL(url);

  if (pending_tasks[file.id]) {
    Ti.API.info("Pending " + file.id + ": " + url);
    return pending_tasks[file.id];
  }

  if (file.is_cached && !file.expired()) {
    file.updateLastUsedAt().save();
    Ti.API.info("Cached " + file.id + ": " + url);
    return Q(file);
  }

  var waitingForDownload = requestDispatch()
    .then(function() {
      var waitForHttp = Q.defer();
      spawnHTTPClient(url, waitForHttp);
      Ti.API.info("Downloading " + file.id + ": " + url);
      return waitForHttp.promise;
    })
    .get("source")
    .get("responseData")
    .then(function(data) {
      if (!file.write(data)) {
        throw new Error("Failed to save data from " + url + " to " + file.getPath());
      }
      file.downloaded = true;
      file.updateLastUsedAt().save();
      return file;
    })
    .fin(function() {
      delete pending_tasks[file.id];
      dispatchNextTask();
    });

  pending_tasks[file.id] = waitingForDownload;
  dispatchNextTask();

  return waitingForDownload;
};

// pruneStaleCache - (alias: gc) Remove stale cache files {{{2
FileLoader.pruneStaleCache = FileLoader.gc = function(force) {
  var id, file;
  for (id in metadata) {
    file = new File(id);
    if (force || file.expired()) {
      file.expunge();
    }
  }
};

// Export File class {{{2
FileLoader.File = File;
// }}}1

module.exports  = FileLoader;
/* vim:set ts=2 sw=2 et fdm=marker: */
<|endoftext|>


<commit_before>const StorageProviderMixin = require('../mixins/storage-provider-mixin');
const Homework = require('./homework');
const Student = require('./student');

class Course extends StorageProviderMixin(Object) {
    constructor(title, presentations) {
        super();

        this.title = title;
        this.presentations = presentations;
    }

    get title() {
        return this._title;
    }

    set title(title) {
        this._title = title;
    }

    get presentations() {
        return this._presentations;
    }

    set presentations(presentations) {
        this._presentations = presentations;
    }

    addStudent(name) {
        const names = name.split(' '),
            homework = new Homework(this.presentations.length),
            student = new Student(...names, homework);

        var generatedId = this.addItem(student);
        return generatedId;
    }

    getAllStudents() {
        const allStudents = this.getAllItems('Student'),
            formattedOutput = [];

        allStudents.forEach(student => {
            formattedOutput.push({
                firstname: student.firstname,
                lastname: student.lastname,
                id: student.id
            });
        });

        return formattedOutput;
    }

    submitHomework(studentId, homeworkId) {
        const student = this.getItemById(studentId, 'Student')[0];
        student.homework.assignments.filter(hw => hw.presentationId === homeworkId)[0].status = true;
        this.getItemById(studentId, 'Student', student);
    }
}

module.exports = Course;
<commit_msg>update<commit_after>const StorageProviderMixin = require('../mixins/storage-provider-mixin');
const Homework = require('./homework');
const Student = require('./student');

const STARTS_WITH_WHITESPACE = /^\s+/;
const ENDS_WITH_WHITESPACE = /\s$/;
const VALID_NAME = /^[A-Z][a-z]*$/;

class Course extends StorageProviderMixin(Object) {
    constructor(title, presentations) {
        super();

        if (!presentations) {
            throw new Error();
        }

        if (!Array.isArray(presentations)) {
            throw new Error();
        }

        if (presentations.length === 0) {
            throw new Error();
        }

        presentations.forEach(presentation => {
            if (typeof presentation !== 'string') {
                throw new Error();
            }

            if (presentation.length === 0) {
                throw new Error();
            }

            if (STARTS_WITH_WHITESPACE.test(presentation)) {
                throw new Error();
            }

            if (ENDS_WITH_WHITESPACE.test(presentation)) {
                throw new Error();
            }
        })

        this.title = title;
        this.presentations = presentations;
    }

    get title() {
        return this._title;
    }

    set title(title) {
        this._title = title;
    }

    get presentations() {
        return this._presentations;
    }

    set presentations(presentations) {
        this._presentations = presentations;
    }

    addStudent(name) {
        const names = name.split(' '),
            homework = new Homework(this.presentations.length),
            student = new Student(...names, homework);

        if (names.length < 2) {
            throw new Error();
        }

        names.forEach(name => {
            if (!VALID_NAME.test(name)) {
                throw new Error();
            }
        });

        var generatedId = this.addItem(student);
        return generatedId;
    }

    getAllStudents() {
        const allStudents = this.getAllItems('Student'),
            formattedOutput = [];

        allStudents.forEach(student => {
            formattedOutput.push({
                firstname: student.firstname,
                lastname: student.lastname,
                id: student.id
            });
        });

        return formattedOutput;
    }

    submitHomework(studentId, homeworkId) {
        let student = this.getItemById(studentId, 'Student');
        if (student.length === 0 || student.length > 1) {
            throw new Error();
        } else {
            student = student[0];
        }

        let homework = student.homework.assignments.filter(hw => hw.presentationId === homeworkId);
        if (homework.length === 0 || homework.length > 1) {
            throw new Error();
        } else {
            homework[0].status = true;
        }

        this.setItemById(studentId, 'Student', student);
    }
}

module.exports = Course;
<|endoftext|>


<commit_before><commit_msg>Removed unused file<commit_after><|endoftext|>