<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import MicroContainer from 'react-micro-container';
import {createHttpClient} from './http';
import Alert from './components/Alert';
import Select from './components/Select';
import {sprintf} from 'sprintf-js';

class SettingView extends MicroContainer {

  constructor(props) {
    super(props);
    this.state = {
      alert: {
        visible: false,
        kind: '',
        message: '',
      },
      userId: 0,
      email: '',
      timeSpan: {
        editable: false,
        timeSpans: [], // {fromHour:23, fromMinute:0, toHour:23, toMinute:30}
      },
    };
  }

  componentDidMount() {
    this.subscribe({
      showAlert: this.handleShowAlert,
      // Email
      hideAlert: this.handleHideAlert,
      onChangeEmail: this.handleOnChangeEmail,
      updateEmail: this.handleUpdateEmail,
      // TimeSpan
      setTimeSpanEditable: this.handleSetTimeSpanEditable,
      addTimeSpan: this.handleAddTimeSpan,
      deleteTimeSpan: this.handleDeleteTimeSpan,
      updateTimeSpan: this.handleUpdateTimeSpan,
      onChangeTimeSpan: this.handleOnChangeTimeSpan,
    });

    this.fetch();
  }

  render() {
    if ([1, 705, 715].includes(this.state.userId)) { // AB test
      return (

          <Alert dispatch={this.dispatch} {...this.state.alert}/>
          <EmailForm dispatch={this.dispatch} value={this.state.email}/>
          <NotificationTimeSpanForm dispatch={this.dispatch} {...this.state.timeSpan}/>

      );
    } else {
      return (

          <Alert dispatch={this.dispatch} {...this.state.alert}/>
          <EmailForm dispatch={this.dispatch} value={this.state.email}/>

      );
    }
  }

  fetch() {
    const client = createHttpClient();
    client.get('/api/v1/me')
      .then((response) => {
        console.log(response.data);
        const timeSpans = response.data['notificationTimeSpans'] ? response.data['notificationTimeSpans'] : [];
        this.setState({
          userId: response.data['userId'],
          email: response.data['email'],
          timeSpan: {
            editable: this.state.timeSpan.editable,
            timeSpans: timeSpans,
          },
        })
      })
      .catch((error) => {
        console.log(error);
        this.handleShowAlert('danger', 'システムエラーが発生しました');
      });
  }

  handleShowAlert(kind, message) {
    this.setState({
      alert: {visible: true, kind: kind, message: message}
    })
  }

  handleHideAlert() {
    this.setState({
      alert: {visible: false}
    })
  }

  handleOnChangeEmail(email) {
    this.setState({email: email})
  }

  handleUpdateEmail(email) {
    //alert('email is ' + email);
    const client = createHttpClient();
    client.post('/api/v1/me/email', {
      email: email,
    })
      .then((response) => {
        this.handleShowAlert('success', 'メールアドレスを更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいメールアドレスを入力してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });
  }

  handleSetTimeSpanEditable(value) {
    this.setState({
      timeSpan: {
        editable: value,
        timeSpans: this.state.timeSpan.timeSpans,
      }
    })
  }

  handleAddTimeSpan() {
    if (this.state.timeSpan.timeSpans.length === 3) {
      return;
    }
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: [...this.state.timeSpan.timeSpans, {fromHour: 0, fromMinute: 0, toHour: 0, toMinute: 0}],
      }
    });
  }

  handleDeleteTimeSpan(index) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    if (index >= timeSpans.length) {
      return;
    }
    timeSpans.splice(index, 1);
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      },
    });
  }

  handleOnChangeTimeSpan(name, index, value) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    timeSpans[index][name] = value;
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      }
    });
  }

  handleUpdateTimeSpan() {
    const timeSpans = [];
    for (const timeSpan of this.state.timeSpan.timeSpans) {
      for (const [k, v] of Object.entries(timeSpan)) {
        timeSpan[k] = parseInt(v);
      }
      if (timeSpan.fromHour === 0
        && timeSpan.fromMinute === 0
        && timeSpan.toHour === 0
        && timeSpan.toMinute === 0) {
        // Ignore zero value
        continue;
      }
      timeSpans.push(timeSpan);
    }

    const client = createHttpClient();
    client.post('/api/v1/me/notificationTimeSpan', {
      notificationTimeSpans: timeSpans,
    })
      .then((response) => {
        this.handleShowAlert('success', 'レッスン希望時間帯を更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいレッスン希望時間帯を選択してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });

    this.setState({
      timeSpan: {
        editable: false,
        timeSpans: timeSpans,
      }
    });
  }
}

class EmailForm extends React.Component {

  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }

  onChange(e) {
    this.props.dispatch('onChangeEmail', e.target.value);
  }

  render() {
    return (
      <form className="form-horizontal">

            Email

            <input
              type="email" className="form-control" name="email" id="email"
              placeholder="Email" required autoFocus autoComplete="on"
              value={this.props.value} onChange={this.onChange}/>

            <button
              type="button"
              disabled={!this.props.value}
              className="btn btn-primary"
              onClick={() => this.props.dispatch('updateEmail', this.props.value)}
            >
              変更
            </button>

      </form>
    );
  }
}

class NotificationTimeSpanForm extends React.Component {
  constructor(props) {
    super(props);
    this.onClickPlus = this.onClickPlus.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  onClickPlus(e) {
    e.preventDefault();
    this.props.dispatch('addTimeSpan');
  }

  onClickMinus(e, index) {
    e.preventDefault();
    this.props.dispatch('deleteTimeSpan', index);
  }

  onChange(event) {
    const a = event.target.name.split('_');
    const name = a[0];
    const index = a[1];
    const timeSpans = this.props.timeSpans.slice();
    timeSpans[index][name] = event.target.value;
    this.props.dispatch('onChangeTimeSpan', name, index, event.target.value);
  }

  createTimeSpanRow(timeSpan, index) {
    let hourOptions = [];
    for (let i = 0; i <= 23; i++) {
      hourOptions.push({value: i, label: i})
    }
    let minuteOptions = [];
    for (const i of [0, 30]) {
      minuteOptions.push({value: i, label: sprintf('%02d', i)});
    }

    return (

        <Select
          name={'fromHour_' + index}
          value={timeSpan.fromHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'fromMinute_' + index}
          value={timeSpan.fromMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

          〜

        <Select
          name={'toHour_' + index}
          value={timeSpan.toHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'toMinute_' + index}
          value={timeSpan.toMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

        <button type="button" className="btn btn-link button-plus" onClick={() => this.onClickPlus(event)}>

        </button>
        <button type="button" className="btn btn-link button-plus" onClick={() => this.onClickMinus(event, index)}>

        </button>

    );
  }

  render() {
    let content = [];
    if (this.props.editable) {
      if (this.props.timeSpans.length > 0) {
        this.props.timeSpans.map((timeSpan, i) => {
          content.push(this.createTimeSpanRow(timeSpan, i));
        });
      } else {
        this.props.dispatch('addTimeSpan');
      }
    } else {
      if (this.props.timeSpans.length > 0) {
        for (let timeSpan of this.props.timeSpans) {
          content.push(

{timeSpan.fromHour}:{sprintf('%02d', timeSpan.fromMinute)} 〜 {timeSpan.toHour}:{sprintf('%02d', timeSpan.toMinute)}
);
        }
      } else {
        content.push(

データがありません。編集ボタンで追加できます。

        );
      }
    }

    let updateButton;
    if (this.props.editable) {
      updateButton =
        <button
          type="button" className="btn btn-primary"
          onClick={() => this.props.dispatch('updateTimeSpan', false)}
        >
          更新
        </button>;
    } else {
      updateButton =
        <button
          type="button" className="btn btn-default"
          onClick={() => this.props.dispatch('setTimeSpanEditable', true)}
        >
          編集
        </button>;
    }

    return (
      <form className="form-horizontal">

            レッスン希望時間帯

            {content}

            {updateButton}

      </form>
    );
  }
}

ReactDOM.render(
  <SettingView/>,
  document.getElementById('root')
);
<commit_msg>Release notification time span<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import MicroContainer from 'react-micro-container';
import {createHttpClient} from './http';
import Alert from './components/Alert';
import Select from './components/Select';
import {sprintf} from 'sprintf-js';

class SettingView extends MicroContainer {

  constructor(props) {
    super(props);
    this.state = {
      alert: {
        visible: false,
        kind: '',
        message: '',
      },
      userId: 0,
      email: '',
      timeSpan: {
        editable: false,
        timeSpans: [], // {fromHour:23, fromMinute:0, toHour:23, toMinute:30}
      },
    };
  }

  componentDidMount() {
    this.subscribe({
      showAlert: this.handleShowAlert,
      // Email
      hideAlert: this.handleHideAlert,
      onChangeEmail: this.handleOnChangeEmail,
      updateEmail: this.handleUpdateEmail,
      // TimeSpan
      setTimeSpanEditable: this.handleSetTimeSpanEditable,
      addTimeSpan: this.handleAddTimeSpan,
      deleteTimeSpan: this.handleDeleteTimeSpan,
      updateTimeSpan: this.handleUpdateTimeSpan,
      onChangeTimeSpan: this.handleOnChangeTimeSpan,
    });

    this.fetch();
  }

  render() {
    return (

        <Alert dispatch={this.dispatch} {...this.state.alert}/>
        <EmailForm dispatch={this.dispatch} value={this.state.email}/>
        <NotificationTimeSpanForm dispatch={this.dispatch} {...this.state.timeSpan}/>

    );
  }

  fetch() {
    const client = createHttpClient();
    client.get('/api/v1/me')
      .then((response) => {
        console.log(response.data);
        const timeSpans = response.data['notificationTimeSpans'] ? response.data['notificationTimeSpans'] : [];
        this.setState({
          userId: response.data['userId'],
          email: response.data['email'],
          timeSpan: {
            editable: this.state.timeSpan.editable,
            timeSpans: timeSpans,
          },
        })
      })
      .catch((error) => {
        console.log(error);
        this.handleShowAlert('danger', 'システムエラーが発生しました');
      });
  }

  handleShowAlert(kind, message) {
    this.setState({
      alert: {visible: true, kind: kind, message: message}
    })
  }

  handleHideAlert() {
    this.setState({
      alert: {visible: false}
    })
  }

  handleOnChangeEmail(email) {
    this.setState({email: email})
  }

  handleUpdateEmail(email) {
    //alert('email is ' + email);
    const client = createHttpClient();
    client.post('/api/v1/me/email', {
      email: email,
    })
      .then((response) => {
        this.handleShowAlert('success', 'メールアドレスを更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいメールアドレスを入力してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });
  }

  handleSetTimeSpanEditable(value) {
    this.setState({
      timeSpan: {
        editable: value,
        timeSpans: this.state.timeSpan.timeSpans,
      }
    })
  }

  handleAddTimeSpan() {
    if (this.state.timeSpan.timeSpans.length === 3) {
      return;
    }
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: [...this.state.timeSpan.timeSpans, {fromHour: 0, fromMinute: 0, toHour: 0, toMinute: 0}],
      }
    });
  }

  handleDeleteTimeSpan(index) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    if (index >= timeSpans.length) {
      return;
    }
    timeSpans.splice(index, 1);
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      },
    });
  }

  handleOnChangeTimeSpan(name, index, value) {
    let timeSpans = this.state.timeSpan.timeSpans.slice();
    timeSpans[index][name] = value;
    this.setState({
      timeSpan: {
        editable: this.state.timeSpan.editable,
        timeSpans: timeSpans,
      }
    });
  }

  handleUpdateTimeSpan() {
    const timeSpans = [];
    for (const timeSpan of this.state.timeSpan.timeSpans) {
      for (const [k, v] of Object.entries(timeSpan)) {
        timeSpan[k] = parseInt(v);
      }
      if (timeSpan.fromHour === 0
        && timeSpan.fromMinute === 0
        && timeSpan.toHour === 0
        && timeSpan.toMinute === 0) {
        // Ignore zero value
        continue;
      }
      timeSpans.push(timeSpan);
    }

    const client = createHttpClient();
    client.post('/api/v1/me/notificationTimeSpan', {
      notificationTimeSpans: timeSpans,
    })
      .then((response) => {
        this.handleShowAlert('success', 'レッスン希望時間帯を更新しました！');
      })
      .catch((error) => {
        console.log(error);
        if (error.response.status === 400) {
          this.handleShowAlert('danger', '正しいレッスン希望時間帯を選択してください');
        } else {
          // TODO: external message
          this.handleShowAlert('danger', 'システムエラーが発生しました');
        }
      });

    this.setState({
      timeSpan: {
        editable: false,
        timeSpans: timeSpans,
      }
    });
  }
}

class EmailForm extends React.Component {

  constructor(props) {
    super(props);
    this.onChange = this.onChange.bind(this);
  }

  onChange(e) {
    this.props.dispatch('onChangeEmail', e.target.value);
  }

  render() {
    return (
      <form className="form-horizontal">

            Email

            <input
              type="email" className="form-control" name="email" id="email"
              placeholder="Email" required autoFocus autoComplete="on"
              value={this.props.value} onChange={this.onChange}/>

            <button
              type="button"
              disabled={!this.props.value}
              className="btn btn-primary"
              onClick={() => this.props.dispatch('updateEmail', this.props.value)}
            >
              変更
            </button>

      </form>
    );
  }
}

class NotificationTimeSpanForm extends React.Component {
  constructor(props) {
    super(props);
    this.onClickPlus = this.onClickPlus.bind(this);
    this.onChange = this.onChange.bind(this);
  }

  onClickPlus(e) {
    e.preventDefault();
    this.props.dispatch('addTimeSpan');
  }

  onClickMinus(e, index) {
    e.preventDefault();
    this.props.dispatch('deleteTimeSpan', index);
  }

  onChange(event) {
    const a = event.target.name.split('_');
    const name = a[0];
    const index = a[1];
    const timeSpans = this.props.timeSpans.slice();
    timeSpans[index][name] = event.target.value;
    this.props.dispatch('onChangeTimeSpan', name, index, event.target.value);
  }

  createTimeSpanRow(timeSpan, index) {
    let hourOptions = [];
    for (let i = 0; i <= 23; i++) {
      hourOptions.push({value: i, label: i})
    }
    let minuteOptions = [];
    for (const i of [0, 30]) {
      minuteOptions.push({value: i, label: sprintf('%02d', i)});
    }

    return (

        <Select
          name={'fromHour_' + index}
          value={timeSpan.fromHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'fromMinute_' + index}
          value={timeSpan.fromMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

          〜

        <Select
          name={'toHour_' + index}
          value={timeSpan.toHour}
          onChange={this.onChange}
          options={hourOptions}
          className="custom-select mr-sm-2"
        />時

        <Select
          name={'toMinute_' + index}
          value={timeSpan.toMinute}
          onChange={this.onChange}
          options={minuteOptions}
          className="custom-select mr-sm-2"
        />分

        <button type="button" className="btn btn-link button-plus" onClick={() => this.onClickPlus(event)}>

        </button>
        <button type="button" className="btn btn-link button-plus" onClick={() => this.onClickMinus(event, index)}>

        </button>

    );
  }

  render() {
    let content = [];
    if (this.props.editable) {
      if (this.props.timeSpans.length > 0) {
        this.props.timeSpans.map((timeSpan, i) => {
          content.push(this.createTimeSpanRow(timeSpan, i));
        });
      } else {
        this.props.dispatch('addTimeSpan');
      }
    } else {
      if (this.props.timeSpans.length > 0) {
        for (let timeSpan of this.props.timeSpans) {
          content.push(

{timeSpan.fromHour}:{sprintf('%02d', timeSpan.fromMinute)} 〜 {timeSpan.toHour}:{sprintf('%02d', timeSpan.toMinute)}
);
        }
      } else {
        content.push(

データがありません。編集ボタンで追加できます。

        );
      }
    }

    let updateButton;
    if (this.props.editable) {
      updateButton =
        <button
          type="button" className="btn btn-primary"
          onClick={() => this.props.dispatch('updateTimeSpan', false)}
        >
          更新
        </button>;
    } else {
      updateButton =
        <button
          type="button" className="btn btn-default"
          onClick={() => this.props.dispatch('setTimeSpanEditable', true)}
        >
          編集
        </button>;
    }

    return (
      <form className="form-horizontal">

            レッスン希望時間帯

            {content}

            {updateButton}

      </form>
    );
  }
}

ReactDOM.render(
  <SettingView/>,
  document.getElementById('root')
);
<|endoftext|>


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

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const rp = require('request-promise');
const promisePool = require('es6-promise-pool');
const PromisePool = promisePool.PromisePool;
const secureCompare = require('secure-compare');
// Maximum concurrent account deletions.
const MAX_CONCURRENT = 3;

/**
 * When requested this Function will delete every user accounts that has been inactive for 30 days.
 * The request needs to be authorized by passing a 'key' query parameter in the URL. This key must
 * match a key set as an environment variable using `firebase functions:config:set cron.key="YOUR_KEY"`.
 */
exports.accountcleanup = functions.https.onRequest((req, res) => {
  const key = req.query.key;

  // Exit if the keys don't match
  if (!secureCompare(key, functions.config().cron.key)) {
    console.log('The key provided in the request does not match the key set in the environment. Check that', key,
      'matches the cron.key attribute in `firebase env:get`');
    res.status(403).send('Security key does not match. Make sure your "key" URL query parameter matches the ' +
      'cron.key environment variable.');
    return null;
  }

  // Fetch all user details.
  return getUsers().then((users) => {
    // Find users that have not signed in in the last 30 days.
    const inactiveUsers = users.filter(
        (user) => parseInt(user.lastLoginAt, 10) < Date.now() - 30 * 24 * 60 * 60 * 1000);

    // Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.
    const promisePool = new PromisePool(() => {
      if (inactiveUsers.length > 0) {
        const userToDelete = inactiveUsers.pop();
        // Delete the inactive user.
        return admin.auth().deleteUser(userToDelete.localId);
      }
      return reject();
    }, MAX_CONCURRENT);

    return promisePool.start();
  }).then(() => {
    console.log('Deleted user account', userToDelete.localId, 'because of inactivity');
    return null;
  }).catch((error) => {
    console.error('Deletion of inactive user account', userToDelete.localId, 'failed:', error);
    return null;
  }).then(() => {
    console.log('User cleanup finished');
    res.send('User cleanup finished');
    return null;
  });
});

/**
 * Returns the list of all users with their ID and lastLogin timestamp.
 */
function getUsers(userIds = [], nextPageToken, accessToken) {
  return getAccessToken(accessToken).then((accessToken) => {
    const options = {
      method: 'POST',
      uri: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/downloadAccount?fields=users/localId,users/lastLoginAt,nextPageToken&access_token=' + accessToken,
      body: {
        nextPageToken: nextPageToken,
        maxResults: 1000,
      },
      json: true,
    };

    return rp(options);
  }).then((resp) => {
    if (!resp.users) {
      return userIds;
    }
    if (resp.nextPageToken) {
      return getUsers(userIds.concat(resp.users), resp.nextPageToken, accessToken);
    }
    return userIds.concat(resp.users);
  });
}

/**
 * Returns an access token using the Google Cloud metadata server.
 */
function getAccessToken(accessToken) {
  // If we have an accessToken in cache to re-use we pass it directly.
  if (accessToken) {
    return Promise.resolve(accessToken);
  }

  const options = {
    uri: 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token',
    headers: {'Metadata-Flavor': 'Google'},
    json: true,
  };

  return rp(options).then((resp) => resp.access_token);
}
<commit_msg>Reverted to version before refactoring<commit_after>/**
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const rp = require('request-promise');
const promisePool = require('es6-promise-pool');
const PromisePool = promisePool.PromisePool;
const secureCompare = require('secure-compare');
// Maximum concurrent account deletions.
const MAX_CONCURRENT = 3;

/**
 * When requested this Function will delete every user accounts that has been inactive for 30 days.
 * The request needs to be authorized by passing a 'key' query parameter in the URL. This key must
 * match a key set as an environment variable using `firebase functions:config:set cron.key="YOUR_KEY"`.
 */
exports.accountcleanup = functions.https.onRequest((req, res) => {
  const key = req.query.key;

  // Exit if the keys don't match
  if (!secureCompare(key, functions.config().cron.key)) {
    console.log('The key provided in the request does not match the key set in the environment. Check that', key,
        'matches the cron.key attribute in `firebase env:get`');
    res.status(403).send('Security key does not match. Make sure your "key" URL query parameter matches the ' +
        'cron.key environment variable.');
    return null;
  }

  // Fetch all user details.
  return getUsers().then((users) => {
    // Find users that have not signed in in the last 30 days.
    const inactiveUsers = users.filter(
        user => parseInt(user.lastLoginAt, 10) < Date.now() - 30 * 24 * 60 * 60 * 1000);

    // Use a pool so that we delete maximum `MAX_CONCURRENT` users in parallel.
    const promisePool = new PromisePool(() => {
      if (inactiveUsers.length > 0) {
        const userToDelete = inactiveUsers.pop();

        // Delete the inactive user.
        return admin.auth().deleteUser(userToDelete.localId).then(() => {
          console.log('Deleted user account', userToDelete.localId, 'because of inactivity');
          return null;
        }).catch(error => {
          console.error('Deletion of inactive user account', userToDelete.localId, 'failed:', error);
          return null;
        });
      }
    }, MAX_CONCURRENT);

    return promisePool.start().then(() => {
      console.log('User cleanup finished');
      res.send('User cleanup finished');
      return null;
    });
  });
});

/**
 * Returns the list of all users with their ID and lastLogin timestamp.
 */
function getUsers(userIds = [], nextPageToken, accessToken) {
  return getAccessToken(accessToken).then((accessToken) => {
    const options = {
      method: 'POST',
      uri: 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/downloadAccount?fields=users/localId,users/lastLoginAt,nextPageToken&access_token=' + accessToken,
      body: {
        nextPageToken: nextPageToken,
        maxResults: 1000,
      },
      json: true,
    };

    return rp(options);
  }).then((resp) => {
    if (!resp.users) {
      return userIds;
    }
    if (resp.nextPageToken) {
      return getUsers(userIds.concat(resp.users), resp.nextPageToken, accessToken);
    }
    return userIds.concat(resp.users);
  });
}

/**
 * Returns an access token using the Google Cloud metadata server.
 */
function getAccessToken(accessToken) {
  // If we have an accessToken in cache to re-use we pass it directly.
  if (accessToken) {
    return Promise.resolve(accessToken);
  }

  const options = {
    uri: 'http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token',
    headers: {'Metadata-Flavor': 'Google'},
    json: true,
  };

  return rp(options).then((resp) => resp.access_token);
}
<|endoftext|>


<commit_before>foam.CLASS({
  package: 'foam.nanos.script',
  name: 'TestRunnerScript',
  extends: 'foam.nanos.script.Script',

  javaImports: [
    'foam.dao.DAO',
    'foam.dao.ArraySink',
    'foam.nanos.test.Test',
    'java.util.*',
    'foam.nanos.logger.LogLevelFilterLogger',
    'foam.util.SafetyUtil'
  ],
  constants: [
    {
      name: 'CHECK_MARK',
      type: 'String',
      value: '\u2713'
    },
    {
      name: 'CROSS_MARK',
      type: 'String',
      value: '\u2718'
    },
    {
      name: 'GREEN_COLOR',
      type: 'String',
      value: '\u001B[32m'
    },
    {
      name: 'RED_COLOR',
      type: 'String',
      value: '\u001B[31m'
    },
    {
      name: 'RESET_COLOR',
      type: 'String',
      value: '\u001B[0m'
    }
  ],
  properties: [
    {
      name: 'failedTests',
      class: 'Int',
      value: 0
    },
    {
      name: 'passedTests',
      class: 'Int',
      value: 0
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        // turn off logging to get rid of clutter.
        LogLevelFilterLogger loggerFilter = (LogLevelFilterLogger) x.get("logger");
        loggerFilter.setLogInfo(false);
        loggerFilter.setLogDebug(false);
        loggerFilter.setLogWarning(false);

        DAO testDAO = (DAO) x.get("testDAO");
        ArraySink tests = (ArraySink) testDAO.select(new ArraySink());
        List testArray = tests.getArray();

        for(int i = 0; i < testArray.size(); i ++) {
          Test test = (Test) testArray.get(i);
          test = (Test) test.fclone();
          if( test.getServer() ) {
            runServerSideTest(x, test);
          } else {
            // TODO: Run client side tests in a headless browser.
          }
        }

        System.out.println("DONE RUNNING TESTS");

        printBold(GREEN_COLOR + " " +  "PASSED: " + Integer.toString(getPassedTests()) + " " + RESET_COLOR);
        printBold(RED_COLOR + " " + "FAILED: " + Integer.toString(getFailedTests()) + " " + RESET_COLOR);

        // Exit with the appropriate output.
        if ( getFailedTests() > 0 ) {
          System.exit(1);
        }
        System.exit(0);
      `
    },
    {
      name: 'runServerSideTest',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        },
        {
          name: 'test', javaType: 'Test'
        }
      ],
      javaCode: `
        printBold(test.getId());
        try {
            test.runScript(x);
            printOutput(test);
        }
        catch ( Exception e ) {
          e.printStackTrace();
          setFailedTests(getFailedTests() + 1);
        }
      `
    },
    {
      name: 'printBold',
      args: [
        {
          name: 'message', javaType: 'String'
        }
      ],
      javaReturns: 'void',
      javaCode: 'System.out.println("\\033[0;1m" + message + RESET_COLOR);'
    },
    {
      name: 'printOutput',
      args: [
        {
          name: 'test', javaType: 'Test'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        String outputs[] = test.getOutput().split("\\n");
        for( String output: outputs ) {
          if ( output.startsWith("SUCCESS") ) {
            System.out.println("\\t" + GREEN_COLOR + " " + CHECK_MARK + " " + output + " " + RESET_COLOR);
             setPassedTests(getPassedTests() + 1);
          }
          else if ( output.startsWith("FAILURE") ) {
            System.out.println("\\t" + RED_COLOR + " "+ CROSS_MARK + " " + output + " " + RESET_COLOR);
             setFailedTests(getFailedTests() + 1);
          }
          else {
            if ( ! SafetyUtil.isEmpty(output) ) {
              System.out.println(output);
            }
          }
        }
      `
    }

  ]

});
<commit_msg>formatting<commit_after>foam.CLASS({
  package: 'foam.nanos.script',
  name: 'TestRunnerScript',
  extends: 'foam.nanos.script.Script',

  javaImports: [
    'foam.dao.DAO',
    'foam.dao.ArraySink',
    'foam.nanos.test.Test',
    'java.util.*',
    'foam.nanos.logger.LogLevelFilterLogger',
    'foam.util.SafetyUtil'
  ],
  constants: [
    {
      name: 'CHECK_MARK',
      type: 'String',
      value: '\u2713'
    },
    {
      name: 'CROSS_MARK',
      type: 'String',
      value: '\u2718'
    },
    {
      name: 'GREEN_COLOR',
      type: 'String',
      value: '\u001B[32m'
    },
    {
      name: 'RED_COLOR',
      type: 'String',
      value: '\u001B[31m'
    },
    {
      name: 'RESET_COLOR',
      type: 'String',
      value: '\u001B[0m'
    }
  ],
  properties: [
    {
      name: 'failedTests',
      class: 'Int'
    },
    {
      name: 'passedTests',
      class: 'Int'
    }
  ],

  methods: [
    {
      name: 'runScript',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        // turn off logging to get rid of clutter.
        LogLevelFilterLogger loggerFilter = (LogLevelFilterLogger) x.get("logger");
        loggerFilter.setLogInfo(false);
        loggerFilter.setLogDebug(false);
        loggerFilter.setLogWarning(false);

        DAO testDAO = (DAO) x.get("testDAO");
        ArraySink tests = (ArraySink) testDAO.select(new ArraySink());
        List testArray = tests.getArray();

        for ( int i = 0; i < testArray.size(); i ++ ) {
          Test test = (Test) testArray.get(i);
          test = (Test) test.fclone();
          if ( test.getServer() ) {
            runServerSideTest(x, test);
          } else {
            // TODO: Run client side tests in a headless browser.
          }
        }

        System.out.println("DONE RUNNING TESTS");

        printBold(GREEN_COLOR + " " +  "PASSED: " + Integer.toString(getPassedTests()) + " " + RESET_COLOR);
        printBold(RED_COLOR + " " + "FAILED: " + Integer.toString(getFailedTests()) + " " + RESET_COLOR);

        // Exit with the appropriate output.
        if ( getFailedTests() > 0 ) {
          System.exit(1);
        }
        System.exit(0);
      `
    },
    {
      name: 'runServerSideTest',
      args: [
        {
          name: 'x', javaType: 'foam.core.X'
        },
        {
          name: 'test', javaType: 'Test'
        }
      ],
      javaCode: `
        printBold(test.getId());
        try {
          test.runScript(x);
          printOutput(test);
        }
        catch ( Exception e ) {
          e.printStackTrace();
          setFailedTests(getFailedTests() + 1);
        }
      `
    },
    {
      name: 'printBold',
      args: [
        {
          name: 'message', javaType: 'String'
        }
      ],
      javaReturns: 'void',
      javaCode: 'System.out.println("\\033[0;1m" + message + RESET_COLOR);'
    },
    {
      name: 'printOutput',
      args: [
        {
          name: 'test', javaType: 'Test'
        }
      ],
      javaReturns: 'void',
      javaCode: `
        String outputs[] = test.getOutput().split("\\n");
        for( String output: outputs ) {
          if ( output.startsWith("SUCCESS") ) {
            System.out.println("\\t" + GREEN_COLOR + " " + CHECK_MARK + " " + output + " " + RESET_COLOR);
             setPassedTests(getPassedTests() + 1);
          }
          else if ( output.startsWith("FAILURE") ) {
            System.out.println("\\t" + RED_COLOR + " "+ CROSS_MARK + " " + output + " " + RESET_COLOR);
             setFailedTests(getFailedTests() + 1);
          }
          else {
            if ( ! SafetyUtil.isEmpty(output) ) {
              System.out.println(output);
            }
          }
        }
      `
    }
  ]
});
<|endoftext|>


<commit_before>(function($) {
    'use strict';

    // document load event
    $(document).ready(function() {

        // initialize swiper when document ready
        // http://idangero.us/swiper/api/
        $('.js__news-carousel').each(function(){
          $(this).swiper({
            nextButton: $(this).parent().find('.js__news-carousel__btn-next'),
            prevButton: $(this).parent().find('.js__news-carousel__btn-prev'),
            pagination: '.js__news-carousel__pagination',
            paginationClickable: true,
            slidesPerView: 4,
            preloadImages: false,
            spaceBetween: 30,

            // Responsive breakpoints
            breakpoints: {

                // when window width is <= 480px
                500: {
                    slidesPerView: 1
                },
                // when window width is <= 768px
                767: {
                    slidesPerView: 2
                },
                // when window width is <= 992px
                991: {
                    slidesPerView: 3
                },
                // when window width is <= 1199px
                1199: {
                    slidesPerView: 4
                }
            }
          });
        });
    });

})(jQuery);
<commit_msg>[STYLE] fix js formatting<commit_after>(function($) {
    'use strict';

    // document load event
    $(document).ready(function() {

        // initialize swiper when document ready
        // http://idangero.us/swiper/api/
        $('.js__news-carousel').each(function() {
            $(this).swiper({
                nextButton: $(this).parent().find('.js__news-carousel__btn-next'),
                prevButton: $(this).parent().find('.js__news-carousel__btn-prev'),
                pagination: '.js__news-carousel__pagination',
                paginationClickable: true,
                slidesPerView: 4,
                preloadImages: false,
                spaceBetween: 30,

                // Responsive breakpoints
                breakpoints: {

                    // when window width is <= 480px
                    500: {
                        slidesPerView: 1
                    },
                    // when window width is <= 768px
                    767: {
                        slidesPerView: 2
                    },
                    // when window width is <= 992px
                    991: {
                        slidesPerView: 3
                    },
                    // when window width is <= 1199px
                    1199: {
                        slidesPerView: 4
                    }
                }
            });
        });
    });

})(jQuery);
<|endoftext|>


<commit_before>
LoveLetterOnline.Lobby = function(game){
	var texto;
	var texto_AddPartida;
	var that;
	var texto_partidasEnJuego = [];
	var partidasEnJuego = [];
};

LoveLetterOnline.Lobby.prototype = {

	create: function(){
		that = this;
		texto_partidasEnJuego = [];
		partidasEnJuego = [];
		this.mostrarTexto();

		//$(document).ready(function(){})
		/*

		 this.loadPartidas(function(partidas){

			that.mostrarTexto();

			//Cuando las partidas se han cargado desde el servidor
			/*
			for(var i=0; i<partidas.length; i++){
				jugsRanking.push(jugadores[i].partidasGanadas);

			}

			//that.mostrarJugadores();

		});
		*/

	},
	volver: function(){
		this.state.start('Menu');
	},

	loadPartidas: function(callback){
		$.ajax({
			url: 'http://localhost:8080/partidas'
		}).done(function (partidas) {
			console.log('Partidas loaded: ' + JSON.stringify(partidas));
			callback(partidas);
		});
	},

	mostrarJugadores: function(){
		var textoNombre;
		for(var i=0; i<5; i++){
			if(jugsRanking[i]!==undefined){
				textoNombre = jugsRanking[i];
			}else textoNombre = '-';
			that.add.text(that.world.centerX, (that.world.centerY-200)+50*i, (i+1) + '. '+textoNombre, {fill: "#ffffff"});
		}
	},

	obtenerJugador: function(){

	},

	addPartida: function(a, b, nombrePartida){

		var nomPartida = prompt("Introduce aquí el nombre de la partida", "Partida");
		var numJugs = prompt("Introduce aquí el número de jugadores", "2");

		var jugCreador = that.obtenerJugador();

		var p_jav = {
				nombre: nomPartida,
				numJugMax: numJugs,
				jugsPartida: [jugCreador]
		}

		partidasEnJuego.push(p_jav);

		var offset = texto_partidasEnJuego.length;
		var p = that.add.text(that.world.centerX-150, (that.world.centerY-150)+50*offset,nomPartida+': para '+numJugs+' jugadores',{fill: "#ffffff"});
		p.inputEnabled = true;
		texto_partidasEnJuego.push(p);

	},

	mostrarTexto: function(){
		texto = that.add.text(that.world.centerX, that.world.centerY-250,'L O V E  L E T T E R  O N L I N E',{fill: "#ffffff"});
		texto.anchor.x = 0.5;
		texto = that.add.text(that.world.centerX, that.world.centerY+150,'Volver',{fill: "#ffffff"});
		texto.anchor.x = 0.5;
		texto.inputEnabled = true;
		texto.events.onInputDown.add(that.volver, this);

		texto_AddPartida = that.add.text(that.world.centerX-150, that.world.centerY-200,'Crear partida',{fill: "#ffffff"});
		texto_AddPartida.anchor.x = 0.5;
		texto_AddPartida.inputEnabled = true;
		texto_AddPartida.events.onInputDown.add(that.addPartida, this, 0, "Partida 1");
	}
};<commit_msg>Crear Partidas<commit_after>
LoveLetterOnline.Lobby = function(game){
	var texto;
	var texto_AddPartida;
	var that;
	var texto_partidasEnJuego = [];
	var partidasEnJuego = [];
};

LoveLetterOnline.Lobby.prototype = {

	create: function(){
		that = this;
		texto_partidasEnJuego = [];
		partidasEnJuego = [];
		this.mostrarTexto();

		//$(document).ready(function(){})
		/*

		 this.loadPartidas(function(partidas){

			that.mostrarTexto();

			//Cuando las partidas se han cargado desde el servidor
			/*
			for(var i=0; i<partidas.length; i++){
				jugsRanking.push(jugadores[i].partidasGanadas);

			}

			//that.mostrarJugadores();

		});
		*/

	},
	volver: function(){
		this.state.start('Menu');
	},

	loadPartidas: function(callback){
		$.ajax({
			url: 'http://localhost:8080/partidas'
		}).done(function (partidas) {
			console.log('Partidas loaded: ' + JSON.stringify(partidas));
			callback(partidas);
		});
	},

	mostrarJugadores: function(){
		var textoNombre;
		for(var i=0; i<5; i++){
			if(jugsRanking[i]!==undefined){
				textoNombre = jugsRanking[i];
			}else textoNombre = '-';
			that.add.text(that.world.centerX, (that.world.centerY-200)+50*i, (i+1) + '. '+textoNombre, {fill: "#ffffff"});
		}
	},

	obtenerJugador: function(){
		$.ajax({
			url: 'http://localhost:8080/jugadores/' + game.id_jugador
		}).done(function (jugador) {
			console.log('Jugador loaded: ' + JSON.stringify(jugador));

		});
		return jugador;
	},

	addPartida: function(a, b, nombrePartida){

		var nomPartida = prompt("Introduce aquí el nombre de la partida", "Partida");
		var numJugs = prompt("Introduce aquí el número de jugadores", "2");

		var jugCreador = that.obtenerJugador();

		var p_jav = {
				nombre: nomPartida,
				numJugMax: numJugs,
				jugsPartida: [jugCreador]
		}

		partidasEnJuego.push(p_jav);

		var offset = texto_partidasEnJuego.length;
		var p = that.add.text(that.world.centerX-150, (that.world.centerY-150)+50*offset,nomPartida+': para '+numJugs+' jugadores',{fill: "#ffffff"});
		p.inputEnabled = true;
		texto_partidasEnJuego.push(p);

	},

	mostrarTexto: function(){
		texto = that.add.text(that.world.centerX, that.world.centerY-250,'L O V E  L E T T E R  O N L I N E',{fill: "#ffffff"});
		texto.anchor.x = 0.5;
		texto = that.add.text(that.world.centerX, that.world.centerY+150,'Volver',{fill: "#ffffff"});
		texto.anchor.x = 0.5;
		texto.inputEnabled = true;
		texto.events.onInputDown.add(that.volver, this);

		texto_AddPartida = that.add.text(that.world.centerX-150, that.world.centerY-200,'Crear partida',{fill: "#ffffff"});
		texto_AddPartida.anchor.x = 0.5;
		texto_AddPartida.inputEnabled = true;
		texto_AddPartida.events.onInputDown.add(that.addPartida, this, 0, "Partida 1");
	}
};<|endoftext|>


<commit_before>import Color from "./Color.js";
import defaultValue from "./defaultValue.js";
import defined from "./defined.js";
import DeveloperError from "./DeveloperError.js";

function measureText(context2D, textString, font, stroke, fill) {
  var metrics = context2D.measureText(textString);
  var isSpace = !/\S/.test(textString);

  if (!isSpace) {
    var fontSize = document.defaultView
      .getComputedStyle(context2D.canvas)
      .getPropertyValue("font-size")
      .replace("px", "");
    var canvas = document.createElement("canvas");
    var padding = 100;
    var width = (metrics.width + padding) | 0;
    var height = 3 * fontSize;
    var baseline = height / 2;
    canvas.width = width;
    canvas.height = height;

    var ctx = canvas.getContext("2d");
    ctx.font = font;
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, canvas.width + 1, canvas.height + 1);

    if (stroke) {
      ctx.strokeStyle = "black";
      ctx.lineWidth = context2D.lineWidth;
      ctx.strokeText(textString, padding / 2, baseline);
    }

    if (fill) {
      ctx.fillStyle = "black";
      ctx.fillText(textString, padding / 2, baseline);
    }

    // Context image data has width * height * 4 elements, because
    // each pixel's R, G, B and A are consecutive values in the array.
    var pixelData = ctx.getImageData(0, 0, width, height).data;
    var length = pixelData.length;
    var width4 = width * 4;
    var i, j;

    var ascent, descent;
    // Find the number of rows (from the top) until the first non-white pixel
    for (i = 0; i < length; ++i) {
      if (pixelData[i] !== 255) {
        ascent = (i / width4) | 0;
        break;
      }
    }

    // Find the number of rows (from the bottom) until the first non-white pixel
    for (i = length - 1; i >= 0; --i) {
      if (pixelData[i] !== 255) {
        descent = (i / width4) | 0;
        break;
      }
    }

    var minx = -1;
    // For each column, for each row, check for first non-white pixel
    for (i = 0; i < width && minx === -1; ++i) {
      for (j = 0; j < height; ++j) {
        var pixelIndex = i * 4 + j * width4;
        if (
          pixelData[pixelIndex] !== 255 ||
          pixelData[pixelIndex + 1] !== 255 ||
          pixelData[pixelIndex + 2] !== 255 ||
          pixelData[pixelIndex + 3] !== 255
        ) {
          minx = i;
          break;
        }
      }
    }

    return {
      width: metrics.width,
      height: descent - ascent,
      ascent: baseline - ascent,
      descent: descent - baseline,
      minx: minx - padding / 2,
    };
  }

  return {
    ascent: 0,
    descent: 0,
    minx: 0,
    width: 0,
    height: 0,
  };
}

var imageSmoothingEnabledName;

/**
 * Writes the given text into a new canvas.  The canvas will be sized to fit the text.
 * If text is blank, returns undefined.
 *
 * @param {String} text The text to write.
 * @param {Object} [options] Object with the following properties:
 * @param {String} [options.font='10px sans-serif'] The CSS font to use.
 * @param {String} [options.textBaseline='bottom'] The baseline of the text.
 * @param {Boolean} [options.fill=true] Whether to fill the text.
 * @param {Boolean} [options.stroke=false] Whether to stroke the text.
 * @param {Color} [options.fillColor=Color.WHITE] The fill color.
 * @param {Color} [options.strokeColor=Color.BLACK] The stroke color.
 * @param {Number} [options.strokeWidth=1] The stroke width.
 * @param {Color} [options.backgroundColor=Color.TRANSPARENT] The background color of the canvas.
 * @param {Number} [options.padding=0] The pixel size of the padding to add around the text.
 * @returns {HTMLCanvasElement|undefined} A new canvas with the given text drawn into it.  The dimensions object
 *                   from measureText will also be added to the returned canvas. If text is
 *                   blank, returns undefined.
 * @function writeTextToCanvas
 */
function writeTextToCanvas(text, options) {
  //>>includeStart('debug', pragmas.debug);
  if (!defined(text)) {
    throw new DeveloperError("text is required.");
  }
  //>>includeEnd('debug');
  if (text === "") {
    return undefined;
  }

  options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  var font = defaultValue(options.font, "10px sans-serif");
  var stroke = defaultValue(options.stroke, false);
  var fill = defaultValue(options.fill, true);
  var strokeWidth = defaultValue(options.strokeWidth, 1);
  var backgroundColor = defaultValue(
    options.backgroundColor,
    Color.TRANSPARENT
  );
  var padding = defaultValue(options.padding, 0);
  var doublePadding = padding * 2.0;

  var canvas = document.createElement("canvas");
  canvas.width = 1;
  canvas.height = 1;
  canvas.style.font = font;
  var context2D = canvas.getContext("2d");

  if (!defined(imageSmoothingEnabledName)) {
    if (defined(context2D.imageSmoothingEnabled)) {
      imageSmoothingEnabledName = "imageSmoothingEnabled";
    } else if (defined(context2D.mozImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "mozImageSmoothingEnabled";
    } else if (defined(context2D.webkitImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "webkitImageSmoothingEnabled";
    } else if (defined(context2D.msImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "msImageSmoothingEnabled";
    }
  }

  context2D.font = font;
  context2D.lineJoin = "round";
  context2D.lineWidth = strokeWidth;
  context2D[imageSmoothingEnabledName] = false;

  // in order for measureText to calculate style, the canvas has to be
  // (temporarily) added to the DOM.
  canvas.style.visibility = "hidden";
  document.body.appendChild(canvas);

  var dimensions = measureText(context2D, text, font, stroke, fill);
  // Set canvas.dimensions to be accessed in LabelCollection
  canvas.dimensions = dimensions;

  document.body.removeChild(canvas);
  canvas.style.visibility = "";

  // Some characters, such as the letter j, have a non-zero starting position.
  // This value is used for kerning later, but we need to take it into account
  // now in order to draw the text completely on the canvas
  var x = -dimensions.minx;

  // Expand the width to include the starting position.
  var width = Math.ceil(dimensions.width) + x + doublePadding;

  // While the height of the letter is correct, we need to adjust
  // where we start drawing it so that letters like j and y properly dip
  // below the line.

  var height = dimensions.height + doublePadding;
  var baseline = height - dimensions.ascent + padding;
  var y = height - baseline + doublePadding;

  canvas.width = width;
  canvas.height = height;

  // Properties must be explicitly set again after changing width and height
  context2D.font = font;
  context2D.lineJoin = "round";
  context2D.lineWidth = strokeWidth;
  context2D[imageSmoothingEnabledName] = false;

  // Draw background
  if (backgroundColor !== Color.TRANSPARENT) {
    context2D.fillStyle = backgroundColor.toCssColorString();
    context2D.fillRect(0, 0, canvas.width, canvas.height);
  }

  if (stroke) {
    var strokeColor = defaultValue(options.strokeColor, Color.BLACK);
    context2D.strokeStyle = strokeColor.toCssColorString();
    context2D.strokeText(text, x + padding, y);
  }

  if (fill) {
    var fillColor = defaultValue(options.fillColor, Color.WHITE);
    context2D.fillStyle = fillColor.toCssColorString();
    context2D.fillText(text, x + padding, y);
  }

  return canvas;
}
export default writeTextToCanvas;
<commit_msg>Reorder properties<commit_after>import Color from "./Color.js";
import defaultValue from "./defaultValue.js";
import defined from "./defined.js";
import DeveloperError from "./DeveloperError.js";

function measureText(context2D, textString, font, stroke, fill) {
  var metrics = context2D.measureText(textString);
  var isSpace = !/\S/.test(textString);

  if (!isSpace) {
    var fontSize = document.defaultView
      .getComputedStyle(context2D.canvas)
      .getPropertyValue("font-size")
      .replace("px", "");
    var canvas = document.createElement("canvas");
    var padding = 100;
    var width = (metrics.width + padding) | 0;
    var height = 3 * fontSize;
    var baseline = height / 2;
    canvas.width = width;
    canvas.height = height;

    var ctx = canvas.getContext("2d");
    ctx.font = font;
    ctx.fillStyle = "white";
    ctx.fillRect(0, 0, canvas.width + 1, canvas.height + 1);

    if (stroke) {
      ctx.strokeStyle = "black";
      ctx.lineWidth = context2D.lineWidth;
      ctx.strokeText(textString, padding / 2, baseline);
    }

    if (fill) {
      ctx.fillStyle = "black";
      ctx.fillText(textString, padding / 2, baseline);
    }

    // Context image data has width * height * 4 elements, because
    // each pixel's R, G, B and A are consecutive values in the array.
    var pixelData = ctx.getImageData(0, 0, width, height).data;
    var length = pixelData.length;
    var width4 = width * 4;
    var i, j;

    var ascent, descent;
    // Find the number of rows (from the top) until the first non-white pixel
    for (i = 0; i < length; ++i) {
      if (pixelData[i] !== 255) {
        ascent = (i / width4) | 0;
        break;
      }
    }

    // Find the number of rows (from the bottom) until the first non-white pixel
    for (i = length - 1; i >= 0; --i) {
      if (pixelData[i] !== 255) {
        descent = (i / width4) | 0;
        break;
      }
    }

    var minx = -1;
    // For each column, for each row, check for first non-white pixel
    for (i = 0; i < width && minx === -1; ++i) {
      for (j = 0; j < height; ++j) {
        var pixelIndex = i * 4 + j * width4;
        if (
          pixelData[pixelIndex] !== 255 ||
          pixelData[pixelIndex + 1] !== 255 ||
          pixelData[pixelIndex + 2] !== 255 ||
          pixelData[pixelIndex + 3] !== 255
        ) {
          minx = i;
          break;
        }
      }
    }

    return {
      width: metrics.width,
      height: descent - ascent,
      ascent: baseline - ascent,
      descent: descent - baseline,
      minx: minx - padding / 2,
    };
  }

  return {
    width: 0,
    height: 0,
    ascent: 0,
    descent: 0,
    minx: 0,
  };
}

var imageSmoothingEnabledName;

/**
 * Writes the given text into a new canvas.  The canvas will be sized to fit the text.
 * If text is blank, returns undefined.
 *
 * @param {String} text The text to write.
 * @param {Object} [options] Object with the following properties:
 * @param {String} [options.font='10px sans-serif'] The CSS font to use.
 * @param {String} [options.textBaseline='bottom'] The baseline of the text.
 * @param {Boolean} [options.fill=true] Whether to fill the text.
 * @param {Boolean} [options.stroke=false] Whether to stroke the text.
 * @param {Color} [options.fillColor=Color.WHITE] The fill color.
 * @param {Color} [options.strokeColor=Color.BLACK] The stroke color.
 * @param {Number} [options.strokeWidth=1] The stroke width.
 * @param {Color} [options.backgroundColor=Color.TRANSPARENT] The background color of the canvas.
 * @param {Number} [options.padding=0] The pixel size of the padding to add around the text.
 * @returns {HTMLCanvasElement|undefined} A new canvas with the given text drawn into it.  The dimensions object
 *                   from measureText will also be added to the returned canvas. If text is
 *                   blank, returns undefined.
 * @function writeTextToCanvas
 */
function writeTextToCanvas(text, options) {
  //>>includeStart('debug', pragmas.debug);
  if (!defined(text)) {
    throw new DeveloperError("text is required.");
  }
  //>>includeEnd('debug');
  if (text === "") {
    return undefined;
  }

  options = defaultValue(options, defaultValue.EMPTY_OBJECT);
  var font = defaultValue(options.font, "10px sans-serif");
  var stroke = defaultValue(options.stroke, false);
  var fill = defaultValue(options.fill, true);
  var strokeWidth = defaultValue(options.strokeWidth, 1);
  var backgroundColor = defaultValue(
    options.backgroundColor,
    Color.TRANSPARENT
  );
  var padding = defaultValue(options.padding, 0);
  var doublePadding = padding * 2.0;

  var canvas = document.createElement("canvas");
  canvas.width = 1;
  canvas.height = 1;
  canvas.style.font = font;
  var context2D = canvas.getContext("2d");

  if (!defined(imageSmoothingEnabledName)) {
    if (defined(context2D.imageSmoothingEnabled)) {
      imageSmoothingEnabledName = "imageSmoothingEnabled";
    } else if (defined(context2D.mozImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "mozImageSmoothingEnabled";
    } else if (defined(context2D.webkitImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "webkitImageSmoothingEnabled";
    } else if (defined(context2D.msImageSmoothingEnabled)) {
      imageSmoothingEnabledName = "msImageSmoothingEnabled";
    }
  }

  context2D.font = font;
  context2D.lineJoin = "round";
  context2D.lineWidth = strokeWidth;
  context2D[imageSmoothingEnabledName] = false;

  // in order for measureText to calculate style, the canvas has to be
  // (temporarily) added to the DOM.
  canvas.style.visibility = "hidden";
  document.body.appendChild(canvas);

  var dimensions = measureText(context2D, text, font, stroke, fill);
  // Set canvas.dimensions to be accessed in LabelCollection
  canvas.dimensions = dimensions;

  document.body.removeChild(canvas);
  canvas.style.visibility = "";

  // Some characters, such as the letter j, have a non-zero starting position.
  // This value is used for kerning later, but we need to take it into account
  // now in order to draw the text completely on the canvas
  var x = -dimensions.minx;

  // Expand the width to include the starting position.
  var width = Math.ceil(dimensions.width) + x + doublePadding;

  // While the height of the letter is correct, we need to adjust
  // where we start drawing it so that letters like j and y properly dip
  // below the line.

  var height = dimensions.height + doublePadding;
  var baseline = height - dimensions.ascent + padding;
  var y = height - baseline + doublePadding;

  canvas.width = width;
  canvas.height = height;

  // Properties must be explicitly set again after changing width and height
  context2D.font = font;
  context2D.lineJoin = "round";
  context2D.lineWidth = strokeWidth;
  context2D[imageSmoothingEnabledName] = false;

  // Draw background
  if (backgroundColor !== Color.TRANSPARENT) {
    context2D.fillStyle = backgroundColor.toCssColorString();
    context2D.fillRect(0, 0, canvas.width, canvas.height);
  }

  if (stroke) {
    var strokeColor = defaultValue(options.strokeColor, Color.BLACK);
    context2D.strokeStyle = strokeColor.toCssColorString();
    context2D.strokeText(text, x + padding, y);
  }

  if (fill) {
    var fillColor = defaultValue(options.fillColor, Color.WHITE);
    context2D.fillStyle = fillColor.toCssColorString();
    context2D.fillText(text, x + padding, y);
  }

  return canvas;
}
export default writeTextToCanvas;
<|endoftext|>


<commit_before>'use strict';

let getTokenDao = require('./token.dao.js');
let utils = require('../utils');
let uuid = require('node-uuid');
let tokenService, tokenDao;

module.exports = function(config) {
  tokenDao = getTokenDao(config);

  function save(tokenObj) {
    let id = uuid.v4().replace(new RegExp('-', 'g'), '');
    let token = uuid.v4().replace(new RegExp('-', 'g'), '');
    let baseTokenProps = {
      id,
      tokenEncrypted: utils.encrypt(token, config.crypto),
      expiresAt: Date.now() + config.tokens.timeToExpiry
    }
    let tokenProps = Object.assign(baseTokenProps, tokenObj);

    utils.appendCreatedAt(tokenProps);

    if (tokenProps.scopes && Array.isArray(tokenProps.scopes)) {
      tokenProps.scopes = JSON.stringify(tokenProps.scopes.sort());
    }

    return tokenDao.save(tokenProps)
    .then(() => id.concat('|', token));
  }

  function findOrSave(tokenObj) {
    return find(tokenObj)
    .then(token => {
      return token ? token : save(tokenObj);
    });
  }

  function find(tokenObj) {
    let tokenQueryCriteria = Object.assign({}, tokenObj);

    if (tokenQueryCriteria.scopes && Array.isArray(tokenQueryCriteria.scopes)) {
      tokenQueryCriteria.scopes = JSON.stringify(tokenQueryCriteria.scopes.sort());
    }

    return tokenDao.find(tokenQueryCriteria)
    .then((token) => {
      if (!token) {
        return null;
      }
      return formToken(token);
    })
  }

  function get(_token) {
    let tokenId = _token.split('|')[0];

    return tokenDao.get(tokenId)
    .then(token => {
      if (!token) {
        return null;
      }

      if (token.scopes) {
        token.scopes = JSON.parse(token.scopes);
      }

      token.tokenDecrypted = utils.decrypt(token.tokenEncrypted, config.crypto);
      delete token.tokenEncrypted;

      return token;
    })
  }

  function formToken(tokenObj) {
    return tokenObj.id.concat('|', utils.decrypt(tokenObj.tokenEncrypted, config.crypto))
  }

  tokenService = {
    findOrSave,
    find,
    save,
    get
  };

  return tokenService;
}<commit_msg>Revert "added token service (#128)" (#131)<commit_after>'use strict';

let getTokenDao = require('./token.dao.js');
let utils = require('../utils');
let uuid = require('node-uuid');
let tokenService, tokenDao;

module.exports = function(config) {
  tokenDao = getTokenDao(config);

  function save(tokenObj) {
    let id = uuid.v4().replace(new RegExp('-', 'g'), '');
    let token = uuid.v4().replace(new RegExp('-', 'g'), '');
    let baseTokenProps = {
      id,
      tokenEncrypted: utils.encrypt(token, config.crypto),
      expiresAt: Date.now() + config.tokens.timeToExpiry
    }
    let tokenProps = Object.assign(baseTokenProps, tokenObj);

    utils.appendCreatedAt(tokenProps);

    if (tokenProps.scopes && Array.isArray(tokenProps.scopes)) {
      tokenProps.scopes = JSON.stringify(tokenProps.scopes.sort());
    }

    return tokenDao.save(tokenProps)
    .then(() => id.concat('|', token));
  }

  function findOrSave(tokenObj) {
    return find(tokenObj)
    .then(token => {
      return token ? token : save(tokenObj);
    });
  }

  function find(tokenObj) {
    let tokenQueryCriteria = Object.assign({}, tokenObj);

    if (tokenQueryCriteria.scopes && Array.isArray(tokenQueryCriteria.scopes)) {
      tokenQueryCriteria.scopes = JSON.stringify(tokenQueryCriteria.scopes.sort());
    }

    return tokenDao.find(tokenQueryCriteria)
    .then((token) => {
      if (!token) {
        return null;
      }
      return formToken(token);
    })
  }

  function get(_token) {
    let tokenId = _token.split('|')[0];

    return tokenDao.get(tokenId)
    .then(token => {
      if (!token) {
        return null;
      }

      if (token.scopes) {
        token.scopes = JSON.parse(token.scopes);
      }

      token.tokenDecrypted = utils.decrypt(token.tokenEncrypted, config.crypto);
      delete token.tokenEncrypted;

      return token;
    })
  }

  function formToken(tokenObj) {
    return tokenObj.id.concat('|', utils.decrypt(tokenObj.tokenEncrypted, config.crypto))
  }

  tokenService = {
    findOrSave,
    find,
    save,
    get
  };

  return tokenService;
}<|endoftext|>


<commit_before>
	var Content = document.getElementById('content');
	var Navbar	= document.getElementById('navbar');
	var Sidebar = document.getElementById('sidebar');
	var Title	= document.getElementById('navTitle');
	var Footer	= document.getElementById('footer');
	var searchNavbar= document.getElementById('searchNavbar');
	var baseNavbar	= document.getElementById('baseNavbar');
	var navRight	= document.getElementById('navRight');

	window.addEventListener('load', function() {
		window.addEventListener('hashchange', app.navigate);
		document.addEventListener('visibilitychange', function() {
		  if(document.hidden) {
			console.log('save')
		  }
		});
		Footer.style.display = 'none'
		app.navigate()
	});

	var app = {
		navigate: function(hash){

				var hashParams = {};
				var e,
					a = /\+/g,  // Regex for replacing addition symbol with a space
					r = /([^&;=]+)=?([^&;]*)/g,
					d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
					q = window.location.hash.substring(1);

				while (e = r.exec(q)) hashParams[d(e[1])] = d(e[2]);

				if(hashParams.v){
					switch(hashParams.v){
						case 'about':

						break;
					}
				}

		},
		msg: function(str, duration, c, callback){
			Materialize.toast(str, duration || 3000, c || '', callback || function(){return false;})
		},
		modal: function(param, callback){
			var index =  $('.modal').length + 1;
			var id = param.id || ('modal'+index.toString());
			$('#'+id).remove();
			if(param.buttons){
				var footer = crEl('div',{c:'modal-footer'});
				for(var i=0, l= param.buttons.length; i<l; i++){
					//param.buttons[i].onclick = function(){$(".lean-overlay").remove()}
					footer.appendChild(param.buttons[i])
				}
			}
			document.body.appendChild(crEl('div',{id:id, class:'modal '+ (param.c || '')},
				crEl('div',{c:'modal-content'}, param.content),
				footer || ''
			));
			return new Promise(function(resolve, reject) {

				$('#' + id ).openModal({
					in_duration: param.in_duration || 350,
					out_duration: param.out_duration || 250,
					dismissible: !!(param.dismissible  || true),
					opacity: param.opacity || .5,
					ready: function() {
						resolve(id);
						if(typeof(callback)=='function'){callback(id)}
					},
					complete: function() {

						setTimeout( function(){ $('#' + id ).remove(); }, 1);
					}
				})

			//	$('#modal1').openModal();
			//	$('#modal1').closeModal();

			})

		},

		alert: function(message, alertCallback, title, buttonName){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.alert)!=='undefined'){
				navigator.notification.alert(message, alertCallback, title, buttonName);
			}
		},
		confirm: function(message, confirmCallback, title, buttonLabels){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.confirm)!=='undefined'){
				navigator.notification.confirm(message, confirmCallback, title, buttonLabels);
			}
		},
		prompt: function(message, promptCallback, title, buttonLabels, defaultText){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.prompt)!=='undefined'){
				navigator.notification.prompt(message, promptCallback, title, buttonLabels, defaultText);
			}
		},
		beep: function(times){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.beep)!=='undefined'){
				navigator.notification.beep(times);
			}
		},
		vibrate: function(milliseconds){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.vibrate)!=='undefined'){
				navigator.notification.vibrate(milliseconds);
			}
		}
	};

		var ls = {
			set:function(key,value){window.localStorage.setItem(key, value)},
			get:function(key){return window.localStorage.getItem(key)},
			unset:function(key){ window.localStorage.removeItem(key)},
			clear:function(){window.localStorage.clear();}
		}

		var MIcon = function(name, attrs){
			var e = crEl('i', attrs, name);
			e.classList.add('material-icons');
			return e;
		}

		try {
			app.db = openDatabase("base","0.1","Основная база кеша приложения", 2 * 1024 * 1024);
			console.log(app.db)
		} catch(e) {
			// Error handling code goes here.
			if (e == 2) {
				// Version number mismatch.
				alert("Invalid database version.");
			} else {
				alert("Unknown error "+e+".");
			}

		}

		if(!app.db){alert("Failed to connect to database.");}

		app.error = function(d){
			console.error(d)
		}

		app.db.transaction(function(tx) {
			tx.executeSql("CREATE TABLE IF NOT EXISTS points (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, lat REAL, lon REAL, color TEXT);", [], console.log, app.error);
			tx.executeSql("CREATE TABLE IF NOT EXISTS notes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, id_point INTEGER, date INTEGER, temp REAL);", [], null, null);
		});

		app.sqlError =  function(tx,res){
		console.error(tx,res)
			if(res && res.error){
				alert(res.error)
			}
		}

		document.title = "Tempus";
		Title.innerHTML = "Tempus";

		navRight.innerHTML = ''
		navRight.appendChild(crEl('li',
			crEl('a',{href:'javascript:void(0)', id:'initSearch', e:{click: function(){
				searchNavbar.classList.toggle('hide')
				baseNavbar.classList.toggle('hide')
				document.getElementById("search").focus();
				document.getElementById("searchForm").onsubmit = function(){
					Content.appendChild(crEl('p', document.getElementById("search").value))
					document.getElementById("search").value = "";
					document.getElementById("search").focus()
				}
			}}}, new MIcon('create'))
		))

		Content.innerHTML = '';

		document.getElementById("searchClear").onclick = function(){
			document.getElementById("search").value = "";
			searchNavbar.classList.toggle('hide')
			baseNavbar.classList.toggle('hide')

		}

		Sidebar.innerHTML = '';
		Sidebar.appendChild(crEl('li', crEl('div',{s:'padding:40px 20px; margin-bottom:16px; line-height:16px', c:'blue white-text'}, crEl('strong',{s:'font-size:30px; font-weight:200'},'TEMPUS'), crEl('br'),crEl('small',{s:'font-size:13px; font-weight:200; opacity:0.8'},'\u00a0 weather aggregator'))))

		Sidebar.appendChild(crEl('li', crEl('a',{c:'waves-effect',href:'javascript:app.addPoint()'}, 'Add location')));
		Sidebar.appendChild(crEl('li',{c:'divider'}))
		Sidebar.appendChild(crEl('li', crEl('a',{c:'waves-effect',href:'#about'}, 'О программе')));

		$("#sidebarCollapser").sideNav({closeOnClick: true });

	/*
		app.db.transaction(function(tx) {
			tx.executeSql("SELECT name FROM travel where id=" + id, [], function(tx, result){
				document.title = result.rows[0].name;
				Title.innerHTML = result.rows[0].name;

			}, app.sqlError);
		})
		*/

		app.addPoint = function(){

					if (navigator.geolocation) {
						navigator.geolocation.getCurrentPosition(function(position){
							lat = position.coords.latitude
							lon = position.coords.longitude

							app.db.transaction(function(tx) {
								tx.executeSql("INSERT INTO points (name, lat, lon) values(?,?,?)", [prompt('Name of location:', 'Home, sweet home'), lat, lon], function(tx, result){
									app.msg("Success","success")
								}, app.sqlError);
							})

						});
					} else {
						alert('geolocation is not available')
					}

		}

		var slider = crEl('div',{c:'carousel carousel-slider center', d:{indicators:true}});
		Content.appendChild(slider)

			app.db.transaction(function(tx) {
				tx.executeSql("SELECT id, name, lat, lon FROM points", [], function(tx, result){
					if(result.rows && result.rows.length){
						function Slide(point){

						$.getJSON('//api.openweathermap.org/data/2.5/weather?units=metric&lat='+point.lat+'&lon='+point.lon+'&cnt=' + 1 + '&APPID=cd86e235fc7c0bcac549923298b74a71&lang=ru', function(w){

							console.info(w)
						})

							return crEl('div',{c:'carousel-item'},
								crEl('h2', point.name)
							)
						}
						for(var i=0; i<result.rows.length; i++){
							slider.appendChild(new Slide(result.rows[i]))
						}
						$(slider).carousel({full_width: true});
					}

				}, app.sqlError);
			})

<commit_msg>0.0.1<commit_after>
	var Content = document.getElementById('content');
	var Navbar	= document.getElementById('navbar');
	var Sidebar = document.getElementById('sidebar');
	var Title	= document.getElementById('navTitle');
	var Footer	= document.getElementById('footer');
	var searchNavbar= document.getElementById('searchNavbar');
	var baseNavbar	= document.getElementById('baseNavbar');
	var navRight	= document.getElementById('navRight');

	window.addEventListener('load', function() {
		window.addEventListener('hashchange', app.navigate);
		document.addEventListener('visibilitychange', function() {
		  if(document.hidden) {
			console.log('save')
		  }
		});
		Footer.style.display = 'none'
		app.navigate()
	});

	var app = {
		navigate: function(hash){

				var hashParams = {};
				var e,
					a = /\+/g,  // Regex for replacing addition symbol with a space
					r = /([^&;=]+)=?([^&;]*)/g,
					d = function (s) { return decodeURIComponent(s.replace(a, " ")); },
					q = window.location.hash.substring(1);

				while (e = r.exec(q)) hashParams[d(e[1])] = d(e[2]);

				if(hashParams.v){
					switch(hashParams.v){
						case 'about':

						break;
					}
				}

		},
		msg: function(str, duration, c, callback){
			Materialize.toast(str, duration || 3000, c || '', callback || function(){return false;})
		},
		modal: function(param, callback){
			var index =  $('.modal').length + 1;
			var id = param.id || ('modal'+index.toString());
			$('#'+id).remove();
			if(param.buttons){
				var footer = crEl('div',{c:'modal-footer'});
				for(var i=0, l= param.buttons.length; i<l; i++){
					//param.buttons[i].onclick = function(){$(".lean-overlay").remove()}
					footer.appendChild(param.buttons[i])
				}
			}
			document.body.appendChild(crEl('div',{id:id, class:'modal '+ (param.c || '')},
				crEl('div',{c:'modal-content'}, param.content),
				footer || ''
			));
			return new Promise(function(resolve, reject) {

				$('#' + id ).openModal({
					in_duration: param.in_duration || 350,
					out_duration: param.out_duration || 250,
					dismissible: !!(param.dismissible  || true),
					opacity: param.opacity || .5,
					ready: function() {
						resolve(id);
						if(typeof(callback)=='function'){callback(id)}
					},
					complete: function() {

						setTimeout( function(){ $('#' + id ).remove(); }, 1);
					}
				})

			//	$('#modal1').openModal();
			//	$('#modal1').closeModal();

			})

		},

		alert: function(message, alertCallback, title, buttonName){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.alert)!=='undefined'){
				navigator.notification.alert(message, alertCallback, title, buttonName);
			}
		},
		confirm: function(message, confirmCallback, title, buttonLabels){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.confirm)!=='undefined'){
				navigator.notification.confirm(message, confirmCallback, title, buttonLabels);
			}
		},
		prompt: function(message, promptCallback, title, buttonLabels, defaultText){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.prompt)!=='undefined'){
				navigator.notification.prompt(message, promptCallback, title, buttonLabels, defaultText);
			}
		},
		beep: function(times){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.beep)!=='undefined'){
				navigator.notification.beep(times);
			}
		},
		vibrate: function(milliseconds){
			if(navigator && typeof(navigator.notification)!=='undefined' && typeof(navigator.notification.vibrate)!=='undefined'){
				navigator.notification.vibrate(milliseconds);
			}
		}
	};

		var ls = {
			set:function(key,value){window.localStorage.setItem(key, value)},
			get:function(key){return window.localStorage.getItem(key)},
			unset:function(key){ window.localStorage.removeItem(key)},
			clear:function(){window.localStorage.clear();}
		}

		var MIcon = function(name, attrs){
			var e = crEl('i', attrs, name);
			e.classList.add('material-icons');
			return e;
		}

		try {
			app.db = openDatabase("base","0.1","Основная база кеша приложения", 2 * 1024 * 1024);
			console.log(app.db)
		} catch(e) {
			// Error handling code goes here.
			if (e == 2) {
				// Version number mismatch.
				alert("Invalid database version.");
			} else {
				alert("Unknown error "+e+".");
			}

		}

		if(!app.db){alert("Failed to connect to database.");}

		app.error = function(d){
			console.error(d)
		}

		app.db.transaction(function(tx) {
			tx.executeSql("CREATE TABLE IF NOT EXISTS points (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT, lat REAL, lon REAL, color TEXT);", [], console.log, app.error);
			tx.executeSql("CREATE TABLE IF NOT EXISTS notes (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, id_point INTEGER, date INTEGER, temp REAL);", [], null, null);
		});

		app.sqlError =  function(tx,res){
		console.error(tx,res)
			if(res && res.error){
				alert(res.error)
			}
		}

		document.title = "Tempus";
		Title.innerHTML = "Tempus";

		navRight.innerHTML = ''
		navRight.appendChild(crEl('li',
			crEl('a',{href:'javascript:void(0)', id:'initSearch', e:{click: function(){
				searchNavbar.classList.toggle('hide')
				baseNavbar.classList.toggle('hide')
				document.getElementById("search").focus();
				document.getElementById("searchForm").onsubmit = function(){
					Content.appendChild(crEl('p', document.getElementById("search").value))
					document.getElementById("search").value = "";
					document.getElementById("search").focus()
				}
			}}}, new MIcon('create'))
		))

		Content.innerHTML = '';

		document.getElementById("searchClear").onclick = function(){
			document.getElementById("search").value = "";
			searchNavbar.classList.toggle('hide')
			baseNavbar.classList.toggle('hide')

		}

		Sidebar.innerHTML = '';
		Sidebar.appendChild(crEl('li', crEl('div',{s:'padding:40px 20px; margin-bottom:16px; line-height:16px', c:'blue white-text'}, crEl('strong',{s:'font-size:30px; font-weight:200'},'TEMPUS'), crEl('br'),crEl('small',{s:'font-size:13px; font-weight:200; opacity:0.8'},'\u00a0 weather aggregator'))))

		Sidebar.appendChild(crEl('li', crEl('a',{c:'waves-effect',href:'javascript:app.addPoint()'}, 'Add location')));
		Sidebar.appendChild(crEl('li',{c:'divider'}))
		Sidebar.appendChild(crEl('li', crEl('a',{c:'waves-effect',href:'#about'}, 'О программе')));

		$("#sidebarCollapser").sideNav({closeOnClick: true });

	/*
		app.db.transaction(function(tx) {
			tx.executeSql("SELECT name FROM travel where id=" + id, [], function(tx, result){
				document.title = result.rows[0].name;
				Title.innerHTML = result.rows[0].name;

			}, app.sqlError);
		})
		*/

		app.addPoint = function(){

					if (navigator.geolocation) {
						navigator.geolocation.getCurrentPosition(function(position){
							lat = position.coords.latitude
							lon = position.coords.longitude

							app.db.transaction(function(tx) {
								tx.executeSql("INSERT INTO points (name, lat, lon) values(?,?,?)", [prompt('Name of location:', 'Home, sweet home'), lat, lon], function(tx, result){
									app.msg("Success","success")
								}, app.sqlError);
							})

						});
					} else {
						alert('geolocation is not available')
					}

		}

		var slider = crEl('div',{c:'carousel carousel-slider center', d:{indicators:true}});
		Content.appendChild(slider)

			app.db.transaction(function(tx) {
				tx.executeSql("SELECT id, name, lat, lon FROM points", [], function(tx, result){
					if(result.rows && result.rows.length){
						function Slide(point){

						$.getJSON('http://api.openweathermap.org/data/2.5/weather', {units:'metric',lat:point.lat, lon: point.lon, APPID:'cd86e235fc7c0bcac549923298b74a71', lang:'ru'}, function(w){
							console.info(w)
						})

						$.getJSON('https://api.weather.yandex.ru/v1/forecast', {units:'metric',lat:point.lat, lon: point.lon, extra:'true', lang:'ru'}, function(w){
							console.info(w)
						})

							return crEl('div',{c:'carousel-item'},
								crEl('h2', point.name)
							)
						}
						for(var i=0; i<result.rows.length; i++){
							slider.appendChild(new Slide(result.rows[i]))
						}
						$(slider).carousel({full_width: true});
					}

				}, app.sqlError);
			})

<|endoftext|>


<commit_before>Array.prototype.equals = function(array) {
  // if the other array is a falsy value, return
  if (!array)
    return false;

  // compare lengths - can save a lot of time
  if (this.length != array.length)
    return false;

  for (var i = 0, l = this.length; i < l; i++) {
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
      // recurse into the nested arrays
      if (!this[i].equals(array[i]))
        return false;
    } else if (this[i] != array[i]) {
      // Warning - two different object instances will never be equal: {x:20} != {x:20}
      return false;
    }
  }
  return true;
}

var installed_date = new Date().getTime();

var DefaultSettings = {
  "installed_date": installed_date,
  "timestamp": "absolute",
  "full_after_24h": false,
  "name_display": "both",
  "typeahead_display_username_only": true,
  "circled_avatars": true,
  "no_columns_icons": true,
  "yt_rm_button": true,
  "small_icons_compose": false,
  "grayscale_notification_icons": false,
  "url_redirection": true,
  "blurred_modals": true,
  "only_one_thumbnails": true,
  "minimal_mode": true,
  "flash_tweets": "mentions",
  "shorten_text": false,
  "share_button": true,
  "providers": {
    "500px": true,
    "bandcamp": true,
    "cloudapp": true,
    "dailymotion": true,
    "deviantart": true,
    "dribbble": true,
    "droplr": true,
    "flickr": true,
    "imgly": true,
    "imgur": true,
    "instagram": true,
    "mobyto": true,
    "soundcloud": true,
    "ted": true,
    "toresize": true,
    "tumblr": true,
    "vimeo": true,
    "yfrog": true,
  }
};

var currentOptions;

function onInstall() {
  chrome.tabs.create({
    url: "options/options.html#installed"
  });
}

function onUpdate() {
  chrome.tabs.create({
    url: "options/options.html#updated"
  });
}

function getVersion() {
  var details = chrome.app.getDetails();
  return details.version;
}

var currVersion = getVersion();

if (!localStorage['version']) {
  localStorage['version'] = currVersion;
}

var prevVersion = localStorage['version'];

function init() {
  const TWEETDECK_WEB_URL = 'https://tweetdeck.twitter.com';

  /**
   * Step 2: Collate a list of all the open tabs.
   */
  function gatherTabs(urls, itemInfos) {
    var allTheTabs = [];
    var windowsChecked = 0;
    console.log("gatherTabs", itemInfos.timestamp);

    // First get all the windows...
    chrome.windows.getAll(function(windows) {
      for (var i = 0; i < windows.length; i++) {
        // ... and then all their tabs.
        chrome.tabs.getAllInWindow(windows[i].id, function(tabs) {
          windowsChecked++;
          allTheTabs = allTheTabs.concat(tabs);

          if (windowsChecked === windows.length) {
            // We have all the tabs! Search for a TweetDeck...
            openApp(urls, allTheTabs, itemInfos);
          }
        });

      }
    });
  }

  function openApp(urls, tabs, itemInfos) {
    console.log("openApp", itemInfos.timestamp);
    // Search urls in priority order...
    for (var i = 0; i < urls.length; i++) {
      var url = urls[i];

      // Search tabs...
      for (var j = 0; j < tabs.length; j++) {
        var tab = tabs[j];
        if (tab.url.indexOf(url) === 0) {
          // Found it!
          var tabId = tab.id;
          chrome.windows.update(tab.windowId, {
            focused: true
          });
          chrome.tabs.update(tabId, {
            selected: true,
            active: true,
            highlighted: true
          }, function() {
            console.log("update", itemInfos.timestamp);
            var text = itemInfos.text;
            var url = itemInfos.url;
            chrome.tabs.sendMessage(tabId, {
              text: text,
              url: url,
              timestamp: itemInfos.timestamp,
              count: 2
            })
          });
          return;
        }
      }
    }

    // Didn't find it! Open a new one!
    chrome.tabs.create({
      url: urls[0]
    }, function(tab) {
      var count = 0;
      chrome.tabs.onUpdated.addListener(function(tabId, info) {

        if (info.status == "complete") {
          count += 1;
          console.log("onUpdated", itemInfos.timestamp, count);
          chrome.tabs.sendMessage(tabId, {
            text: itemInfos.text,
            url: itemInfos.url,
            timestamp: itemInfos.timestamp,
            count: count
          });
        }
      })
    });
  };

  var clickHandler = function(info, tab) {
    var text;
    var url;

    if (info.selectionText && currentOptions.shorten_text) {
      text = "\"" + info.selectionText.substr(0, 110) + "\"";
    } else if (info.selectionText) {
      text = "\"" + info.selectionText + "\"";
    } else {
      text = tab.title.substr(0, 110);
    }

    if (info.linkUrl) {
      url = info.linkUrl
    } else {
      url = info.pageUrl;
    }

    if (info.mediaType === "image") {
      url = info.srcUrl;
      text = "";
    }

    gatherTabs([TWEETDECK_WEB_URL], {
      "text": text,
      "url": url,
      "timestamp": new Date().getTime()
    });
  };

  if (currentOptions.share_button) {
  	console.debug('Share button enabled');
    chrome.contextMenus.create({
      "title": "Share on (Better) TweetDeck",
      "contexts": ["page", "selection", "image", "link"],
      "onclick": clickHandler
    });
  }

}

chrome.storage.sync.get("BTDSettings", function(obj) {
  if (obj.BTDSettings !== undefined) {
    currentOptions = obj.BTDSettings;
    var reApply = false;

    for (var setting in DefaultSettings) {
      if (currentOptions[setting] == undefined) {
        console.debug("Defining", setting, "to default value", DefaultSettings[setting]);
        currentOptions[setting] = DefaultSettings[setting];
        reApply = true;
      }
    }

    if (currVersion != prevVersion) {
      if (!(prevVersion.split(".")[0] == currVersion.split(".")[0] && prevVersion.split(".")[1] == currVersion.split(".")[1])) {
        onUpdate();
      }
      localStorage['version'] = currVersion;
    }

    for (var provider in DefaultSettings["providers"]) {
      if (currentOptions["providers"][provider] == undefined) {
        console.log("Adding", provider, "as a new provider with value", DefaultSettings["providers"][provider]);
        currentOptions["providers"][provider] = DefaultSettings["providers"][provider];
        reApply = true;
      }
    }

    for (var setting in currentOptions) {
      if (DefaultSettings[setting] == undefined) {
        console.log("Deleting", setting);
        delete currentOptions[setting];
        reApply = true;
      }
    }

    for (var setting in currentOptions["providers"]) {
      if (DefaultSettings["providers"][setting] == undefined) {
        delete currentOptions["providers"][setting];
        reApply = true;
      }
    }

    if (reApply === true) {
      chrome.storage.sync.set({
        "BTDSettings": currentOptions
      }, function() {
        console.log("Options updated!");
        console.log(currentOptions);
        init();
      });
    } else {
      init();
    }
  } else {
    chrome.storage.sync.set({
      "BTDSettings": DefaultSettings
    }, function() {
      console.log("Default options set");
      onInstall();
      init();
    })
  }
});<commit_msg>Revert ":wrench: invert small_icon_compose default value"<commit_after>Array.prototype.equals = function(array) {
  // if the other array is a falsy value, return
  if (!array)
    return false;

  // compare lengths - can save a lot of time
  if (this.length != array.length)
    return false;

  for (var i = 0, l = this.length; i < l; i++) {
    // Check if we have nested arrays
    if (this[i] instanceof Array && array[i] instanceof Array) {
      // recurse into the nested arrays
      if (!this[i].equals(array[i]))
        return false;
    } else if (this[i] != array[i]) {
      // Warning - two different object instances will never be equal: {x:20} != {x:20}
      return false;
    }
  }
  return true;
}

var installed_date = new Date().getTime();

var DefaultSettings = {
  "installed_date": installed_date,
  "timestamp": "absolute",
  "full_after_24h": false,
  "name_display": "both",
  "typeahead_display_username_only": true,
  "circled_avatars": true,
  "no_columns_icons": true,
  "yt_rm_button": true,
  "small_icons_compose": true,
  "grayscale_notification_icons": false,
  "url_redirection": true,
  "blurred_modals": true,
  "only_one_thumbnails": true,
  "minimal_mode": true,
  "flash_tweets": "mentions",
  "shorten_text": false,
  "share_button": true,
  "providers": {
    "500px": true,
    "bandcamp": true,
    "cloudapp": true,
    "dailymotion": true,
    "deviantart": true,
    "dribbble": true,
    "droplr": true,
    "flickr": true,
    "imgly": true,
    "imgur": true,
    "instagram": true,
    "mobyto": true,
    "soundcloud": true,
    "ted": true,
    "toresize": true,
    "tumblr": true,
    "vimeo": true,
    "yfrog": true,
  }
};

var currentOptions;

function onInstall() {
  chrome.tabs.create({
    url: "options/options.html#installed"
  });
}

function onUpdate() {
  chrome.tabs.create({
    url: "options/options.html#updated"
  });
}

function getVersion() {
  var details = chrome.app.getDetails();
  return details.version;
}

var currVersion = getVersion();

if (!localStorage['version']) {
  localStorage['version'] = currVersion;
}

var prevVersion = localStorage['version'];

function init() {
  const TWEETDECK_WEB_URL = 'https://tweetdeck.twitter.com';

  /**
   * Step 2: Collate a list of all the open tabs.
   */
  function gatherTabs(urls, itemInfos) {
    var allTheTabs = [];
    var windowsChecked = 0;
    console.log("gatherTabs", itemInfos.timestamp);

    // First get all the windows...
    chrome.windows.getAll(function(windows) {
      for (var i = 0; i < windows.length; i++) {
        // ... and then all their tabs.
        chrome.tabs.getAllInWindow(windows[i].id, function(tabs) {
          windowsChecked++;
          allTheTabs = allTheTabs.concat(tabs);

          if (windowsChecked === windows.length) {
            // We have all the tabs! Search for a TweetDeck...
            openApp(urls, allTheTabs, itemInfos);
          }
        });

      }
    });
  }

  function openApp(urls, tabs, itemInfos) {
    console.log("openApp", itemInfos.timestamp);
    // Search urls in priority order...
    for (var i = 0; i < urls.length; i++) {
      var url = urls[i];

      // Search tabs...
      for (var j = 0; j < tabs.length; j++) {
        var tab = tabs[j];
        if (tab.url.indexOf(url) === 0) {
          // Found it!
          var tabId = tab.id;
          chrome.windows.update(tab.windowId, {
            focused: true
          });
          chrome.tabs.update(tabId, {
            selected: true,
            active: true,
            highlighted: true
          }, function() {
            console.log("update", itemInfos.timestamp);
            var text = itemInfos.text;
            var url = itemInfos.url;
            chrome.tabs.sendMessage(tabId, {
              text: text,
              url: url,
              timestamp: itemInfos.timestamp,
              count: 2
            })
          });
          return;
        }
      }
    }

    // Didn't find it! Open a new one!
    chrome.tabs.create({
      url: urls[0]
    }, function(tab) {
      var count = 0;
      chrome.tabs.onUpdated.addListener(function(tabId, info) {

        if (info.status == "complete") {
          count += 1;
          console.log("onUpdated", itemInfos.timestamp, count);
          chrome.tabs.sendMessage(tabId, {
            text: itemInfos.text,
            url: itemInfos.url,
            timestamp: itemInfos.timestamp,
            count: count
          });
        }
      })
    });
  };

  var clickHandler = function(info, tab) {
    var text;
    var url;

    if (info.selectionText && currentOptions.shorten_text) {
      text = "\"" + info.selectionText.substr(0, 110) + "\"";
    } else if (info.selectionText) {
      text = "\"" + info.selectionText + "\"";
    } else {
      text = tab.title.substr(0, 110);
    }

    if (info.linkUrl) {
      url = info.linkUrl
    } else {
      url = info.pageUrl;
    }

    if (info.mediaType === "image") {
      url = info.srcUrl;
      text = "";
    }

    gatherTabs([TWEETDECK_WEB_URL], {
      "text": text,
      "url": url,
      "timestamp": new Date().getTime()
    });
  };

  if (currentOptions.share_button) {
  	console.debug('Share button enabled');
    chrome.contextMenus.create({
      "title": "Share on (Better) TweetDeck",
      "contexts": ["page", "selection", "image", "link"],
      "onclick": clickHandler
    });
  }

}

chrome.storage.sync.get("BTDSettings", function(obj) {
  if (obj.BTDSettings !== undefined) {
    currentOptions = obj.BTDSettings;
    var reApply = false;

    for (var setting in DefaultSettings) {
      if (currentOptions[setting] == undefined) {
        console.debug("Defining", setting, "to default value", DefaultSettings[setting]);
        currentOptions[setting] = DefaultSettings[setting];
        reApply = true;
      }
    }

    if (currVersion != prevVersion) {
      if (!(prevVersion.split(".")[0] == currVersion.split(".")[0] && prevVersion.split(".")[1] == currVersion.split(".")[1])) {
        onUpdate();
      }
      localStorage['version'] = currVersion;
    }

    for (var provider in DefaultSettings["providers"]) {
      if (currentOptions["providers"][provider] == undefined) {
        console.log("Adding", provider, "as a new provider with value", DefaultSettings["providers"][provider]);
        currentOptions["providers"][provider] = DefaultSettings["providers"][provider];
        reApply = true;
      }
    }

    for (var setting in currentOptions) {
      if (DefaultSettings[setting] == undefined) {
        console.log("Deleting", setting);
        delete currentOptions[setting];
        reApply = true;
      }
    }

    for (var setting in currentOptions["providers"]) {
      if (DefaultSettings["providers"][setting] == undefined) {
        delete currentOptions["providers"][setting];
        reApply = true;
      }
    }

    if (reApply === true) {
      chrome.storage.sync.set({
        "BTDSettings": currentOptions
      }, function() {
        console.log("Options updated!");
        console.log(currentOptions);
        init();
      });
    } else {
      init();
    }
  } else {
    chrome.storage.sync.set({
      "BTDSettings": DefaultSettings
    }, function() {
      console.log("Default options set");
      onInstall();
      init();
    })
  }
});<|endoftext|>


<commit_before>import {_getCSRFCookie} from './etools-ajax-utils';

const activeXhrRequests = {};

export function getActiveXhrRequests() {
  return activeXhrRequests;
}

export async function upload(config, rawFile, filename, onProgressCallback) {
  const headers = await _getHeaders(config.jwtLocalStorageKey);
  const options = {
    method: 'POST',
    url: _getEndpoint(config.endpointInfo, config.uploadEndpoint),
    body: _prepareBody(rawFile, filename, config.endpointInfo),
    rejectWithRequest: config.endpointInfo && config.endpointInfo.rejectWithRequest,
    headers
  };
  return sendRequest(options, filename, onProgressCallback)
    .then((response) => {
      delete activeXhrRequests[filename];
      if (typeof response === 'string') {
        response = JSON.parse(response);
      }
      return response;
    })
    .catch((error) => {
      delete activeXhrRequests[filename];
      throw error;
    });
}

async function _getHeaders(jwtLocalStorageKey) {
  const csrfToken = _getCSRFCookie();
  let jwtToken = _getJwtToken(jwtLocalStorageKey);
  const headers = {};
  if (csrfToken) {
    headers['x-csrftoken'] = csrfToken;
  }
  if (jwtLocalStorageKey) {
    if (window.AppMsalInstance) {
      try {
        jwtToken = await window.AppMsalInstance.acquireTokenSilent();
      } catch (err) {
        window.location.reload(true);
      }
    }
    if (jwtToken) {
      headers['authorization'] = 'JWT ' + jwtToken;
    }
  }
  return headers;
}

function _getEndpoint(endpointInfo, uploadEndpoint) {
  if (endpointInfo && endpointInfo.endpoint) {
    return endpointInfo.endpoint;
  }
  return uploadEndpoint;
}

function _prepareBody(rawFile, filename, endpointInfo) {
  const fd = new FormData();

  const rawFileProperty = _getRawFilePropertyName(endpointInfo);
  fd.append(rawFileProperty, rawFile, filename);

  if (endpointInfo && endpointInfo.extraInfo) {
    _addAnyExtraInfoToBody(fd, endpointInfo.extraInfo);
  }
  return fd;
}

function sendRequest(options, requestKey, onProgressCallback) {
  const request = document.createElement('iron-request');
  if(typeof(onProgressCallback) === 'function') {
    request.xhr.upload.onprogress = onProgressCallback;
  }
  activeXhrRequests[requestKey] = request;
  request.send(options);
  return request.completes.then((request) => {
    return request.response;
  });
}

function _getRawFilePropertyName(endpointInfo) {
  if (endpointInfo && endpointInfo.rawFilePropertyName) {
    return endpointInfo.rawFilePropertyName;
  }
  return 'file';
}

function _addAnyExtraInfoToBody(formData, extraInfo) {
  for (const prop in extraInfo) {
    if (Object.prototype.hasOwnProperty.call(extraInfo, prop)) {
      formData.append(prop, extraInfo[prop]);
    }
  }
}

function _getJwtToken(jwtLocalStorageKey) {
  return localStorage.getItem(jwtLocalStorageKey);
}

export function abortActiveRequests(activeReqKeys) {
  if (!activeXhrRequests) {
    return;
  }
  const keys = activeReqKeys || Object.keys(activeXhrRequests);
  if (keys.length) {
    keys.forEach((key) => {
      try {
        activeXhrRequests[key].abort();
        delete activeXhrRequests[key];
      } catch (error) {
        //
      }
    });
  }
}
<commit_msg>adjustments<commit_after>import {_getCSRFCookie} from './etools-ajax-utils';

const activeXhrRequests = {};

export function getActiveXhrRequests() {
  return activeXhrRequests;
}

export async function upload(config, rawFile, filename, onProgressCallback) {
  const headers = await _getHeaders(config.jwtLocalStorageKey);
  const options = {
    method: 'POST',
    url: _getEndpoint(config.endpointInfo, config.uploadEndpoint),
    body: _prepareBody(rawFile, filename, config.endpointInfo),
    rejectWithRequest: config.endpointInfo && config.endpointInfo.rejectWithRequest,
    headers
  };
  return sendRequest(options, filename, onProgressCallback)
    .then((response) => {
      delete activeXhrRequests[filename];
      if (typeof response === 'string') {
        response = JSON.parse(response);
      }
      return response;
    })
    .catch((error) => {
      delete activeXhrRequests[filename];
      throw error;
    });
}

async function _getHeaders(jwtLocalStorageKey) {
  const csrfToken = _getCSRFCookie();
  let jwtToken = _getJwtToken(jwtLocalStorageKey);
  const headers = {};
  if (csrfToken) {
    headers['x-csrftoken'] = csrfToken;
  }
  if (jwtLocalStorageKey) {
    if (window.AppMsalInstance) {
      try {
        jwtToken = await window.AppMsalInstance.acquireTokenSilent();
      } catch (err) {
        window.location.reload(true);
      }
    }
    if (jwtToken) {
      headers['authorization'] = 'JWT ' + jwtToken;
    }
  }
  return headers;
}

function _getEndpoint(endpointInfo, uploadEndpoint) {
  if (endpointInfo && endpointInfo.endpoint) {
    return endpointInfo.endpoint;
  }
  return uploadEndpoint;
}

function _prepareBody(rawFile, filename, endpointInfo) {
  const fd = new FormData();

  const rawFileProperty = _getRawFilePropertyName(endpointInfo);
  fd.append(rawFileProperty, rawFile, filename);

  if (endpointInfo && endpointInfo.extraInfo) {
    _addAnyExtraInfoToBody(fd, endpointInfo.extraInfo);
  }
  return fd;
}

function sendRequest(options, requestKey, onProgressCallback) {
  const request = document.createElement('iron-request');
  if (typeof(onProgressCallback) === 'function') {
    request.xhr.upload.onprogress = onProgressCallback;
  }
  activeXhrRequests[requestKey] = request;
  request.send(options);
  return request.completes.then((request) => {
    return request.response;
  });
}

function _getRawFilePropertyName(endpointInfo) {
  if (endpointInfo && endpointInfo.rawFilePropertyName) {
    return endpointInfo.rawFilePropertyName;
  }
  return 'file';
}

function _addAnyExtraInfoToBody(formData, extraInfo) {
  for (const prop in extraInfo) {
    if (Object.prototype.hasOwnProperty.call(extraInfo, prop)) {
      formData.append(prop, extraInfo[prop]);
    }
  }
}

function _getJwtToken(jwtLocalStorageKey) {
  return localStorage.getItem(jwtLocalStorageKey);
}

export function abortActiveRequests(activeReqKeys) {
  if (!activeXhrRequests) {
    return;
  }
  const keys = activeReqKeys || Object.keys(activeXhrRequests);
  if (keys.length) {
    keys.forEach((key) => {
      try {
        activeXhrRequests[key].abort();
        delete activeXhrRequests[key];
      } catch (error) {
        //
      }
    });
  }
}
<|endoftext|>