<commit_before>import React from 'react'

const propTypes = {
  children: React.PropTypes.any,
  index: React.PropTypes.number.isRequired,
  fieldName: React.PropTypes.string.isRequired
}

const childContextTypes = {
  parentFieldName: React.PropTypes.string
}

export default class ArrayContextItem extends React.Component {

  getChildContext () {
    return {
      parentFieldName: `${this.props.fieldName}.${this.props.index}`
    }
  }

  render () {
    return (

        {this.props.children}

    )
  }
}

ArrayContextItem.propTypes = propTypes
ArrayContextItem.childContextTypes = childContextTypes
<commit_msg>react native compatibility for arraycontextitem<commit_after>import React from 'react'

const propTypes = {
  children: React.PropTypes.any,
  index: React.PropTypes.number.isRequired,
  fieldName: React.PropTypes.string.isRequired
}

const childContextTypes = {
  parentFieldName: React.PropTypes.string
}

export default class ArrayContextItem extends React.Component {

  getChildContext () {
    return {
      parentFieldName: `${this.props.fieldName}.${this.props.index}`
    }
  }

  isRN () {
    return navigator.product === 'ReactNative'
  }

  render () {
    if (this.isRn()) {
      const {View} = require('react-native');
      return (
        <View>
          {this.props.children}
        </View>
      )
    }
    return (

        {this.props.children}

    )
  }
}

ArrayContextItem.propTypes = propTypes
ArrayContextItem.childContextTypes = childContextTypes
<|endoftext|>


<commit_before>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import Controller from '@ember/controller';
import generatePassword from 'ghost-admin/utils/password-generator';
import validator from 'validator';
import {
    IMAGE_EXTENSIONS,
    IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {run} from '@ember/runloop';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';

const ICON_EXTENSIONS = ['ico', 'png'];

function randomPassword() {
    let word = generatePassword(6);
    let randomN = Math.floor(Math.random() * 1000);

    return word + randomN;
}

export default Controller.extend({
    config: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),
    ui: service(),

    availableTimezones: null,
    iconExtensions: null,
    iconMimeTypes: 'image/png,image/x-icon',
    imageExtensions: IMAGE_EXTENSIONS,
    imageMimeTypes: IMAGE_MIME_TYPES,
    _scratchFacebook: null,
    _scratchTwitter: null,

    init() {
        this._super(...arguments);
        this.iconExtensions = ICON_EXTENSIONS;
    },

    privateRSSUrl: computed('config.blogUrl', 'settings.publicHash', function () {
        let blogUrl = this.get('config.blogUrl');
        let publicHash = this.get('settings.publicHash');

        return `${blogUrl}/${publicHash}/rss`;
    }),

    actions: {
        save() {
            this.save.perform();
        },

        setTimezone(timezone) {
            this.set('settings.timezone', timezone.name);
        },

        removeImage(image) {
            // setting `null` here will error as the server treats it as "null"
            this.settings.set(image, '');
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload Image" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-setting-action')
                .find('input[type="file"]')
                .click();
        },

        /**
         * Fired after an image upload completes
         * @param  {string} property - Property name to be set on `this.settings`
         * @param  {UploadResult[]} results - Array of UploadResult objects
         * @return {string} The URL that was set on `this.settings.property`
         */
        imageUploaded(property, results) {
            if (results[0]) {
                return this.settings.set(property, results[0].url);
            }
        },

        toggleIsPrivate(isPrivate) {
            let settings = this.settings;

            settings.set('isPrivate', isPrivate);
            settings.get('errors').remove('password');

            let changedAttrs = settings.changedAttributes();

            // set a new random password when isPrivate is enabled
            if (isPrivate && changedAttrs.isPrivate) {
                settings.set('password', randomPassword());

            // reset the password when isPrivate is disabled
            } else if (changedAttrs.password) {
                settings.set('password', changedAttrs.password[0]);
            }
        },

        toggleLeaveSettingsModal(transition) {
            let leaveTransition = this.leaveSettingsTransition;

            if (!transition && this.showLeaveSettingsModal) {
                this.set('leaveSettingsTransition', null);
                this.set('showLeaveSettingsModal', false);
                return;
            }

            if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
                this.set('leaveSettingsTransition', transition);

                // if a save is running, wait for it to finish then transition
                if (this.save.isRunning) {
                    return this.save.last.then(() => {
                        transition.retry();
                    });
                }

                // we genuinely have unsaved data, show the modal
                this.set('showLeaveSettingsModal', true);
            }
        },

        leaveSettings() {
            let transition = this.leaveSettingsTransition;
            let settings = this.settings;

            if (!transition) {
                this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'});
                return;
            }

            // roll back changes on settings props
            settings.rollbackAttributes();

            return transition.retry();
        },

        validateFacebookUrl() {
            let newUrl = this._scratchFacebook;
            let oldUrl = this.get('settings.facebook');
            let errMessage = '';

            // reset errors and validation
            this.get('settings.errors').remove('facebook');
            this.get('settings.hasValidated').removeObject('facebook');

            if (newUrl === '') {
                // Clear out the Facebook url
                this.set('settings.facebook', '');
                return;
            }

            // _scratchFacebook will be null unless the user has input something
            if (!newUrl) {
                newUrl = oldUrl;
            }

            try {
                // strip any facebook URLs out
                newUrl = newUrl.replace(/(https?:\/\/)?(www\.)?facebook\.com/i, '');

                // don't allow any non-facebook urls
                if (newUrl.match(/^(http|\/\/)/i)) {
                    throw 'invalid url';
                }

                // strip leading / if we have one then concat to full facebook URL
                newUrl = newUrl.replace(/^\//, '');
                newUrl = `https://www.facebook.com/${newUrl}`;

                // don't allow URL if it's not valid
                if (!validator.isURL(newUrl)) {
                    throw 'invalid url';
                }

                this.set('settings.facebook', '');
                run.schedule('afterRender', this, function () {
                    this.set('settings.facebook', newUrl);
                });
            } catch (e) {
                if (e === 'invalid url') {
                    errMessage = 'The URL must be in a format like '
                               + 'https://www.facebook.com/yourPage';
                    this.get('settings.errors').add('facebook', errMessage);
                    return;
                }

                throw e;
            } finally {
                this.get('settings.hasValidated').pushObject('facebook');
            }
        },

        validateTwitterUrl() {
            let newUrl = this._scratchTwitter;
            let oldUrl = this.get('settings.twitter');
            let errMessage = '';

            // reset errors and validation
            this.get('settings.errors').remove('twitter');
            this.get('settings.hasValidated').removeObject('twitter');

            if (newUrl === '') {
                // Clear out the Twitter url
                this.set('settings.twitter', '');
                return;
            }

            // _scratchTwitter will be null unless the user has input something
            if (!newUrl) {
                newUrl = oldUrl;
            }

            if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-z\d.]+)/i)) {
                let username = [];

                if (newUrl.match(/(?:twitter\.com\/)(\S+)/)) {
                    [, username] = newUrl.match(/(?:twitter\.com\/)(\S+)/);
                } else {
                    [username] = newUrl.match(/([^/]+)\/?$/mi);
                }

                // check if username starts with http or www and show error if so
                if (username.match(/^(http|www)|(\/)/) || !username.match(/^[a-z\d._]{1,15}$/mi)) {
                    errMessage = !username.match(/^[a-z\d._]{1,15}$/mi) ? 'Your Username is not a valid Twitter Username' : 'The URL must be in a format like https://twitter.com/yourUsername';

                    this.get('settings.errors').add('twitter', errMessage);
                    this.get('settings.hasValidated').pushObject('twitter');
                    return;
                }

                newUrl = `https://twitter.com/${username}`;

                this.get('settings.hasValidated').pushObject('twitter');

                this.set('settings.twitter', '');
                run.schedule('afterRender', this, function () {
                    this.set('settings.twitter', newUrl);
                });
            } else {
                errMessage = 'The URL must be in a format like '
                           + 'https://twitter.com/yourUsername';
                this.get('settings.errors').add('twitter', errMessage);
                this.get('settings.hasValidated').pushObject('twitter');
                return;
            }
        }
    },

    _deleteTheme() {
        let theme = this.store.peekRecord('theme', this.themeToDelete.name);

        if (!theme) {
            return;
        }

        return theme.destroyRecord().catch((error) => {
            this.notifications.showAPIError(error);
        });
    },

    save: task(function* () {
        let notifications = this.notifications;
        let config = this.config;

        try {
            let settings = yield this.settings.save();
            config.set('blogTitle', settings.get('title'));

            // this forces the document title to recompute after a blog title change
            this.ui.updateDocumentTitle();

            return settings;
        } catch (error) {
            if (error) {
                notifications.showAPIError(error, {key: 'settings.save'});
            }
            throw error;
        }
    })
});
<commit_msg>🐛 Fixed social settings not saved on keyboard shortcut (#1633)<commit_after>/* eslint-disable ghost/ember/alias-model-in-controller */
import $ from 'jquery';
import Controller from '@ember/controller';
import generatePassword from 'ghost-admin/utils/password-generator';
import validator from 'validator';
import {
    IMAGE_EXTENSIONS,
    IMAGE_MIME_TYPES
} from 'ghost-admin/components/gh-image-uploader';
import {computed} from '@ember/object';
import {inject as service} from '@ember/service';
import {task} from 'ember-concurrency';

const ICON_EXTENSIONS = ['ico', 'png'];

function randomPassword() {
    let word = generatePassword(6);
    let randomN = Math.floor(Math.random() * 1000);

    return word + randomN;
}

export default Controller.extend({
    config: service(),
    ghostPaths: service(),
    notifications: service(),
    session: service(),
    settings: service(),
    ui: service(),

    availableTimezones: null,
    iconExtensions: null,
    iconMimeTypes: 'image/png,image/x-icon',
    imageExtensions: IMAGE_EXTENSIONS,
    imageMimeTypes: IMAGE_MIME_TYPES,
    _scratchFacebook: null,
    _scratchTwitter: null,

    init() {
        this._super(...arguments);
        this.iconExtensions = ICON_EXTENSIONS;
    },

    privateRSSUrl: computed('config.blogUrl', 'settings.publicHash', function () {
        let blogUrl = this.get('config.blogUrl');
        let publicHash = this.get('settings.publicHash');

        return `${blogUrl}/${publicHash}/rss`;
    }),

    actions: {
        save() {
            this.save.perform();
        },

        setTimezone(timezone) {
            this.set('settings.timezone', timezone.name);
        },

        removeImage(image) {
            // setting `null` here will error as the server treats it as "null"
            this.settings.set(image, '');
        },

        /**
         * Opens a file selection dialog - Triggered by "Upload Image" buttons,
         * searches for the hidden file input within the .gh-setting element
         * containing the clicked button then simulates a click
         * @param  {MouseEvent} event - MouseEvent fired by the button click
         */
        triggerFileDialog(event) {
            // simulate click to open file dialog
            // using jQuery because IE11 doesn't support MouseEvent
            $(event.target)
                .closest('.gh-setting-action')
                .find('input[type="file"]')
                .click();
        },

        /**
         * Fired after an image upload completes
         * @param  {string} property - Property name to be set on `this.settings`
         * @param  {UploadResult[]} results - Array of UploadResult objects
         * @return {string} The URL that was set on `this.settings.property`
         */
        imageUploaded(property, results) {
            if (results[0]) {
                return this.settings.set(property, results[0].url);
            }
        },

        toggleIsPrivate(isPrivate) {
            let settings = this.settings;

            settings.set('isPrivate', isPrivate);
            settings.get('errors').remove('password');

            let changedAttrs = settings.changedAttributes();

            // set a new random password when isPrivate is enabled
            if (isPrivate && changedAttrs.isPrivate) {
                settings.set('password', randomPassword());

            // reset the password when isPrivate is disabled
            } else if (changedAttrs.password) {
                settings.set('password', changedAttrs.password[0]);
            }
        },

        toggleLeaveSettingsModal(transition) {
            let leaveTransition = this.leaveSettingsTransition;

            if (!transition && this.showLeaveSettingsModal) {
                this.set('leaveSettingsTransition', null);
                this.set('showLeaveSettingsModal', false);
                return;
            }

            if (!leaveTransition || transition.targetName === leaveTransition.targetName) {
                this.set('leaveSettingsTransition', transition);

                // if a save is running, wait for it to finish then transition
                if (this.save.isRunning) {
                    return this.save.last.then(() => {
                        transition.retry();
                    });
                }

                // we genuinely have unsaved data, show the modal
                this.set('showLeaveSettingsModal', true);
            }
        },

        leaveSettings() {
            let transition = this.leaveSettingsTransition;
            let settings = this.settings;

            if (!transition) {
                this.notifications.showAlert('Sorry, there was an error in the application. Please let the Ghost team know what happened.', {type: 'error'});
                return;
            }

            // roll back changes on settings props
            settings.rollbackAttributes();

            return transition.retry();
        },

        validateFacebookUrl() {
            let newUrl = this._scratchFacebook;
            let oldUrl = this.get('settings.facebook');
            let errMessage = '';

            // reset errors and validation
            this.get('settings.errors').remove('facebook');
            this.get('settings.hasValidated').removeObject('facebook');

            if (newUrl === '') {
                // Clear out the Facebook url
                this.set('settings.facebook', '');
                return;
            }

            // _scratchFacebook will be null unless the user has input something
            if (!newUrl) {
                newUrl = oldUrl;
            }

            try {
                // strip any facebook URLs out
                newUrl = newUrl.replace(/(https?:\/\/)?(www\.)?facebook\.com/i, '');

                // don't allow any non-facebook urls
                if (newUrl.match(/^(http|\/\/)/i)) {
                    throw 'invalid url';
                }

                // strip leading / if we have one then concat to full facebook URL
                newUrl = newUrl.replace(/^\//, '');
                newUrl = `https://www.facebook.com/${newUrl}`;

                // don't allow URL if it's not valid
                if (!validator.isURL(newUrl)) {
                    throw 'invalid url';
                }

                this.settings.set('facebook', newUrl);
                this.settings.notifyPropertyChange('facebook');
            } catch (e) {
                if (e === 'invalid url') {
                    errMessage = 'The URL must be in a format like '
                               + 'https://www.facebook.com/yourPage';
                    this.get('settings.errors').add('facebook', errMessage);
                    return;
                }

                throw e;
            } finally {
                this.get('settings.hasValidated').pushObject('facebook');
            }
        },

        validateTwitterUrl() {
            let newUrl = this._scratchTwitter;
            let oldUrl = this.get('settings.twitter');
            let errMessage = '';

            // reset errors and validation
            this.get('settings.errors').remove('twitter');
            this.get('settings.hasValidated').removeObject('twitter');

            if (newUrl === '') {
                // Clear out the Twitter url
                this.set('settings.twitter', '');
                return;
            }

            // _scratchTwitter will be null unless the user has input something
            if (!newUrl) {
                newUrl = oldUrl;
            }

            if (newUrl.match(/(?:twitter\.com\/)(\S+)/) || newUrl.match(/([a-z\d.]+)/i)) {
                let username = [];

                if (newUrl.match(/(?:twitter\.com\/)(\S+)/)) {
                    [, username] = newUrl.match(/(?:twitter\.com\/)(\S+)/);
                } else {
                    [username] = newUrl.match(/([^/]+)\/?$/mi);
                }

                // check if username starts with http or www and show error if so
                if (username.match(/^(http|www)|(\/)/) || !username.match(/^[a-z\d._]{1,15}$/mi)) {
                    errMessage = !username.match(/^[a-z\d._]{1,15}$/mi) ? 'Your Username is not a valid Twitter Username' : 'The URL must be in a format like https://twitter.com/yourUsername';

                    this.get('settings.errors').add('twitter', errMessage);
                    this.get('settings.hasValidated').pushObject('twitter');
                    return;
                }

                newUrl = `https://twitter.com/${username}`;

                this.settings.get('hasValidated').pushObject('twitter');
                this.settings.set('twitter', newUrl);
                this.settings.notifyPropertyChange('twitter');
            } else {
                errMessage = 'The URL must be in a format like '
                           + 'https://twitter.com/yourUsername';
                this.get('settings.errors').add('twitter', errMessage);
                this.get('settings.hasValidated').pushObject('twitter');
                return;
            }
        }
    },

    _deleteTheme() {
        let theme = this.store.peekRecord('theme', this.themeToDelete.name);

        if (!theme) {
            return;
        }

        return theme.destroyRecord().catch((error) => {
            this.notifications.showAPIError(error);
        });
    },

    save: task(function* () {
        let notifications = this.notifications;
        let config = this.config;

        if (this.settings.get('twitter') !== this._scratchTwitter) {
            this.send('validateTwitterUrl');
        }

        if (this.settings.get('facebook') !== this._scratchFacebook) {
            this.send('validateFacebookUrl');
        }

        try {
            let settings = yield this.settings.save();
            config.set('blogTitle', settings.get('title'));

            // this forces the document title to recompute after a blog title change
            this.ui.updateDocumentTitle();

            return settings;
        } catch (error) {
            if (error) {
                notifications.showAPIError(error, {key: 'settings.save'});
            }
            throw error;
        }
    })
});
<|endoftext|>


<commit_before>import { merge, set } from 'lodash/fp';

import {
  UPDATE_PROFILE_FIELDS,
  PROFILE_LOADING_FINISHED,
  FETCHING_LATEST_MHV_TERMS,
  FETCHING_LATEST_MHV_TERMS_SUCCESS,
  FETCHING_LATEST_MHV_TERMS_FAILURE,
  ACCEPTING_LATEST_MHV_TERMS,
  ACCEPTING_LATEST_MHV_TERMS_SUCCESS,
  ACCEPTING_LATEST_MHV_TERMS_FAILURE,
  FETCHING_MHV_ACCOUNT,
  FETCH_MHV_ACCOUNT_FAILURE,
  FETCH_MHV_ACCOUNT_SUCCESS,
  CREATING_MHV_ACCOUNT,
  CREATE_MHV_ACCOUNT_FAILURE,
  CREATE_MHV_ACCOUNT_SUCCESS
} from '../actions';
import { UPDATE_LOGGEDIN_STATUS, FETCH_LOGIN_URLS_FAILED } from '../../login/actions';

const MAX_POLL_TIMES = 10;

const initialState = {
  userFullName: {
    first: null,
    middle: null,
    last: null,
    suffix: null,
  },
  email: null,
  dob: null,
  gender: null,
  accountType: null,
  mhv: {
    account: {
      errors: null,
      loading: false,
      polling: false,
      polledTimes: 0,
      state: 'unknown'
    },
    terms: {
      accepted: false,
      errors: null,
      loading: false
    }
  },
  savedForms: [],
  prefillsAvailable: [],
  loading: true
};

function profileInformation(state = initialState, action) {
  switch (action.type) {
    case UPDATE_PROFILE_FIELDS:
      return merge(state, action.newState);

    case PROFILE_LOADING_FINISHED:
    case FETCH_LOGIN_URLS_FAILED:
    case UPDATE_LOGGEDIN_STATUS:
      return set('loading', false, state);

    case ACCEPTING_LATEST_MHV_TERMS:
    case FETCHING_LATEST_MHV_TERMS:
    case ACCEPTING_LATEST_MHV_TERMS_SUCCESS:
      return set('mhv.terms', {
        ...state.mhv.terms,
        errors: null,
        loading: false,
      }, state);

    case FETCHING_LATEST_MHV_TERMS_SUCCESS: {
      return set('mhv.terms', {
        ...state.mhv.terms,
        ...action.terms,
        loading: false,
      }, state);
    }

    case ACCEPTING_LATEST_MHV_TERMS_FAILURE:
    case FETCHING_LATEST_MHV_TERMS_FAILURE:
      return set('mhv.terms', {
        ...state.mhv.terms,
        errors: action.errors,
        loading: false,
      }, state);

    case FETCHING_MHV_ACCOUNT:
    case CREATING_MHV_ACCOUNT:
      return set('mhv.account', {
        ...state.mhv.account,
        errors: null,
        loading: true
      }, state);

    case FETCH_MHV_ACCOUNT_FAILURE:
    case CREATE_MHV_ACCOUNT_FAILURE:
      return set('mhv.account', {
        ...state.mhv.account,
        errors: action.errors,
        loading: false
      }, state);

    case FETCH_MHV_ACCOUNT_SUCCESS: {
      const { accountState } = action.data.attributes;
      const { polling, polledTimes } = state.mhv.account;
      const shouldPoll =
        accountState !== 'upgraded' &&
        polling &&
        polledTimes < MAX_POLL_TIMES;

      return set('mhv.account', {
        errors: null,
        loading: false,
        polling: shouldPoll,
        polledTimes: shouldPoll ? polledTimes + 1 : 0,
        state: accountState
      }, state);
    }

    case CREATE_MHV_ACCOUNT_SUCCESS: {
      const { accountState } = action.data.attributes;
      return set('mhv.account', {
        ...state.mhv.account,
        errors: null,
        loading: false,
        polling: accountState !== 'upgraded',
        polledTimes: 0,
        state: accountState
      }, state);
    }

    default:
      return state;
  }
}

export default profileInformation;
<commit_msg>Corrected reducer cases and handlers.<commit_after>import { merge, set } from 'lodash/fp';

import {
  UPDATE_PROFILE_FIELDS,
  PROFILE_LOADING_FINISHED,
  FETCHING_LATEST_MHV_TERMS,
  FETCHING_LATEST_MHV_TERMS_SUCCESS,
  FETCHING_LATEST_MHV_TERMS_FAILURE,
  ACCEPTING_LATEST_MHV_TERMS,
  ACCEPTING_LATEST_MHV_TERMS_SUCCESS,
  ACCEPTING_LATEST_MHV_TERMS_FAILURE,
  FETCHING_MHV_ACCOUNT,
  FETCH_MHV_ACCOUNT_FAILURE,
  FETCH_MHV_ACCOUNT_SUCCESS,
  CREATING_MHV_ACCOUNT,
  CREATE_MHV_ACCOUNT_FAILURE,
  CREATE_MHV_ACCOUNT_SUCCESS
} from '../actions';
import { UPDATE_LOGGEDIN_STATUS, FETCH_LOGIN_URLS_FAILED } from '../../login/actions';

const MAX_POLL_TIMES = 10;

const initialState = {
  userFullName: {
    first: null,
    middle: null,
    last: null,
    suffix: null,
  },
  email: null,
  dob: null,
  gender: null,
  accountType: null,
  mhv: {
    account: {
      errors: null,
      loading: false,
      polling: false,
      polledTimes: 0,
      state: 'unknown'
    },
    terms: {
      accepted: false,
      errors: null,
      loading: false
    }
  },
  savedForms: [],
  prefillsAvailable: [],
  loading: true
};

function profileInformation(state = initialState, action) {
  switch (action.type) {
    case UPDATE_PROFILE_FIELDS:
      return merge(state, action.newState);

    case PROFILE_LOADING_FINISHED:
    case FETCH_LOGIN_URLS_FAILED:
    case UPDATE_LOGGEDIN_STATUS:
      return set('loading', false, state);

    case ACCEPTING_LATEST_MHV_TERMS:
    case FETCHING_LATEST_MHV_TERMS:
      return set('mhv.terms', {
        ...state.mhv.terms,
        errors: null,
        loading: true,
      }, state);

    case ACCEPTING_LATEST_MHV_TERMS_SUCCESS:
      return set('mhv.terms.loading', false, state);

    case FETCHING_LATEST_MHV_TERMS_SUCCESS: {
      return set('mhv.terms', {
        ...state.mhv.terms,
        ...action.terms,
        loading: false,
      }, state);
    }

    case ACCEPTING_LATEST_MHV_TERMS_FAILURE:
    case FETCHING_LATEST_MHV_TERMS_FAILURE:
      return set('mhv.terms', {
        ...state.mhv.terms,
        errors: action.errors,
        loading: false,
      }, state);

    case FETCHING_MHV_ACCOUNT:
    case CREATING_MHV_ACCOUNT:
      return set('mhv.account', {
        ...state.mhv.account,
        errors: null,
        loading: true
      }, state);

    case FETCH_MHV_ACCOUNT_FAILURE:
    case CREATE_MHV_ACCOUNT_FAILURE:
      return set('mhv.account', {
        ...state.mhv.account,
        errors: action.errors,
        loading: false
      }, state);

    case FETCH_MHV_ACCOUNT_SUCCESS: {
      const { accountState } = action.data.attributes;
      const { polling, polledTimes } = state.mhv.account;
      const shouldPoll =
        accountState !== 'upgraded' &&
        polling &&
        polledTimes < MAX_POLL_TIMES;

      return set('mhv.account', {
        errors: null,
        loading: false,
        polling: shouldPoll,
        polledTimes: shouldPoll ? polledTimes + 1 : 0,
        state: accountState
      }, state);
    }

    case CREATE_MHV_ACCOUNT_SUCCESS: {
      const { accountState } = action.data.attributes;
      return set('mhv.account', {
        ...state.mhv.account,
        errors: null,
        loading: false,
        polling: accountState !== 'upgraded',
        polledTimes: 0,
        state: accountState
      }, state);
    }

    default:
      return state;
  }
}

export default profileInformation;
<|endoftext|>


<commit_before>(function (B) {
    if (typeof require == "function" && typeof module == "object") {
        return;
    }

    buster.util.testCase("BrowserWiringTest", {
        setUp: function () {
            this.emitter = createRemoteEmitter();
            this.readyListener = sinon.spy();
            this.emitter.on("ready", this.readyListener);
            B.configureTestClient(this.emitter);

            this.bnt = buster.nextTick;

            buster.nextTick = function (callback) {
                callback();
            };
        },

        tearDown: function () {
            buster.nextTick = this.bnt;
        },

        "should connect bayeux emitter": function () {
            assertTrue(this.emitter.connect.calledOnce);
        },

        "should not emit ready immediately": function () {
            assertFalse(this.readyListener.called);
        },

        "should emit ready when connected": function () {
            this.emitter.connect.args[0][0]();

            assertTrue(this.readyListener.calledOnce);
        },

        "should emit user agent string with ready event": function () {
            this.emitter.connect.args[0][0]();

            assertTrue(/Mozilla/.test(this.readyListener.args[0][0].data));
        },

        "should set up console and buster.log shortcut": function () {
            assertObject(buster.console);
            assertFunction(buster.log);
        },

        "should format log messages nicely": function () {
            var message;
            buster.console.on("log", function (msg) { message = msg.message });
            buster.console.log("Hello world", { id: 42 });

            assertEquals("Hello world { id: 42 }", message);
        },

        "should log messages through buster.log": function () {
            var message;
            buster.console.on("log", function (msg) { message = msg.message });
            buster.log("Hello world", { id: 42 });

            buster.assert.equals(message, "Hello world { id: 42 }");
        },

        "should send log messages over the wire": function () {
            var listener = sinon.spy();
            this.emitter.on("log", listener);

            buster.console.warn("Hello world", { id: 42 });
            assertTrue(listener.calledOnce);

            var msg = listener.args[0][0];
            assertEquals("log", msg.topic);

            assertEquals({
                message: "Hello world { id: 42 }",
                level: "warn"
            }, msg.data);
        },

        "should start running tests when emitting tests:run": function () {
            var listener = sinon.spy();
            this.emitter.on("suite:start", listener);

            buster.testCase("SomeTest", { "should do it": function () {} });
            this.emitter.emit("tests:run");

            assertTrue(listener.calledOnce);
        },

        "should run specs when emitting tests:run": function () {
            var listener = sinon.spy();
            this.emitter.on("context:start", listener);

            buster.spec.describe("Spec", function () {
                buster.spec.should("do it", function () {});
            });

            this.emitter.emit("tests:run");

            assertTrue(listener.calledOnce);
            assertEquals(listener.args[0][0].data.name, "Spec");
        },

        "should run parsable context when emitting tests:run": function () {
            var listener = sinon.spy();
            this.emitter.on("context:start", listener);

            buster.addTestContext({
                parse: function () {
                    return { name: "Parsed", tests: [] };
                }
            });

            this.emitter.emit("tests:run");

            assertTrue(listener.calledOnce);
            assertEquals(listener.args[0][0].data.name, "Parsed");
        },

        "should create test runner with options": function () {
            this.emitter.emit("tests:run", {
                timeout: 25, failOnNoAssertions: false
            });

            assertEquals(buster.wiredRunner.timeout, 25);
            assertFalse(buster.wiredRunner.failOnNoAssertions);
        },

        "should configure assertions to not throw": function () {
            this.emitter.emit("tests:run");

            assertFalse(buster.assertions.throwOnFailure);
        },

        "should count assertions": function () {
            var counts = [];

            buster.testCase("AssertionCountTest", {
                tearDown: function () {
                    counts.push(buster.wiredRunner.assertionCount());
                },

                "test #1": function () {
                    buster.assert(true);
                },

                "test #2": function () {
                    buster.assert(true);
                    buster.assert(true);
                },
            });

            this.emitter.emit("tests:run");

            assertEquals(counts, [1, 2]);
        }
    });
}(buster));
<commit_msg>Fix test for compiled test cases<commit_after>(function (B) {
    if (typeof require == "function" && typeof module == "object") {
        return;
    }

    buster.util.testCase("BrowserWiringTest", {
        setUp: function () {
            this.emitter = createRemoteEmitter();
            this.readyListener = sinon.spy();
            this.emitter.on("ready", this.readyListener);
            B.configureTestClient(this.emitter);

            this.bnt = buster.nextTick;
            sinon.spy(buster.testRunner, "runSuite");

            buster.nextTick = function (callback) {
                callback();
            };
        },

        tearDown: function () {
            buster.nextTick = this.bnt;
            buster.testRunner.runSuite.restore();
        },

        "should connect bayeux emitter": function () {
            assertTrue(this.emitter.connect.calledOnce);
        },

        "should not emit ready immediately": function () {
            assertFalse(this.readyListener.called);
        },

        "should emit ready when connected": function () {
            this.emitter.connect.args[0][0]();

            assertTrue(this.readyListener.calledOnce);
        },

        "should emit user agent string with ready event": function () {
            this.emitter.connect.args[0][0]();

            assertTrue(/Mozilla/.test(this.readyListener.args[0][0].data));
        },

        "should set up console and buster.log shortcut": function () {
            assertObject(buster.console);
            assertFunction(buster.log);
        },

        "should format log messages nicely": function () {
            var message;
            buster.console.on("log", function (msg) { message = msg.message });
            buster.console.log("Hello world", { id: 42 });

            assertEquals("Hello world { id: 42 }", message);
        },

        "should log messages through buster.log": function () {
            var message;
            buster.console.on("log", function (msg) { message = msg.message });
            buster.log("Hello world", { id: 42 });

            buster.assert.equals(message, "Hello world { id: 42 }");
        },

        "should send log messages over the wire": function () {
            var listener = sinon.spy();
            this.emitter.on("log", listener);

            buster.console.warn("Hello world", { id: 42 });
            assertTrue(listener.calledOnce);

            var msg = listener.args[0][0];
            assertEquals("log", msg.topic);

            assertEquals({
                message: "Hello world { id: 42 }",
                level: "warn"
            }, msg.data);
        },

        "should start running tests when emitting tests:run": function () {
            var listener = sinon.spy();
            this.emitter.on("suite:start", listener);

            buster.testCase("SomeTest", { "should do it": function () {} });
            this.emitter.emit("tests:run");

            assertTrue(listener.calledOnce);
        },

        "should run specs when emitting tests:run": function () {
            var listener = sinon.spy();
            this.emitter.on("context:start", listener);

            buster.spec.describe("Spec", function () {
                buster.spec.should("do it", function () {});
            });

            this.emitter.emit("tests:run");

            assertTrue(listener.calledOnce);
            assertEquals(listener.args[0][0].data.name, "Spec");
        },

        "should run parsable context when emitting tests:run": function () {
            var context = { name: "Parsed", tests: [] };

            buster.addTestContext({
                parse: function () {
                    return context;
                }
            });

            this.emitter.emit("tests:run");
            assertTrue(buster.testRunner.runSuite.calledWith([context]));
        },

        "should create test runner with options": function () {
            this.emitter.emit("tests:run", {
                timeout: 25, failOnNoAssertions: false
            });

            assertEquals(buster.wiredRunner.timeout, 25);
            assertFalse(buster.wiredRunner.failOnNoAssertions);
        },

        "should configure assertions to not throw": function () {
            this.emitter.emit("tests:run");

            assertFalse(buster.assertions.throwOnFailure);
        },

        "should count assertions": function () {
            var counts = [];

            buster.testCase("AssertionCountTest", {
                tearDown: function () {
                    counts.push(buster.wiredRunner.assertionCount());
                },

                "test #1": function () {
                    buster.assert(true);
                },

                "test #2": function () {
                    buster.assert(true);
                    buster.assert(true);
                },
            });

            this.emitter.emit("tests:run");

            assertEquals(counts, [1, 2]);
        }
    });
}(buster));
<|endoftext|>


<commit_before>function mostrar() {
    var tinteres = 0;
    var vprincipal = 0;
    var bandera = false;
    if( $("#cajita").is(':checked')){
        bandera = true;
    }
    var x = parseInt($("#droplistbox").val());
    if(parseInt($("#tasainteres").val())<0 || parseInt($("#tasainteres").val())>100){
        alert("Por favor inserta una tasa de interés válida");
        return;
    }else if($("#principal").val()!=""){
        tinteres = parseInt($("#tasainteres").val());
    }
    if($("#principal").val()!=""){
        vprincipal = parseInt($("#principal").val());
    }
    agregar(x,bandera);
}

function agregar(limite,bandera){
    var codigo='

PERIOD

OUTFLOWS

INFLOWS

NET CASH FLOW

COMULATIVE CASH FLOW

';
    if(bandera){
        codigo +='

0
<input type="number" class="col-md-3" id="outflow0"><input type="number" class="col-md-3" id="inflow0"><input type="number" class="col-md-3" id="netcash0" disabled><input type="number" class="col-md-2" id="comulativecash0" disabled>

';
    }
    for(i=1;i<limite+1;i++){
        codigo += '

'+i+'
<input type="number" class="col-md-3" id="outflow'+i+'"><input type="number" class="col-md-3" id="inflow'+i+'"><input type="number" class="col-md-3" id="netcash'+i+'" disabled><input type="number" class="col-md-2" id="comulativecash'+i+'" disabled>

';
    }
    codigo += '

<button type="button" class="btn btn-default" onclick="imprimirresultados()" >Imprimir resultados</button>

<input type="number" class="col-md-5" id="netpresentvalue" disabled>

';
    $("#contenedor").html(codigo);
}

function valores() {
    var netCashFlows = [];
    var interest = parseFloat($("#tasainteres").val()) / 100;
    var tax = parseFloat($("#tax").val()) / 100;
    var length = parseInt($("#droplistbox").val());
    var inflow = parseInt($("#inflow0").val());
    var outflow = parseInt($("#outflow0").val());
    var net = inflow - outflow;
    var COF = 0;
    var cum = net;

    netCashFlows.push(net);
    // Set Net y cumulative de period 0.
    $("#netcash0").val(net);
    $("#comulativecash0").val(cum);

    // Recorrer desde 1 hasta length.
    for (var i = 1; i < length + 1; i++) {

        // Calcular net (inflow - outflow)
        inflow = parseInt($('#inflow' + i).val());
        outflow = parseInt($('#outflow' + i).val());
        net = inflow - outflow;
        netCashFlows.push(net);

        // Calcular COF
        if (cum < 0) {
            COF = cum * interest;
        } else {
            COF = 0;
        }

        // Calcular Cumulative.
        cum += (net + COF);

        // Set net and cumulative.
        $('#netcash' + i).val(net);
        $('#comulativecash' + i).val(cum);
    }
    return netCashFlows;
}

function imprimirresultados() {
    var netCashFlows = valores();
    var interest = parseFloat($("#tasainteres").val()) / 100;
    var tax = parseFloat($("#tax").val()) / 100;
    var npv = 0;

    for (var i = 0; i < netCashFlows.length; i++) {
        console.log(netCashFlows[i]);
        npv += (netCashFlows[i] * (1 - tax)) / (Math.pow(1 + interest, i));
    }
    $("#netpresentvalue").val(npv);
}

function netcashflow(inflow, outflow) {
    return inflow - outflow;
}
<commit_msg>Primera etapa<commit_after>function mostrar() {
    var tinteres = 0;
    var vprincipal = 0;
    var bandera = false;
    if( $("#cajita").is(':checked')){
        bandera = true;
    }
    var x = parseInt($("#droplistbox").val());
    if(parseInt($("#tasainteres").val())<0 || parseInt($("#tasainteres").val())>100){
        alert("Por favor inserta una tasa de interés válida");
        return;
    }else if($("#principal").val()!=""){
        tinteres = parseInt($("#tasainteres").val());
    }
    if($("#principal").val()!=""){
        vprincipal = parseInt($("#principal").val());
    }
    agregar(x,bandera);
}

function agregar(limite,bandera){
    var codigo='

PERIOD

OUTFLOWS

INFLOWS

NET CASH FLOW

CUMULATIVE CASH FLOW

';
    if(bandera){
        codigo +='

0
<input type="number" class="col-md-3" id="outflow0"><input type="number" class="col-md-3" id="inflow0"><input type="number" class="col-md-3" id="netcash0" disabled><input type="number" class="col-md-2" id="cumulativecash0" disabled>

';
    }
    for(i=1;i<limite+1;i++){
        codigo += '

'+i+'
<input type="number" class="col-md-3" id="outflow'+i+'"><input type="number" class="col-md-3" id="inflow'+i+'"><input type="number" class="col-md-3" id="netcash'+i+'" disabled><input type="number" class="col-md-2" id="cumulativecash'+i+'" disabled>

';
    }
    codigo += '

<button type="button" class="btn btn-default" onclick="imprimirresultados()" >Imprimir resultados</button>

<input type="number" class="col-md-5" id="netpresentvalue" disabled>

';
    $("#contenedor").html(codigo);
}

function valores() {
    var netCashFlows = [];
    var interest = parseFloat($("#tasainteres").val()) / 100;
    var tax = parseFloat($("#tax").val()) / 100;
    var length = parseInt($("#droplistbox").val());
    var inflow = parseInt($("#inflow0").val());
    var outflow = parseInt($("#outflow0").val());
    var net = inflow - outflow;
    var COF = 0;
    var cum = net;

    netCashFlows.push(net);
    // Set Net y cumulative de period 0.
    $("#netcash0").val(net);
    $("#cumulativecash0").val(cum);

    // Recorrer desde 1 hasta length.
    for (var i = 1; i < length + 1; i++) {

        // Calcular net (inflow - outflow)
        inflow = parseInt($('#inflow' + i).val());
        outflow = parseInt($('#outflow' + i).val());
        net = inflow - outflow;
        netCashFlows.push(net);

        // Calcular COF
        if (cum < 0) {
            COF = cum * interest;
        } else {
            COF = 0;
        }

        // Calcular Cumulative.
        cum += (net + COF);

        // Set net and cumulative.
        $('#netcash' + i).val(net);
        $('#cumulativecash' + i).val(cum);
    }
    return netCashFlows;
}

function imprimirresultados() {
    var netCashFlows = valores();
    var interest = parseFloat($("#tasainteres").val()) / 100;
    var tax = parseFloat($("#tax").val()) / 100;
    var npv = 0;

    for (var i = 0; i < netCashFlows.length; i++) {
        console.log(netCashFlows[i]);
        npv += (netCashFlows[i] * (1 - tax)) / (Math.pow(1 + interest, i));
    }
    $("#netpresentvalue").val(npv);
}

function netcashflow(inflow, outflow) {
    return inflow - outflow;
}
<|endoftext|>


<commit_before>describe('Backbone IndexedDB', function () {
  var dbNameArray = [];
  var Collection;

  // is_safari worse than IE
  var is_safari = window.navigator.userAgent.indexOf('Safari') !== -1 &&
    window.navigator.userAgent.indexOf('Chrome') === -1 &&
    window.navigator.userAgent.indexOf('Android') === -1;

  beforeEach(function(){
    var storePrefix = 'Test-';
    var name = Date.now().toString();
    Collection = Backbone.IDBCollection.extend({
      storePrefix: storePrefix,
      name: name
    });
    dbNameArray.push( storePrefix + name );
  });

  it('should be in a valid state', function (done) {
    var collection = new Collection();
    expect( collection ).to.be.ok;
    expect( collection.db ).not.to.be.undefined;

    collection.db.open()
      .then( function( database ){
        expect( database ).to.be.instanceOf( IDBDatabase );
        collection.db.close();
        done();
      })
      .catch( done );
  });

  it('should create a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      special: true,
      error: done,
      success: function( model, response, options ) {
        expect(model.isNew()).to.be.false;
        expect(response.age).to.eql(52);
        expect(options.special).to.be.true;
        done();
      }
    });
  });

  it('should update an existing model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function( model, reposonse, options ){
        model.save({ age: 54 }, {
          special: true,
          error: done,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(54);
            expect(resp.age).to.eql(54);
            expect(opts.special).to.be.true;
            done();
          }
        });
      }
    });

  });

  it('should fetch a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function(model){
        model.set({ age: 53 });
        model.fetch({
          special: true,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(52);
            expect(resp.age).to.eql(52);
            expect(opts.special).to.be.true;
            done();
          }
        });
      }
    });
  });

  it('should trigger fetch error arguments', function (done) {
    var collection = new Collection();
    var keyPath = collection.db.opts.keyPath;
    var model = {};
    model[keyPath] = null;
    model = collection.add(model);
    model.fetch({
      special: true,
      success: done,
      error: function(m, resp, opts){
        expect(m).to.eql(model);
        expect(resp).to.be.instanceOf(window.Error);
        expect(opts.special).to.be.true;
        done();
      }
    });
  });

  it('should destroy a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function(model){
        model.destroy({
          wait: true,
          special: true,
          error: done,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(52);
            expect(resp).to.be.undefined;
            expect(opts.special).to.be.true;
            expect(collection).to.have.length(0);
            done();
          }
        });
      }
    });
  });

  it('should destroy a new model', function (done) {
    var collection = new Collection();
    var model = collection.add({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    });
    expect( model.destroy() ).to.be.false;
    expect( collection ).to.have.length(0);
    done();
  });

  it('should batch save a collection of models', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname : 'Smith',
          age      : 35,
          email    : 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname : 'Doe',
          age      : 52,
          email    : 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname : 'Bloggs',
          age      : 28,
          email    : 'joebloggs@example.com'
        }
      ])
      .then(function (records) {
        expect(collection).to.have.length(0);
        expect(records).eqls([1, 2, 3]);
        done();
      });
  });

  it('should count indexeddb records', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname: 'Smith',
          age: 35,
          email: 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname: 'Doe',
          age: 52,
          email: 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname: 'Bloggs',
          age: 28,
          email: 'joebloggs@example.com'
        }
      ])
      .then( function() {
        collection.count()
          .then(function(count){
            expect( count ).equals( 3 );
            done();
          });
      });
  });

  it('should fetch \'collection.pageSize\' records by default', function(done){
    for(var data = [], i = 0; i < 100; i++) {
      data.push({ foo: i });
    }

    var Col = Collection.extend({
      pageSize: Math.floor((Math.random() * 100) + 1)
    });
    var collection = new Col();

    collection.putBatch(data)
      .then(function(){
        collection.fetch({
          special: true,
          error: done,
          success: function(collection, response, options){
            expect( collection ).to.have.length( collection.pageSize );
            expect( options.special).to.be.true;
            done();
          }
        })
      });
  });

  it('should fetch \'filter[limit]\' records', function(done){
    for(var data = [], i = 0; i < 100; i++) {
      data.push({ foo: i });
    }

    var Col = Collection.extend({
      pageSize: Math.floor((Math.random() * 100) + 1)
    });
    var collection = new Col();

    collection.putBatch(data)
      .then(function(){
        var random = Math.floor((Math.random() * 100) + 1);
        collection.fetch({
          data: {
            filter: {
              limit: random,
            }
          },
          special: true,
          error: done,
          success: function(){
            expect( collection ).to.have.length( random );

            collection.fetch({
              data: {
                filter: {
                  limit: -1,
                }
              },
              reset: true,
              error: done,
              success: function(){
                expect( collection ).to.have.length( 100 );
                done();
              }
            });

          }
        });
      });
  });

  it('should merge models on a non-keyPath attribute', function (done) {
    var Model = Backbone.IDBModel.extend({
      idAttribute: 'local_id'
    });

    var DualCollection = Collection.extend({
      model  : Model,
      keyPath: 'local_id',
      indexes: [
        {name: 'id', keyPath: 'id', unique: true}
      ],
    });

    var collection = new DualCollection();
    collection.putBatch([{id: 266}, {id: 8345}, {id: 2346}])
      .then(function () {
        return collection.putBatch(
          [{id: 266, foo: 'bar'}, {id: 8345, foo: 'baz'}],
          {index: 'id'}
        );
      })
      .then(function (records) {
        expect(records).eqls([1, 2]);

        collection.fetch({
          error  : done,
          success: function (collection) {
            expect(collection).to.have.length(3);
            expect(collection.findWhere({id: 266}).get('foo')).equals('bar');
            expect(collection.findWhere({id: 8345}).get('foo')).equals('baz');
            done();
          }
        })

      })
      .catch(done);

  });

  it('should merge models with a custom merge function', function (done) {
    var Model = Backbone.IDBModel.extend({
      idAttribute: 'local_id'
    });

    var DualCollection = Collection.extend({
      model: Model,
      keyPath: 'local_id',
      indexes: [
        {name: 'id', keyPath: 'id', unique: true}
      ],
    });

    var collection = new DualCollection();
    collection.putBatch([ { id: 11 }, { id: 12 }, { id: 13 } ])
      .then( function( records ) {
        return collection.putBatch(
          [{id: 11, foo: 'bar'}, {id: 12, foo: 'baz'}, {id: 14, foo: 'boo'}],
          {
            index: {
              keyPath: 'id',
              merge  : function (oldData, newData) {
                if (_.has(oldData, 'local_id')) {
                  newData._state = 'updated';
                } else {
                  newData._state = 'new';
                }
                return _.merge({}, oldData, newData);
              }
            }
          }
        );
      })
      .then( function() {
        collection.fetch({
          error: done,
          success: function( collection ){
            expect( collection ).to.have.length( 4 );
            expect( collection.findWhere({ id: 11 }).get('_state') ).equals('updated');
            expect( collection.findWhere({ id: 12 }).get('_state') ).equals('updated');
            expect( collection.findWhere({ id: 13 }).get('_state') ).to.be.undefined;
            expect( collection.findWhere({ id: 14 }).get('_state') ).equals('new');
            done();
          }
        })

      })
      .catch(done);

  });

  it('should clear a collection', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname: 'Smith',
          age: 35,
          email: 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname: 'Doe',
          age: 52,
          email: 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname: 'Bloggs',
          age: 28,
          email: 'joebloggs@example.com'
        }
      ])
      .then( function( records ) {
        collection.add(records);
        expect( collection ).to.have.length( 3 );
        collection.clear()
          .then(function(){
            expect( collection ).to.have.length( 0 );
            collection.count()
              .then(function(count){
                expect( count ).equals( 0 );
                done();
              });
          });
      });
  });

  it('should get the highest entry by index', function (done) {
    var IndexedCollection = Collection.extend({
      indexes: [
        {name: 'age', keyPath: 'age'}
      ],
    });
    var collection = new IndexedCollection();

    collection.putBatch([
      {
        firstname: 'Jane',
        lastname: 'Smith',
        age: 35,
        email: 'janesmith@example.com'
      }, {
        firstname: 'John',
        lastname: 'Doe',
        age: 52,
        email: 'johndoe@example.com'
      }, {
        firstname: 'Joe',
        lastname: 'Bloggs',
        age: 28,
        email: 'joebloggs@example.com'
      }
    ])
    .then(function(){
      return collection.db.findHighestIndex('age');
    })
    .then(function(response){
      expect(response).equals(52);
      done();
    });
  });

  it('should count the records on open', function(done){
    var random = Math.floor((Math.random() * 100) + 1);
    for(var data = [], i = 0; i < random; i++) {
      data.push({ foo: i });
    }

    var collection = new Collection();

    collection.putBatch(data)
      .then(function() {
        expect(collection.db.length).equals(0);
        collection.db.close();
        return collection.db.open();
      })
      .then(function () {
        expect(collection.db.length).equals(random);
        return collection.clear()
      })
      .then(function () {
        expect(collection.db.length).equals(0);
        done();
      });
  });

  /**
   * Unit testing is not good for benchmarking
   * eg: an open console will slow indexedDB dramatically
   * this is just a rough comparison
   *
   * 10000 records
   */
  it('should be performant with putBatch', function(done) {
    var collection = new Collection();

    var count = is_safari ? 2000 : 10000;

    for(var data = [], i = 0; i < count; i++) {
      data.push({ foo: i });
    }
    this.timeout(10000);

    var start = Date.now();

    collection.putBatch(data)
      .then(function() {
        var time = Date.now() - start;
        expect(time).to.be.below(2000);
        console.log(time);
        done();
      })
      .catch(done);
  });

  /**
   * 1000 records
   */
  it('should be performant with putBatch merge', function(done) {
    var Model = Backbone.IDBModel.extend({
      idAttribute: 'local_id'
    });

    var DualCollection = Collection.extend({
      model  : Model,
      keyPath: 'local_id',
      indexes: [
        {name: 'id', keyPath: 'id', unique: true}
      ],
    });

    var count = is_safari ? 200 : 1000;

    for(var data = [], i = 0; i < count; i++) {
      data.push({ id: i });
    }
    this.timeout(10000);

    var start = Date.now();

    var collection = new DualCollection();
    collection.putBatch(data)
      .then(function () {
        return collection.putBatch(data, {index: 'id'});
      })
      .then(function () {
        var time = Date.now() - start;
        expect(time).to.be.below(2000);
        console.log(time);
        done();
      })
      .catch(done);
  });

  describe('IDBAdapter', function(){

    it('should have a hasGetParams method', function(){
      var collection = new Collection();
      expect(collection.db).respondsTo('hasGetParams');
      expect(collection.db.hasGetParams()).to.be.false;
      expect(collection.db.hasGetParams({page: 2})).to.be.true;
      expect(collection.db.hasGetParams({fields: ['id', '_state']})).to.be.true;
      expect(collection.db.hasGetParams({filter: {limit: -1}})).to.be.false;
      expect(collection.db.hasGetParams({filter: {limit: -1, in: [1,2,3]}})).to.be.true;
      expect(collection.db.hasGetParams({filter: {in: [1,2,3]}})).to.be.true;
    });

  });

  after(function( done ) {
    var indexedDB = window.indexedDB;
    _.each( dbNameArray, function( dbName ){
      indexedDB.deleteDatabase( dbName );
    });
    done();
  });

});<commit_msg>remove performance tests for saucelabs<commit_after>describe('Backbone IndexedDB', function () {
  var dbNameArray = [];
  var Collection;

  // is_safari worse than IE
  var is_safari = window.navigator.userAgent.indexOf('Safari') !== -1 &&
    window.navigator.userAgent.indexOf('Chrome') === -1 &&
    window.navigator.userAgent.indexOf('Android') === -1;

  beforeEach(function(){
    var storePrefix = 'Test-';
    var name = Date.now().toString();
    Collection = Backbone.IDBCollection.extend({
      storePrefix: storePrefix,
      name: name
    });
    dbNameArray.push( storePrefix + name );
  });

  it('should be in a valid state', function (done) {
    var collection = new Collection();
    expect( collection ).to.be.ok;
    expect( collection.db ).not.to.be.undefined;

    collection.db.open()
      .then( function( database ){
        expect( database ).to.be.instanceOf( IDBDatabase );
        collection.db.close();
        done();
      })
      .catch( done );
  });

  it('should create a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      special: true,
      error: done,
      success: function( model, response, options ) {
        expect(model.isNew()).to.be.false;
        expect(response.age).to.eql(52);
        expect(options.special).to.be.true;
        done();
      }
    });
  });

  it('should update an existing model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function( model, reposonse, options ){
        model.save({ age: 54 }, {
          special: true,
          error: done,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(54);
            expect(resp.age).to.eql(54);
            expect(opts.special).to.be.true;
            done();
          }
        });
      }
    });

  });

  it('should fetch a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function(model){
        model.set({ age: 53 });
        model.fetch({
          special: true,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(52);
            expect(resp.age).to.eql(52);
            expect(opts.special).to.be.true;
            done();
          }
        });
      }
    });
  });

  it('should trigger fetch error arguments', function (done) {
    var collection = new Collection();
    var keyPath = collection.db.opts.keyPath;
    var model = {};
    model[keyPath] = null;
    model = collection.add(model);
    model.fetch({
      special: true,
      success: done,
      error: function(m, resp, opts){
        expect(m).to.eql(model);
        expect(resp).to.be.instanceOf(window.Error);
        expect(opts.special).to.be.true;
        done();
      }
    });
  });

  it('should destroy a model', function (done) {
    var collection = new Collection();
    collection.create({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    }, {
      wait: true,
      error: done,
      success: function(model){
        model.destroy({
          wait: true,
          special: true,
          error: done,
          success: function(m, resp, opts) {
            expect(m).to.eql(model);
            expect(m.get('age')).to.eql(52);
            expect(resp).to.be.undefined;
            expect(opts.special).to.be.true;
            expect(collection).to.have.length(0);
            done();
          }
        });
      }
    });
  });

  it('should destroy a new model', function (done) {
    var collection = new Collection();
    var model = collection.add({
      firstname: 'John',
      lastname: 'Doe',
      age: 52,
      email: 'johndoe@example.com'
    });
    expect( model.destroy() ).to.be.false;
    expect( collection ).to.have.length(0);
    done();
  });

  it('should batch save a collection of models', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname : 'Smith',
          age      : 35,
          email    : 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname : 'Doe',
          age      : 52,
          email    : 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname : 'Bloggs',
          age      : 28,
          email    : 'joebloggs@example.com'
        }
      ])
      .then(function (records) {
        expect(collection).to.have.length(0);
        expect(records).eqls([1, 2, 3]);
        done();
      });
  });

  it('should count indexeddb records', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname: 'Smith',
          age: 35,
          email: 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname: 'Doe',
          age: 52,
          email: 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname: 'Bloggs',
          age: 28,
          email: 'joebloggs@example.com'
        }
      ])
      .then( function() {
        collection.count()
          .then(function(count){
            expect( count ).equals( 3 );
            done();
          });
      });
  });

  it('should fetch \'collection.pageSize\' records by default', function(done){
    for(var data = [], i = 0; i < 100; i++) {
      data.push({ foo: i });
    }

    var Col = Collection.extend({
      pageSize: Math.floor((Math.random() * 100) + 1)
    });
    var collection = new Col();

    collection.putBatch(data)
      .then(function(){
        collection.fetch({
          special: true,
          error: done,
          success: function(collection, response, options){
            expect( collection ).to.have.length( collection.pageSize );
            expect( options.special).to.be.true;
            done();
          }
        })
      });
  });

  it('should fetch \'filter[limit]\' records', function(done){
    for(var data = [], i = 0; i < 100; i++) {
      data.push({ foo: i });
    }

    var Col = Collection.extend({
      pageSize: Math.floor((Math.random() * 100) + 1)
    });
    var collection = new Col();

    collection.putBatch(data)
      .then(function(){
        var random = Math.floor((Math.random() * 100) + 1);
        collection.fetch({
          data: {
            filter: {
              limit: random,
            }
          },
          special: true,
          error: done,
          success: function(){
            expect( collection ).to.have.length( random );

            collection.fetch({
              data: {
                filter: {
                  limit: -1,
                }
              },
              reset: true,
              error: done,
              success: function(){
                expect( collection ).to.have.length( 100 );
                done();
              }
            });

          }
        });
      });
  });

  it('should merge models on a non-keyPath attribute', function (done) {
    var Model = Backbone.IDBModel.extend({
      idAttribute: 'local_id'
    });

    var DualCollection = Collection.extend({
      model  : Model,
      keyPath: 'local_id',
      indexes: [
        {name: 'id', keyPath: 'id', unique: true}
      ],
    });

    var collection = new DualCollection();
    collection.putBatch([{id: 266}, {id: 8345}, {id: 2346}])
      .then(function () {
        return collection.putBatch(
          [{id: 266, foo: 'bar'}, {id: 8345, foo: 'baz'}],
          {index: 'id'}
        );
      })
      .then(function (records) {
        expect(records).eqls([1, 2]);

        collection.fetch({
          error  : done,
          success: function (collection) {
            expect(collection).to.have.length(3);
            expect(collection.findWhere({id: 266}).get('foo')).equals('bar');
            expect(collection.findWhere({id: 8345}).get('foo')).equals('baz');
            done();
          }
        })

      })
      .catch(done);

  });

  it('should merge models with a custom merge function', function (done) {
    var Model = Backbone.IDBModel.extend({
      idAttribute: 'local_id'
    });

    var DualCollection = Collection.extend({
      model: Model,
      keyPath: 'local_id',
      indexes: [
        {name: 'id', keyPath: 'id', unique: true}
      ],
    });

    var collection = new DualCollection();
    collection.putBatch([ { id: 11 }, { id: 12 }, { id: 13 } ])
      .then( function( records ) {
        return collection.putBatch(
          [{id: 11, foo: 'bar'}, {id: 12, foo: 'baz'}, {id: 14, foo: 'boo'}],
          {
            index: {
              keyPath: 'id',
              merge  : function (oldData, newData) {
                if (_.has(oldData, 'local_id')) {
                  newData._state = 'updated';
                } else {
                  newData._state = 'new';
                }
                return _.merge({}, oldData, newData);
              }
            }
          }
        );
      })
      .then( function() {
        collection.fetch({
          error: done,
          success: function( collection ){
            expect( collection ).to.have.length( 4 );
            expect( collection.findWhere({ id: 11 }).get('_state') ).equals('updated');
            expect( collection.findWhere({ id: 12 }).get('_state') ).equals('updated');
            expect( collection.findWhere({ id: 13 }).get('_state') ).to.be.undefined;
            expect( collection.findWhere({ id: 14 }).get('_state') ).equals('new');
            done();
          }
        })

      })
      .catch(done);

  });

  it('should clear a collection', function (done) {
    var collection = new Collection();
    collection.putBatch([
        {
          firstname: 'Jane',
          lastname: 'Smith',
          age: 35,
          email: 'janesmith@example.com'
        }, {
          firstname: 'John',
          lastname: 'Doe',
          age: 52,
          email: 'johndoe@example.com'
        }, {
          firstname: 'Joe',
          lastname: 'Bloggs',
          age: 28,
          email: 'joebloggs@example.com'
        }
      ])
      .then( function( records ) {
        collection.add(records);
        expect( collection ).to.have.length( 3 );
        collection.clear()
          .then(function(){
            expect( collection ).to.have.length( 0 );
            collection.count()
              .then(function(count){
                expect( count ).equals( 0 );
                done();
              });
          });
      });
  });

  it('should get the highest entry by index', function (done) {
    var IndexedCollection = Collection.extend({
      indexes: [
        {name: 'age', keyPath: 'age'}
      ],
    });
    var collection = new IndexedCollection();

    collection.putBatch([
      {
        firstname: 'Jane',
        lastname: 'Smith',
        age: 35,
        email: 'janesmith@example.com'
      }, {
        firstname: 'John',
        lastname: 'Doe',
        age: 52,
        email: 'johndoe@example.com'
      }, {
        firstname: 'Joe',
        lastname: 'Bloggs',
        age: 28,
        email: 'joebloggs@example.com'
      }
    ])
    .then(function(){
      return collection.db.findHighestIndex('age');
    })
    .then(function(response){
      expect(response).equals(52);
      done();
    });
  });

  it('should count the records on open', function(done){
    var random = Math.floor((Math.random() * 100) + 1);
    for(var data = [], i = 0; i < random; i++) {
      data.push({ foo: i });
    }

    var collection = new Collection();

    collection.putBatch(data)
      .then(function() {
        expect(collection.db.length).equals(0);
        collection.db.close();
        return collection.db.open();
      })
      .then(function () {
        expect(collection.db.length).equals(random);
        return collection.clear()
      })
      .then(function () {
        expect(collection.db.length).equals(0);
        done();
      });
  });

  /**
   * Unit testing is not good for benchmarking
   * eg: an open console will slow indexedDB dramatically
   * this is just a rough comparison
   *
   * 10000 records
   */
  //it('should be performant with putBatch', function(done) {
  //  var collection = new Collection();
  //
  //  var count = is_safari ? 2000 : 10000;
  //
  //  for(var data = [], i = 0; i < count; i++) {
  //    data.push({ foo: i });
  //  }
  //  this.timeout(10000);
  //
  //  var start = Date.now();
  //
  //  collection.putBatch(data)
  //    .then(function() {
  //      var time = Date.now() - start;
  //      expect(time).to.be.below(2000);
  //      console.log(time);
  //      done();
  //    })
  //    .catch(done);
  //});

  /**
   * 1000 records
   */
  //it('should be performant with putBatch merge', function(done) {
  //  var Model = Backbone.IDBModel.extend({
  //    idAttribute: 'local_id'
  //  });
  //
  //  var DualCollection = Collection.extend({
  //    model  : Model,
  //    keyPath: 'local_id',
  //    indexes: [
  //      {name: 'id', keyPath: 'id', unique: true}
  //    ],
  //  });
  //
  //  var count = is_safari ? 200 : 1000;
  //
  //  for(var data = [], i = 0; i < count; i++) {
  //    data.push({ id: i });
  //  }
  //  this.timeout(10000);
  //
  //  var start = Date.now();
  //
  //  var collection = new DualCollection();
  //  collection.putBatch(data)
  //    .then(function () {
  //      return collection.putBatch(data, {index: 'id'});
  //    })
  //    .then(function () {
  //      var time = Date.now() - start;
  //      expect(time).to.be.below(2000);
  //      console.log(time);
  //      done();
  //    })
  //    .catch(done);
  //});

  describe('IDBAdapter', function(){

    it('should have a hasGetParams method', function(){
      var collection = new Collection();
      expect(collection.db).respondsTo('hasGetParams');
      expect(collection.db.hasGetParams()).to.be.false;
      expect(collection.db.hasGetParams({page: 2})).to.be.true;
      expect(collection.db.hasGetParams({fields: ['id', '_state']})).to.be.true;
      expect(collection.db.hasGetParams({filter: {limit: -1}})).to.be.false;
      expect(collection.db.hasGetParams({filter: {limit: -1, in: [1,2,3]}})).to.be.true;
      expect(collection.db.hasGetParams({filter: {in: [1,2,3]}})).to.be.true;
    });

  });

  after(function( done ) {
    var indexedDB = window.indexedDB;
    _.each( dbNameArray, function( dbName ){
      indexedDB.deleteDatabase( dbName );
    });
    done();
  });

});<|endoftext|>


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

jimport("java.io.File");
jimport("java.io.FileOutputStream");
jimport("java.lang.System.out.println");
jimport("java.io.ByteArrayInputStream");
jimport("java.io.ByteArrayOutputStream");
jimport("java.io.DataInputStream");
jimport("java.io.DataOutputStream");
jimport("net.appjet.common.sars.SarsClient");
jimport("com.etherpad.openofficeservice.OpenOfficeService");
jimport("com.etherpad.openofficeservice.UnsupportedFormatException");
jimport("com.etherpad.openofficeservice.TemporaryFailure");

import("etherpad.log");
import("etherpad.utils");
import("sync");
import("execution");
import("varz");
import("exceptionutils");

function _log(obj) {
  log.custom("import-export", obj);
}

function onStartup() {
  execution.initTaskThreadPool("importexport", 1);
}

var formats = {
  pdf: 'application/pdf',
  doc: 'application/msword',
  html: 'text/html; charset=utf-8',
  odt: 'application/vnd.oasis.opendocument.text',
  txt: 'text/plain; charset=utf-8'
}

function _createTempFile(bytes, type) {
        var f = File.createTempFile("ooconvert-", (type === null ? null : (type == "" ? "" : "."+type)));
	if (bytes) {
		var fos = new FileOutputStream(f);
		fos.write(bytes);
	}
	return f;
}

function _initConverterClient(convertServer) {
  if (convertServer) {
    var convertHost = convertServer.split(":")[0];
    var convertPort = Number(convertServer.split(":")[1]);
    if (! appjet.scopeCache.converter) {
      var converter = new SarsClient("ooffice-password", convertHost, convertPort);
      appjet.scopeCache.converter = converter;
      converter.setConnectTimeout(5000);
      converter.setReadTimeout(40000);
      appjet.scopeCache.converter.connect();
    }
    return appjet.scopeCache.converter;
  } else {
    return null;
  }
}

function _conversionSarsFailure() {
  delete appjet.scopeCache.converter;
}

function errorUnsupported(from) {
  return "Unsupported file type"+(from ? ": "+from+"." : ".")+" Etherpad can only import txt, html, rtf, doc, and docx files.";
}
var errorTemporary = "A temporary failure occurred; please try again later.";

function doSlowFileConversion(from, to, bytes, continuation) {
  var bytes = convertFileSlowly(from, to, bytes);
  continuation.resume();
  return bytes;
}

function _convertOverNetwork(convertServer, from, to, bytes) {
  var c = _initConverterClient(convertServer);
  var reqBytes = new ByteArrayOutputStream();
  var req = new DataOutputStream(reqBytes);
  req.writeUTF(from);
  req.writeUTF(to);
  req.writeInt(bytes.length);
  req.write(bytes, 0, bytes.length);

  var retBtyes;
  try {
    retBytes = c.message(reqBytes.toByteArray());
  } catch (e) {
    if (e.javaException) {
      net.appjet.oui.exceptionlog.apply(e.javaException)
    }
    _conversionSarsFailure();
    return "A communications failure occurred; please try again later.";
  }

  if (retBytes.length == 0) {
    return "An unknown failure occurred; please try again later. (#5)";
  }
  var res = new DataInputStream(new ByteArrayInputStream(retBytes));
  var status = res.readInt();
  if (status == 0) { // success
    var len = res.readInt();
    var resBytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len);
    res.readFully(resBytes);
    return resBytes;
  } else if (status == 1) {
    return errorTemporary;
  } else if (status == 2) {
    var permFailureCode = res.readInt();
    if (permFailureCode == 0) {
      return "An unknown failure occurred. (#1)";
    } else if (permFailureCode == 1) {
      return errorUnsupported(from);
    }
  } else {
    return "An unknown failure occurred. (#2)";
  }
}

function convertFileSlowly(from, to, bytes) {
  var convertServer = appjet.config["etherpad.sofficeConversionServer"];
  if (convertServer) {
    return _convertOverNetwork(convertServer, from, to, bytes);
  }

  if (! utils.hasOffice()) {
    return "EtherPad is not configured to import or export formats other than txt and html. Please contact your system administrator for details.";
  }
  OpenOfficeService.setExecutable(appjet.config["etherpad.soffice"]);
  try {
    return OpenOfficeService.convertFile(from, to, bytes);
  } catch (e) {
    if (e.javaException instanceof TemporaryFailure) {
      return errorTemporary;
    } else if (e.javaException instanceof UnsupportedFormatException) {
      return errorUnsupported(from);
    } else {
      return "An unknown failure occurred. (#3)";
    }
  }
}

function _noteConversionAttempt() {
  varz.incrementInt("importexport-conversions-attempted");
}

function _noteConversionSuccess() {
  varz.incrementInt("importexport-conversions-successful");
}

function _noteConversionFailure() {
  varz.incrementInt("importexport-conversions-failed");
}

function _noteConversionTimeout() {
  varz.incrementInt("importexport-conversions-timeout");
}

function _noteConversionImpossible() {
  varz.incrementInt("importexport-conversions-impossible");
}

function precomputedConversionResult(from, to, bytes) {
  try {
    var retBytes = request.cache.conversionCallable.get(500, java.util.concurrent.TimeUnit.MILLISECONDS);
    var delay = Date.now() - request.cache.startTime;
    _log({type: "conversion-latency", from: from, to: to,
          numBytes: request.cache.conversionByteLength,
          delay: delay});
    varz.addToInt("importexport-total-conversion-millis", delay);
    if (typeof(retBytes) == 'string') {
      _log({type: "error", error: "conversion-failed", from: from, to: to,
            numBytes: request.cache.conversionByteLength,
            delay: delay});
      _noteConversionFailure();
    } else {
      _noteConversionSuccess();
    }
    return retBytes;
  } catch (e) {
    if (e.javaException instanceof java.util.concurrent.TimeoutException) {
      _noteConversionTimeout();
      request.cache.conversionCallable.cancel(false);
      _log({type: "error", error: "conversion-failed", from: from, to: to,
            numBytes: request.cache.conversionByteLength,
            delay: -1});
      return "Conversion timed out. Please try again later.";
    }
    _log({type: "error", error: "conversion-failed", from: from, to: to,
          numBytes: request.cache.conversionByteLength,
          trace: exceptionutils.getStackTracePlain(e)});
    _noteConversionFailure();
    return "An unknown failure occurred. (#4)";
  }
}

function convertFile(from, to, bytes) {
  if (request.cache.conversionCallable) {
    return precomputedConversionResult(from, to, bytes);
  }

  _noteConversionAttempt();
  if (from == to) {
    _noteConversionSuccess();
    return bytes;
  }
  if (from == "txt" && to == "html") {
    _noteConversionSuccess();
    return (new java.lang.String(utils.renderTemplateAsString('pad/exporthtml.ejs', {
      content: String(new java.lang.String(bytes, "UTF-8")).replace(/&/g, "&").replace(/</g, "<"),
      pre: true
    }))).getBytes("UTF-8");
  }

  request.cache.conversionByteLength = bytes.length;
  request.cache.conversionCallable =
    execution.scheduleTask("importexport", "doSlowFileConversion", 0, [
      from, to, bytes, request.continuation
    ]);
  request.cache.startTime = Date.now();
  request.continuation.suspend(45000);
  _noteConversionImpossible();
  return "An unexpected error occurred."; // Shouldn't ever get here.
}<commit_msg>Better error message. Fixes #258.<commit_after>/**
 * Copyright 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

jimport("java.io.File");
jimport("java.io.FileOutputStream");
jimport("java.lang.System.out.println");
jimport("java.io.ByteArrayInputStream");
jimport("java.io.ByteArrayOutputStream");
jimport("java.io.DataInputStream");
jimport("java.io.DataOutputStream");
jimport("net.appjet.common.sars.SarsClient");
jimport("com.etherpad.openofficeservice.OpenOfficeService");
jimport("com.etherpad.openofficeservice.UnsupportedFormatException");
jimport("com.etherpad.openofficeservice.TemporaryFailure");

import("etherpad.log");
import("etherpad.utils");
import("sync");
import("execution");
import("varz");
import("exceptionutils");

function _log(obj) {
  log.custom("import-export", obj);
}

function onStartup() {
  execution.initTaskThreadPool("importexport", 1);
}

var formats = {
  pdf: 'application/pdf',
  doc: 'application/msword',
  html: 'text/html; charset=utf-8',
  odt: 'application/vnd.oasis.opendocument.text',
  txt: 'text/plain; charset=utf-8'
}

function _createTempFile(bytes, type) {
        var f = File.createTempFile("ooconvert-", (type === null ? null : (type == "" ? "" : "."+type)));
	if (bytes) {
		var fos = new FileOutputStream(f);
		fos.write(bytes);
	}
	return f;
}

function _initConverterClient(convertServer) {
  if (convertServer) {
    var convertHost = convertServer.split(":")[0];
    var convertPort = Number(convertServer.split(":")[1]);
    if (! appjet.scopeCache.converter) {
      var converter = new SarsClient("ooffice-password", convertHost, convertPort);
      appjet.scopeCache.converter = converter;
      converter.setConnectTimeout(5000);
      converter.setReadTimeout(40000);
      appjet.scopeCache.converter.connect();
    }
    return appjet.scopeCache.converter;
  } else {
    return null;
  }
}

function _conversionSarsFailure() {
  delete appjet.scopeCache.converter;
}

function errorUnsupported(from) {
  return "Unsupported file type"+(from ? ": "+from+"." : ".")+" Etherpad can only import txt, html, rtf, doc, and docx files.";
}
var errorTemporary = "A temporary failure occurred; please try again later.";

function doSlowFileConversion(from, to, bytes, continuation) {
  var bytes = convertFileSlowly(from, to, bytes);
  continuation.resume();
  return bytes;
}

function _convertOverNetwork(convertServer, from, to, bytes) {
  var c = _initConverterClient(convertServer);
  var reqBytes = new ByteArrayOutputStream();
  var req = new DataOutputStream(reqBytes);
  req.writeUTF(from);
  req.writeUTF(to);
  req.writeInt(bytes.length);
  req.write(bytes, 0, bytes.length);

  var retBtyes;
  try {
    retBytes = c.message(reqBytes.toByteArray());
  } catch (e) {
    if (e.javaException) {
      net.appjet.oui.exceptionlog.apply(e.javaException)
    }
    _conversionSarsFailure();
    return "A communications failure occurred; please try again later.";
  }

  if (retBytes.length == 0) {
    return "An unknown failure occurred; please try again later. (#5)";
  }
  var res = new DataInputStream(new ByteArrayInputStream(retBytes));
  var status = res.readInt();
  if (status == 0) { // success
    var len = res.readInt();
    var resBytes = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, len);
    res.readFully(resBytes);
    return resBytes;
  } else if (status == 1) {
    return errorTemporary;
  } else if (status == 2) {
    var permFailureCode = res.readInt();
    if (permFailureCode == 0) {
      return "An unknown failure occurred. (#1)";
    } else if (permFailureCode == 1) {
      return errorUnsupported(from);
    }
  } else {
    return "An unknown failure occurred. (#2)";
  }
}

function convertFileSlowly(from, to, bytes) {
  var convertServer = appjet.config["etherpad.sofficeConversionServer"];
  if (convertServer) {
    return _convertOverNetwork(convertServer, from, to, bytes);
  }

  if (! utils.hasOffice()) {
    return "EtherPad is running without ConversionServer, so the requested import or export could not be completed. Please contact your system administrator for details.";
  }
  OpenOfficeService.setExecutable(appjet.config["etherpad.soffice"]);
  try {
    return OpenOfficeService.convertFile(from, to, bytes);
  } catch (e) {
    if (e.javaException instanceof TemporaryFailure) {
      return errorTemporary;
    } else if (e.javaException instanceof UnsupportedFormatException) {
      return errorUnsupported(from);
    } else {
      return "An unknown failure occurred. (#3)";
    }
  }
}

function _noteConversionAttempt() {
  varz.incrementInt("importexport-conversions-attempted");
}

function _noteConversionSuccess() {
  varz.incrementInt("importexport-conversions-successful");
}

function _noteConversionFailure() {
  varz.incrementInt("importexport-conversions-failed");
}

function _noteConversionTimeout() {
  varz.incrementInt("importexport-conversions-timeout");
}

function _noteConversionImpossible() {
  varz.incrementInt("importexport-conversions-impossible");
}

function precomputedConversionResult(from, to, bytes) {
  try {
    var retBytes = request.cache.conversionCallable.get(500, java.util.concurrent.TimeUnit.MILLISECONDS);
    var delay = Date.now() - request.cache.startTime;
    _log({type: "conversion-latency", from: from, to: to,
          numBytes: request.cache.conversionByteLength,
          delay: delay});
    varz.addToInt("importexport-total-conversion-millis", delay);
    if (typeof(retBytes) == 'string') {
      _log({type: "error", error: "conversion-failed", from: from, to: to,
            numBytes: request.cache.conversionByteLength,
            delay: delay});
      _noteConversionFailure();
    } else {
      _noteConversionSuccess();
    }
    return retBytes;
  } catch (e) {
    if (e.javaException instanceof java.util.concurrent.TimeoutException) {
      _noteConversionTimeout();
      request.cache.conversionCallable.cancel(false);
      _log({type: "error", error: "conversion-failed", from: from, to: to,
            numBytes: request.cache.conversionByteLength,
            delay: -1});
      return "Conversion timed out. Please try again later.";
    }
    _log({type: "error", error: "conversion-failed", from: from, to: to,
          numBytes: request.cache.conversionByteLength,
          trace: exceptionutils.getStackTracePlain(e)});
    _noteConversionFailure();
    return "An unknown failure occurred. (#4)";
  }
}

function convertFile(from, to, bytes) {
  if (request.cache.conversionCallable) {
    return precomputedConversionResult(from, to, bytes);
  }

  _noteConversionAttempt();
  if (from == to) {
    _noteConversionSuccess();
    return bytes;
  }
  if (from == "txt" && to == "html") {
    _noteConversionSuccess();
    return (new java.lang.String(utils.renderTemplateAsString('pad/exporthtml.ejs', {
      content: String(new java.lang.String(bytes, "UTF-8")).replace(/&/g, "&").replace(/</g, "<"),
      pre: true
    }))).getBytes("UTF-8");
  }

  request.cache.conversionByteLength = bytes.length;
  request.cache.conversionCallable =
    execution.scheduleTask("importexport", "doSlowFileConversion", 0, [
      from, to, bytes, request.continuation
    ]);
  request.cache.startTime = Date.now();
  request.continuation.suspend(45000);
  _noteConversionImpossible();
  return "An unexpected error occurred."; // Shouldn't ever get here.
}<|endoftext|>


<commit_before>var fs = require('fs');
var yaml = require('js-yaml');
var _ = require('lodash-node');

module.exports = function (config) {
  process.env.NODE_ENV = 'test'

  switch (process.env.ENV) {
    case 'CI':
      _.extend(process.env, saucelabsVariables());
      config.set(saucelabs());
      break;
    default:
      config.set(local());
      break;
  }

  function saucelabs() {
    var customLaunchers = {
      sl_chrome: {
        base: 'SauceLabs',
        browserName: 'chrome',
        platform: 'Windows 7',
        version: '38'
      },
      sl_firefox: {
        base: 'SauceLabs',
        browserName: 'firefox',
        version: '33'
      },
      sl_safari: {
        base: 'SauceLabs',
        browserName: 'safari',
        version: '5'
      },
      sl_ie_9: {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 7',
        version: '9'
      },
      sl_ie_10: {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 7',
        version: '10'
      },
      sl_ie_11: {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 8.1',
        version: '11'
      }
    };

    return _.extend(base(), {
      sauceLabs: {
        testName: 'Marty Tests'
      },
      browserDisconnectTimeout : 10000,
      browserDisconnectTolerance : 1,
      browserNoActivityTimeout : 4 * 60 * 1000,
      captureTimeout : 4 * 60 * 1000,
      customLaunchers: customLaunchers,
      browsers: Object.keys(customLaunchers),
      reporters: ['dots', 'saucelabs'],
      singleRun: true
    });
  }

  function local() {
    return _.extend(base(), {
      reporters: ['spec'],
      browsers: ['Chrome'],
      autoWatch: true,
      singleRun: false,
      colors: true
    });
  }

  function base() {
    return {
      basePath: '',
      frameworks: ['mocha', 'browserify'],
      browserify: {
        transform: ['reactify', 'envify'],
        debug: true
      },
      files: [
        'index.js',
        'lib/*.js',
        'test/**/*.js'
      ],
      exclude: [
        'test/lib/mockServer/*'
      ],
      preprocessors: {
        'lib/*': ['browserify'],
        'index.js': ['browserify'],
        'test/**/*.js': ['browserify']
      },
      port: 9876,
      logLevel: config.LOG_INFO,
    };
  }

  function saucelabsVariables() {
    return _.pick(travisGlobalVariables(), 'SAUCE_USERNAME', 'SAUCE_ACCESS_KEY');

    function travisGlobalVariables() {
      var config = {};
      var travis = yaml.safeLoad(fs.readFileSync('./.travis.yml', 'utf-8'));

      travis.env.global.forEach(function (variable) {
        var parts = /(.*)="(.*)"/.exec(variable);

        config[parts[1]] = parts[2];
      });

      return config;
    }
  }
};
<commit_msg>Removing IE support<commit_after>var fs = require('fs');
var yaml = require('js-yaml');
var _ = require('lodash-node');

module.exports = function (config) {
  process.env.NODE_ENV = 'test'

  switch (process.env.ENV) {
    case 'CI':
      _.extend(process.env, saucelabsVariables());
      config.set(saucelabs());
      break;
    default:
      config.set(local());
      break;
  }

  function saucelabs() {
    var customLaunchers = {
      sl_chrome: {
        base: 'SauceLabs',
        browserName: 'chrome',
        platform: 'Windows 7',
        version: '38'
      },
      sl_firefox: {
        base: 'SauceLabs',
        browserName: 'firefox',
        version: '33'
      },
      sl_safari: {
        base: 'SauceLabs',
        browserName: 'safari',
        version: '5'
      },
      sl_ie_10: {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 7',
        version: '10'
      },
      sl_ie_11: {
        base: 'SauceLabs',
        browserName: 'internet explorer',
        platform: 'Windows 8.1',
        version: '11'
      }
    };

    return _.extend(base(), {
      sauceLabs: {
        testName: 'Marty Tests'
      },
      browserDisconnectTimeout : 10000,
      browserDisconnectTolerance : 1,
      browserNoActivityTimeout : 4 * 60 * 1000,
      captureTimeout : 4 * 60 * 1000,
      customLaunchers: customLaunchers,
      browsers: Object.keys(customLaunchers),
      reporters: ['dots', 'saucelabs'],
      singleRun: true
    });
  }

  function local() {
    return _.extend(base(), {
      reporters: ['spec'],
      browsers: ['Chrome'],
      autoWatch: true,
      singleRun: false,
      colors: true
    });
  }

  function base() {
    return {
      basePath: '',
      frameworks: ['mocha', 'browserify'],
      browserify: {
        transform: ['reactify', 'envify'],
        debug: true
      },
      files: [
        'index.js',
        'lib/*.js',
        'test/**/*.js'
      ],
      exclude: [
        'test/lib/mockServer/*'
      ],
      preprocessors: {
        'lib/*': ['browserify'],
        'index.js': ['browserify'],
        'test/**/*.js': ['browserify']
      },
      port: 9876,
      logLevel: config.LOG_INFO,
    };
  }

  function saucelabsVariables() {
    return _.pick(travisGlobalVariables(), 'SAUCE_USERNAME', 'SAUCE_ACCESS_KEY');

    function travisGlobalVariables() {
      var config = {};
      var travis = yaml.safeLoad(fs.readFileSync('./.travis.yml', 'utf-8'));

      travis.env.global.forEach(function (variable) {
        var parts = /(.*)="(.*)"/.exec(variable);

        config[parts[1]] = parts[2];
      });

      return config;
    }
  }
};
<|endoftext|>


<commit_before>/*
	######################################
	# Authentication Components          #
	######################################
*/

/*
	Register component
*/

var RegisterForm = React.createClass({
	render: function () {
		return (

				Username
				<input type="text" name="username" placeholder="Username">
				Password
				<input type="text" name="password" placeholder="Password">

		);
	}
});

/*
	Login component
*/

/*
	######################################
	# Graphing Components                #
	######################################
*/

/*
	######################################
	# Social Wall Components             #
	######################################
*/

/*
	Post component
	Used to show wall posts
*/

/*
	Wall component
	Shows posts in walls
*/

/*
	Post Form Component
	This can be used to make posts on walls
*/

ReactDOM.render(

Hello, world!
,
	document.getElementById('example')
);<commit_msg>Add small login component<commit_after>/*
	######################################
	# Authentication Components          #
	######################################
*/

/*
	Register component
*/

var RegisterForm = React.createClass({
	render: function () {
		return (
			<form className="register">
				Username
				<input type="text" name="username" placeholder="Username">
				Password
				<input type="text" name="password" placeholder="Password">
			</form>
		);
	}
});

/*
	Login component
*/

var LoginForm = React.createClass({
	render: function () {
		return (
			<form className="login">
				Username
				<input type="text" name="username" placeholder="Username">
				Password
				<input type="text" name="password" placeholder="Password">
			</form>
		);
	}
});

/*
	######################################
	# Graphing Components                #
	######################################
*/

/*
	######################################
	# Social Wall Components             #
	######################################
*/

/*
	Post component
	Used to show wall posts
*/

/*
	Wall component
	Shows posts in walls
*/

/*
	Post Form Component
	This can be used to make posts on walls
*/

ReactDOM.render(

Hello, world!
,
	document.getElementById('example')
);<|endoftext|>