<commit_before>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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.                                            *
 * ------------------------------------------------------------------------- */
import { ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Edit as EditIcon } from 'iconoir-react'
import { Stack, Typography } from '@mui/material'

import { useResizeMutation } from 'client/features/OneApi/vm'
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
import { ResizeCapacityForm } from 'client/components/Forms/Vm'
import { List } from 'client/components/Tabs/Common'
import { Tr, Translate } from 'client/components/HOC'

import { isVCenter, isAvailableAction } from 'client/models/VirtualMachine'
import { formatNumberByCurrency, jsonToXml } from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, VM, VM_ACTIONS } from 'client/constants'

/**
 * Renders mainly capacity tab.
 *
 * @param {object} props - Props
 * @param {VM} props.vm - Virtual machine
 * @param {string[]} props.actions - Available actions to capacity tab
 * @returns {ReactElement} Capacity tab
 */
const CapacityPanel = ({ vm = {}, actions }) => {
  const {
    CPU,
    VCPU = '-',
    MEMORY,
    CPU_COST,
    MEMORY_COST,
    TOPOLOGY: { CORES = '-', SOCKETS = '-' } = {},
  } = vm?.TEMPLATE || {}

  const memoryCost = useMemo(() => {
    const cost = MEMORY_COST || 0
    const monthCost = formatNumberByCurrency(MEMORY * cost * 24 * 30)

    return <Translate word={T.CostEachMonth} values={[monthCost]} />
  }, [MEMORY, MEMORY_COST])

  const cpuCost = useMemo(() => {
    const cost = CPU_COST || 0
    const monthCost = formatNumberByCurrency(CPU * cost * 24 * 30)

    return <Translate word={T.CostEachMonth} values={[monthCost]} />
  }, [CPU, CPU_COST])

  const info = [
    {
      name: T.PhysicalCpu,
      value: CPU,
      dataCy: 'cpu',
    },
    {
      name: T.VirtualCpu,
      value: VCPU,
      dataCy: 'vcpu',
    },
    isVCenter(vm) && {
      name: T.VirtualCores,
      value: [
        `${Tr(T.Cores)} x ${CORES}`,
        `${Tr(T.Sockets)} x ${SOCKETS}`,
      ].join(' | '),
      dataCy: 'virtualcores',
    },
    {
      name: T.Memory,
      value: prettyBytes(+MEMORY, 'MB'),
      dataCy: 'memory',
    },
    {
      name: T.CostCpu,
      value: cpuCost,
      dataCy: 'cpucost',
    },
    {
      name: T.CostMemory,
      value: memoryCost,
      dataCy: 'memorycost',
    },
  ].filter(Boolean)

  return <List title={<PanelHeader vm={vm} actions={actions} />} list={info} />
}

CapacityPanel.propTypes = {
  actions: PropTypes.arrayOf(PropTypes.string),
  vm: PropTypes.object,
}

CapacityPanel.displayName = 'CapacityPanel'

/**
 * Renders header of capacity panel.
 *
 * @param {object} props - Props
 * @param {VM} props.vm - Virtual machine
 * @param {string[]} props.actions - Available actions to capacity tab
 * @returns {ReactElement} Capacity panel header
 */
const PanelHeader = ({ vm = {}, actions = [] }) => {
  const [resizeCapacity] = useResizeMutation()

  const handleResizeCapacity = async (formData) => {
    const { enforce, ...restOfData } = formData
    const template = jsonToXml(restOfData)

    await resizeCapacity({ id: vm.ID, enforce, template })
  }

  const resizeIsAvailable = useMemo(
    () =>
      actions
        .filter((action) => isAvailableAction(action, vm))
        .includes?.(VM_ACTIONS.RESIZE_CAPACITY),
    [vm]
  )

  return (
    <Stack
      width={1}
      direction="row"
      alignItems="center"
      justifyContent="space-between"
    >
      <Typography noWrap>
        <Translate word={T.Capacity} />
      </Typography>
      {resizeIsAvailable && (
        <ButtonToTriggerForm
          buttonProps={{
            'data-cy': 'resize-capacity',
            icon: <EditIcon />,
            tooltip: <Translate word={T.Resize} />,
          }}
          options={[
            {
              dialogProps: { title: T.ResizeCapacity },
              form: () => ResizeCapacityForm({ initialValues: vm.TEMPLATE }),
              onSubmit: handleResizeCapacity,
            },
          ]}
        />
      )}
    </Stack>
  )
}

PanelHeader.propTypes = { ...CapacityPanel.propTypes }
PanelHeader.displayName = 'PanelHeader'

export default CapacityPanel
<commit_msg>F #5422: Add icons to capacity section (#2096)<commit_after>/* ------------------------------------------------------------------------- *
 * Copyright 2002-2022, OpenNebula Project, OpenNebula Systems               *
 *                                                                           *
 * 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.                                            *
 * ------------------------------------------------------------------------- */
import { ReactElement, useMemo } from 'react'
import PropTypes from 'prop-types'
import { Edit, Server, Cpu } from 'iconoir-react'
import { Stack, Typography } from '@mui/material'

import { useResizeMutation } from 'client/features/OneApi/vm'
import ButtonToTriggerForm from 'client/components/Forms/ButtonToTriggerForm'
import { ResizeCapacityForm } from 'client/components/Forms/Vm'
import { List } from 'client/components/Tabs/Common'
import { MemoryIcon } from 'client/components/Icons'
import { Tr, Translate } from 'client/components/HOC'

import { isAvailableAction } from 'client/models/VirtualMachine'
import { formatNumberByCurrency, jsonToXml } from 'client/models/Helper'
import { prettyBytes } from 'client/utils'
import { T, VM, VM_ACTIONS } from 'client/constants'

/**
 * Renders mainly capacity tab.
 *
 * @param {object} props - Props
 * @param {VM} props.vm - Virtual machine
 * @param {string[]} props.actions - Available actions to capacity tab
 * @returns {ReactElement} Capacity tab
 */
const CapacityPanel = ({ vm = {}, actions }) => {
  const {
    CPU,
    VCPU = '-',
    MEMORY,
    CPU_COST,
    MEMORY_COST,
    TOPOLOGY: { CORES, SOCKETS } = {},
  } = vm?.TEMPLATE || {}

  const memoryCost = useMemo(() => {
    const cost = MEMORY_COST || 0
    const monthCost = formatNumberByCurrency(MEMORY * cost * 24 * 30)

    return <Translate word={T.CostEachMonth} values={[monthCost]} />
  }, [MEMORY, MEMORY_COST])

  const cpuCost = useMemo(() => {
    const cost = CPU_COST || 0
    const monthCost = formatNumberByCurrency(CPU * cost * 24 * 30)

    return <Translate word={T.CostEachMonth} values={[monthCost]} />
  }, [CPU, CPU_COST])

  const info = [
    {
      icon: <Server />,
      name: T.PhysicalCpu,
      value: CPU,
      dataCy: 'cpu',
    },
    {
      icon: <Cpu />,
      name: T.VirtualCpu,
      value: VCPU,
      dataCy: 'vcpu',
    },
    {
      icon: <MemoryIcon />,
      name: T.Memory,
      value: prettyBytes(+MEMORY, 'MB'),
      dataCy: 'memory',
    },
    ![CORES, SOCKETS].includes(undefined) && {
      name: T.VirtualCores,
      value: [
        `${Tr(T.Cores)} x ${CORES}`,
        `${Tr(T.Sockets)} x ${SOCKETS}`,
      ].join(' | '),
      dataCy: 'virtualcores',
    },
    {
      name: T.CostCpu,
      value: cpuCost,
      dataCy: 'cpucost',
    },
    {
      name: T.CostMemory,
      value: memoryCost,
      dataCy: 'memorycost',
    },
  ].filter(Boolean)

  return <List title={<PanelHeader vm={vm} actions={actions} />} list={info} />
}

CapacityPanel.propTypes = {
  actions: PropTypes.arrayOf(PropTypes.string),
  vm: PropTypes.object,
}

CapacityPanel.displayName = 'CapacityPanel'

/**
 * Renders header of capacity panel.
 *
 * @param {object} props - Props
 * @param {VM} props.vm - Virtual machine
 * @param {string[]} props.actions - Available actions to capacity tab
 * @returns {ReactElement} Capacity panel header
 */
const PanelHeader = ({ vm = {}, actions = [] }) => {
  const [resizeCapacity] = useResizeMutation()

  const handleResizeCapacity = async (formData) => {
    const { enforce, ...restOfData } = formData
    const template = jsonToXml(restOfData)

    await resizeCapacity({ id: vm.ID, enforce, template })
  }

  const resizeIsAvailable = useMemo(
    () =>
      actions
        .filter((action) => isAvailableAction(action, vm))
        .includes?.(VM_ACTIONS.RESIZE_CAPACITY),
    [vm]
  )

  return (
    <Stack
      width={1}
      direction="row"
      alignItems="center"
      justifyContent="space-between"
    >
      <Typography noWrap>
        <Translate word={T.Capacity} />
      </Typography>
      {resizeIsAvailable && (
        <ButtonToTriggerForm
          buttonProps={{
            'data-cy': 'resize-capacity',
            icon: <Edit />,
            tooltip: <Translate word={T.Resize} />,
          }}
          options={[
            {
              dialogProps: { title: T.ResizeCapacity },
              form: () => ResizeCapacityForm({ initialValues: vm.TEMPLATE }),
              onSubmit: handleResizeCapacity,
            },
          ]}
        />
      )}
    </Stack>
  )
}

PanelHeader.propTypes = { ...CapacityPanel.propTypes }
PanelHeader.displayName = 'PanelHeader'

export default CapacityPanel
<|endoftext|>


<commit_before>function appendDirectory (file) {
  var $file = $('<tr>').addClass('file')
  if (file.alter == 1) {
    $file.addClass('alter')
  }

  var $name = $('<td>').addClass('name').attr('id', getExtention(file)).attr('data-file', file.name).html(
    file.isdir ?
      $('<a>').attr('href', '#' + document.location.hash.substring(1) + file.name + '/').text(file.name.substring(0, 50)) :
      file.name.substring(0, 30)).appendTo($file)
  if (file.isfile)
    $name.draggable({
      revert: true
    })
  else
    $name.droppable({
      greedy: true,
      drop: function (data) {
        var folder = $(this).attr('data-file')
        var file = $(data.toElement).attr('data-file')
        var path = document.location.hash.substring(1)

        socket.emit('mv-d', {
          'file': file,
          'path': path,
          'folder': folder
        })
      }
    })

  var $size = $('<td>').addClass('size').text(formatSize(file.size)).appendTo($file)
  var $date = $('<td>').addClass('date').text(formatDate(file.ctime)).appendTo($file)
  var $actions = $('<td>').addClass('actions')

  if (file.isfile) {
    var $downloadBut = $('<i>').addClass('but fa fa-download').attr('id', 'download').text('download').appendTo($actions).click(function () {
      window.open('files/?f=' + document.location.hash.substring(1) + file.name)
    })
  }
  var $deleteBut = $('<i>').addClass('but fa fa-remove').attr('id', 'delete').text('delete').appendTo($actions).click(function () {
    if (confirm('Confirmer la suppression ?'))
      socket.emit('remove-d', document.location.hash.substring(1) + file.name)
  })
  var $renameBut = $('<i>').addClass('but fa fa-pencil').attr('id', 'rename').text('rename').appendTo($actions).click(function () {
    var name = prompt('New Name', file.name)
    if (name) {
      name = name.split('\/').pop()
      socket.emit('rename-d', {
        'path': document.location.hash.substring(1),
        'oldname': file.name,
        'newname': name
      })
    }
  })
  if (file.isfile) {
    var $infoBut = $('<i>').addClass('but fa fa-info').attr('id', 'info').text('infos').appendTo($actions).click(function () {
      var title = $($(this).parent().parent().children()[0]).text()
      mediaInfoGet(title)
    })
  }

  $actions.appendTo($file)

  $file.appendTo('.container .directory .list')
}

function appendDirectorySize (size) {
  $('.folder-size').text(formatSize(size))
}

$('.but#mkdir i').click(function () {
  var name = prompt('Nom du nouveau dossier ?')
  if (name)
    socket.emit('mkdir-d', {
      'path': document.location.hash.substring(1),
      'name': name
    })
})
<commit_msg>add name of the file into remove popup<commit_after>function appendDirectory (file) {
  var $file = $('<tr>').addClass('file')
  if (file.alter == 1) {
    $file.addClass('alter')
  }

  var $name = $('<td>').addClass('name').attr('id', getExtention(file)).attr('data-file', file.name).html(
    file.isdir ?
      $('<a>').attr('href', '#' + document.location.hash.substring(1) + file.name + '/').text(file.name.substring(0, 50)) :
      file.name.substring(0, 30)).appendTo($file)
  if (file.isfile)
    $name.draggable({
      revert: true
    })
  else
    $name.droppable({
      greedy: true,
      drop: function (data) {
        var folder = $(this).attr('data-file')
        var file = $(data.toElement).attr('data-file')
        var path = document.location.hash.substring(1)

        socket.emit('mv-d', {
          'file': file,
          'path': path,
          'folder': folder
        })
      }
    })

  var $size = $('<td>').addClass('size').text(formatSize(file.size)).appendTo($file)
  var $date = $('<td>').addClass('date').text(formatDate(file.ctime)).appendTo($file)
  var $actions = $('<td>').addClass('actions')

  if (file.isfile) {
    var $downloadBut = $('<i>').addClass('but fa fa-download').attr('id', 'download').text('download').appendTo($actions).click(function () {
      window.open('files/?f=' + document.location.hash.substring(1) + file.name)
    })
  }
  var $deleteBut = $('<i>').addClass('but fa fa-remove').attr('id', 'delete').text('delete').appendTo($actions).click(function () {
    if (confirm('Confirmer la suppression de '+file.name+' ?'))
      socket.emit('remove-d', document.location.hash.substring(1) + file.name)
  })
  var $renameBut = $('<i>').addClass('but fa fa-pencil').attr('id', 'rename').text('rename').appendTo($actions).click(function () {
    var name = prompt('New Name', file.name)
    if (name) {
      name = name.split('\/').pop()
      socket.emit('rename-d', {
        'path': document.location.hash.substring(1),
        'oldname': file.name,
        'newname': name
      })
    }
  })
  if (file.isfile) {
    var $infoBut = $('<i>').addClass('but fa fa-info').attr('id', 'info').text('infos').appendTo($actions).click(function () {
      var title = $($(this).parent().parent().children()[0]).text()
      mediaInfoGet(title)
    })
  }

  $actions.appendTo($file)

  $file.appendTo('.container .directory .list')
}

function appendDirectorySize (size) {
  $('.folder-size').text(formatSize(size))
}

$('.but#mkdir i').click(function () {
  var name = prompt('Nom du nouveau dossier ?')
  if (name)
    socket.emit('mkdir-d', {
      'path': document.location.hash.substring(1),
      'name': name
    })
})
<|endoftext|>


<commit_before>/**
  __The session provides access to the current authentication state as well as
  any data the authenticator resolved with__ (see
  [`Authenticators.Base#authenticate`](#SimpleAuth-Authenticators-Base-authenticate)).
  It is created when Ember Simple Auth is set up and __injected into all
  controllers and routes so that these parts of the application can always
  access the current authentication state and other data__, depending on the
  authenticator in use and whether the session is actually authenticated (see
  [`Authenticators.Base`](#SimpleAuth-Authenticators-Base)).

  The session also provides methods to authenticate and to invalidate itself
  (see
  [`Session#authenticate`](#SimpleAuth-Session-authenticate),
  [`Session#invaldiate`](#SimpleAuth-Session-invaldiate)).
  These methods are usually invoked through actions from routes or controllers.
  To authenticate the session manually, simple call the
  [`Session#authenticate`](#SimpleAuth-Session-authenticate)
  method with the authenticator factory to use as well as any options the
  authenticator needs to authenticate the session:

  ```js
  this.get('session').authenticate('authenticator:custom', { some: 'option' }).then(function() {
    // authentication was successful
  }, function() {
    // authentication failed
  });
  ```

  The session also observes the store and - if it is authenticated - the
  authenticator for changes (see
  [`Authenticators.Base`](#SimpleAuth-Authenticators-Base)
  end [`Stores.Base`](#SimpleAuth-Stores-Base)).

  @class Session
  @namespace SimpleAuth
  @module simple-auth/session
  @extends Ember.ObjectProxy
  @uses Ember.Evented
*/
export default Ember.ObjectProxy.extend(Ember.Evented, {
  /**
    Triggered __whenever the session is successfully authenticated__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionAuthenticationSucceeded`](#SimpleAuth-ApplicationRouteMixin-sessionAuthenticationSucceeded)
    will be invoked whenever this event is triggered.

    @event sessionAuthenticationSucceeded
  */
  /**
    Triggered __whenever an attempt to authenticate the session fails__. When
    the application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionAuthenticationFailed`](#SimpleAuth-ApplicationRouteMixin-sessionAuthenticationFailed)
    will be invoked whenever this event is triggered.

    @event sessionAuthenticationFailed
    @param {Object} error The error object; this depends on the authenticator in use, see [SimpleAuth.Authenticators.Base#authenticate](#SimpleAuth-Authenticators-Base-authenticate)
  */
  /**
    Triggered __whenever the session is successfully invalidated__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionInvalidationSucceeded`](#SimpleAuth-ApplicationRouteMixin-sessionInvalidationSucceeded)
    will be invoked whenever this event is triggered.

    @event sessionInvalidationSucceeded
  */
  /**
    Triggered __whenever an attempt to invalidate the session fails__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionInvalidationFailed`](#SimpleAuth-ApplicationRouteMixin-sessionInvalidationFailed)
    will be invoked whenever this event is triggered.

    @event sessionInvalidationFailed
    @param {Object} error The error object; this depends on the authenticator in use, see [SimpleAuth.Authenticators.Base#invalidate](#SimpleAuth-Authenticators-Base-invalidate)
  */
  /**
    Triggered __whenever the server rejects the authorization information
    passed with a request and responds with status 401__. When the application
    uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#authorizationFailed`](#SimpleAuth-ApplicationRouteMixin-authorizationFailed)
    will be invoked whenever this event is triggered.

    @event authorizationFailed
  */

  /**
    The authenticator factory to use as it is registered with Ember's
    container, see
    [Ember's API docs](http://emberjs.com/api/classes/Ember.Application.html#method_register).
    This is only set when the session is currently authenticated.

    @property authenticator
    @type String
    @readOnly
    @default null
  */
  authenticator: null,
  /**
    The store used to persist session properties.

    @property store
    @type SimpleAuth.Stores.Base
    @readOnly
    @default null
  */
  store: null,
  /**
    The Ember.js container,

    @property container
    @type Container
    @readOnly
    @default null
  */
  container: null,
  /**
    Returns whether the session is currently authenticated.

    @property isAuthenticated
    @type Boolean
    @readOnly
    @default false
  */
  isAuthenticated: false,
  /**
    @property attemptedTransition
    @private
  */
  attemptedTransition: null,
  /**
    @property content
    @private
  */
  content: { secure: {} },

  /**
    @method init
    @private
  */
  init: function() {
    this.set('content', { secure: {} });
  },

  /**
    Authenticates the session with an `authenticator` and appropriate
    `options`. __This delegates the actual authentication work to the
    `authenticator`__ and handles the returned promise accordingly (see
    [`Authenticators.Base#authenticate`](#SimpleAuth-Authenticators-Base-authenticate)).
    All data the authenticator resolves with will be saved in the session.

    __This method returns a promise itself. A resolving promise indicates that
    the session was successfully authenticated__ while a rejecting promise
    indicates that authentication failed and the session remains
    unauthenticated.

    @method authenticate
    @param {String} authenticator The authenticator factory to use as it is registered with Ember's container, see [Ember's API docs](http://emberjs.com/api/classes/Ember.Application.html#method_register)
    @param {Any} [...args] The arguments to pass to the authenticator; depending on the type of authenticator these might be a set of credentials, a Facebook OAuth Token, etc.
    @return {Ember.RSVP.Promise} A promise that resolves when the session was authenticated successfully
  */
  authenticate: function() {
    var args          = Array.prototype.slice.call(arguments);
    var authenticator = args.shift();
    Ember.assert('Session#authenticate requires the authenticator factory to be specified, was "' + authenticator + '"!', !Ember.isEmpty(authenticator));
    var _this            = this;
    var theAuthenticator = this.container.lookup(authenticator);
    Ember.assert('No authenticator for factory "' + authenticator + '" could be found!', !Ember.isNone(theAuthenticator));
    return new Ember.RSVP.Promise(function(resolve, reject) {
      theAuthenticator.authenticate.apply(theAuthenticator, args).then(function(content) {
        _this.setup(authenticator, content, true);
        resolve();
      }, function(error) {
        _this.clear();
        _this.trigger('sessionAuthenticationFailed', error);
        reject(error);
      });
    });
  },

  /**
    Invalidates the session with the authenticator it is currently
    authenticated with (see
    [`Session#authenticator`](#SimpleAuth-Session-authenticator)). __This
    invokes the authenticator's `invalidate` method and handles the returned
    promise accordingly__ (see
    [`Authenticators.Base#invalidate`](#SimpleAuth-Authenticators-Base-invalidate)).

    __This method returns a promise itself. A resolving promise indicates that
    the session was successfully invalidated__ while a rejecting promise
    indicates that the promise returned by the `authenticator` rejected and
    thus invalidation was cancelled. In that case the session remains
    authenticated. Once the session is successfully invalidated it clears all
    of its data.

    @method invalidate
    @return {Ember.RSVP.Promise} A promise that resolves when the session was invalidated successfully
  */
  invalidate: function() {
    Ember.assert('Session#invalidate requires the session to be authenticated!', this.get('isAuthenticated'));
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var authenticator = _this.container.lookup(_this.authenticator);
      authenticator.invalidate(_this.content).then(function() {
        authenticator.off('sessionDataUpdated');
        _this.clear(true);
        resolve();
      }, function(error) {
        _this.trigger('sessionInvalidationFailed', error);
        reject(error);
      });
    });
  },

  /**
    @method restore
    @private
  */
  restore: function() {
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var restoredContent = _this.store.restore();
      var authenticator   = (restoredContent.secure || {}).authenticator;
      if (!!authenticator) {
        delete restoredContent.secure.authenticator;
        _this.container.lookup(authenticator).restore(restoredContent).then(function(content) {
          _this.setup(authenticator, content);
          resolve();
        }, function() {
          _this.clear();
          reject();
        });
      } else {
        _this.clear();
        reject();
      }
    });
  },

  /**
    @method setup
    @private
  */
  setup: function(authenticator, secureContent, trigger) {
    //TODO: clean this up, it's so messy!
    trigger = !!trigger && !this.get('isAuthenticated');
    this.beginPropertyChanges();
    this.setProperties({
      isAuthenticated: true,
      authenticator:   authenticator
    });
    Ember.set(this.content, 'secure', secureContent);
    this.bindToAuthenticatorEvents();
    this.updateStore();
    this.endPropertyChanges();
    if (trigger) {
      this.trigger('sessionAuthenticationSucceeded');
    }
  },

  /**
    @method clear
    @private
  */
  clear: function(trigger, content) {
    //TODO: clean this up, it's so messy!
    trigger = !!trigger && this.get('isAuthenticated');
    content = content || this.get('content');
    this.beginPropertyChanges();
    this.setProperties({
      isAuthenticated: false,
      authenticator:   null
    });
    Ember.set(this, 'content', content || {});
    Ember.set(this.content, 'secure', {});
    this.updateStore();
    this.endPropertyChanges();
    if (trigger) {
      this.trigger('sessionInvalidationSucceeded');
    }
  },

  /**
    @method setUnknownProperty
    @private
  */
  setUnknownProperty: function(key, value) {
    Ember.assert('"secure" is a reserved key used by Ember Simple Auth!', key !== 'secure');
    var result = this._super(key, value);
    this.updateStore();
    return result;
  },

  /**
    @method updateStore
    @private
  */
  updateStore: function() {
    var data = this.content;
    if (!Ember.isEmpty(this.authenticator)) {
      data.secure = Ember.merge({ authenticator: this.authenticator }, data.secure || {});
    }
    this.store.persist(data);
  },

  /**
    @method bindToAuthenticatorEvents
    @private
  */
  bindToAuthenticatorEvents: function() {
    var _this = this;
    var authenticator = this.container.lookup(this.authenticator);
    authenticator.off('sessionDataUpdated');
    authenticator.off('sessionDataInvalidated');
    authenticator.on('sessionDataUpdated', function(content) {
      _this.setup(_this.authenticator, content);
    });
    authenticator.on('sessionDataInvalidated', function(content) {
      _this.clear(true, content);
    });
  },

  /**
    @method bindToStoreEvents
    @private
  */
  bindToStoreEvents: Ember.observer('store', function() {
    //TODO: reflect all updated properties in the session (setup will only set the secure part)
    var _this = this;
    this.store.on('sessionDataUpdated', function(content) {
      var authenticator = (content.secure || {}).authenticator;
      if (!!authenticator) {
        delete content.secure.authenticator;
        _this.container.lookup(authenticator).restore(content).then(function(content) {
          _this.setup(authenticator, content, true);
        }, function() {
          _this.clear(true, content);
        });
      } else {
        _this.clear(true, content);
      }
    });
  })
});
<commit_msg>pass secure session content for restoration only<commit_after>/**
  __The session provides access to the current authentication state as well as
  any data the authenticator resolved with__ (see
  [`Authenticators.Base#authenticate`](#SimpleAuth-Authenticators-Base-authenticate)).
  It is created when Ember Simple Auth is set up and __injected into all
  controllers and routes so that these parts of the application can always
  access the current authentication state and other data__, depending on the
  authenticator in use and whether the session is actually authenticated (see
  [`Authenticators.Base`](#SimpleAuth-Authenticators-Base)).

  The session also provides methods to authenticate and to invalidate itself
  (see
  [`Session#authenticate`](#SimpleAuth-Session-authenticate),
  [`Session#invaldiate`](#SimpleAuth-Session-invaldiate)).
  These methods are usually invoked through actions from routes or controllers.
  To authenticate the session manually, simple call the
  [`Session#authenticate`](#SimpleAuth-Session-authenticate)
  method with the authenticator factory to use as well as any options the
  authenticator needs to authenticate the session:

  ```js
  this.get('session').authenticate('authenticator:custom', { some: 'option' }).then(function() {
    // authentication was successful
  }, function() {
    // authentication failed
  });
  ```

  The session also observes the store and - if it is authenticated - the
  authenticator for changes (see
  [`Authenticators.Base`](#SimpleAuth-Authenticators-Base)
  end [`Stores.Base`](#SimpleAuth-Stores-Base)).

  @class Session
  @namespace SimpleAuth
  @module simple-auth/session
  @extends Ember.ObjectProxy
  @uses Ember.Evented
*/
export default Ember.ObjectProxy.extend(Ember.Evented, {
  /**
    Triggered __whenever the session is successfully authenticated__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionAuthenticationSucceeded`](#SimpleAuth-ApplicationRouteMixin-sessionAuthenticationSucceeded)
    will be invoked whenever this event is triggered.

    @event sessionAuthenticationSucceeded
  */
  /**
    Triggered __whenever an attempt to authenticate the session fails__. When
    the application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionAuthenticationFailed`](#SimpleAuth-ApplicationRouteMixin-sessionAuthenticationFailed)
    will be invoked whenever this event is triggered.

    @event sessionAuthenticationFailed
    @param {Object} error The error object; this depends on the authenticator in use, see [SimpleAuth.Authenticators.Base#authenticate](#SimpleAuth-Authenticators-Base-authenticate)
  */
  /**
    Triggered __whenever the session is successfully invalidated__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionInvalidationSucceeded`](#SimpleAuth-ApplicationRouteMixin-sessionInvalidationSucceeded)
    will be invoked whenever this event is triggered.

    @event sessionInvalidationSucceeded
  */
  /**
    Triggered __whenever an attempt to invalidate the session fails__. When the
    application uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#sessionInvalidationFailed`](#SimpleAuth-ApplicationRouteMixin-sessionInvalidationFailed)
    will be invoked whenever this event is triggered.

    @event sessionInvalidationFailed
    @param {Object} error The error object; this depends on the authenticator in use, see [SimpleAuth.Authenticators.Base#invalidate](#SimpleAuth-Authenticators-Base-invalidate)
  */
  /**
    Triggered __whenever the server rejects the authorization information
    passed with a request and responds with status 401__. When the application
    uses the
    [`ApplicationRouteMixin` mixin](#SimpleAuth-ApplicationRouteMixin),
    [`ApplicationRouteMixin.actions#authorizationFailed`](#SimpleAuth-ApplicationRouteMixin-authorizationFailed)
    will be invoked whenever this event is triggered.

    @event authorizationFailed
  */

  /**
    The authenticator factory to use as it is registered with Ember's
    container, see
    [Ember's API docs](http://emberjs.com/api/classes/Ember.Application.html#method_register).
    This is only set when the session is currently authenticated.

    @property authenticator
    @type String
    @readOnly
    @default null
  */
  authenticator: null,
  /**
    The store used to persist session properties.

    @property store
    @type SimpleAuth.Stores.Base
    @readOnly
    @default null
  */
  store: null,
  /**
    The Ember.js container,

    @property container
    @type Container
    @readOnly
    @default null
  */
  container: null,
  /**
    Returns whether the session is currently authenticated.

    @property isAuthenticated
    @type Boolean
    @readOnly
    @default false
  */
  isAuthenticated: false,
  /**
    @property attemptedTransition
    @private
  */
  attemptedTransition: null,
  /**
    @property content
    @private
  */
  content: { secure: {} },

  /**
    @method init
    @private
  */
  init: function() {
    this.set('content', { secure: {} });
  },

  /**
    Authenticates the session with an `authenticator` and appropriate
    `options`. __This delegates the actual authentication work to the
    `authenticator`__ and handles the returned promise accordingly (see
    [`Authenticators.Base#authenticate`](#SimpleAuth-Authenticators-Base-authenticate)).
    All data the authenticator resolves with will be saved in the session.

    __This method returns a promise itself. A resolving promise indicates that
    the session was successfully authenticated__ while a rejecting promise
    indicates that authentication failed and the session remains
    unauthenticated.

    @method authenticate
    @param {String} authenticator The authenticator factory to use as it is registered with Ember's container, see [Ember's API docs](http://emberjs.com/api/classes/Ember.Application.html#method_register)
    @param {Any} [...args] The arguments to pass to the authenticator; depending on the type of authenticator these might be a set of credentials, a Facebook OAuth Token, etc.
    @return {Ember.RSVP.Promise} A promise that resolves when the session was authenticated successfully
  */
  authenticate: function() {
    var args          = Array.prototype.slice.call(arguments);
    var authenticator = args.shift();
    Ember.assert('Session#authenticate requires the authenticator factory to be specified, was "' + authenticator + '"!', !Ember.isEmpty(authenticator));
    var _this            = this;
    var theAuthenticator = this.container.lookup(authenticator);
    Ember.assert('No authenticator for factory "' + authenticator + '" could be found!', !Ember.isNone(theAuthenticator));
    return new Ember.RSVP.Promise(function(resolve, reject) {
      theAuthenticator.authenticate.apply(theAuthenticator, args).then(function(content) {
        _this.setup(authenticator, content, true);
        resolve();
      }, function(error) {
        _this.clear();
        _this.trigger('sessionAuthenticationFailed', error);
        reject(error);
      });
    });
  },

  /**
    Invalidates the session with the authenticator it is currently
    authenticated with (see
    [`Session#authenticator`](#SimpleAuth-Session-authenticator)). __This
    invokes the authenticator's `invalidate` method and handles the returned
    promise accordingly__ (see
    [`Authenticators.Base#invalidate`](#SimpleAuth-Authenticators-Base-invalidate)).

    __This method returns a promise itself. A resolving promise indicates that
    the session was successfully invalidated__ while a rejecting promise
    indicates that the promise returned by the `authenticator` rejected and
    thus invalidation was cancelled. In that case the session remains
    authenticated. Once the session is successfully invalidated it clears all
    of its data.

    @method invalidate
    @return {Ember.RSVP.Promise} A promise that resolves when the session was invalidated successfully
  */
  invalidate: function() {
    Ember.assert('Session#invalidate requires the session to be authenticated!', this.get('isAuthenticated'));
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var authenticator = _this.container.lookup(_this.authenticator);
      authenticator.invalidate(_this.content).then(function() {
        authenticator.off('sessionDataUpdated');
        _this.clear(true);
        resolve();
      }, function(error) {
        _this.trigger('sessionInvalidationFailed', error);
        reject(error);
      });
    });
  },

  /**
    @method restore
    @private
  */
  restore: function() {
    var _this = this;
    return new Ember.RSVP.Promise(function(resolve, reject) {
      var restoredContent = _this.store.restore();
      var authenticator   = (restoredContent.secure || {}).authenticator;
      if (!!authenticator) {
        delete restoredContent.secure.authenticator;
        _this.container.lookup(authenticator).restore(restoredContent.secure).then(function(content) {
          _this.setup(authenticator, content);
          resolve();
        }, function() {
          _this.clear();
          reject();
        });
      } else {
        _this.clear();
        reject();
      }
    });
  },

  /**
    @method setup
    @private
  */
  setup: function(authenticator, secureContent, trigger) {
    //TODO: clean this up, it's so messy!
    trigger = !!trigger && !this.get('isAuthenticated');
    this.beginPropertyChanges();
    this.setProperties({
      isAuthenticated: true,
      authenticator:   authenticator
    });
    Ember.set(this.content, 'secure', secureContent);
    this.bindToAuthenticatorEvents();
    this.updateStore();
    this.endPropertyChanges();
    if (trigger) {
      this.trigger('sessionAuthenticationSucceeded');
    }
  },

  /**
    @method clear
    @private
  */
  clear: function(trigger, content) {
    //TODO: clean this up, it's so messy!
    trigger = !!trigger && this.get('isAuthenticated');
    content = content || this.get('content');
    this.beginPropertyChanges();
    this.setProperties({
      isAuthenticated: false,
      authenticator:   null
    });
    Ember.set(this, 'content', content || {});
    Ember.set(this.content, 'secure', {});
    this.updateStore();
    this.endPropertyChanges();
    if (trigger) {
      this.trigger('sessionInvalidationSucceeded');
    }
  },

  /**
    @method setUnknownProperty
    @private
  */
  setUnknownProperty: function(key, value) {
    Ember.assert('"secure" is a reserved key used by Ember Simple Auth!', key !== 'secure');
    var result = this._super(key, value);
    this.updateStore();
    return result;
  },

  /**
    @method updateStore
    @private
  */
  updateStore: function() {
    var data = this.content;
    if (!Ember.isEmpty(this.authenticator)) {
      data.secure = Ember.merge({ authenticator: this.authenticator }, data.secure || {});
    }
    this.store.persist(data);
  },

  /**
    @method bindToAuthenticatorEvents
    @private
  */
  bindToAuthenticatorEvents: function() {
    var _this = this;
    var authenticator = this.container.lookup(this.authenticator);
    authenticator.off('sessionDataUpdated');
    authenticator.off('sessionDataInvalidated');
    authenticator.on('sessionDataUpdated', function(content) {
      _this.setup(_this.authenticator, content);
    });
    authenticator.on('sessionDataInvalidated', function(content) {
      _this.clear(true, content);
    });
  },

  /**
    @method bindToStoreEvents
    @private
  */
  bindToStoreEvents: Ember.observer('store', function() {
    //TODO: reflect all updated properties in the session (setup will only set the secure part)
    var _this = this;
    this.store.on('sessionDataUpdated', function(content) {
      var authenticator = (content.secure || {}).authenticator;
      if (!!authenticator) {
        delete content.secure.authenticator;
        _this.container.lookup(authenticator).restore(content.secure).then(function(content) {
          _this.setup(authenticator, content, true);
        }, function() {
          _this.clear(true, content);
        });
      } else {
        _this.clear(true, content);
      }
    });
  })
});
<|endoftext|>


<commit_before>/**
 * Quink, Copyright (c) 2013-2014 IMD - International Institute for Management Development, Switzerland.
 *
 * This file is part of Quink.
 *
 * Quink is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Quink is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Quink.  If not, see <http://www.gnu.org/licenses/>.
 */

/*global embedded_svg_edit */
require([
    'Underscore',
    'jquery',
    'ext/PluginAdapterContext'
], function (_, $, Context) {
    'use strict';

    var frame,
        svgCanvas;

    /**
     * Runs func every delay milliseconds until func returns true.
     */
    function until(func, delay) {
        if (!func()) {
            _.delay(until, delay, func, delay);
        }
    }

    /**
     * Size the body of the iframe to match the body of the containing document. Anything else
     * doesn't seem to work on iOS. Specifically trying to do it all in css using height 100%
     * results in an iframe that's too tall for the viewport.
     * The timeout is to allow the reflow to take place after resizing the iframe's body. The
     * additional resize is needed on desktop Chrome which won't have resized the iframe to
     * accommodate the changed body size. The additional iframe resize breaks the layout on iOS
     * which will have already resized the frame.
     */
    function sizeFrame() {
        var reqHeight = window.innerHeight,
            initialHeight = frame.height();
        frame.contents().find('body').height(reqHeight);
        setTimeout(function () {
            if (frame.height() === initialHeight) {
                frame.height(reqHeight);
            }
        }, 0);
    }

    /**
     * Embedded method draw shouldn't have the stand alone file menu because the host app will
     * provide that functionality.
     * If data is provided, load method draw with that svg so that it can be edited.
     */
    function configureForEmbed(data) {
        var frameContents = frame.contents(),
            fileMenu = frameContents.find('.menu').get(1),
            menuDone = false,
            canvasDone = true,
            canvas;
        if (fileMenu) {
            $(fileMenu).hide();
            menuDone = true;
        }
        if (data) {
            canvasDone = false;
            canvas = frameContents.find('#svgcanvas');
            if (canvas) {
                svgCanvas.setSvgString(data);
                canvasDone = true;
            }
        }
        if (menuDone && canvasDone) {
            sizeFrame();
            // Try to avoid flickering on resize.
            setTimeout(function () {
                frame.removeClass('qk_invisible');
                Context.publish('opened');
            }, 0);
        }
        return menuDone && canvasDone;
    }

    function hide(topic, data) {
        frame.detach();
        frame.addClass('qk_invisible');
        window.removeEventListener('orientationchange', sizeFrame, false);
        Context.publish(topic, data);
    }

    /**
     * Callback.
     */
    function save() {
        svgCanvas.getSvgString()(function (data, error) {
            if (error) {
                console.log('save error: ' + error);
            } else {
                hide('saved', data);
            }
        });
    }

    /**
     * Callback.
     */
    function exit() {
        hide('exited');
    }

    /**
     * Callback.
     * Loads the method draw DOM nodes into the document and configures it to run embedded.
     */
    function open(data) {
        frame.appendTo('body');
        window.addEventListener('orientationchange', sizeFrame, false);
        until(_.partial(configureForEmbed, data), 100);
    }

    function fetchMarkup() {
        var url = Context.adapterUrl('method-draw/MethodDrawEmbed.html');
        $.get(url).done(function (data) {
            frame = $(data);
            svgCanvas = new embedded_svg_edit(frame[0]);
            Context.publish('loaded', {
                open: open,
                save: save,
                exit: exit,
                dom: frame[0].parentNode
            });
            console.log('Downloaded method draw markup');
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load method draw markup from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    function fetchCss() {
        var url = Context.adapterUrl('method-draw/MethodDrawEmbed.css');
        $.get(url).done(function (data) {
            $('<style>').html(data).appendTo('head');
            fetchMarkup();
            console.log('Downloaded method draw css');
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load method draw css from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    function fetchScript() {
        var url = Context.pluginUrl('method-draw/embedapi.js');
        $.getScript(url).done(function () {
            fetchCss();
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load embedapi from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    fetchScript();
});
<commit_msg>Makes Method Draw display properly on Android where previously it only took up part of the viewport. The window height was being calculated before the keyboard had retracted. This resizes MD when the window is resized which means that it will be a bit judder to draw in some cases as it draws then redraws when the resize event is fired, but that's better than it only taking up part of the screen and simpler than trying to wait for the keyboard to retract.<commit_after>/**
 * Quink, Copyright (c) 2013-2014 IMD - International Institute for Management Development, Switzerland.
 *
 * This file is part of Quink.
 *
 * Quink is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Quink is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Quink.  If not, see <http://www.gnu.org/licenses/>.
 */

/*global embedded_svg_edit */
require([
    'Underscore',
    'jquery',
    'ext/PluginAdapterContext'
], function (_, $, Context) {
    'use strict';

    var frame,
        svgCanvas,
        throttledSizeFrame;

    /**
     * Runs func every delay milliseconds until func returns true.
     */
    function until(func, delay) {
        if (!func()) {
            _.delay(until, delay, func, delay);
        }
    }

    /**
     * Size the body of the iframe to match the body of the containing document. Anything else
     * doesn't seem to work on iOS. Specifically trying to do it all in css using height 100%
     * results in an iframe that's too tall for the viewport.
     * The timeout is to allow the reflow to take place after resizing the iframe's body. The
     * additional resize is needed on desktop Chrome which won't have resized the iframe to
     * accommodate the changed body size. The additional iframe resize breaks the layout on iOS
     * which will have already resized the frame.
     */
    function sizeFrame() {
        var reqHeight = window.innerHeight,
            initialHeight = frame.height();
        frame.contents().find('body').height(reqHeight);
        setTimeout(function () {
            if (frame.height() === initialHeight) {
                frame.height(reqHeight);
            }
        }, 0);
    }

    /**
     * Embedded method draw shouldn't have the stand alone file menu because the host app will
     * provide that functionality.
     * If data is provided, load method draw with that svg so that it can be edited.
     */
    function configureForEmbed(data) {
        var frameContents = frame.contents(),
            fileMenu = frameContents.find('.menu').get(1),
            menuDone = false,
            canvasDone = true,
            canvas;
        if (fileMenu) {
            $(fileMenu).hide();
            menuDone = true;
        }
        if (data) {
            canvasDone = false;
            canvas = frameContents.find('#svgcanvas');
            if (canvas) {
                svgCanvas.setSvgString(data);
                canvasDone = true;
            }
        }
        if (menuDone && canvasDone) {
            sizeFrame();
            // Try to avoid flickering on resize.
            setTimeout(function () {
                frame.removeClass('qk_invisible');
                Context.publish('opened');
            }, 0);
        }
        return menuDone && canvasDone;
    }

    function hide(topic, data) {
        frame.detach();
        frame.addClass('qk_invisible');
        window.removeEventListener('orientationchange', sizeFrame, false);
        window.removeEventListener('resize', throttledSizeFrame, false);
        Context.publish(topic, data);
    }

    /**
     * Callback.
     */
    function save() {
        svgCanvas.getSvgString()(function (data, error) {
            if (error) {
                console.log('save error: ' + error);
            } else {
                hide('saved', data);
            }
        });
    }

    /**
     * Callback.
     */
    function exit() {
        hide('exited');
    }

    /**
     * Callback.
     * Loads the method draw DOM nodes into the document and configures it to run embedded.
     */
    function open(data) {
        frame.appendTo('body');
        window.addEventListener('orientationchange', sizeFrame, false);
        window.addEventListener('resize', throttledSizeFrame, false);
        until(_.partial(configureForEmbed, data), 100);
    }

    function fetchMarkup() {
        var url = Context.adapterUrl('method-draw/MethodDrawEmbed.html');
        $.get(url).done(function (data) {
            frame = $(data);
            svgCanvas = new embedded_svg_edit(frame[0]);
            Context.publish('loaded', {
                open: open,
                save: save,
                exit: exit,
                dom: frame[0].parentNode
            });
            console.log('Downloaded method draw markup');
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load method draw markup from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    function fetchCss() {
        var url = Context.adapterUrl('method-draw/MethodDrawEmbed.css');
        $.get(url).done(function (data) {
            $('<style>').html(data).appendTo('head');
            fetchMarkup();
            console.log('Downloaded method draw css');
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load method draw css from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    function fetchScript() {
        var url = Context.pluginUrl('method-draw/embedapi.js');
        $.getScript(url).done(function () {
            fetchCss();
        }).fail(function (jqxhr, textStatus, error) {
            console.log('Failed to load embedapi from: ' + url + '. ' + jqxhr.status + '. ' + error);
        });
    }

    throttledSizeFrame = _.throttle(sizeFrame, 100);
    fetchScript();
});
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import {FormattedValue, Typography} from 'tocco-ui'
import {FormattedMessage} from 'react-intl'
import {js} from 'tocco-util'
import _get from 'lodash/get'

const areEqual = (prevProps, nextProps) => {
  const diff = Object.keys(js.difference(prevProps, nextProps))
  return diff.length === 0
}

export const ManyRelationsCheckBox = React.memo(({entityData, entityKey, name, setSelectedMultipleAll, isSelected}) => {
  if (entityData.value.length === 0) {
    return null
  }

  const onChange = e => setSelectedMultipleAll(name, entityKey, e.target.checked)

  return <>
    <input type="checkbox" onChange={onChange} checked={isSelected} id={`${name}${entityKey}`}/>
    <Typography.Label for={`${name}${entityKey}`}>
      <FormattedMessage id="client.merge.allRelations"/> ({entityData.value.length})
    </Typography.Label>
  </>
}, areEqual)

ManyRelationsCheckBox.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.arrayOf(PropTypes.string)
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedMultipleAll: PropTypes.func.isRequired,
  isSelected: PropTypes.bool.isRequired
}

export const RelationsCheckBoxes = React.memo(({
  entityData,
  entityKey,
  name,
  setSelectedMultiple,
  selectedMultiple
}) => {
  return entityData.value.map(relationEntity => {
    const isSelected = _get(selectedMultiple, [name, relationEntity.key]) === entityKey
    const onChange = e => setSelectedMultiple(name, entityKey, relationEntity.key, e.target.checked)

    return <div key={`relation-checkbox-${relationEntity.key}`}>
      <input type="checkbox" onChange={onChange} checked={isSelected} id={`${name}${entityKey}-${relationEntity.key}`}/>
      <Typography.Label for={`${name}${entityKey}-${relationEntity.key}`}>
        <FormattedValue type="single-select" value={relationEntity}/>
      </Typography.Label>

  })
}, areEqual)

RelationsCheckBoxes.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.array
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedMultiple: PropTypes.func.isRequired,
  selectedMultiple: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string))
}

export const FormattedValueRadio = React.memo(({entityData, entityKey, name, setSelectedSingle, isSelected}) => {
  const typeMapper = {
    entity: 'single-select'
  }

  const {type, writable} = entityData
  const mappedType = typeMapper[type] || type
  const onChange = () => {
    setSelectedSingle(name, entityKey)
  }

  return <>
    <input
      type="radio"
      name={name}
      onChange={onChange}
      checked={isSelected}
      id={`${name}${entityKey}`}
      disabled={!writable}/>
    <Typography.Label for={`${name}${entityKey}`}>
      <FormattedValue type={mappedType} value={entityData.value}/>
    </Typography.Label>
  </>
}, areEqual)

FormattedValueRadio.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.any,
    writable: PropTypes.bool,
    type: PropTypes.string
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedSingle: PropTypes.func.isRequired,
  isSelected: PropTypes.bool.isRequired
}
<commit_msg>refactor(merge): use field factory<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import {FormattedValue, Typography} from 'tocco-ui'
import {field} from 'tocco-app-extensions'
import {FormattedMessage} from 'react-intl'
import {js} from 'tocco-util'
import _get from 'lodash/get'

const areEqual = (prevProps, nextProps) => {
  const diff = Object.keys(js.difference(prevProps, nextProps))
  return diff.length === 0
}

export const ManyRelationsCheckBox = React.memo(({entityData, entityKey, name, setSelectedMultipleAll, isSelected}) => {
  if (entityData.value.length === 0) {
    return null
  }

  const onChange = e => setSelectedMultipleAll(name, entityKey, e.target.checked)

  return <>
    <input type="checkbox" onChange={onChange} checked={isSelected} id={`${name}${entityKey}`}/>
    <Typography.Label for={`${name}${entityKey}`}>
      <FormattedMessage id="client.merge.allRelations"/> ({entityData.value.length})
    </Typography.Label>
  </>
}, areEqual)

ManyRelationsCheckBox.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.arrayOf(PropTypes.string)
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedMultipleAll: PropTypes.func.isRequired,
  isSelected: PropTypes.bool.isRequired
}

export const RelationsCheckBoxes = React.memo(({
  entityData,
  entityKey,
  name,
  setSelectedMultiple,
  selectedMultiple
}) => {
  return entityData.value.map(relationEntity => {
    const isSelected = _get(selectedMultiple, [name, relationEntity.key]) === entityKey
    const onChange = e => setSelectedMultiple(name, entityKey, relationEntity.key, e.target.checked)

    return <div key={`relation-checkbox-${relationEntity.key}`}>
      <input type="checkbox" onChange={onChange} checked={isSelected} id={`${name}${entityKey}-${relationEntity.key}`}/>
      <Typography.Label for={`${name}${entityKey}-${relationEntity.key}`}>
        <FormattedValue type="single-select" value={relationEntity}/>
      </Typography.Label>

  })
}, areEqual)

RelationsCheckBoxes.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.array
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedMultiple: PropTypes.func.isRequired,
  selectedMultiple: PropTypes.objectOf(PropTypes.objectOf(PropTypes.string))
}

export const FormattedValueRadio = React.memo(({entityData, entityKey, name, setSelectedSingle, isSelected}) => {
  const typeMapper = {
    entity: 'single-select-box'
  }

  const {type, writable} = entityData
  const mappedType = typeMapper[type] || type
  const onChange = () => {
    setSelectedSingle(name, entityKey)
  }

  const Field = field.factory('list', mappedType)

  return <>
    <input
      type="radio"
      name={name}
      onChange={onChange}
      checked={isSelected}
      id={`${name}${entityKey}`}
      disabled={!writable}/>
    <Typography.Label for={`${name}${entityKey}`}>
      <Field
        formField={{dataType: mappedType}}
        value={entityData.value}
      />
    </Typography.Label>
  </>
}, areEqual)

FormattedValueRadio.propTypes = {
  entityData: PropTypes.shape({
    value: PropTypes.any,
    writable: PropTypes.bool,
    type: PropTypes.string
  }),
  entityKey: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  setSelectedSingle: PropTypes.func.isRequired,
  isSelected: PropTypes.bool.isRequired
}
<|endoftext|>


<commit_before>/*!
* jQuery modal activation
*
* Copyright 2016, Slad
*/

(function ($) {

        // gestion du remplissage et de l'ouverture de la fenetre modal
        $('#dayModal').on('show.bs.modal', function (event) {
            var button = $(event.relatedTarget);

            var recipient = button.data('whatever');
            result 	= recipient.split("--");
            var modal = $(this);
            url =  Routing.generate('day_index', { _locale: locale }),
            $.ajax({
                type : 'POST',
                data : { date: result[0],
                         item: result[1]
                         },
                url : url,
               success : function(code_html, statut){
                   modal.find('.modal-body').html(code_html);
                   code_html='';
               },
               error : function(resultat, statut, erreur){
                   alert('error');  // messsage a personnaliser
               }
            });
            var mes = Translator.trans('booking_date', {}, 'SladBookingBundle');
            modal.find('#dayModalLabel').text(mes + ' ' + result[0]);
        });

})(jQuery);
<commit_msg>Update modal_activate.js<commit_after>/*!
* jQuery modal activation
*
* Copyright 2016, Slad
*/

(function ($) {

        // gestion du remplissage et de l'ouverture de la fenetre modal
        $('#dayModal').on('show.bs.modal', function (event) {
            var button = $(event.relatedTarget);

            var recipient = button.data('whatever');
            result 	= recipient.split("--");
            var modal = $(this);
            url =  Routing.generate('day_index', { _locale: locale }),
            $.ajax({
                type : 'POST',
                data : { date: result[0],
                         item: result[1],
                         project: result[2]
                         },
                url : url,
               success : function(code_html, statut){
                   modal.find('.modal-body').html(code_html);
                   code_html='';
               },
               error : function(resultat, statut, erreur){
                   alert('error');  // messsage a personnaliser
               }
            });
            var mes = Translator.trans('booking_date', {}, 'SladBookingBundle');
            modal.find('#dayModalLabel').text(mes + ' ' + result[0]);
        });

})(jQuery);
<|endoftext|>


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

    var mds = angular.module('mds', [
        'motech-dashboard', 'entityService','instanceService', 'mdsSettingsService', 'ngCookies', 'ui.directives',
        'ngRoute', 'ui.directives'
    ]);

    $.get('../mds/available/mdsTabs').done(function(data) {
         mds.constant('AVAILABLE_TABS', data);
    });

    mds.run(function ($rootScope, AVAILABLE_TABS) {
        $rootScope.AVAILABLE_TABS = AVAILABLE_TABS;
    });

    mds.config(function ($routeProvider, AVAILABLE_TABS) {
        angular.forEach(AVAILABLE_TABS, function (tab) {
            $routeProvider.when(
                '/{0}'.format(tab),
                {
                    templateUrl: '../mds/resources/partials/{0}.html'.format(tab),
                    controller: '{0}Ctrl'.format(tab.capitalize())
                }
            );
        });

        $routeProvider.otherwise({
            redirectTo: '/{0}'.format(AVAILABLE_TABS[0])
        });
    });
}());
<commit_msg>MDS: Fix ajax error while checking available tabs<commit_after>(function () {
    'use strict';

    var mds = angular.module('mds', [
        'motech-dashboard', 'entityService','instanceService', 'mdsSettingsService', 'ngCookies', 'ui.directives',
        'ngRoute', 'ui.directives'
    ]);

    $.ajax({
        url:      '../mds/available/mdsTabs',
        success:  function(data) {
            mds.constant('AVAILABLE_TABS', data);
        },
        async:    false
    });

    mds.run(function ($rootScope, AVAILABLE_TABS) {
        $rootScope.AVAILABLE_TABS = AVAILABLE_TABS;
    });

    mds.config(function ($routeProvider, AVAILABLE_TABS) {
        angular.forEach(AVAILABLE_TABS, function (tab) {
            $routeProvider.when(
                '/{0}'.format(tab),
                {
                    templateUrl: '../mds/resources/partials/{0}.html'.format(tab),
                    controller: '{0}Ctrl'.format(tab.capitalize())
                }
            );
        });

        $routeProvider.otherwise({
            redirectTo: '/{0}'.format(AVAILABLE_TABS[0])
        });
    });
}());
<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');
const Mongoose = require('mongoose');
const { parseType } = require('strapi-utils');

/**
 * Module dependencies
 */

module.exports = (mongoose = Mongoose) => {
  mongoose.Schema.Types.Decimal = require('mongoose-float').loadType(mongoose, 2);
  mongoose.Schema.Types.Float = require('mongoose-float').loadType(mongoose, 20);

  /**
   * Convert MongoDB ID to the stringify version as GraphQL throws an error if not.
   *
   * Refer to: https://github.com/graphql/graphql-js/commit/3521e1429eec7eabeee4da65c93306b51308727b#diff-87c5e74dd1f7d923143e0eee611f598eR183
   */
  mongoose.Types.ObjectId.prototype.valueOf = function() {
    return this.toString();
  };

  const convertType = (name, attr) => {
    switch (attr.type.toLowerCase()) {
      case 'array':
        return { type: Array };
      case 'boolean':
        return { type: 'Boolean' };
      case 'binary':
        return { type: 'Buffer' };
      case 'time':
        return {
          type: String,
          validate: value =>
            (!attr.required && _.isNil(value)) || parseType({ type: 'time', value }),
          set: value =>
            !attr.required && _.isNil(value) ? value : parseType({ type: 'time', value }),
        };
      case 'date':
        return {
          type: String,
          validate: value =>
            (!attr.required && _.isNil(value)) || parseType({ type: 'date', value }),
          set: value =>
            !attr.required && _.isNil(value) ? value : parseType({ type: 'date', value }),
        };
      case 'datetime':
        return {
          type: Date,
        };
      case 'timestamp':
        return {
          type: Date,
        };
      case 'decimal':
        return { type: 'Decimal' };
      case 'float':
        return { type: 'Float' };
      case 'json':
        return { type: 'Mixed' };
      case 'biginteger':
        return { type: 'Long' };
      case 'integer':
        return { type: 'Number' };
      case 'uuid':
        return { type: 'ObjectId' };
      case 'enumeration':
        return {
          type: 'String',
          enum: attr.enum.concat(null),
          default: null,
        };
      case 'email':
      case 'password':
      case 'string':
      case 'text':
      case 'richtext':
        return { type: 'String' };
      case 'uid': {
        return {
          type: 'String',
          index: {
            unique: true,
            partialFilterExpression: { [name]: { $type: 'string' } },
          },
        };
      }
      default:
        return undefined;
    }
  };

  const isMongoId = value => {
    if (value instanceof mongoose.Types.ObjectId) {
      return true;
    }

    if (!_.isString(value)) {
      return false;
    }

    // Here we don't use mongoose.Types.ObjectId.isValid method because it's a weird check,
    // it returns for instance true for any integer value
    const hexadecimal = /^[0-9A-F]+$/i;
    return hexadecimal.test(value) && value.length === 24;
  };

  const valueToId = value => {
    if (Array.isArray(value)) return value.map(valueToId);

    if (isMongoId(value)) {
      return mongoose.Types.ObjectId(value);
    }

    return value;
  };

  return {
    convertType,
    valueToId,
    isMongoId,
  };
};
<commit_msg>Add support for columnType in Mongoose (#8723)<commit_after>'use strict';

const _ = require('lodash');
const Mongoose = require('mongoose');
const { parseType } = require('strapi-utils');

/**
 * Module dependencies
 */

module.exports = (mongoose = Mongoose) => {
  mongoose.Schema.Types.Decimal = require('mongoose-float').loadType(mongoose, 2);
  mongoose.Schema.Types.Float = require('mongoose-float').loadType(mongoose, 20);

  /**
   * Convert MongoDB ID to the stringify version as GraphQL throws an error if not.
   *
   * Refer to: https://github.com/graphql/graphql-js/commit/3521e1429eec7eabeee4da65c93306b51308727b#diff-87c5e74dd1f7d923143e0eee611f598eR183
   */
  mongoose.Types.ObjectId.prototype.valueOf = function() {
    return this.toString();
  };

  const convertType = (name, attr) => {
    if (_.has(attr, 'columnType')) {
      return { type: attr.columnType }
    }

    switch (attr.type.toLowerCase()) {
      case 'array':
        return { type: Array };
      case 'boolean':
        return { type: 'Boolean' };
      case 'binary':
        return { type: 'Buffer' };
      case 'time':
        return {
          type: String,
          validate: value =>
            (!attr.required && _.isNil(value)) || parseType({ type: 'time', value }),
          set: value =>
            !attr.required && _.isNil(value) ? value : parseType({ type: 'time', value }),
        };
      case 'date':
        return {
          type: String,
          validate: value =>
            (!attr.required && _.isNil(value)) || parseType({ type: 'date', value }),
          set: value =>
            !attr.required && _.isNil(value) ? value : parseType({ type: 'date', value }),
        };
      case 'datetime':
        return {
          type: Date,
        };
      case 'timestamp':
        return {
          type: Date,
        };
      case 'decimal':
        return { type: 'Decimal' };
      case 'float':
        return { type: 'Float' };
      case 'json':
        return { type: 'Mixed' };
      case 'biginteger':
        return { type: 'Long' };
      case 'integer':
        return { type: 'Number' };
      case 'uuid':
        return { type: 'ObjectId' };
      case 'enumeration':
        return {
          type: 'String',
          enum: attr.enum.concat(null),
          default: null,
        };
      case 'email':
      case 'password':
      case 'string':
      case 'text':
      case 'richtext':
        return { type: 'String' };
      case 'uid': {
        return {
          type: 'String',
          index: {
            unique: true,
            partialFilterExpression: { [name]: { $type: 'string' } },
          },
        };
      }
      default:
        return undefined;
    }
  };

  const isMongoId = value => {
    if (value instanceof mongoose.Types.ObjectId) {
      return true;
    }

    if (!_.isString(value)) {
      return false;
    }

    // Here we don't use mongoose.Types.ObjectId.isValid method because it's a weird check,
    // it returns for instance true for any integer value
    const hexadecimal = /^[0-9A-F]+$/i;
    return hexadecimal.test(value) && value.length === 24;
  };

  const valueToId = value => {
    if (Array.isArray(value)) return value.map(valueToId);

    if (isMongoId(value)) {
      return mongoose.Types.ObjectId(value);
    }

    return value;
  };

  return {
    convertType,
    valueToId,
    isMongoId,
  };
};
<|endoftext|>


<commit_before>Meteor.startup(function () {

  // Notification email

  Router.route('/email/notification/:id?', {
    name: 'notification',
    where: 'server',
    action: function() {
      var notification = Notifications.findOne(this.params.id);
      var notificationContents = buildEmailNotification(notification);
      this.response.write(notificationContents.html);
      this.response.end();
    }
  });

});<commit_msg>Fix #584<commit_after>Meteor.startup(function () {

  // Notification email

  Router.route('/email/notification/:id?', {
    name: 'notification',
    where: 'server',
    action: function() {
      var notification = Herald.collection.findOne(this.params.id);
      var notificationContents = buildEmailNotification(notification);
      this.response.write(notificationContents.html);
      this.response.end();
    }
  });

});<|endoftext|>


<commit_before>import Ember from 'ember';
import layout from '../templates/components/cs-version-control';
import { task } from 'ember-concurrency';
import { transitionTo } from '../private-api';

export default Ember.Component.extend({
  layout,
  tagName: '',
  opened: true,
  resourceMetadata: Ember.inject.service(),
  store: Ember.inject.service(),
  cardstackRouting: Ember.inject.service(),

  modelMeta: Ember.computed('model', function() {
    return this.get('resourceMetadata').read(this.get('model'));
  }),

  onMaster: Ember.computed('modelMeta.branch', function() {
    return this.get('modelMeta').branch === this.get('cardstackRouting.defaultBranch');
  }),

  upstreamMeta: Ember.computed('upstreamModel', function() {
    let upstream = this.get('upstreamModel');
    if (upstream) {
      return this.get('resourceMetadata').read(upstream);
    }
  }),

  fetchUpstreamModel: task(function * () {
    this.set('upstreamModel', null);
    if (this.get('onMaster')) {
      // Nothing to do, we're already on the default branch, so there
      // is no model more upstream than ours.
      return;
    }
    let model = this.get('model');
    let type = model.constructor.modelName;
    let id = model.get('id');
    if (id == null) {
      // Nothing to do, we don't have an id yet. It's vanishingly
      // unlikely that we're about to collide with something on
      // upstream anyway.
      return;
    }
    try {
      let upstreamModel = yield this.get('store').findRecord('cardstack-generic', `master/${type}/${id}`);
      this.set('upstreamModel', upstreamModel);
    } catch (err) {
      if (err.isAdapterError && err.errors && err.errors.length > 0 && err.errors[0].code === 404) {
        // There's no upstream model, that's OK.
        return;
      }
      throw err;
    }
  }).observes('modelMeta.branch', 'model.id').on('init'),

  // this describes the state of our model relative to its branch. So
  // "saved" here means it has been saved to its branch, etc.
  modificationState: Ember.computed('model.isNew', 'model.hasDirtyFields', function() {
    if (this.get('model.isNew')) {
      return "new";
    } else  if (this.get('model.hasDirtyFields')) {
      return "changed";
    } else {
      return "saved";
    }
  }),

  // this describes the state of our model relative to its value on
  // the default branch.
  upstreamState: Ember.computed('upstreamMeta.hash', 'modelMeta.hash', 'onMaster', 'fetchUpstreamModel.isRunning', function() {
    if (this.get('onMaster')) {
      return 'self';
    }

    if (this.get('fetchUpstreamModel.isRunning')) {
      return 'pending';
    }

    let upstreamMeta = this.get('upstreamMeta');
    if (!upstreamMeta) {
      return 'created';
    }

    let meta = this.get('modelMeta');
    if (meta.hash === upstreamMeta.hash) {
      return 'same';
    } else {
      return 'different';
    }

  }),

  titleClass: Ember.computed('upstreamState', function() {
    let state = this.get('upstreamState');
    if (state === 'created' || state === 'different') {
      return 'cs-version-control-preview';
    }
  }),

  title: Ember.computed('modificationState', 'upstreamState', function() {
    switch(this.get('modificationState')) {
    case 'new':
      return 'Drafted'
    case 'changed':
      return 'Changed';
    case 'saved':
      switch(this.get('upstreamState')) {
      case 'pending':
        return '...';
      case 'self':
        return 'Live';
      case 'created':
      case 'different':
      case 'same':
        return 'Preview';
      }
    }
  }),

  anythingPending: Ember.computed('model.isNew', 'model.hasDirtyFields', function() {
    return this.get('model.isNew') || this.get('model.hasDirtyFields');
  }),

  update: task(function * () {
    let model = this.get('model');
    let creating = model.get('isNew');
    yield model.save();
    if (creating && model.get('slug')) {
      let { name, args, queryParams } = this.get('cardstackRouting').routeFor(model.get('type'), model.get('slug'), this.get('modelMeta.branch'));
      yield transitionTo(Ember.getOwner(this), name, args, queryParams);
    }
  }),

  actions: {
    open() {
      this.set('opened', true);
    },
    close() {
      this.set('opened', false);
    }
  }
});
<commit_msg>don't auto-redirect<commit_after>import Ember from 'ember';
import layout from '../templates/components/cs-version-control';
import { task } from 'ember-concurrency';

export default Ember.Component.extend({
  layout,
  tagName: '',
  opened: true,
  resourceMetadata: Ember.inject.service(),
  store: Ember.inject.service(),
  cardstackRouting: Ember.inject.service(),

  modelMeta: Ember.computed('model', function() {
    return this.get('resourceMetadata').read(this.get('model'));
  }),

  onMaster: Ember.computed('modelMeta.branch', function() {
    return this.get('modelMeta').branch === this.get('cardstackRouting.defaultBranch');
  }),

  upstreamMeta: Ember.computed('upstreamModel', function() {
    let upstream = this.get('upstreamModel');
    if (upstream) {
      return this.get('resourceMetadata').read(upstream);
    }
  }),

  fetchUpstreamModel: task(function * () {
    this.set('upstreamModel', null);
    if (this.get('onMaster')) {
      // Nothing to do, we're already on the default branch, so there
      // is no model more upstream than ours.
      return;
    }
    let model = this.get('model');
    let type = model.constructor.modelName;
    let id = model.get('id');
    if (id == null) {
      // Nothing to do, we don't have an id yet. It's vanishingly
      // unlikely that we're about to collide with something on
      // upstream anyway.
      return;
    }
    try {
      let upstreamModel = yield this.get('store').findRecord('cardstack-generic', `master/${type}/${id}`);
      this.set('upstreamModel', upstreamModel);
    } catch (err) {
      if (err.isAdapterError && err.errors && err.errors.length > 0 && err.errors[0].code === 404) {
        // There's no upstream model, that's OK.
        return;
      }
      throw err;
    }
  }).observes('modelMeta.branch', 'model.id').on('init'),

  // this describes the state of our model relative to its branch. So
  // "saved" here means it has been saved to its branch, etc.
  modificationState: Ember.computed('model.isNew', 'model.hasDirtyFields', function() {
    if (this.get('model.isNew')) {
      return "new";
    } else  if (this.get('model.hasDirtyFields')) {
      return "changed";
    } else {
      return "saved";
    }
  }),

  // this describes the state of our model relative to its value on
  // the default branch.
  upstreamState: Ember.computed('upstreamMeta.hash', 'modelMeta.hash', 'onMaster', 'fetchUpstreamModel.isRunning', function() {
    if (this.get('onMaster')) {
      return 'self';
    }

    if (this.get('fetchUpstreamModel.isRunning')) {
      return 'pending';
    }

    let upstreamMeta = this.get('upstreamMeta');
    if (!upstreamMeta) {
      return 'created';
    }

    let meta = this.get('modelMeta');
    if (meta.hash === upstreamMeta.hash) {
      return 'same';
    } else {
      return 'different';
    }

  }),

  titleClass: Ember.computed('upstreamState', function() {
    let state = this.get('upstreamState');
    if (state === 'created' || state === 'different') {
      return 'cs-version-control-preview';
    }
  }),

  title: Ember.computed('modificationState', 'upstreamState', function() {
    switch(this.get('modificationState')) {
    case 'new':
      return 'Drafted'
    case 'changed':
      return 'Changed';
    case 'saved':
      switch(this.get('upstreamState')) {
      case 'pending':
        return '...';
      case 'self':
        return 'Live';
      case 'created':
      case 'different':
      case 'same':
        return 'Preview';
      }
    }
  }),

  anythingPending: Ember.computed('model.isNew', 'model.hasDirtyFields', function() {
    return this.get('model.isNew') || this.get('model.hasDirtyFields');
  }),

  update: task(function * () {
    let model = this.get('model');
    yield model.save();
  }).keepLatest(),

  actions: {
    open() {
      this.set('opened', true);
    },
    close() {
      this.set('opened', false);
    }
  }
});
<|endoftext|>


<commit_before>
/* Shuffle the array on quotes once at launch */
fyShuffle(quotes);

/* Populate an initial quote */
newQuote();

/* Allow user to generate new quotes */
document.getElementById("getQuoteBtn").addEventListener("click", newQuote);

/* Can use this to avoid warnings about - "Unsafe assignment to innerHTML" */
function escapeHTML(str) {
	escapeHTML.replacements = { "&": "&", '"': """, "'": "'", "<": "<", ">": ">" };
	return str.replace(/[&"'<>]/g, (m) => escapeHTML.replacements[m]);
}

/* Fisher-Yates Shuffle algorithm
 * Reference: https://stackoverflow.com/a/18806417/319204
 */
function fyShuffle(array) {
    var i = array.length,
        j = 0,
        temp;

    while (i--) {
        j = Math.floor(Math.random() * (i+1));

        /* swap randomly chosen element with current element */
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

function get(url, callbackFn)
{
    var httpReq = new XMLHttpRequest();

	/* Who will handle the response */
	httpReq.addEventListener("load", callbackFn);

	/* Wait upto 10s for a response */
	httpReq.timeout = 10000;

	/* ASYNC GET */
	httpReq.open("GET", url, true);
	httpReq.send(null);
}

/* Used by array.filter method to remove duplicate entries in an array */
function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function loadBackgroundImage(keywords)
{
	/* Check and Initialise rateLimit detected flag */
    if ( typeof loadBackgroundImage.atUnsplashRateLimit == 'undefined' ) {
        var atUnsplashRateLimit = 0;
    }

	/* Immediately clear current background image before continuing */
	for (i = 0; i < 3; i++) {
		document.getElementById('img' + i).style.backgroundImage = "none";
	}

	/* ...and immediately clear the background image attribution as well */
	document.getElementById('imgAttr').innerHTML = '';

	/* TODO: Add logic to choose image source here.
	 * For now prefer unsplash over flickr
	 * unless recently rate-limited by unsplash.
	 */

	if (loadBackgroundImage.atUnsplashRateLimit == 1) {
		/* If temporarily backing-off unsplash, directly use flickr */
		loadBackgroundImageFromFlickr(keywords);
	} else {
		/* Try unsplash. If response received is failure,
		 * then async response handler will initiate fallback to flickr.
		 */
		loadBackgroundImageFromUnsplash(keywords);
	}
}

/* Return list of words from a sentence sorted in order of decreasing length */
function getWordList(sentence) {

	/* Split the quote into an array of individual words
	 * Note: Define any characters/words to ignore as a separator.
	 */
	var separators = ['-', ' ', '"', '+', '(', ')', '*', ':', ';', '?', '.', ',', '<', '>', '&', '!',];
	var wordsArray = sentence.toLowerCase().split(new RegExp('(quot)|[' + separators.join('') + ']', 'g'));

	/* Sort the array in order of decreasing word-length */
	var wordsArray = wordsArray.sort(function(a, b) {
		return b.length - a.length;
	});

	return wordsArray;
}

/* Returns a random quote with random font */
function newQuote() {

	/* Hide the button first */
	document.getElementById("getQuoteBtn").style.opacity = 0;

	/* Also hide the image attribution text */
	document.getElementById('imgAttr').style.opacity = 0;

	/* Next hide the quote text */
	document.getElementById('quoteText').style.opacity = 0;

	/* Check and Initialise the quote pointer */
    if ( typeof newQuote.quoteId == 'undefined' ) {
        newQuote.quoteId = -1;
    }

	/* Pick the next quote from the shuffled list of quotes */
	newQuote.quoteId++;

	/* If hit the end, re-shuffle array of quotes and repeat at the beginning */
	if (newQuote.quoteId >= quotes.length) {
		fyShuffle(quotes);
		newQuote.quoteId = 0;
	}

	/* Do NOT show the new quote yet */

	/* Update quoteText font-family randomly */
	var randomFontFamily = Math.floor(Math.random() * 7);
	var supportedFonts = [ "Arima Madurai", "Farsan", "Gruppo", "Special Elite", "Love Ya Like A Sister", "Miriam Libre"];
	document.getElementById("quoteText").style.fontFamily = supportedFonts[randomFontFamily]

	/* Update a quoteText font-size randomly */
	var randomFontSize = Math.floor(Math.random() * 12);
	document.getElementById("quoteText").style.fontSize = (20 + randomFontSize) + 'px';

	keywordsArray = getWordList(quotes[newQuote.quoteId])

	/* Pick the 3 longest words in the quote */
	keywords = keywordsArray[0] + ',' + keywordsArray[1] + ',' + keywordsArray[2];

	/* Attempt to fetch a relevant backgroundimage, and update it if found */
	loadBackgroundImage(keywords)

	/* Show the image attribution */
	document.getElementById('imgAttr').style.opacity = 1.0;

	/* Finally show the new quote */
	document.getElementById('quoteText').innerHTML = quotes[newQuote.quoteId] + '
' + '
';
	document.getElementById('quoteText').style.opacity = 1.0;

	/* and Finally show the button */
	document.getElementById("getQuoteBtn").style.opacity = 1.0;

}<commit_msg>Refactor quote text usage<commit_after>
/* Shuffle the array on quotes once at launch */
fyShuffle(quotes);

/* Populate an initial quote */
newQuote();

/* Allow user to generate new quotes */
document.getElementById("getQuoteBtn").addEventListener("click", newQuote);

/* Can use this to avoid warnings about - "Unsafe assignment to innerHTML" */
function escapeHTML(str) {
	escapeHTML.replacements = { "&": "&", '"': """, "'": "'", "<": "<", ">": ">" };
	return str.replace(/[&"'<>]/g, (m) => escapeHTML.replacements[m]);
}

/* Fisher-Yates Shuffle algorithm
 * Reference: https://stackoverflow.com/a/18806417/319204
 */
function fyShuffle(array) {
    var i = array.length,
        j = 0,
        temp;

    while (i--) {
        j = Math.floor(Math.random() * (i+1));

        /* swap randomly chosen element with current element */
        temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }
    return array;
}

function get(url, callbackFn)
{
    var httpReq = new XMLHttpRequest();

	/* Who will handle the response */
	httpReq.addEventListener("load", callbackFn);

	/* Wait upto 10s for a response */
	httpReq.timeout = 10000;

	/* ASYNC GET */
	httpReq.open("GET", url, true);
	httpReq.send(null);
}

/* Used by array.filter method to remove duplicate entries in an array */
function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function loadBackgroundImage(keywords)
{
	/* Check and Initialise rateLimit detected flag */
    if ( typeof loadBackgroundImage.atUnsplashRateLimit == 'undefined' ) {
        var atUnsplashRateLimit = 0;
    }

	/* Immediately clear current background image before continuing */
	for (i = 0; i < 3; i++) {
		document.getElementById('img' + i).style.backgroundImage = "none";
	}

	/* ...and immediately clear the background image attribution as well */
	document.getElementById('imgAttr').innerHTML = '';

	/* TODO: Add logic to choose image source here.
	 * For now prefer unsplash over flickr
	 * unless recently rate-limited by unsplash.
	 */

	if (loadBackgroundImage.atUnsplashRateLimit == 1) {
		/* If temporarily backing-off unsplash, directly use flickr */
		loadBackgroundImageFromFlickr(keywords);
	} else {
		/* Try unsplash. If response received is failure,
		 * then async response handler will initiate fallback to flickr.
		 */
		loadBackgroundImageFromUnsplash(keywords);
	}
}

/* Return list of words from a sentence sorted in order of decreasing length */
function getWordList(sentence) {

	/* Split the quote into an array of individual words
	 * Note: Define any characters/words to ignore as a separator.
	 */
	var separators = ['-', ' ', '"', '+', '(', ')', '*', ':', ';', '?', '.', ',', '<', '>', '&', '!',];
	var wordsArray = sentence.toLowerCase().split(new RegExp('(quot)|[' + separators.join('') + ']', 'g'));

	/* Sort the array in order of decreasing word-length */
	var wordsArray = wordsArray.sort(function(a, b) {
		return b.length - a.length;
	});

	return wordsArray;
}

/* Returns a random quote with random font */
function newQuote() {

	/* Hide the button first */
	document.getElementById("getQuoteBtn").style.opacity = 0;

	/* Also hide the image attribution text */
	document.getElementById('imgAttr').style.opacity = 0;

	/* Next hide the quote text */
	document.getElementById('quoteText').style.opacity = 0;

	/* Check and Initialise the quote pointer */
    if ( typeof newQuote.quoteId == 'undefined' ) {
        newQuote.quoteId = -1;
    }

	/* Pick the next quote from the shuffled list of quotes */
	newQuote.quoteId++;

	/* If hit the end, re-shuffle array of quotes and repeat at the beginning */
	if (newQuote.quoteId >= quotes.length) {
		fyShuffle(quotes);
		newQuote.quoteId = 0;
	}

	/* Do NOT show the new quote yet */

	/* Update quoteText font-family randomly */
	var randomFontFamily = Math.floor(Math.random() * 7);
	var supportedFonts = [ "Arima Madurai", "Farsan", "Gruppo", "Special Elite", "Love Ya Like A Sister", "Miriam Libre"];
	document.getElementById("quoteText").style.fontFamily = supportedFonts[randomFontFamily]

	/* Update a quoteText font-size randomly */
	var randomFontSize = Math.floor(Math.random() * 12);
	document.getElementById("quoteText").style.fontSize = (20 + randomFontSize) + 'px';

	var htmlQuote = quotes[newQuote.quoteId];

	/* Trim HTML tags */
	var plainTextQuote = htmlQuote.replace(/
/g,' ')
					.replace(/<\/p>/g, ' ')
					.replace(/"/g, '"')
					.replace(/(<([^>]+)>)/g, '');

	keywordsArray = getWordList(htmlQuote)

	/* Pick the 3 longest words in the quote */
	keywords = keywordsArray[0] + ',' + keywordsArray[1] + ',' + keywordsArray[2];

	/* Attempt to fetch a relevant backgroundimage, and update it if found */
	loadBackgroundImage(keywords)

	/* Show the image attribution */
	document.getElementById('imgAttr').style.opacity = 1.0;

	/* Finally show the new quote */
	document.getElementById('quoteText').innerHTML = htmlQuote + '
' + '
';
	document.getElementById('quoteText').style.opacity = 1.0;

	/* and Finally show the button */
	document.getElementById("getQuoteBtn").style.opacity = 1.0;

}<|endoftext|>


<commit_before>import RESTAdapter from 'ember-data/adapters/rest';
import DS from 'ember-data';
import {QueryDSL} from 'ember-es-adapter/utils/es-tools';
import Ember from 'ember';

export default RESTAdapter.extend({

  urlForCreateRecord(modelName, snapshot) {
    return [this.buildURL(modelName), snapshot.id].join('/');
  },

  query(store, type, params) {
    const url = [this.buildURL(type.modelName), '_search'].join('/');

    let query = Ember.get(params, 'esQuery') || null;

    // No EsQuery object was supplied, so we'll make a new one. This
    // allows us to build the query outside of the adapter if needed.
    if (Ember.isEmpty(query)) {
      // Initiate new instance with params if supplied
      // Inject _params into params object
      let es = new QueryDSL(params);

      query = es.getQuery();
    }

    return fetch(url, {
      method: "POST",
      body: JSON.stringify(query)
    })
    .then(function(resp) {
      return resp.json();
    });
  },

  findAll(store, type) {
    const url = [this.buildURL(type.modelName), '_search'].join('/');

    let es = new QueryDSL({ 'size': 10000 });

    return fetch(url, {
      method: "post",
      body: JSON.stringify(es.buildQuery())
    })
    .then(function(resp) {
      return resp.json();
    });
  },

  createRecord(nodelName, type, snapshot) {
    let data = {},
        serializer = this.store.serializerFor(type.modelName);

    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });

    let id = snapshot.id,
        url = this.buildURL(type.modelName, id, snapshot, 'createRecord');

    url = [url, '_create'].join('/');

    return fetch(url, {
      method: "post",
      body: JSON.stringify(data)
    })
    .then(function(resp) {
      return resp.json();
    });

  },

  updateRecord(nodelName, type, snapshot) {
    let data = {},
        serializer = this.store.serializerFor(type.modelName);

    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });

    let id = snapshot.id,
        url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');

    return fetch(url, {
      method: "post",
      body: JSON.stringify(data)
    })
    .then(function(resp) {
      console.log(resp);
      return resp.json()
        .then((_resp) => {
          console.log('updateRecord');
          console.log(_resp);
          if (_resp.error) {
            console.log('updateRecord Error');
            console.log(_resp);
            return Ember.RSVP.reject(new DS.InvalidError([
              {
                message: _resp.error.reason,
                cause: _resp.error.caused_by.reason,
                type: _resp.error.type,
                error: _resp.error,
                status: _resp.status
              }
            ]));
          }
          return _resp;
        });
    });

  },

  handleResponse: function(status, headers, payload){
    console.log('handle response');
    console.log({status, headers, payload});

    return this._super(...arguments);
  }

});
<commit_msg>Updated adapter<commit_after>import DS from 'ember-data';
import {QueryDSL} from 'ember-es-adapter/utils/es-tools';
import Ember from 'ember';
import fetch from 'fetch';

export default DS.JSONAPIAdapter.extend({
  urlForCreateRecord(modelName, snapshot) {
    //Ember.Logger.info('[ES-Adapter][urlForCreateRecord]');
    return [this.buildURL(modelName), snapshot.id].join('/');
  },

  //used for GET requests from elasticsearch
  urlForQuery(query, modelName) {
    // http://stackoverflow.com/a/34209399/707580
    const esc = encodeURIComponent;
    const get = Object.keys(query)
          .map(k => esc(k) + '=' + esc(query[k]))
          .join('&');

    const model = this._super.apply(this, arguments);
    const url = [model, '_search'].join('/');

    return [url, get].join('?');
  },

  query(store, type, params) {
    let options = {};
    let query;

    if (params.method === 'get') {
      query = {
        size: params.size,
        from: params.from,
        sort: params.sort,
        q: this.buildGetQuery(params.query)
      };

      if (params.hasOwnProperty('_source_include')) {
        query._source_include = params._source_include;
      }
    }
    else if (params.method === 'post') {
      let es = new QueryDSL(params.post);
      let dsl = es.getQuery();

      options = {
        method: 'post',
        body: JSON.stringify(dsl),
      }
    }

    let url = this.buildURL(type.modelName, null, null, 'query', query);

    //Ember.Logger.debug('[ES-Adapter][query]',{url, params});

    return fetch(url, options).then((resp) => resp.json());
  },

  buildGetQuery(query) {
    if (query.hasOwnProperty('items') // we are stil nesting
        && Ember.isArray(query.items)) {
      const result = query.items.map(this.buildGetQuery.bind(this));
      return '('
           + result.join(' ' + query.type + ' ')
           + ')';
    }

    // build out the individual item
    const esc = encodeURIComponent;
    return Object.keys(query).map(k => esc(k) + ':' + esc(query[k]));
  },

  findAll(store, type) {
    //Ember.Logger.info('[ES-Adapter][findAll]');
    const url = [this.buildURL(type.modelName), '_search'].join('/');

    let es = new QueryDSL({ 'size': 10000 });

    return fetch(url, {
      method: "post",
      body: JSON.stringify(es.getQuery())
    })
    .then(function(resp) {
      return resp.json();
    });
  },

  createRecord(nodelName, type, snapshot) {
    //Ember.Logger.info('[ES-Adapter][createRecord]');
    let data = {},
        serializer = this.store.serializerFor(type.modelName);

    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });

    let id = snapshot.id,
        url = this.buildURL(type.modelName, id, snapshot, 'createRecord');

    url = [url, '_create'].join('/');

    return fetch(url, {
      method: "post",
      body: JSON.stringify(data)
    })
    .then(function(resp) {
      return resp.json();
    });
  },

  updateRecord(nodelName, type, snapshot) {
    //Ember.Logger.info('[ES-Adapter][updateRecord]');
    let data = {},
        serializer = this.store.serializerFor(type.modelName);

    serializer.serializeIntoHash(data, type, snapshot, { includeId: true });

    let id = snapshot.id,
        url = this.buildURL(type.modelName, id, snapshot, 'updateRecord');

    return fetch(url, {
      method: "post",
      body: JSON.stringify(data)
    })
    .then(function(resp) {
      //console.log(resp);
      return resp.json()
        .then((_resp) => {
          //console.log('updateRecord');
          //console.log(_resp);
          if (_resp.error) {
            //console.log('updateRecord Error');
            //console.log(_resp);
            return Ember.RSVP.reject(new DS.InvalidError([
              {
                message: _resp.error.reason,
                cause: _resp.error.caused_by.reason,
                type: _resp.error.type,
                error: _resp.error,
                status: _resp.status
              }
            ]));
          }
          return _resp;
        });
    });

  },

  //normalizeErrorResponse: function(status, headers, payload) {
  //  Ember.Logger.debug('[ES-Adapter][normalizeErrorResponse]', {status, headers, payload});
  //  if (payload && typeof payload === 'object' && payload.errors) {
  //    return payload.errors;
  //  } else {
  //    return [
  //      {
  //        status: `${status}`,
  //        title: "The backend responded with an error",
  //        detail: `${payload}`
  //      }
  //    ];
  //  }
  //},

  handleResponse: function(status, headers, payload){
    //Ember.Logger.debug('[ES-Adapter][handleResponse]');

    if (typeof payload === 'undefined') payload = {};

    if (String(status).charAt(0) === '4' || String(status).charAt(0) === '5') {
      payload['errors'] = [];

      payload.errors.push({
        status: `${status}`,
        title: "Not Found",
        detail: "Document " + payload._id + " was not found"
      });
      return DS.AdapterError(payload.errors, "Document " + payload._id + " was not found");
    }

    return this._super(status, headers, payload);
  }

});
<|endoftext|>