<commit_before>window.macommune = {};

macommune.Autocomplete = function( nav, id, parentId ) {
    this.nav = nav;
    this.id = id;
    this.parentId = parentId;
    var cache = {};

    this.init = function() {
        $( this.id ).autocomplete( {
	        source: this.getData,
          select: this.onSelect,
          appendTo: this.parentId,
          autoFocus: true,
        } );
        $( this.id ).keydown( function( event ) {
            if( event.keyCode == 13 ) {
              event.preventDefault();
            }
        } );
    };

    this.getData = function( request, response ) {
        var term = request.term;
        if ( term in cache ) {
            response( cache[ term ] );
            return;
        }
        $.get( '/api/complete/' + term ).then( function( data ) {
            cache[ term ] = data.values;
            response( data.values );
        } );
    };

    this.onSelect = function( event, ui ) {
        navigation.loadPage( ui.item.qid, ui.item.value, true );
    };

    return this.init();
};

macommune.Navigation = function() {
    this.qid = null;
    this.title = null;
    this.searchInput = $( '#search-input' );
    this.ui;
    this.mapSelector;
    this.autocomplete;

    var nav = this;

    this.init = function() {
        var params = nav.getParams();

        if ( nav.ui === undefined ) {
            nav.mapSelector = new macommune.MapSelector( nav );
            nav.autocomplete = new macommune.Autocomplete( nav, '#search-input', '#navbar' );
            nav.ui = new macommune.Ui( nav, params[ 0 ] );
            window.onpopstate = nav.onpopstate;
        }

        if ( params[ 0 ] === undefined || params[ 0 ] === '' ) {
            if ( nav.qid !== null ) {
                nav.resetView();
            }
            return;
        }
        nav.loadPage( params[ 0 ], params[ 1 ] );
    };

    this.loadPage = function( qid, title, changeHistory ) {
        nav.qid = qid;
        nav.title = title

        nav.ui.changePage( nav.qid, nav.fetchData() );

        nav.searchInput.val( decodeURI( title ) );
        document.title = decodeURI( title ) + " — Ma Commune Wikipédia";

        if ( changeHistory === true ) {
            history.pushState( { qid: nav.qid, title: nav.title }, '', '/' + nav.qid + '/' + nav.title );
        }
    }

    this.resetView = function() {
        nav.qid = null;
        nav.title = null;
        nav.ui.setHome();
    };

    this.fetchData = function() {
        return $.get( '/api/item/' + nav.qid )
    };

    this.getParams = function() {
        var params = window.location.pathname.split( '/' );
        params.shift();
        return params
    };

    this.onpopstate = function( event ) {
        if ( event.state === null ) {
            nav.init()
            return;
        }
        navigation.loadPage( event.state.qid, event.state.title );
    };

    return this.init();
};

macommune.Ui = function( nav, qid ) {
    this.nav = nav;
    this.qid = qid;
    this.data = null;
    this.homeVue;
    this.spinnerVue;
    this.blocs;

    var ui = this;

    this.init = function() {
        console.log( 'new UI initialised' )
        if ( ui.qid === undefined ) {
            ui.setHome();
        }

        ui.homeVue = new Vue({
          el: '#home',
          data: {
            visible: true
          }
        } );

        ui.spinnerVue = new Vue({
          el: '#spinner',
          data: {
            visible: false
          }
        } );

        ui.blocs = new macommune.Blocs();

        $( '.hidden' ).removeClass( 'hidden' );
    };

    this.setHome = function() {
        ui.spinnerVue.visible = false;
        ui.blocs.hideAll();

        ui.homeVue.visible = true;
    };

    this.changePage = function( qid, promise ) {
        ui.qid = qid;
        ui.setSpinner();
        promise.then( ui.setTimeline );
    };

    this.setSpinner = function() {
        ui.homeVue.visible = false;
        ui.blocs.hideAll();

        ui.spinnerVue.visible = true;
    };

    this.setTimeline = function( data ) {
        ui.data = data;

        ui.homeVue.visible = false;
        ui.spinnerVue.visible = false;

        if ( data.latlng.latitude !== undefined && data.latlng.longitude !== undefined ) {
            ui.nav.mapSelector.move( data.latlng.latitude, data.latlng.longitude );
        }

        ui.blocs.setAll( data );
    };

    return this.init();
};

function formatNumber (num) {
    return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1 ")
};

$( function() {
    window.navigation = new macommune.Navigation();
} );
<commit_msg>[frontend] fix display of the article's name when missing<commit_after>window.macommune = {};

macommune.Autocomplete = function( nav, id, parentId ) {
    this.nav = nav;
    this.id = id;
    this.parentId = parentId;
    var cache = {};

    this.init = function() {
        $( this.id ).autocomplete( {
	        source: this.getData,
          select: this.onSelect,
          appendTo: this.parentId,
          autoFocus: true,
        } );
        $( this.id ).keydown( function( event ) {
            if( event.keyCode == 13 ) {
              event.preventDefault();
            }
        } );
    };

    this.getData = function( request, response ) {
        var term = request.term;
        if ( term in cache ) {
            response( cache[ term ] );
            return;
        }
        $.get( '/api/complete/' + term ).then( function( data ) {
            cache[ term ] = data.values;
            response( data.values );
        } );
    };

    this.onSelect = function( event, ui ) {
        navigation.loadPage( ui.item.qid, ui.item.value, true );
    };

    return this.init();
};

macommune.Navigation = function() {
    this.qid = null;
    this.title = null;
    this.searchInput = $( '#search-input' );
    this.ui;
    this.mapSelector;
    this.autocomplete;

    var nav = this;

    this.init = function() {
        var params = nav.getParams();

        if ( nav.ui === undefined ) {
            nav.mapSelector = new macommune.MapSelector( nav );
            nav.autocomplete = new macommune.Autocomplete( nav, '#search-input', '#navbar' );
            nav.ui = new macommune.Ui( nav, params[ 0 ] );
            window.onpopstate = nav.onpopstate;
        }

        if ( params[ 0 ] === undefined || params[ 0 ] === '' ) {
            if ( nav.qid !== null ) {
                nav.resetView();
            }
            return;
        }
        nav.loadPage( params[ 0 ], params[ 1 ] );
    };

    this.loadPage = function( qid, title, changeHistory ) {
        nav.qid = qid;
        nav.title = title

        nav.ui.changePage( nav.qid, nav.fetchData() );

        if ( title !== undefined ) {
            nav.searchInput.val( decodeURI( title ) );
            document.title = decodeURI( title ) + " — Ma Commune Wikipédia";
        }
        else {
            nav.searchInput.val( '' );
            document.title = qid + " — Ma Commune Wikipédia";
        }

        if ( changeHistory === true ) {
            history.pushState( { qid: nav.qid, title: nav.title }, '', '/' + nav.qid + '/' + nav.title );
        }
    }

    this.resetView = function() {
        nav.qid = null;
        nav.title = null;
        nav.ui.setHome();
    };

    this.fetchData = function() {
        return $.get( '/api/item/' + nav.qid )
    };

    this.getParams = function() {
        var params = window.location.pathname.split( '/' );
        params.shift();
        return params
    };

    this.onpopstate = function( event ) {
        if ( event.state === null ) {
            nav.init()
            return;
        }
        navigation.loadPage( event.state.qid, event.state.title );
    };

    return this.init();
};

macommune.Ui = function( nav, qid ) {
    this.nav = nav;
    this.qid = qid;
    this.data = null;
    this.homeVue;
    this.spinnerVue;
    this.blocs;

    var ui = this;

    this.init = function() {
        console.log( 'new UI initialised' )
        if ( ui.qid === undefined ) {
            ui.setHome();
        }

        ui.homeVue = new Vue({
          el: '#home',
          data: {
            visible: true
          }
        } );

        ui.spinnerVue = new Vue({
          el: '#spinner',
          data: {
            visible: false
          }
        } );

        ui.blocs = new macommune.Blocs();

        $( '.hidden' ).removeClass( 'hidden' );
    };

    this.setHome = function() {
        ui.spinnerVue.visible = false;
        ui.blocs.hideAll();

        ui.homeVue.visible = true;
    };

    this.changePage = function( qid, promise ) {
        ui.qid = qid;
        ui.setSpinner();
        promise.then( ui.setTimeline );
    };

    this.setSpinner = function() {
        ui.homeVue.visible = false;
        ui.blocs.hideAll();

        ui.spinnerVue.visible = true;
    };

    this.setTimeline = function( data ) {
        ui.data = data;

        ui.homeVue.visible = false;
        ui.spinnerVue.visible = false;

        if ( data.latlng.latitude !== undefined && data.latlng.longitude !== undefined ) {
            ui.nav.mapSelector.move( data.latlng.latitude, data.latlng.longitude );
        }

        ui.blocs.setAll( data );
    };

    return this.init();
};

function formatNumber (num) {
    return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1 ")
};

$( function() {
    window.navigation = new macommune.Navigation();
} );
<|endoftext|>


<commit_before>const fs = require('fs-extra')
const path = require('path')
const cwd = process.cwd()

const dest = `${cwd}/src/models/`
const src = path.resolve(__dirname, '..', '..', '..', 'files/model.js')

module.exports = (names, options) => {
  fs.ensureDir(dest).then(() => {
    const copyAction = []
    names.forEach((name) => {
      copyAction.push(fs.copy(src, `${dest}${name}.js`))
    })
    return copyAction
  }).then(copyAction => Promise.all(copyAction).then(() => {

  }).catch(err => console.dir(err))
    ).catch(err => {
      if (err instanceof Error) throw err
      else throw new Error(err)
    })
}
<commit_msg>feat(replace): now replaces and 'model_name' within file<commit_after>const fs = require('fs-extra')
const path = require('path')
const replace = require('replace-in-file')
const cwd = process.cwd()

const dest = `${cwd}/src/models/`
const src = path.resolve(__dirname, '..', '..', '..', 'files/model.js')

module.exports = (names, options) => {
  fs.ensureDir(dest).then(() => {
    const copyAction = []
    names.forEach((name) => {
      copyAction.push(fs.copy(src, `${dest}${name}.js`))
    })
    return copyAction
  }).then(copyAction => Promise.all(copyAction).then(() => {
    const replaceAction = []
    names.forEach(name => {
      replaceAction.push(replace({
        files: `${dest}${name}.js`,
        from: /model_name/g,
        to: name
      }))
    })
    return replaceAction
  }).catch(err => new Error(err))
    .then(replaceAction => Promise.all(replaceAction).then(() => {
    })).catch(err => new Error(err)))
    .catch(err => {
      if (err instanceof Error) throw err
      else throw new Error(err)
    })
}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  Text,
} from 'react-native';
import { mapProps, compose } from 'recompose';
import PropTypes from 'prop-types';
import isEmpty from 'lodash/isEmpty';
import Header from '@modules/Header';
import FlexedView from '@primitives/FlexedView';
import withGive from '@data/withGive';
import ContributionForm from './ContributionForm';
import PersonalDetailsForm from './PersonalDetailsForm';

export class Now extends Component {
  static propTypes = {
    onSubmitContributionForm: PropTypes.func,
    onSubmitPersonalDetailsForm: PropTypes.func,
  };

  static defaultProps = {
    onSubmitContributionForm() {},
    onSubmitPersonalDetailsForm() {},
  };

  render() {
    return (
      <FlexedView>
        <Header titleText="Give Dashboard" />
        <Text>Step 1</Text>
        <ContributionForm
          onSubmit={this.props.onSubmitContributionForm}
        />

        <Text>Step 2</Text>
        <PersonalDetailsForm
          onSubmit={this.props.onSubmitPersonalDetailsForm}
        />

        <Text>Step 3 - Billing Address</Text>
        <Text>Step 4 - Payment Details (CC or ACH)</Text>
        <Text>Step 5 - Confirmation</Text>
        <Text>Step 6 - Thank you</Text>

      </FlexedView>
    );
  }
}

const enhance = compose(
  withGive,
  mapProps(props => ({
    ...props,
    onSubmitContributionForm(formValues) {
      // Should this be just one setter? This is the business logic
      props.resetContributions();
      props.addContribution(formValues.firstContribution);

      if (!isEmpty(formValues.secondContribution)) {
        props.addContribution(formValues.secondContribution);
      }
      props.setContributionFrequency(formValues.frequencyId);
      props.setContributionStartDate(formValues.startDate);
    },
    onSubmitPersonalDetailsForm(formValues) {
      console.log(formValues);
    },
  })),
);

export default enhance(Now);
<commit_msg>Use setBillingPerson<commit_after>import React, { Component } from 'react';
import {
  Text,
} from 'react-native';
import { mapProps, compose } from 'recompose';
import PropTypes from 'prop-types';
import isEmpty from 'lodash/isEmpty';
import Header from '@modules/Header';
import FlexedView from '@primitives/FlexedView';
import withGive from '@data/withGive';
import ContributionForm from './ContributionForm';
import PersonalDetailsForm from './PersonalDetailsForm';

export class Now extends Component {
  static propTypes = {
    onSubmitContributionForm: PropTypes.func,
    onSubmitPersonalDetailsForm: PropTypes.func,
  };

  static defaultProps = {
    onSubmitContributionForm() {},
    onSubmitPersonalDetailsForm() {},
  };

  render() {
    console.log(this.props);
    return (
      <FlexedView>
        <Header titleText="Give Dashboard" />
        <Text>Step 1</Text>
        <ContributionForm
          onSubmit={this.props.onSubmitContributionForm}
        />

        <Text>Step 2</Text>
        <PersonalDetailsForm
          onSubmit={this.props.onSubmitPersonalDetailsForm}
        />

        <Text>Step 3 - Billing Address</Text>
        <Text>Step 4 - Payment Details (CC or ACH)</Text>
        <Text>Step 5 - Confirmation</Text>
        <Text>Step 6 - Thank you</Text>

      </FlexedView>
    );
  }
}

const enhance = compose(
  withGive,
  mapProps(props => ({
    ...props,
    onSubmitContributionForm(formValues) {
      // Should this be just one setter? This is the business logic
      props.resetContributions();
      props.addContribution(formValues.firstContribution);

      if (!isEmpty(formValues.secondContribution)) {
        props.addContribution(formValues.secondContribution);
      }
      props.setContributionFrequency(formValues.frequencyId);
      props.setContributionStartDate(formValues.startDate);
    },
    onSubmitPersonalDetailsForm(formValues) {
      props.setBillingPerson(formValues);
    },
  })),
);

export default enhance(Now);
<|endoftext|>


<commit_before>/**
 * Test functions.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import {
	getTimeInSeconds,
	changeToPercent,
	prepareSecondsForDisplay,
	removeURLParameter,
	decodeHtmlEntity,
	readableLargeNumber,
	numberFormat,
	getDaysBetweenDates,
	getQueryParameter,
	extractForSparkline,
	getReAuthURL,
	fillFilterWithComponent,
	getSiteKitAdminURL,
	isFrontendIframeLoaded,
	validateJSON,
	validateOptimizeID,
	appendNotificationsCount,
	sendAnalyticsTrackingEvent,
	storageAvailable,
	findTagInHtmlContent,
	activateOrDeactivateModule,
	toggleConfirmModuleSettings,
	showErrorNotification,
} from 'GoogleUtil';
import data from 'GoogleComponents/data';
import md5 from 'md5';

const { setCache, getCache, deleteCache, invalidateCacheGroup, getCacheKey } = data;

window.googlesitekit = window.googlesitekit || {};

googlesitekit.testFunctions = {
	getTimeInSeconds,
	changeToPercent,
	prepareSecondsForDisplay,
	removeURLParameter,
	decodeHtmlEntity,
	readableLargeNumber,
	numberFormat,
	getDaysBetweenDates,
	getQueryParameter,
	extractForSparkline,
	getReAuthURL,
	fillFilterWithComponent,
	getSiteKitAdminURL,
	isFrontendIframeLoaded,
	validateJSON,
	validateOptimizeID,
	appendNotificationsCount,
	sendAnalyticsTrackingEvent,
	storageAvailable,
	findTagInHtmlContent,
	activateOrDeactivateModule,
	toggleConfirmModuleSettings,
	showErrorNotification,

	// Data functions.
	setCache,
	getCache,
	deleteCache,
	invalidateCacheGroup,
	getCacheKey,
};

googlesitekit.testUtilities = {
	md5,
};
<commit_msg>fix: Remove ununused import/export<commit_after>/**
 * Test functions.
 *
 * Site Kit by Google, Copyright 2019 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * External dependencies
 */
import {
	getTimeInSeconds,
	changeToPercent,
	prepareSecondsForDisplay,
	removeURLParameter,
	decodeHtmlEntity,
	readableLargeNumber,
	numberFormat,
	getDaysBetweenDates,
	getQueryParameter,
	extractForSparkline,
	getReAuthURL,
	fillFilterWithComponent,
	getSiteKitAdminURL,
	validateJSON,
	validateOptimizeID,
	appendNotificationsCount,
	sendAnalyticsTrackingEvent,
	storageAvailable,
	findTagInHtmlContent,
	activateOrDeactivateModule,
	toggleConfirmModuleSettings,
	showErrorNotification,
} from 'GoogleUtil';
import data from 'GoogleComponents/data';
import md5 from 'md5';

const { setCache, getCache, deleteCache, invalidateCacheGroup, getCacheKey } = data;

window.googlesitekit = window.googlesitekit || {};

googlesitekit.testFunctions = {
	getTimeInSeconds,
	changeToPercent,
	prepareSecondsForDisplay,
	removeURLParameter,
	decodeHtmlEntity,
	readableLargeNumber,
	numberFormat,
	getDaysBetweenDates,
	getQueryParameter,
	extractForSparkline,
	getReAuthURL,
	fillFilterWithComponent,
	getSiteKitAdminURL,
	validateJSON,
	validateOptimizeID,
	appendNotificationsCount,
	sendAnalyticsTrackingEvent,
	storageAvailable,
	findTagInHtmlContent,
	activateOrDeactivateModule,
	toggleConfirmModuleSettings,
	showErrorNotification,

	// Data functions.
	setCache,
	getCache,
	deleteCache,
	invalidateCacheGroup,
	getCacheKey,
};

googlesitekit.testUtilities = {
	md5,
};
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { FormattedHTMLMessage, FormattedMessage, defineMessages } from 'react-intl';
import MappedMessage from './MappedMessage';
import Message from './Message';
import Login from './Login';
import config from 'config';
import { stringHelper } from '../customHelpers';
import { capitalize } from '../helpers';

const messages = defineMessages({
  agreeTerms: {
    id: 'agree.terms',
    defaultMessage: 'By signing in, you agree to the Check {tosLink} and {ppLink}.',
  },
  loginSupport: {
    id: 'login.support',
    defaultMessage: 'For support contact check@meedan.com.',
  },
});

class LoginContainer extends Component {

  render() {
    return (

            <FormattedHTMLMessage id="browser.support.message" defaultMessage='Best viewed with Chrome for Desktop.' />

        <Message message={this.props.message} />

        <Login loginCallback={this.props.loginCallback} />

          { config.appName === 'check' ? [
              <FormattedMessage
                id="loginContainer.agreeTerms" defaultMessage={'By signing in, you agree to the {appName} {tosLink} and {ppLink}.'}
                values={{ appName: capitalize(config.appName), tosLink: <Link to=" /check/tos" className="login-container__footer-link"><FormattedMessage id="tos.title" defaultMessage="Terms of Service" /></Link>, ppLink: <Link to="/check/privacy" className="login-container__footer-link"><FormattedMessage id="privacy.policy.title" defaultMessage="Privacy Policy" /></Link> }}
              />
            ] : [
              <FormattedMessage
                id="loginContainer.agreeTerms" defaultMessage={'By signing in, you agree to the {appName} {tosLink} and {ppLink}.'}
                values={{ appName: capitalize(config.appName), tosLink: <FormattedMessage id="tos.title" defaultMessage="Terms of Service" />, ppLink: <FormattedMessage id="privacy.policy.title" defaultMessage="Privacy Policy" /> }}
              />
            ]
          }

          <FormattedHTMLMessage id="login.contactSupport" defaultMessage='For support contact {supportEmail}.' values={{supportEmail: config.supportEmail}} />

    );
  }
}

Login.propTypes = {
  loginCallback: PropTypes.func,
  message: PropTypes.string,
};

export default LoginContainer;
<commit_msg>Fix TOS and PP links to open blank tab<commit_after>import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { FormattedHTMLMessage, FormattedMessage, defineMessages } from 'react-intl';
import MappedMessage from './MappedMessage';
import Message from './Message';
import Login from './Login';
import config from 'config';
import { stringHelper } from '../customHelpers';
import { capitalize } from '../helpers';

const messages = defineMessages({
  agreeTerms: {
    id: 'agree.terms',
    defaultMessage: 'By signing in, you agree to the Check {tosLink} and {ppLink}.',
  },
  loginSupport: {
    id: 'login.support',
    defaultMessage: 'For support contact check@meedan.com.',
  },
});

class LoginContainer extends Component {

  render() {
    return (

            <FormattedHTMLMessage id="browser.support.message" defaultMessage='Best viewed with Chrome for Desktop.' />

        <Message message={this.props.message} />

        <Login loginCallback={this.props.loginCallback} />

          { config.appName === 'check' ? [
              <FormattedMessage
                id="loginContainer.agreeTerms" defaultMessage={'By signing in, you agree to the {appName} {tosLink} and {ppLink}.'}
                values={{
                  appName: capitalize(config.appName),
                  tosLink:
                    <Link to=" /check/tos" className="login-container__footer-link">
                      <FormattedMessage id="tos.title" defaultMessage="Terms of Service" />
                    </Link>,
                  ppLink:
                    <Link to="/check/privacy" className="login-container__footer-link">
                      <FormattedMessage id="privacy.policy.title" defaultMessage="Privacy Policy" />
                    </Link>
                }}
              />
            ] : [
              <FormattedMessage
                id="loginContainer.agreeTerms" defaultMessage={'By signing in, you agree to the {appName} {tosLink} and {ppLink}.'}
                values={{
                  appName: capitalize(config.appName),
                  tosLink:

                      <FormattedMessage id="tos.title" defaultMessage="Terms of Service" />
                    ,
                  ppLink:

                      <FormattedMessage id="privacy.policy.title" defaultMessage="Privacy Policy" />

                }}
              />
            ]
          }

          <FormattedHTMLMessage id="login.contactSupport" defaultMessage='For support contact {supportEmail}.' values={{supportEmail: config.supportEmail}} />

    );
  }
}

Login.propTypes = {
  loginCallback: PropTypes.func,
  message: PropTypes.string,
};

export default LoginContainer;
<|endoftext|>


<commit_before>import React from 'react';

import PrimaryCallToAction from '../../../shared/js/components/gironde.fr/PrimaryCallToAction';

/*
interface TotalAppetizerProps{
    total: number,
    year: number,
    totalUrl: total
}
 */

export default function ({total, year, exploreUrl}) {

    return React.createElement('div', { className: 'appetizer total-appetizer' },
        React.createElement('h1', {},
            React.createElement('div', {className: 'number'},
                total ? (total/Math.pow(10, 9)).toFixed(1).replace('.', ',') : ''
            ),
            React.createElement('div', {className: 'text'},
                `Milliards de dépenses en ${year}`
            )
        ),
        React.createElement('hr', {}),
        React.createElement('p', {},
            `Ce budget est composé de dépenses de fonctionnement, nécessaires aux missions et gestion des services de la collectivité, et de dépenses d’investissement dédiées à des programmes structurants ou stratégiques pour le territoire.`
        ),
        React.createElement(PrimaryCallToAction, { href: exploreUrl, text: 'Explorer le budget'})
    );
}
<commit_msg>Changement texte apetizer exploration des comptes<commit_after>import React from 'react';

import PrimaryCallToAction from '../../../shared/js/components/gironde.fr/PrimaryCallToAction';

/*
interface TotalAppetizerProps{
    total: number,
    year: number,
    totalUrl: total
}
 */

export default function ({total, year, exploreUrl}) {

    return React.createElement('div', { className: 'appetizer total-appetizer' },
        React.createElement('h1', {},
            React.createElement('div', {className: 'number'},
                total ? (total/Math.pow(10, 9)).toFixed(1).replace('.', ',') : ''
            ),
            React.createElement('div', {className: 'text'},
                `Milliards de dépenses en ${year}`
            )
        ),
        React.createElement('hr', {}),
        React.createElement('p', {},
            `Le département a dépensé 1,6 milliards d’euros pour les girondins en 2016. Explorez les comptes pour comprendre d’où vient cet argent, à quoi il sert et comment il a été dépensé.`
        ),
        React.createElement(PrimaryCallToAction, { href: exploreUrl, text: 'Explorer le budget'})
    );
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var extend = require('xtend');
var inherits = require('util').inherits;
var EventEmitter = require('./lib/event_emitter');

var errors = require('./errors');
var States = require('./reqres_states');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    assert(!channel.destroyed, 'refuse to create connection for destroyed channel');

    var self = this;
    EventEmitter.call(self);
    self.errorEvent = self.defineEvent('error');
    self.timedOutEvent = self.defineEvent('timedOut');
    self.spanEvent = self.defineEvent('span');

    self.closing = false;
    self.closeError = null;
    self.closeEvent = self.defineEvent('close');

    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: {},
        out: {}
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;

    self.startTime = self.timers.now();
    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }

    if (self.lastTimeoutTime) {
        self.timedOutEvent.emit(self);
        return;
    }

    var elapsed = self.timers.now() - self.startTime;
    if (!self.remoteName && elapsed >= self.channel.initTimeout) {
        self.timedOutEvent.emit(self, errors.TimeoutError({
            start: self.startTime,
            elapsed: elapsed,
            timeout: self.channel.initTimeout
        }));
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;
    self.closeError = err;
    self.closeEvent.emit(self, err);

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var logInfo = {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    };

    if (err.type.lastIndexOf('tchannel.socket', 0) < 0) {
        self.logger.warn('resetting connection', logInfo);
        self.errorEvent.emit(self, err);
    } else if (err.type !== 'tchannel.socket-closed') {
        logInfo.error = extend(err);
        logInfo.error.message = err.message;
        self.logger.info('resetting connection', logInfo);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        err = errors.TChannelConnectionResetError(err);
        req.errorEvent.emit(req, err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutRequest(options);

    return self._addOutReq(req);
};

TChannelConnectionBase.prototype._addOutReq = function _addOutReq(req) {
    var self = this;
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    req.errorEvent.on(onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        self.onReqError(req, err);
    }

    function runHandler() {
        self.runHandler(req);
    }
};

TChannelConnectionBase.prototype.onReqError = function onReqError(req, err) {
    var self = this;
    if (!req.res) self.buildResponse(req);
    if (err.type === 'tchannel.timeout') {
        req.res.sendError('Timeout', err.message);
    } else {
        var errName = err.name || err.constructor.name;
        req.res.sendError('UnexpectedError', errName + ': ' + err.message);
    }
};

TChannelConnectionBase.prototype.runHandler = function runHandler(req) {
    var self = this;
    self.channel.handler.handleRequest(req, buildResponse);
    function buildResponse(options) {
        return self.buildResponse(req, options);
    }
};

TChannelConnectionBase.prototype.buildResponse = function buildResponse(req, options) {
    var self = this;
    var done = false;
    if (req.res && req.res.state !== States.Initial) {
        self.errorEvent.emit(self, errors.ResponseAlreadyStarted({
            state: req.res.state
        }));
    }
    req.res = self.buildOutResponse(req, options);
    req.res.finishEvent.on(opDone);
    req.res.spanEvent.on(handleSpanFromRes);
    return req.res;

    function handleSpanFromRes(span) {
        self.spanEvent.emit(self, span);
    }

    function opDone() {
        if (done) return;
        done = true;
        self.onReqDone(req);
    }
};

TChannelConnectionBase.prototype.onReqDone = function onReqDone(req) {
    var self = this;
    // incoming req that timed out are already cleaned up
    if (self.requests.in[req.id] !== req && !req.timedOut) {
        self.logger.warn('mismatched onReqDone callback', {
            hostPort: self.channel.hostPort,
            hasInReq: self.requests.in[req.id] !== undefined,
            id: req.id
        });
    } else {
        delete self.requests.in[req.id];
        self.pending.in--;
    }
};

module.exports = TChannelConnectionBase;
<commit_msg>connection: gaurd against errors without types<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var assert = require('assert');
var extend = require('xtend');
var inherits = require('util').inherits;
var EventEmitter = require('./lib/event_emitter');

var errors = require('./errors');
var States = require('./reqres_states');

var DEFAULT_OUTGOING_REQ_TIMEOUT = 2000;

function TChannelConnectionBase(channel, direction, remoteAddr) {
    assert(!channel.destroyed, 'refuse to create connection for destroyed channel');

    var self = this;
    EventEmitter.call(self);
    self.errorEvent = self.defineEvent('error');
    self.timedOutEvent = self.defineEvent('timedOut');
    self.spanEvent = self.defineEvent('span');

    self.closing = false;
    self.closeError = null;
    self.closeEvent = self.defineEvent('close');

    self.channel = channel;
    self.options = self.channel.options;
    self.logger = channel.logger;
    self.random = channel.random;
    self.timers = channel.timers;
    self.direction = direction;
    self.remoteAddr = remoteAddr;
    self.timer = null;
    self.remoteName = null; // filled in by identify message

    // TODO: factor out an operation collection abstraction
    self.requests = {
        in: {},
        out: {}
    };
    self.pending = {
        in: 0,
        out: 0
    };

    self.lastTimeoutTime = 0;

    self.startTime = self.timers.now();
    self.startTimeoutTimer();
    self.tracer = self.channel.tracer;
}
inherits(TChannelConnectionBase, EventEmitter);

TChannelConnectionBase.prototype.close = function close(callback) {
    var self = this;
    self.resetAll(errors.SocketClosedError({reason: 'local close'}));
    callback();
};

// timeout check runs every timeoutCheckInterval +/- some random fuzz. Range is from
//   base - fuzz/2 to base + fuzz/2
TChannelConnectionBase.prototype.getTimeoutDelay = function getTimeoutDelay() {
    var self = this;
    var base = self.options.timeoutCheckInterval;
    var fuzz = self.options.timeoutFuzz;
    if (fuzz) {
        fuzz = Math.round(Math.floor(self.random() * fuzz) - (fuzz / 2));
    }
    return base + fuzz;
};

TChannelConnectionBase.prototype.startTimeoutTimer = function startTimeoutTimer() {
    var self = this;
    self.timer = self.timers.setTimeout(function onChannelTimeout() {
        // TODO: worth it to clear the fired self.timer objcet?
        self.onTimeoutCheck();
    }, self.getTimeoutDelay());
};

TChannelConnectionBase.prototype.clearTimeoutTimer = function clearTimeoutTimer() {
    var self = this;
    if (self.timer) {
        self.timers.clearTimeout(self.timer);
        self.timer = null;
    }
};

// If the connection has some success and some timeouts, we should probably leave it up,
// but if everything is timing out, then we should kill the connection.
TChannelConnectionBase.prototype.onTimeoutCheck = function onTimeoutCheck() {
    var self = this;
    if (self.closing) {
        return;
    }

    if (self.lastTimeoutTime) {
        self.timedOutEvent.emit(self);
        return;
    }

    var elapsed = self.timers.now() - self.startTime;
    if (!self.remoteName && elapsed >= self.channel.initTimeout) {
        self.timedOutEvent.emit(self, errors.TimeoutError({
            start: self.startTime,
            elapsed: elapsed,
            timeout: self.channel.initTimeout
        }));
    } else {
        self.checkTimeout(self.requests.out, 'out');
        self.checkTimeout(self.requests.in, 'in');
        self.startTimeoutTimer();
    }
};

TChannelConnectionBase.prototype.checkTimeout = function checkTimeout(ops, direction) {
    var self = this;
    var opKeys = Object.keys(ops);
    for (var i = 0; i < opKeys.length; i++) {
        var id = opKeys[i];
        var req = ops[id];
        if (req === undefined) {
            self.logger.warn('unexpected undefined request', {
                direction: direction,
                id: id
            });
        } else if (req.timedOut) {
            self.logger.warn('lingering timed-out request', {
                direction: direction,
                id: id
            });
            delete ops[id];
            self.pending[direction]--;
        } else if (req.checkTimeout()) {
            if (direction === 'out') {
                self.lastTimeoutTime = self.timers.now();
            // } else {
            //     req.res.sendError // XXX may need to build
            }
            delete ops[id];
            self.pending[direction]--;
        }
    }
};

// this connection is completely broken, and is going away
// In addition to erroring out all of the pending work, we reset the state in case anybody
// stumbles across this object in a core dump.
TChannelConnectionBase.prototype.resetAll = function resetAll(err) {
    var self = this;

    self.clearTimeoutTimer();

    if (self.closing) return;
    self.closing = true;
    self.closeError = err;
    self.closeEvent.emit(self, err);

    var inOpKeys = Object.keys(self.requests.in);
    var outOpKeys = Object.keys(self.requests.out);

    if (!err) {
        err = new Error('unknown connection reset'); // TODO typed error
    }

    var logInfo = {
        error: err,
        remoteName: self.remoteName,
        localName: self.channel.hostPort,
        numInOps: inOpKeys.length,
        numOutOps: outOpKeys.length,
        inPending: self.pending.in,
        outPending: self.pending.out
    };

    if (err.type && err.type.lastIndexOf('tchannel.socket', 0) < 0) {
        self.logger.warn('resetting connection', logInfo);
        self.errorEvent.emit(self, err);
    } else if (err.type !== 'tchannel.socket-closed') {
        logInfo.error = extend(err);
        logInfo.error.message = err.message;
        self.logger.info('resetting connection', logInfo);
    }

    // requests that we've received we can delete, but these reqs may have started their
    //   own outgoing work, which is hard to cancel. By setting this.closing, we make sure
    //   that once they do finish that their callback will swallow the response.
    inOpKeys.forEach(function eachInOp(id) {
        // TODO: support canceling pending handlers
        delete self.requests.in[id];
        // TODO report or handle or log errors or something
    });

    // for all outgoing requests, forward the triggering error to the user callback
    outOpKeys.forEach(function eachOutOp(id) {
        var req = self.requests.out[id];
        delete self.requests.out[id];
        err = errors.TChannelConnectionResetError(err);
        req.errorEvent.emit(req, err);
    });

    self.pending.in = 0;
    self.pending.out = 0;
};

TChannelConnectionBase.prototype.popOutReq = function popOutReq(id) {
    var self = this;
    var req = self.requests.out[id];
    if (!req) {
        // TODO else case. We should warn about an incoming response for an
        // operation we did not send out.  This could be because of a timeout
        // or could be because of a confused / corrupted server.
        return;
    }
    delete self.requests.out[id];
    self.pending.out--;
    return req;
};

// create a request
TChannelConnectionBase.prototype.request = function connBaseRequest(options) {
    var self = this;
    if (!options) options = {};
    options.remoteAddr = self.remoteAddr;

    // TODO: use this to protect against >4Mi outstanding messages edge case
    // (e.g. zombie operation bug, incredible throughput, or simply very long
    // timeout
    // assert(!self.requests.out[id], 'duplicate frame id in flight');
    // TODO: provide some sort of channel default for "service"
    // TODO: generate tracing if empty?
    // TODO: refactor callers
    options.checksumType = options.checksum;

    // TODO: better default, support for dynamic
    options.ttl = options.timeout || DEFAULT_OUTGOING_REQ_TIMEOUT;
    options.tracer = self.tracer;
    var req = self.buildOutRequest(options);

    return self._addOutReq(req);
};

TChannelConnectionBase.prototype._addOutReq = function _addOutReq(req) {
    var self = this;
    self.requests.out[req.id] = req;
    self.pending.out++;
    return req;
};

TChannelConnectionBase.prototype.handleCallRequest = function handleCallRequest(req) {
    var self = this;
    req.remoteAddr = self.remoteName;
    self.pending.in++;
    self.requests.in[req.id] = req;
    req.errorEvent.on(onReqError);
    process.nextTick(runHandler);

    function onReqError(err) {
        self.onReqError(req, err);
    }

    function runHandler() {
        self.runHandler(req);
    }
};

TChannelConnectionBase.prototype.onReqError = function onReqError(req, err) {
    var self = this;
    if (!req.res) self.buildResponse(req);
    if (err.type === 'tchannel.timeout') {
        req.res.sendError('Timeout', err.message);
    } else {
        var errName = err.name || err.constructor.name;
        req.res.sendError('UnexpectedError', errName + ': ' + err.message);
    }
};

TChannelConnectionBase.prototype.runHandler = function runHandler(req) {
    var self = this;
    self.channel.handler.handleRequest(req, buildResponse);
    function buildResponse(options) {
        return self.buildResponse(req, options);
    }
};

TChannelConnectionBase.prototype.buildResponse = function buildResponse(req, options) {
    var self = this;
    var done = false;
    if (req.res && req.res.state !== States.Initial) {
        self.errorEvent.emit(self, errors.ResponseAlreadyStarted({
            state: req.res.state
        }));
    }
    req.res = self.buildOutResponse(req, options);
    req.res.finishEvent.on(opDone);
    req.res.spanEvent.on(handleSpanFromRes);
    return req.res;

    function handleSpanFromRes(span) {
        self.spanEvent.emit(self, span);
    }

    function opDone() {
        if (done) return;
        done = true;
        self.onReqDone(req);
    }
};

TChannelConnectionBase.prototype.onReqDone = function onReqDone(req) {
    var self = this;
    // incoming req that timed out are already cleaned up
    if (self.requests.in[req.id] !== req && !req.timedOut) {
        self.logger.warn('mismatched onReqDone callback', {
            hostPort: self.channel.hostPort,
            hasInReq: self.requests.in[req.id] !== undefined,
            id: req.id
        });
    } else {
        delete self.requests.in[req.id];
        self.pending.in--;
    }
};

module.exports = TChannelConnectionBase;
<|endoftext|>


<commit_before>const {createServer} = require("http")
const path = require("path"), fs = require("fs")
const {parse: parseURL} = require("url")

const ModuleServer = require("moduleserve/moduleserver")
const {handleCollabRequest} = require("./collab/server/server")
const ecstatic = require("ecstatic")
const {buildFile} = require("./build/buildfile")
const tariff = require("tariff")

let port = 8000
const root = path.resolve(__dirname, "../public/")

function usage(status) {
  console.log("Usage: demoserver [--port PORT] [--help]")
  process.exit(status)
}

for (let i = 2; i < process.argv.length; i++) {
  let arg = process.argv[i]
  if (arg == "--port") port = +process.argv[++i]
  else if (arg == "--help") usage(0)
  else usage(1)
}

let moduleServer = new ModuleServer({
  root,
  transform(_, content) { return tariff(content) }
})
let fileServer = ecstatic({root: root})

function transformPage(req, resp) {
  let path = parseURL(req.url).pathname
  let dir = /\/([^\.\/]+)?$/.exec(path)
  if (dir) path = (dir[1] ? path : path.slice(0, -1)) + "/index.html"

  if (!/\.html$/.test(path)) return false

  let file = __dirname + "/../pages" + path, mdFile = file.replace(/\.html$/, ".md")
  if (!fs.existsSync(file) && fs.existsSync(mdFile)) file = mdFile
  let text = buildFile(file).replace(
    /<script src="[^"]*prosemirror\.js"><\/script>\s+<script src="([^"]*\.js)"><\/script>/,
    function(_, script) {
      let base = path.replace(/\/[^\/]+$|^\//g, ""), full = /^\//.test(script) ? script : base + "/" + script
      let up = base ? base.replace(/[^\/]+/g, "..") + "/" : ""
      return `<script src="/moduleserve/load.js" data-module="./${up}../pages/${full}" data-require></script>`
    }
  )
  resp.writeHead(200, {"Content-Type": "text/html"})
  resp.end(text)
  return true
}

function maybeCollab(req, resp) {
  let url = req.url, backend = url.replace(/\/collab-backend\b/, "")
  if (backend != url) {
    req.url = backend
    if (handleCollabRequest(req, resp)) return true
    req.url = url
  }
  return false
}

createServer((req, resp) => {
  maybeCollab(req, resp) ||
    moduleServer.handleRequest(req, resp) ||
    transformPage(req, resp) ||
    fileServer(req, resp)
}).listen(port)

console.log("Demo server listening on port " + port)
<commit_msg>Make devserver work with changed file layout<commit_after>const {createServer} = require("http")
const path = require("path"), fs = require("fs")
const {parse: parseURL} = require("url")

const ModuleServer = require("moduleserve/moduleserver")
const {handleCollabRequest} = require("./collab/server/server")
const ecstatic = require("ecstatic")
const {buildFile} = require("./build/buildfile")
const tariff = require("tariff")

let port = 8000
const root = path.resolve(__dirname, "../public/")

function usage(status) {
  console.log("Usage: demoserver [--port PORT] [--help]")
  process.exit(status)
}

for (let i = 2; i < process.argv.length; i++) {
  let arg = process.argv[i]
  if (arg == "--port") port = +process.argv[++i]
  else if (arg == "--help") usage(0)
  else usage(1)
}

let moduleServer = new ModuleServer({
  root,
  transform(_, content) { return tariff(content) }
})
let fileServer = ecstatic({root: root})

function transformPage(req, resp) {
  let path = parseURL(req.url).pathname
  let dir = /\/([^\.\/]+)?$/.exec(path)
  if (dir) path = (dir[1] ? path : path.slice(0, -1)) + "/index.html"

  if (!/\.html$/.test(path)) return false

  let file = __dirname + "/../pages" + path, mdFile = file.replace(/\.html$/, ".md")
  if (!fs.existsSync(file) && fs.existsSync(mdFile)) file = mdFile
  let text = buildFile(file).replace(
    /<script src="[^"]*prosemirror\.js"><\/script>\s+<script src="([^"]*\.js)"><\/script>/,
    function(_, script) {
      let base = path.replace(/\/[^\/]+$|^\//g, ""), full = /^\//.test(script) ? script : base + "/" + script
      let name = /\/([^\/]*?)\/example\.js/.exec(full)
      let up = base ? base.replace(/[^\/]+/g, "..") + "/" : ""
      return `<script src="/moduleserve/load.js" data-module="./${up}../example/${name[1]}/index.js" data-require></script>`
    }
  )
  resp.writeHead(200, {"Content-Type": "text/html"})
  resp.end(text)
  return true
}

function maybeCollab(req, resp) {
  let url = req.url, backend = url.replace(/\/collab-backend\b/, "")
  if (backend != url) {
    req.url = backend
    if (handleCollabRequest(req, resp)) return true
    req.url = url
  }
  return false
}

createServer((req, resp) => {
  maybeCollab(req, resp) ||
    moduleServer.handleRequest(req, resp) ||
    transformPage(req, resp) ||
    fileServer(req, resp)
}).listen(port)

console.log("Demo server listening on port " + port)
<|endoftext|>


<commit_before>method.exports = function (data) {
	var type = typeof data;

	if (type === 'string') {
		this.setHeader('Content-Type', 'text/html');
		this.end(data);
	} else if (type === 'object') {
		if (Buffer.isBuffer(data)) {
			this.setHeader('Content-Type', 'application/octet-stream');
		} else {
			this.setHeader('Content-Type', 'application/json');
		}
		this.end(data);
	}
};

// res.send = function send(body) {
//   var chunk = body;
//   var encoding;
//   var len;
//   var req = this.req;
//   var type;

//   // settings
//   var app = this.app;

//   switch (typeof chunk) {
//     // string defaulting to html
//     case 'string':
//       if (!this.get('Content-Type')) {
//         this.type('html');
//       }
//       break;
//     case 'boolean':
//     case 'number':
//     case 'object':
//       if (chunk === null) {
//         chunk = '';
//       } else if (Buffer.isBuffer(chunk)) {
//         if (!this.get('Content-Type')) {
//           this.type('bin');
//         }
//       } else {
//         return this.json(chunk);
//       }
//       break;
//   }

//   // write strings in utf-8
//   if (typeof chunk === 'string') {
//     encoding = 'utf8';
//     type = this.get('Content-Type');

//     // reflect this in content-type
//     if (typeof type === 'string') {
//       this.set('Content-Type', setCharset(type, 'utf-8'));
//     }
//   }

//   // populate Content-Length
//   if (chunk !== undefined) {
//     if (!Buffer.isBuffer(chunk)) {
//       // convert chunk to Buffer; saves later double conversions
//       chunk = new Buffer(chunk, encoding);
//       encoding = undefined;
//     }

//     len = chunk.length;
//     this.set('Content-Length', len);
//   }

//   // method check
//   var isHead = req.method === 'HEAD';

//   // ETag support
//   if (len !== undefined && (isHead || req.method === 'GET')) {
//     var etag = app.get('etag fn');
//     if (etag && !this.get('ETag')) {
//       etag = etag(chunk, encoding);
//       etag && this.set('ETag', etag);
//     }
//   }

//   // freshness
//   if (req.fresh) this.statusCode = 304;

//   // strip irrelevant headers
//   if (204 == this.statusCode || 304 == this.statusCode) {
//     this.removeHeader('Content-Type');
//     this.removeHeader('Content-Length');
//     this.removeHeader('Transfer-Encoding');
//     chunk = '';
//   }

//   if (isHead) {
//     // skip body for HEAD
//     this.end();
//   } else {
//     // respond
//     this.end(chunk, encoding);
//   }

//   return this;
// };<commit_msg>more work on #41 clearing out the cruft from the express code<commit_after>method.exports = function (data) {
	var type = typeof data;

	if (type === 'string') {
		this.setHeader('Content-Type', 'text/html');
		this.end(data);
	} else if (type === 'object') {
		if (Buffer.isBuffer(data)) {
			this.setHeader('Content-Type', 'application/octet-stream');
		} else {
			this.setHeader('Content-Type', 'application/json');
		}
		this.end(data);
	}
};

// res.send = function send(body) {
//   var chunk = body;
//   var encoding;
//   var len;
//   var req = this.req;
//   var type;

//   switch (typeof chunk) {
//     // string defaulting to html
//     case 'string':
//       if (!this.get('Content-Type')) {
//         this.type('html');
//       }
//       break;
//     case 'boolean':
//     case 'number':
//     case 'object':
//       if (chunk === null) {
//         chunk = '';
//       } else if (Buffer.isBuffer(chunk)) {
//         if (!this.get('Content-Type')) {
//           this.type('bin');
//         }
//       } else {
//         return this.json(chunk);
//       }
//       break;
//   }

//   // write strings in utf-8
//   if (typeof chunk === 'string') {
//     encoding = 'utf8';
//     type = this.get('Content-Type');

//     // reflect this in content-type
//     if (typeof type === 'string') {
//       this.set('Content-Type', setCharset(type, 'utf-8'));
//     }
//   }

//   // populate Content-Length
//   if (chunk !== undefined) {
//     if (!Buffer.isBuffer(chunk)) {
//       // convert chunk to Buffer; saves later double conversions
//       chunk = new Buffer(chunk, encoding);
//       encoding = undefined;
//     }

//     len = chunk.length;
//     this.set('Content-Length', len);
//   }

//   // freshness
//   if (req.fresh) this.statusCode = 304;

//   // strip irrelevant headers
//   if (204 == this.statusCode || 304 == this.statusCode) {
//     this.removeHeader('Content-Type');
//     this.removeHeader('Content-Length');
//     this.removeHeader('Transfer-Encoding');
//     chunk = '';
//   }

//   if (req.method === 'HEAD') {
//     // skip body for HEAD
//     this.end();
//   } else {
//     // respond
//     this.end(chunk, encoding);
//   }

//   return this;
// };<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';

// Reworking the data triggers to use buttons doesn't seem worth it to avoid an eslint warning.
/* eslint-disable jsx-a11y/anchor-is-valid, react/prefer-stateless-function */
export default class Footer extends React.Component {
    render() {
        const session = this.context.session;
        const disabled = !session;
        let userActionRender;

        if (!(session && session['auth.userid'])) {
            userActionRender = Submitter sign-in;
        } else {
            userActionRender = Submitter sign out;
        }
        return (

{this.props.version}

Contact

Terms of Use

{userActionRender}

©{new Date().getFullYear()} Stanford University.

        );
    }
}
/* eslint-enable jsx-a11y/anchor-is-valid, react/prefer-stateless-function */

Footer.contextTypes = {
    session: PropTypes.object,
};

Footer.propTypes = {
    version: PropTypes.string, // App version number
};

Footer.defaultProps = {
    version: '',
};
<commit_msg>ENCD-4045 Add privacy link to footer (#2267)<commit_after>import React from 'react';
import PropTypes from 'prop-types';

/* eslint-disable jsx-a11y/anchor-is-valid */
const Footer = ({ version }, reactContext) => {
    const session = reactContext.session;
    const disabled = !session;
    let userActionRender;

    if (!(session && session['auth.userid'])) {
        userActionRender = Submitter sign-in;
    } else {
        userActionRender = Submitter sign out;
    }
    return (

{version}

Privacy

Contact

Terms of Use

{userActionRender}

©{new Date().getFullYear()} Stanford University.

    );
};
/* eslint-enable jsx-a11y/anchor-is-valid */

Footer.contextTypes = {
    session: PropTypes.object,
};

Footer.propTypes = {
    version: PropTypes.string, // App version number
};

Footer.defaultProps = {
    version: '',
};

export default Footer;
<|endoftext|>


<commit_before>/**
 * @name		Multiselect Module
 * @description	Select multiple albums or photos.
 * @author		Tobias Reich
 * @copyright	2014 by Tobias Reich
 */

multiselect = {

	position: {

		top: null,
		right: null,
		bottom: null,
		left: null

	},

	show: function(e) {

		if (mobileBrowser()) return false;
		if ($('.album:hover, .photo:hover').length!=0) return false;
		if (visible.multiselect()) $('#multiselect').remove();

		multiselect.position.top = e.pageY;
		multiselect.position.right = -1 * (e.pageX - $(document).width());
		multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height());
		multiselect.position.left = e.pageX;

		$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left));
		$(document).on('mousemove', multiselect.resize);

	},

	resize: function(e) {

		var mouse_x = e.pageX,
			mouse_y = e.pageY,
			newHeight,
			newWidth;

		if (multiselect.position.top===null||
			multiselect.position.right===null||
			multiselect.position.bottom===null||
			multiselect.position.left===null) return false;

		if (mouse_y>=multiselect.position.top) {

			// Do not leave the screen
			newHeight = e.pageY - multiselect.position.top;
			if ((multiselect.position.top+newHeight)>=$(document).height())
				newHeight -= (multiselect.position.top + newHeight) - $(document).height() + 2;

			$('#multiselect').css({
				top: multiselect.position.top,
				bottom: 'inherit',
				height: newHeight
			});

		} else {

			$('#multiselect').css({
				top: 'inherit',
				bottom: multiselect.position.bottom,
				height: multiselect.position.top - e.pageY
			});

		}

		if (mouse_x>=multiselect.position.left) {

			// Do not leave the screen
			newWidth = e.pageX - multiselect.position.left;
			if ((multiselect.position.left+newWidth)>=$(document).width())
				newWidth -= (multiselect.position.left + newWidth) - $(document).width() + 2;

			$('#multiselect').css({
				right: 'inherit',
				left: multiselect.position.left,
				width: newWidth
			});

		} else {

			$('#multiselect').css({
				right: multiselect.position.right,
				left: 'inherit',
				width: multiselect.position.left - e.pageX
			});

		}

	},

	stopResize: function() {

		$(document).off('mousemove');

	},

	getSize: function() {

		if (!visible.multiselect()) return false;

		return {
			top: $('#multiselect').offset().top,
			left: $('#multiselect').offset().left,
			width: parseInt($('#multiselect').css('width').replace('px', '')),
			height: parseInt($('#multiselect').css('height').replace('px', ''))
		}

	},

	getSelection: function(e) {

		var id,
			ids = [],
			offset,
			size = multiselect.getSize();

		if (visible.contextMenu()) return false;
		if (!visible.multiselect()) return false;

		$('.photo, .album').each(function() {

			offset = $(this).offset();

			if (offset.top>=size.top&&
				offset.left>=size.left&&
				(offset.top+206)<=(size.top+size.height)&&
				(offset.left+206)<=(size.left+size.width)) {

					id = $(this).data('id');

					if (id!=="0"&&id!==0&&id!=="f"&&id!=="s"&&id!==null&id!==undefined) {

						ids.push(id);
						$(this).addClass('active');

					}

				}

		});

		if (ids.length!=0&&visible.album()) contextMenu.photoMulti(ids, e);
		else if (ids.length!=0&&visible.albums()) contextMenu.albumMulti(ids, e);
		else multiselect.close();

	},

	close: function() {

		multiselect.stopResize();

		multiselect.position.top = null;
		multiselect.position.right = null;
		multiselect.position.bottom = null;
		multiselect.position.left = null;

		lychee.animate('#multiselect', "fadeOut");
		setTimeout(function() {
			$('#multiselect').remove();
		}, 300);

	}

}<commit_msg>Tolerance for multiselect<commit_after>/**
 * @name		Multiselect Module
 * @description	Select multiple albums or photos.
 * @author		Tobias Reich
 * @copyright	2014 by Tobias Reich
 */

multiselect = {

	position: {

		top: null,
		right: null,
		bottom: null,
		left: null

	},

	show: function(e) {

		if (mobileBrowser()) return false;
		if ($('.album:hover, .photo:hover').length!=0) return false;
		if (visible.multiselect()) $('#multiselect').remove();

		multiselect.position.top = e.pageY;
		multiselect.position.right = -1 * (e.pageX - $(document).width());
		multiselect.position.bottom = -1 * (multiselect.position.top - $(window).height());
		multiselect.position.left = e.pageX;

		$('body').append(build.multiselect(multiselect.position.top, multiselect.position.left));
		$(document).on('mousemove', multiselect.resize);

	},

	resize: function(e) {

		var mouse_x = e.pageX,
			mouse_y = e.pageY,
			newHeight,
			newWidth;

		if (multiselect.position.top===null||
			multiselect.position.right===null||
			multiselect.position.bottom===null||
			multiselect.position.left===null) return false;

		if (mouse_y>=multiselect.position.top) {

			// Do not leave the screen
			newHeight = e.pageY - multiselect.position.top;
			if ((multiselect.position.top+newHeight)>=$(document).height())
				newHeight -= (multiselect.position.top + newHeight) - $(document).height() + 2;

			$('#multiselect').css({
				top: multiselect.position.top,
				bottom: 'inherit',
				height: newHeight
			});

		} else {

			$('#multiselect').css({
				top: 'inherit',
				bottom: multiselect.position.bottom,
				height: multiselect.position.top - e.pageY
			});

		}

		if (mouse_x>=multiselect.position.left) {

			// Do not leave the screen
			newWidth = e.pageX - multiselect.position.left;
			if ((multiselect.position.left+newWidth)>=$(document).width())
				newWidth -= (multiselect.position.left + newWidth) - $(document).width() + 2;

			$('#multiselect').css({
				right: 'inherit',
				left: multiselect.position.left,
				width: newWidth
			});

		} else {

			$('#multiselect').css({
				right: multiselect.position.right,
				left: 'inherit',
				width: multiselect.position.left - e.pageX
			});

		}

	},

	stopResize: function() {

		$(document).off('mousemove');

	},

	getSize: function() {

		if (!visible.multiselect()) return false;

		return {
			top: $('#multiselect').offset().top,
			left: $('#multiselect').offset().left,
			width: parseInt($('#multiselect').css('width').replace('px', '')),
			height: parseInt($('#multiselect').css('height').replace('px', ''))
		}

	},

	getSelection: function(e) {

		var tolerance = 150,
			id,
			ids = [],
			offset,
			size = multiselect.getSize();

		if (visible.contextMenu()) return false;
		if (!visible.multiselect()) return false;

		$('.photo, .album').each(function() {

			offset = $(this).offset();

			if (offset.top>=(size.top-tolerance)&&
				offset.left>=(size.left-tolerance)&&
				(offset.top+206)<=(size.top+size.height+tolerance)&&
				(offset.left+206)<=(size.left+size.width+tolerance)) {

					id = $(this).data('id');

					if (id!=="0"&&id!==0&&id!=="f"&&id!=="s"&&id!==null&id!==undefined) {

						ids.push(id);
						$(this).addClass('active');

					}

				}

		});

		if (ids.length!=0&&visible.album()) contextMenu.photoMulti(ids, e);
		else if (ids.length!=0&&visible.albums()) contextMenu.albumMulti(ids, e);
		else multiselect.close();

	},

	close: function() {

		multiselect.stopResize();

		multiselect.position.top = null;
		multiselect.position.right = null;
		multiselect.position.bottom = null;
		multiselect.position.left = null;

		lychee.animate('#multiselect', "fadeOut");
		setTimeout(function() {
			$('#multiselect').remove();
		}, 300);

	}

}<|endoftext|>


<commit_before>angular.module('$strap.directives')

.factory('$modal', ['$rootScope', '$compile', '$http', '$timeout', '$q', '$templateCache', function($rootScope, $compile, $http, $timeout, $q, $templateCache) {

  var ModalFactory = function ModalFactory(options) {

    function Modal(options) {
      if(!options) options = {};

      var scope = options.scope || $rootScope.$new(),
          templateUrl = options.template;

      //@todo support {title, content} object

      return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, {cache: true}).then(function(res) { return res.data; }))
      .then(function onSuccess(template) {

        // Build modal object
        var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, "-") + '-' + scope.$id;
        var $modal = $('

').attr('id', id).addClass('fade').html(template);
        if(options.modalClass) $modal.addClass(options.modalClass);

        $('body').append($modal);

        // Compile modal content
        $timeout(function() {
          $compile($modal)(scope);
        });

        // Provide scope display functions
        scope.$modal = function(name) {
          $modal.modal(name);
        };
        scope.hide = function() {
          $modal.modal('hide');
        };
        scope.show = function() {
          $modal.modal('show');
        };
        scope.dismiss = scope.hide;

        // Emit modal events
        angular.forEach(['show', 'shown', 'hide', 'hidden'], function(name) {
          $modal.on(name, function(ev) {
            scope.$emit('modal-' + name, ev);
          });
        });

        // Support autofocus attribute
        $modal.on('shown', function(event) {
          $('input[autofocus]', $modal).first().trigger('focus');
        });

        if(options.show) {
          $modal.modal('show');
        }

        return $modal;

      });

    }

    return new Modal(options);

  };

  return ModalFactory;

}])

.directive('bsModal', ['$q', '$modal', function($q, $modal) {
  'use strict';

  return {
    restrict: 'A',
    scope: true,
    link: function postLink(scope, iElement, iAttrs, controller) {

      var options = {
        template: scope.$eval(iAttrs.bsModal),
        scope: scope,
        modalClass: iAttrs.modalClass || '',
        backdrop: iAttrs.backdrop*1 || true,
        keyboard: iAttrs.keyboard*1 || true,
        show: iAttrs.show*1 || false
      };

      $q.when($modal(options)).then(function onSuccess(modal) {
        iElement.attr('data-target', '#' + modal.attr('id')).attr('data-toggle', 'modal');
      });

    }
  };
}]);
<commit_msg>chore(modal): cleanup<commit_after>angular.module('$strap.directives')

.factory('$modal', ['$rootScope', '$compile', '$http', '$timeout', '$q', '$templateCache', function($rootScope, $compile, $http, $timeout, $q, $templateCache) {

  var ModalFactory = function ModalFactory(options) {

    function Modal(options) {
      if(!options) options = {};

      var scope = options.scope || $rootScope.$new(),
          templateUrl = options.template;

      //@todo support {title, content} object

      return $q.when($templateCache.get(templateUrl) || $http.get(templateUrl, {cache: true}).then(function(res) { return res.data; }))
      .then(function onSuccess(template) {

        // Build modal object
        var id = templateUrl.replace('.html', '').replace(/[\/|\.|:]/g, "-") + '-' + scope.$id;
        var $modal = $('

').attr('id', id).addClass('fade').html(template);
        if(options.modalClass) $modal.addClass(options.modalClass);

        $('body').append($modal);

        // Compile modal content
        $timeout(function() {
          $compile($modal)(scope);
        });

        // Provide scope display functions
        scope.$modal = function(name) {
          $modal.modal(name);
        };
        angular.forEach(['show', 'hide'], function(name) {
          scope[name] = function() {
            $modal.modal(name);
          };
        });

        // @deprecated
        scope.dismiss = function() {
          console.warn('[DEPRECATED] $strap: scope.dissmiss is deprecated, please use scope.hide() or scope.$modal("hide")');
          return scope.hide();
        };

        // Emit modal events
        angular.forEach(['show', 'shown', 'hide', 'hidden'], function(name) {
          $modal.on(name, function(ev) {
            scope.$emit('modal-' + name, ev);
          });
        });

        // Support autofocus attribute
        $modal.on('shown', function(event) {
          $('input[autofocus]', $modal).first().trigger('focus');
        });

        if(options.show) {
          $modal.modal('show');
        }

        return $modal;

      });

    }

    return new Modal(options);

  };

  return ModalFactory;

}])

.directive('bsModal', ['$q', '$modal', function($q, $modal) {
  'use strict';

  return {
    restrict: 'A',
    scope: true,
    link: function postLink(scope, iElement, iAttrs, controller) {

      var options = {
        template: scope.$eval(iAttrs.bsModal),
        scope: scope,
        modalClass: iAttrs.modalClass || '',
        backdrop: iAttrs.backdrop*1 || true,
        keyboard: iAttrs.keyboard*1 || true,
        show: iAttrs.show*1 || false
      };

      $q.when($modal(options)).then(function onSuccess(modal) {
        iElement.attr('data-target', '#' + modal.attr('id')).attr('data-toggle', 'modal');
      });

    }
  };
}]);
<|endoftext|>


<commit_before>(function (window, angular) {
  'use strict';

  angular.module('angular-server-form', [])
  .provider('serverForm', [function () {

    var provider = this;

    provider.errorsKey = 'errors';
    provider.logging = true;
    provider.$get = ['$http', '$q', '$log', function ($http, $q, $log) {

      // Private
      // Set error message on form value
      function setError (form, message) {
        // Only NgModelController has $render and $setViewValue
        // Don't call $setValidity on FormController
        if (form.$render || form.$setViewValue) {
          form.$setValidity('server', false);
        }
        form.$setPristine();
        form.$server = message;
      }

      var self = this;

      // Serialize form to data object
      self.serialize = function serialize (form) {

        // recurse over the form
        // if the property is a value it gets assigned
        // otherwise dive inside
        function formCrawler (form) {
          var response = {};
          for (var prop in form) {
            if (form.hasOwnProperty(prop) && prop[0] !== '$') {
              if (form[prop].hasOwnProperty('$modelValue')) {
                response[prop] = form[prop].$modelValue;
              } else {
                response[prop] = formCrawler(form[prop]);
              }
            }
          }
          return response;
        }

        var response = {};

        // set root if form name is set
        if (form.$name) {
          response[form.$name] = formCrawler(form);
        } else {
          response = formCrawler(form);
        }
        return response;
      };

      // Apply all messages from errors object on form
      // The error value must be an array of strings or a string
      self.applyErrors = function applyErrors (form, errors) {
        if (angular.isString(errors)) {
          // If it's a string then set the error message
          setError(form, errors);
        } else if (angular.isArray(errors)) {
          // If it's an array then join them and set the error message
          setError(form, errors.join(', '));
        } else {
          // Otherwise we must crawl the errors
          for (var prop in errors) {
            if (errors.hasOwnProperty(prop)) {
              if (form.hasOwnProperty(prop)) {
                // If the form has a property with the same error name
                applyErrors(form[prop], errors[prop]);
              } else if (form.$name === prop) {
                // errors object is for the whole form, dive in
                applyErrors(form, errors[prop]);
              } else {
                if (provider.logging) {
                  $log.warn('No place to render property', prop, 'in form', form);
                }
              }
            }
          }
        }
      };

      // Crawl form and reset errors
      self.clearErrors = function clearErrors (form) {
        // Only NgModelController has $render and $setViewValue
        // Don't call $setValidity on FormController
        if (form.$render || form.$setViewValue) {
          form.$setValidity('server', true);
        }
        form.$setPristine();
        form.$server = '';

        for (var prop in form) {
          if (form.hasOwnProperty(prop) && prop[0] !== '$') {
            clearErrors(form[prop]);
          }
        }
      };

      self.submit = function submit (form, config) {
        self.clearErrors(form); // resets previous server errors
        form.$submitting = true;
        form.$saved = false;

        return $http(config)
          .success(function () {
            form.$saved = true;
          })
          .error(function(res, status) {
            form.$saved = false;
            if (status === 422) {
              if (provider.errorsKey) {
                self.applyErrors(form, res[provider.errorsKey]);
              } else {
                self.applyErrors(form, res);
              }
            }
          })
          .finally(function () {
            form.$submitting = false;
          });
      };

      return self;
    }];
  }])

  .directive('serverForm', ['serverForm', function (serverForm) {

    return {
      restrict: 'A',
      scope: {
        url: '@',
        method: '@',
        onSuccess: '=',
        onError: '='
      },
      require: 'form',
      link: function postLink(scope, iElement, iAttrs, form) {

        function submitForm (ev) {
          if (!submitting) {
            var config = {
              url: scope.url,
              method: scope.method ? scope.method : 'POST',
              data: serverForm.serialize(form)
            };

            submitting = true;

            serverForm.submit(form, config)
            .then(scope.onSuccess, scope.onError)
            .finally(function () {
              submitting = false;
            });
          }

          ev.preventDefault();
          scope.$apply();
        }

        var submitting = false;

        iElement.on('submit', submitForm);
        scope.$on('destroy', function () {
          iElement.off('submit', submitForm);
        });

      }
    };
  }]);

})(window, window.angular);
<commit_msg>refactor(serverForm): move static functions<commit_after>(function (window, angular) {
  'use strict';

  angular.module('angular-server-form', [])
  .provider('serverForm', [function () {

    var provider = this;

    provider.errorsKey = 'errors';
    provider.logging = true;
    provider.$get = ['$http', '$q', '$log', function ($http, $q, $log) {

      // Private
      // Set error message on form value
      function setError (form, message) {
        // Only NgModelController has $render and $setViewValue
        // Don't call $setValidity on FormController
        if (form.$render || form.$setViewValue) {
          form.$setValidity('server', false);
        }
        form.$setPristine();
        form.$server = message;
      }

      // Private
      // recurse over the form
      // if the property is a value it gets assigned
      // otherwise dive inside
      function formCrawler (form) {
        var response = {};
        for (var prop in form) {
          if (form.hasOwnProperty(prop) && prop[0] !== '$') {
            if (form[prop].hasOwnProperty('$modelValue')) {
              response[prop] = form[prop].$modelValue;
            } else {
              response[prop] = formCrawler(form[prop]);
            }
          }
        }
        return response;
      }

      var self = this;

      // Serialize form to data object
      self.serialize = function serialize (form) {
        var response = {};

        // set root if form name is set
        if (form.$name) {
          response[form.$name] = formCrawler(form);
        } else {
          response = formCrawler(form);
        }
        return response;
      };

      // Apply all messages from errors object on form
      // The error value must be an array of strings or a string
      self.applyErrors = function applyErrors (form, errors) {
        if (angular.isString(errors)) {
          // If it's a string then set the error message
          setError(form, errors);
        } else if (angular.isArray(errors)) {
          // If it's an array then join them and set the error message
          setError(form, errors.join(', '));
        } else {
          // Otherwise we must crawl the errors
          for (var prop in errors) {
            if (errors.hasOwnProperty(prop)) {
              if (form.hasOwnProperty(prop)) {
                // If the form has a property with the same error name
                self.applyErrors(form[prop], errors[prop]);
              } else if (form.$name === prop) {
                // errors object is for the whole form, dive in
                self.applyErrors(form, errors[prop]);
              } else {
                if (provider.logging) {
                  $log.warn('No place to render property', prop, 'in form', form);
                }
              }
            }
          }
        }
      };

      // Crawl form and reset errors
      self.clearErrors = function clearErrors (form) {
        // Only NgModelController has $render and $setViewValue
        // Don't call $setValidity on FormController
        if (form.$render || form.$setViewValue) {
          form.$setValidity('server', true);
        }
        form.$setPristine();
        form.$server = '';

        for (var prop in form) {
          if (form.hasOwnProperty(prop) && prop[0] !== '$') {
            clearErrors(form[prop]);
          }
        }
      };

      self.submit = function submit (form, config) {
        self.clearErrors(form); // resets previous server errors
        form.$submitting = true;
        form.$saved = false;

        return $http(config)
          .success(function () {
            form.$saved = true;
          })
          .error(function(res, status) {
            form.$saved = false;
            if (status === 422) {
              if (provider.errorsKey) {
                self.applyErrors(form, res[provider.errorsKey]);
              } else {
                self.applyErrors(form, res);
              }
            }
          })
          .finally(function () {
            form.$submitting = false;
          });
      };

      return self;
    }];
  }])
  .directive('serverForm', ['serverForm', function (serverForm) {

    return {
      restrict: 'A',
      scope: {
        url: '@',
        method: '@',
        onSuccess: '=',
        onError: '='
      },
      require: 'form',
      link: function postLink(scope, iElement, iAttrs, form) {

        function submitForm (ev) {
          if (!form.$submitting) {
            var config = {
              url: scope.url,
              method: scope.method ? scope.method : 'POST',
              data: serverForm.serialize(form)
            };

            serverForm.submit(form, config)
            .then(scope.onSuccess, scope.onError);
          }

          ev.preventDefault();
          scope.$apply();
        }

        var submitting = false;

        iElement.on('submit', submitForm);
        scope.$on('destroy', function () {
          iElement.off('submit', submitForm);
        });

      }
    };
  }]);

})(window, window.angular);
<|endoftext|>