<commit_before>/*global gyrocopter */
(function () {
'use strict';

/**
 * The main controller for the app.
 */
gyrocopter.controller('gyrocopterCtrl', function mainCtrl($scope, Browser) {

  $scope.platforms = Browser.getPlatforms();

  $scope.selected = $scope.platforms[0].browsers[0];
  $scope.selectBrowser = function(browser){
    $scope.selected = browser;
    $scope.updateAxes();
    $scope.saveData();
  };

  $scope.updateAxes = function(){
    var rotation = $scope.css.transform.match(/-?(\d+)(\.(\d+))?/g);

    var b = Number(rotation[0]);
    var c = Number(rotation[1]);
    var a = Number(rotation[2]);

    var alphaMult = $scope.selected.rotation.alpha.reverse ? -1 : 1;
    var betaMult = $scope.selected.rotation.beta.reverse ? -1 : 1;
    var gammaMult = $scope.selected.rotation.gamma.reverse ? -1 : 1;

    $scope.alpha = - (a/alphaMult);
    $scope.beta = betaMult > 0 ? - (b/betaMult) + 90 - 180 : - ((b - 180)/betaMult) + 90 - 180;
    $scope.gamma = (c/gammaMult) - 180;

    while ($scope.alpha < 0) { $scope.alpha += 360; }
    while ($scope.beta < 0) { $scope.beta += 360; }
    while ($scope.gamma < 0) { $scope.gamma += 360; }

  };

  $scope.setDefaultRotation = function(){
    $scope.rotateDevice();
  };

  /**
   * Compute methods
   */

  $scope.getRange = function(max, min){
    return Math.abs(max - min);
  };

  $scope.getAlphaRotation = function(){
    var alpha = $scope.alpha;
    return alpha;
  };

  $scope.getBetaRotation = function(){
    var beta = $scope.beta - 180;
    var range = $scope.getRange($scope.selected.rotation.beta.max, $scope.selected.rotation.beta.min);

    if (range === 180) {
      if (beta > 90) {
        return (90 - beta + 90);
      }
      if (beta < -90) {
        return -1 * (90 + beta + 90);
      }
    }

    return beta;
  };

  $scope.getGammaRotation = function(){
    var gamma = $scope.gamma - 180;

    var range = $scope.getRange($scope.selected.rotation.gamma.max, $scope.selected.rotation.gamma.min);

    if (range === 180) {
      if (gamma > 90) {
        return (gamma - 180);
      }
      if (gamma <= -90) {
        return (gamma + 180);
      }
    }

    if (gamma < -90 && $scope.selected.rotation.gamma.min > 180) {
      gamma = $scope.selected.rotation.gamma.min + gamma + 90;
    }

    return gamma;
  };

  /**
   * Rotation methods
   */

  var prettyRotate = function(val){
    if (val > 360) {
      return val - 360;
    }

    if (val < -360) {
      return val + 360;
    }

    return val;
  };

  $scope.rotateDevice = function(){
    var alpha = $scope.alpha;
    var beta = $scope.beta;
    var gamma = $scope.gamma;
    var alphaMult = $scope.selected.rotation.alpha.reverse ? -1 : 1;
    var betaMult = $scope.selected.rotation.beta.reverse ? -1 : 1;
    var gammaMult = $scope.selected.rotation.gamma.reverse ? -1 : 1;

    $scope.css = {};

    var a = - alpha * alphaMult;
    var b = betaMult > 0 ? (- beta + 90 - 180) * betaMult : (- beta + 90 - 180) * betaMult - 180;
    var c = - (- gamma - 180) * gammaMult;

    a = prettyRotate(a);
    b = prettyRotate(b);
    c = prettyRotate(c);

    $scope.css['transform'] = 'rotateX(' + b + 'deg)' + 'rotateY(' + c + 'deg)' + 'rotateZ(' + a + 'deg)';
    $scope.css['-webkit-transform'] = 'rotateX(' + b + 'deg)' + 'rotateY(' + c + 'deg)' + 'rotateZ(' + a + 'deg)';

    sendJS($scope.getAlphaRotation($scope.alpha), $scope.getBetaRotation($scope.beta), $scope.getGammaRotation($scope.gamma));
  }

  $scope.saveData = function(){
    chrome.storage.local.set({'alpha': $scope.alpha, 'beta': $scope.beta, 'gamma': $scope.gamma, 'browser': JSON.stringify($scope.selected)}, function() {
      console.log('saved');
    });
  };

  chrome.storage.local.get(['alpha', 'beta', 'gamma', 'browser'], function(storage){
    console.log(storage);
    $scope.alpha = Number(storage.alpha) || 0;
    $scope.beta = Number(storage.beta) || 270;
    $scope.gamma = Number(storage.gamma) || 180;
    $scope.selected = JSON.parse(storage.browser || '{}');
    if (!$scope.selected.id) {
      $scope.selected = $scope.platforms[0].browsers[0];
    }
    $scope.rotateDevice();
    $scope.$apply();
  });

  /**
   * Dispatches a DeviceOrientation Event
   * @param  {double?} alpha    Rotate the device frame around its z axis by alpha degrees, with alpha in [0, 360).
   * @param  {double?} beta     Rotate the device frame around its x axis by beta degrees, with beta in [-180, 180).
   * @param  {double?} gamma    Rotate the device frame around its y axis by gamma degrees, with gamma in [-90, 90).
   * @param  {boolean} absolute Indicates absolute values for the three angles or relative to some arbitrary orientation.
   */

  var sendJS = function(alpha, beta, gamma, absolute){
    console.log(alpha, beta, gamma);

    var a = alpha;
    var b = beta;
    var c = gamma;
    absolute = true;
    // var event = document.createEvent("DeviceOrientationEvent");
    // event.initDeviceOrientationEvent("deviceorientation", false, false, alpha, beta, gamma, absolute);
    // window.dispatchEvent(event);

    var js = '';
    js += 'var event = document.createEvent("DeviceOrientationEvent");';
    js += 'event.initDeviceOrientationEvent("deviceorientation", false, false, ' + a + ', ' + b + ', ' + c + ', ' + absolute + ');';
    js += 'window.dispatchEvent(event);';

    chrome.tabs.executeScript({
      code: js
    });
  };

  $scope.reset = function(){
    $scope.alpha = 0;
    $scope.beta = 270;
    $scope.gamma = 180;
    $scope.rotateDevice();
    $scope.saveData();
  };

  var STEPPER = 0.5;

  $scope.compute = function(next, direction, min, max){
    if (direction === 'left') {
      next -= STEPPER;
    }
    else {
      next += STEPPER;
    }

    if (next < min) {
      next = max - STEPPER;
    }
    else if (next >= max) {
      next = min;
    }

    return next;
  };

  $scope.stepAlpha = function(direction){
    var next =  $scope.compute(Number($scope.alpha), direction, 0, 359.5);
    $scope.alpha = next;
    $scope.rotateDevice()
    $scope.saveData();
  };

  $scope.stepBeta = function(direction){
    var next =  $scope.compute(Number($scope.beta), direction, 0.5, 360);
    $scope.beta = next;
    $scope.rotateDevice()
    $scope.saveData();
  };

  $scope.stepGamma = function(direction){
    var next =  $scope.compute(Number($scope.gamma), direction, 0, 359.5);
    $scope.gamma = next;
    $scope.rotateDevice()
    $scope.saveData();
  };
});

})();
<commit_msg>default rotation on reset<commit_after>/*global gyrocopter */
(function () {
'use strict';

/**
 * The main controller for the app.
 */
gyrocopter.controller('gyrocopterCtrl', function mainCtrl($scope, Browser) {

  $scope.platforms = Browser.getPlatforms();

  $scope.selected = $scope.platforms[0].browsers[0];
  $scope.selectBrowser = function(browser){
    $scope.selected = browser;
    $scope.updateAxes();
    $scope.saveData();
  };

  $scope.updateAxes = function(){
    var rotation = $scope.css.transform.match(/-?(\d+)(\.(\d+))?/g);

    var b = Number(rotation[0]);
    var c = Number(rotation[1]);
    var a = Number(rotation[2]);

    var alphaMult = $scope.selected.rotation.alpha.reverse ? -1 : 1;
    var betaMult = $scope.selected.rotation.beta.reverse ? -1 : 1;
    var gammaMult = $scope.selected.rotation.gamma.reverse ? -1 : 1;

    $scope.alpha = - (a/alphaMult);
    $scope.beta = betaMult > 0 ? - (b/betaMult) + 90 - 180 : - ((b - 180)/betaMult) + 90 - 180;
    $scope.gamma = (c/gammaMult) - 180;

    while ($scope.alpha < 0) { $scope.alpha += 360; }
    while ($scope.beta < 0) { $scope.beta += 360; }
    while ($scope.gamma < 0) { $scope.gamma += 360; }

  };

  $scope.setDefaultRotation = function(){
    $scope.rotateDevice();
  };

  /**
   * Compute methods
   */

  $scope.getRange = function(max, min){
    return Math.abs(max - min);
  };

  $scope.getAlphaRotation = function(){
    var alpha = $scope.alpha;
    return alpha;
  };

  $scope.getBetaRotation = function(){
    var beta = $scope.beta - 180;
    var range = $scope.getRange($scope.selected.rotation.beta.max, $scope.selected.rotation.beta.min);

    if (range === 180) {
      if (beta > 90) {
        return (90 - beta + 90);
      }
      if (beta < -90) {
        return -1 * (90 + beta + 90);
      }
    }

    return beta;
  };

  $scope.getGammaRotation = function(){
    var gamma = $scope.gamma - 180;

    var range = $scope.getRange($scope.selected.rotation.gamma.max, $scope.selected.rotation.gamma.min);

    if (range === 180) {
      if (gamma > 90) {
        return (gamma - 180);
      }
      if (gamma <= -90) {
        return (gamma + 180);
      }
    }

    if (gamma < -90 && $scope.selected.rotation.gamma.min > 180) {
      gamma = $scope.selected.rotation.gamma.min + gamma + 90;
    }

    return gamma;
  };

  /**
   * Rotation methods
   */

  var prettyRotate = function(val){
    if (val > 360) {
      return val - 360;
    }

    if (val < -360) {
      return val + 360;
    }

    return val;
  };

  var performeRotation = function(a,b,c){
    $scope.css['transform'] = 'rotateX(' + b + 'deg)' + 'rotateY(' + c + 'deg)' + 'rotateZ(' + a + 'deg)';
    $scope.css['-webkit-transform'] = 'rotateX(' + b + 'deg)' + 'rotateY(' + c + 'deg)' + 'rotateZ(' + a + 'deg)';
  }

  $scope.rotateDevice = function(){
    var alpha = $scope.alpha;
    var beta = $scope.beta;
    var gamma = $scope.gamma;
    var alphaMult = $scope.selected.rotation.alpha.reverse ? -1 : 1;
    var betaMult = $scope.selected.rotation.beta.reverse ? -1 : 1;
    var gammaMult = $scope.selected.rotation.gamma.reverse ? -1 : 1;

    $scope.css = {};

    var a = - alpha * alphaMult;
    var b = betaMult > 0 ? (- beta + 90 - 180) * betaMult : (- beta + 90 - 180) * betaMult - 180;
    var c = - (- gamma - 180) * gammaMult;

    a = prettyRotate(a);
    b = prettyRotate(b);
    c = prettyRotate(c);

    performeRotation(a,b,c);
    sendJS($scope.getAlphaRotation($scope.alpha), $scope.getBetaRotation($scope.beta), $scope.getGammaRotation($scope.gamma));
  };

  $scope.saveData = function(){
    chrome.storage.local.set({'alpha': $scope.alpha, 'beta': $scope.beta, 'gamma': $scope.gamma, 'browser': JSON.stringify($scope.selected)}, function() {
      console.log('saved');
    });
  };

  chrome.storage.local.get(['alpha', 'beta', 'gamma', 'browser'], function(storage){
    console.log(storage);
    $scope.alpha = Number(storage.alpha) || 0;
    $scope.beta = Number(storage.beta) || 270;
    $scope.gamma = Number(storage.gamma) || 180;
    $scope.selected = JSON.parse(storage.browser || '{}');
    if (!$scope.selected.id) {
      $scope.selected = $scope.platforms[0].browsers[0];
    }
    $scope.rotateDevice();
    $scope.$apply();
  });

  /**
   * Dispatches a DeviceOrientation Event
   * @param  {double?} alpha    Rotate the device frame around its z axis by alpha degrees, with alpha in [0, 360).
   * @param  {double?} beta     Rotate the device frame around its x axis by beta degrees, with beta in [-180, 180).
   * @param  {double?} gamma    Rotate the device frame around its y axis by gamma degrees, with gamma in [-90, 90).
   * @param  {boolean} absolute Indicates absolute values for the three angles or relative to some arbitrary orientation.
   */

  var sendJS = function(alpha, beta, gamma, absolute){
    console.log(alpha, beta, gamma);

    var a = alpha;
    var b = beta;
    var c = gamma;
    absolute = true;
    // var event = document.createEvent("DeviceOrientationEvent");
    // event.initDeviceOrientationEvent("deviceorientation", false, false, alpha, beta, gamma, absolute);
    // window.dispatchEvent(event);

    var js = '';
    js += 'var event = document.createEvent("DeviceOrientationEvent");';
    js += 'event.initDeviceOrientationEvent("deviceorientation", false, false, ' + a + ', ' + b + ', ' + c + ', ' + absolute + ');';
    js += 'window.dispatchEvent(event);';

    chrome.tabs.executeScript({
      code: js
    });
  };

  $scope.reset = function(){
    performeRotation(0,0,0);
    $scope.updateAxes()
    sendJS($scope.getAlphaRotation($scope.alpha), $scope.getBetaRotation($scope.beta), $scope.getGammaRotation($scope.gamma));
    $scope.saveData();
  };

  var STEPPER = 0.5;

  $scope.compute = function(next, direction, min, max){
    if (direction === 'left') {
      next -= STEPPER;
    }
    else {
      next += STEPPER;
    }

    if (next < min) {
      next = max - STEPPER;
    }
    else if (next >= max) {
      next = min;
    }

    return next;
  };

  $scope.stepAlpha = function(direction){
    var next =  $scope.compute(Number($scope.alpha), direction, 0, 359.5);
    $scope.alpha = next;
    $scope.rotateDevice()
    $scope.saveData();
  };

  $scope.stepBeta = function(direction){
    var next =  $scope.compute(Number($scope.beta), direction, 0.5, 360);
    $scope.beta = next;
    $scope.rotateDevice()
    $scope.saveData();
  };

  $scope.stepGamma = function(direction){
    var next =  $scope.compute(Number($scope.gamma), direction, 0, 359.5);
    $scope.gamma = next;
    $scope.rotateDevice()
    $scope.saveData();
  };
});

})();
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas 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 Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import Table from '@instructure/ui-core/lib/components/Table'
import ScreenReaderContent from '@instructure/ui-core/lib/components/ScreenReaderContent'
import React from 'react'
import PropTypes from 'prop-types'
import I18n from 'i18n!react_developer_keys'

import DeveloperKey from './DeveloperKey'

class DeveloperKeysTable extends React.Component {
  createSetFocusCallback = (developerKeyId) => {
    const { developerKeysList, setFocus, inherited } = this.props
    let position
    if (developerKeyId) {
      position = developerKeysList.findIndex((key) => key.id === developerKeyId) - 1
    } else {
      position = undefined
    }

    const devKey = developerKeysList[position]
    let ref = devKey ? this[`developerKey-${devKey.id}`] : undefined
    // developerKeys will be populated when show more keys is resolved
    return (developerKeys) => {
      // if position is undefined it means that we are loading more keys
      // and we want to calculate the end of the list after the promise
      // resolves
      if (position === undefined) {
        const list = developerKeys || developerKeysList
        ref = this[`developerKey-${list[list.length - 1].id}`]
      }
      let srMsg
      // If ref is undefined it means that position was -1 and we deleted
      // the first key in the list and focus should go to something other than
      // a dev key
      if (ref === undefined) {
        // When INSTUI-1202 is completed and the fix in canvas this should be used
        // when inherited keys are loaded
        srMsg = I18n.t("Developer key %{developerKeyId} deleted. Focus moved to add developer key button.", {developerKeyId})
        setFocus()
      } else if (inherited) {
        srMsg = I18n.t("Loaded more developer keys. Focus moved to the name of the last loaded developer key in the list.")
        ref.focusName()
      } else {
        if (position === undefined) {
          srMsg = I18n.t("Loaded more developer keys. Focus moved to the delete button of the last loaded developer key in the list.")
        } else {
          srMsg = I18n.t("Developer key %{developerKeyId} deleted. Focus moved to the delete button of the previous developer key in the list.", {developerKeyId})
        }

        ref.focusDeleteLink()
      }
      $.screenReaderFlashMessageExclusive(srMsg);
    }
  }

  render() {
    const {inherited, developerKeysList} = this.props
    if (developerKeysList.length === 0) {
      return null
    }
    let srcontent = I18n.t('Developers Keys Table')
    if (inherited) {
      srcontent = I18n.t('Inherited Developer Keys Table')
    }
    return (

        <Table caption={<ScreenReaderContent>{srcontent}</ScreenReaderContent>} id="keys">

            {I18n.t('Name')}
            {!inherited && {I18n.t('User')}	 }
            {I18n.t('Details')}
            {!inherited && {I18n.t('Stats')}	}

            {!inherited &&  }

          {
            this.props.developerKeysList.map((developerKey) => (
              <DeveloperKey
                ref={key => {
                  this[`developerKey-${developerKey.id}`] = key
                }}
                key={developerKey.id}
                developerKey={developerKey}
                store={this.props.store}
                actions={this.props.actions}
                ctx={this.props.ctx}
                inherited={this.props.inherited}
                onDelete={this.createSetFocusCallback}
              />
            ))}

    )
  }
}

DeveloperKeysTable.propTypes = {
  store: PropTypes.shape({
    dispatch: PropTypes.func.isRequired
  }).isRequired,
  actions: PropTypes.shape({}).isRequired,
  developerKeysList: PropTypes.arrayOf(DeveloperKey.propTypes.developerKey).isRequired,
  ctx: PropTypes.shape({
    params: PropTypes.shape({
      contextId: PropTypes.string.isRequired
    })
  }).isRequired,
  inherited: PropTypes.bool,
  setFocus: PropTypes.func
};

DeveloperKeysTable.defaultProps = { inherited: false, setFocus: () => {} }

export default DeveloperKeysTable
<commit_msg>Add headings to state and actions columns<commit_after>/*
 * Copyright (C) 2018 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas 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 Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import Table from '@instructure/ui-core/lib/components/Table'
import ScreenReaderContent from '@instructure/ui-core/lib/components/ScreenReaderContent'
import React from 'react'
import PropTypes from 'prop-types'
import I18n from 'i18n!react_developer_keys'

import DeveloperKey from './DeveloperKey'

class DeveloperKeysTable extends React.Component {
  createSetFocusCallback = (developerKeyId) => {
    const { developerKeysList, setFocus, inherited } = this.props
    let position
    if (developerKeyId) {
      position = developerKeysList.findIndex((key) => key.id === developerKeyId) - 1
    } else {
      position = undefined
    }

    const devKey = developerKeysList[position]
    let ref = devKey ? this[`developerKey-${devKey.id}`] : undefined
    // developerKeys will be populated when show more keys is resolved
    return (developerKeys) => {
      // if position is undefined it means that we are loading more keys
      // and we want to calculate the end of the list after the promise
      // resolves
      if (position === undefined) {
        const list = developerKeys || developerKeysList
        ref = this[`developerKey-${list[list.length - 1].id}`]
      }
      let srMsg
      // If ref is undefined it means that position was -1 and we deleted
      // the first key in the list and focus should go to something other than
      // a dev key
      if (ref === undefined) {
        // When INSTUI-1202 is completed and the fix in canvas this should be used
        // when inherited keys are loaded
        srMsg = I18n.t("Developer key %{developerKeyId} deleted. Focus moved to add developer key button.", {developerKeyId})
        setFocus()
      } else if (inherited) {
        srMsg = I18n.t("Loaded more developer keys. Focus moved to the name of the last loaded developer key in the list.")
        ref.focusName()
      } else {
        if (position === undefined) {
          srMsg = I18n.t("Loaded more developer keys. Focus moved to the delete button of the last loaded developer key in the list.")
        } else {
          srMsg = I18n.t("Developer key %{developerKeyId} deleted. Focus moved to the delete button of the previous developer key in the list.", {developerKeyId})
        }

        ref.focusDeleteLink()
      }
      $.screenReaderFlashMessageExclusive(srMsg);
    }
  }

  render() {
    const {inherited, developerKeysList} = this.props
    if (developerKeysList.length === 0) {
      return null
    }
    let srcontent = I18n.t('Developers Keys Table')
    if (inherited) {
      srcontent = I18n.t('Inherited Developer Keys Table')
    }
    return (

        <Table caption={<ScreenReaderContent>{srcontent}</ScreenReaderContent>} id="keys">

            {I18n.t('Name')}
            {!inherited && {I18n.t('User')}	 }
            {I18n.t('Details')}
            {!inherited && {I18n.t('Stats')}	}
            {I18n.t('State')}
            {!inherited &&  {I18n.t('Actions')}	}

          {
            this.props.developerKeysList.map((developerKey) => (
              <DeveloperKey
                ref={key => {
                  this[`developerKey-${developerKey.id}`] = key
                }}
                key={developerKey.id}
                developerKey={developerKey}
                store={this.props.store}
                actions={this.props.actions}
                ctx={this.props.ctx}
                inherited={this.props.inherited}
                onDelete={this.createSetFocusCallback}
              />
            ))}

    )
  }
}

DeveloperKeysTable.propTypes = {
  store: PropTypes.shape({
    dispatch: PropTypes.func.isRequired
  }).isRequired,
  actions: PropTypes.shape({}).isRequired,
  developerKeysList: PropTypes.arrayOf(DeveloperKey.propTypes.developerKey).isRequired,
  ctx: PropTypes.shape({
    params: PropTypes.shape({
      contextId: PropTypes.string.isRequired
    })
  }).isRequired,
  inherited: PropTypes.bool,
  setFocus: PropTypes.func
};

DeveloperKeysTable.defaultProps = { inherited: false, setFocus: () => {} }

export default DeveloperKeysTable
<|endoftext|>


<commit_before>module.exports = {

	process : function(ctx, step, next) {
		//console.log('In showPage');
		var page = {};
		if(typeof step.page == 'string') {
			page = ctx.pages[step.page];
		}
		else {
			page = step.page;
		}
		ctx.showItemNSPage(page);
		setTimeout(next, global.STEP_TIMEOUT);
	}
}<commit_msg>#357 sleep 500ms after showPage action<commit_after>module.exports = {

	process : function(ctx, step, next) {
		//console.log('In showPage');
		var page = {};
		if(typeof step.page == 'string') {
			page = ctx.pages[step.page];
		}
		else {
			page = step.page;
		}
		ctx.showItemNSPage(page);
		setTimeout(next, 500); //#357
	}
}<|endoftext|>


<commit_before>import Ember from 'ember';
import Column from './column';
import pagedArray from 'ember-cli-pagination/computed/paged-array';

const { computed } = Ember;
const { alias } = computed;

/**
 * store shared logic to run pager logic
 */
export default Ember.Mixin.create({

  /**
   * This needs to be dynamic meta programming
   * for adding observers on each filter per column
   */
  configureFilterObservers: function () {
    var self = this;
    var observerDefinitions = {};
    var filterParams = {};
    this.get('columns').forEach(function (column, index) {
      var name = 'observer_' + column.get('apiInteractionName');
      observerDefinitions[name] = function () {
        var that = this;
        clearTimeout(this.get('keyTimer' + name));
        this.set('keyTimer' + name, setTimeout(function () {
          that.send('applyFilter', column.get('apiInteractionName'), column.get('filterValue'));
        }, 600));
      }.observes('columns.' + index + '.filterValue');
      filterParams[column.get('apiInteractionName')] = null;
    });
    self.reopen(observerDefinitions);
    self.set('filterParams', filterParams);
  }.on('init'),

  // setup our query params including custom sortField value
  queryParams: ["page", "perPage", "sortField", "with"],

  pageList: [
    50, 100, 250
  ],

  // binding the property on the paged array
  // to the query params on the controller
  //pageBinding: "content.page",
  //perPageBinding: "content.perPage",
  totalPages: alias("content.totalPages"),

  observingPerPage: function () {
    this.set('page', 1);
  }.observes('perPage'),

  // //logic to handle sorting a list
  // sortField: computed('sortProperty', 'sortDirection', function() {
  //   let sortProperty = this.get('sortProperty');
  //   let sortDirection = this.get('sortDirection');
  //   Ember.Logger.debug('sortField being updated');
  //   if (sortDirection) {
  //     return `${sortDirection}${sortProperty}`;
  //   } else {
  //     return sortProperty;
  //   }
  // }),
  sortOrder: computed('sortDirection', function () {
    let sortDirection = this.get('sortDirection');
    if (sortDirection === 'desc') {
      return '-';
    } else {
      return '';
    }
  }),
  with: '',

  // disabled by default
  // set these in the controller to enable and bind to a specific field
  quickSearchField: null,
  quickSearch: null,

  page: 1,
  perPage: 50,
  totalRecords: null,

  infiniteContent: pagedArray('content', {infinite: true}),

  column: Ember.Object.extend({
    display: null,
    field: null
  }),

  quickSearchChanged: function () {
    var self = this;
    clearTimeout(this.get('keyTimer'));
    this.set('keyTimer', setTimeout(function () {
      try {
        self.send('runQuickSearch');
      } catch (e) {
      }
    }, 600));
  }.observes('quickSearch'),

  //load pager specific variables
  columns: [
    Column.create({'displayName': '#', 'fieldName': 'id'})
  ],

  // bootstrap or adminlte specific classes
  // color default|success|primary|warning|danger|info
  box: 'default',
  button: 'default',

  // display the table title if there is one
  tableTitle: 'set tableTitle in controller to NULL to hide otherwise set this to your table title',

  // where should the default open action link-to?
  linkPath: false,
  editPath: false,

  // not sure what this is
  createPath: "set createPath in the controller",

  canLoadMore: false,

  actions: {
    loadNext () {
      if (this.get('infiniteContent.length') > 0 && this.get('canLoadMore')) {
        this.get('infiniteContent').loadNextPage();
        this.set('page', this.get('infiniteContent.page'));
      }
    }
  }
});
<commit_msg>TM-586 - fix duplicated requests on subject/info page<commit_after>import Ember from 'ember';
import Column from './column';
import pagedArray from 'ember-cli-pagination/computed/paged-array';

const { computed } = Ember;
const { alias } = computed;

/**
 * store shared logic to run pager logic
 */
export default Ember.Mixin.create({

  /**
   * This needs to be dynamic meta programming
   * for adding observers on each filter per column
   */
  configureFilterObservers: function () {
    var self = this;
    var observerDefinitions = {};
    var filterParams = {};
    this.get('columns').forEach(function (column, index) {
      var name = 'observer_' + column.get('apiInteractionName');
      observerDefinitions[name] = function () {
        var that = this;
        clearTimeout(this.get('keyTimer' + name));
        this.set('keyTimer' + name, setTimeout(function () {
          if (Ember.typeOf(column.get('filterValue')) !== 'undefined' && Ember.typeOf(column.get('filterValue')) !== 'null') {
            that.send('applyFilter', column.get('apiInteractionName'), column.get('filterValue'));
          }
        }, 600));
      }.observes('columns.' + index + '.filterValue');
      filterParams[column.get('apiInteractionName')] = null;
    });
    self.reopen(observerDefinitions);
    self.set('filterParams', filterParams);
  }.on('init'),

  // setup our query params including custom sortField value
  queryParams: ["page", "perPage", "sortField", "with"],

  pageList: [
    50, 100, 250
  ],

  // binding the property on the paged array
  // to the query params on the controller
  //pageBinding: "content.page",
  //perPageBinding: "content.perPage",
  totalPages: alias("content.totalPages"),

  observingPerPage: function () {
    this.set('page', 1);
  }.observes('perPage'),

  // //logic to handle sorting a list
  // sortField: computed('sortProperty', 'sortDirection', function() {
  //   let sortProperty = this.get('sortProperty');
  //   let sortDirection = this.get('sortDirection');
  //   Ember.Logger.debug('sortField being updated');
  //   if (sortDirection) {
  //     return `${sortDirection}${sortProperty}`;
  //   } else {
  //     return sortProperty;
  //   }
  // }),
  sortOrder: computed('sortDirection', function () {
    let sortDirection = this.get('sortDirection');
    if (sortDirection === 'desc') {
      return '-';
    } else {
      return '';
    }
  }),
  with: '',

  // disabled by default
  // set these in the controller to enable and bind to a specific field
  quickSearchField: null,
  quickSearch: null,

  page: 1,
  perPage: 50,
  totalRecords: null,

  infiniteContent: pagedArray('content', {infinite: true}),

  column: Ember.Object.extend({
    display: null,
    field: null
  }),

  quickSearchChanged: function () {
    var self = this;
    clearTimeout(this.get('keyTimer'));
    this.set('keyTimer', setTimeout(function () {
      try {
        self.send('runQuickSearch');
      } catch (e) {
      }
    }, 600));
  }.observes('quickSearch'),

  //load pager specific variables
  columns: [
    Column.create({'displayName': '#', 'fieldName': 'id'})
  ],

  // bootstrap or adminlte specific classes
  // color default|success|primary|warning|danger|info
  box: 'default',
  button: 'default',

  // display the table title if there is one
  tableTitle: 'set tableTitle in controller to NULL to hide otherwise set this to your table title',

  // where should the default open action link-to?
  linkPath: false,
  editPath: false,

  // not sure what this is
  createPath: "set createPath in the controller",

  canLoadMore: false,

  actions: {
    loadNext () {
      if (this.get('infiniteContent.length') > 0 && this.get('canLoadMore')) {
        this.get('infiniteContent').loadNextPage();
        this.set('page', this.get('infiniteContent.page'));
      }
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var VocablistSchema = require('../modelSchemas/vocablist.schema');

mongoose.model('Vocablist', VocablistSchema);
<commit_msg>Removes comments<commit_after>'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var VocablistSchema = require('../modelSchemas/vocablist.schema');

mongoose.model('Vocablist', VocablistSchema);
<|endoftext|>


<commit_before>(function() {
     function AlbumCtrl() {
     	this.albumData = angular.copy(albumPicasso);
     }

     angular
         .module('blocJams')
         .controller('AlbumCtrl', ['Fixtures', 'SongPlayer', AlbumCtrl]);
 })();<commit_msg>fixed conflicts<commit_after>(function() {
    function AlbumCtrl() {
        this.albumData = angular.copy(albumPicasso);
    }

     angular
         .module('blocJams')
         .controller('AlbumCtrl', ['Fixtures', 'SongPlayer', AlbumCtrl]);
 })();<|endoftext|>


<commit_before>define(["models/account","views/form","validation"],

    function(Account,Form){

        /*
            Custom validator for .codenvy.com tenant names
        */

        jQuery.validator.addMethod("validDomain", function(value) {
            return Account.isValidDomain(value+".codenvy.com");
        });

        var AccountFormBase = Form.extend({

            settings : {
                noDomainErrorMessage : "Please specify a domain name.",
                noEmailErrorMessage : "Please provide an email address.",
                noPasswordErrorMessage : "Please provide your account password.",
                noConfirmPasswordErrorMessage : "Please type your new password again. Both passwords must match.",
                invalidEmailErrorMessage : "You must provide a valid email address.",
                invalidDomainNameErrorMessage : "Your domain name cannot start with a number and must only contain digits and characters. "
            },

            __validationRules : function(){
                return {
                    domain: {
                        required : true,
                        validDomain : true
                    },
                    email: {
                        required: true,
                        email: true
                    }
                };
            },

            __validationMessages : function(){
                return {
                    domain: {
                        required : this.settings.noDomainErrorMessage,
                        validDomain : this.settings.invalidDomainNameErrorMessage
                    },
                    email: {
                        required: this.settings.noEmailErrorMessage,
                        email: this.settings.invalidEmailErrorMessage
                    },
                    password: {
                        required: this.settings.noPasswordErrorMessage
                    },
                    password1: {
                        required: this.settings.noConfirmPasswordErrorMessage,
                        equalTo: this.settings.noConfirmPasswordErrorMessage
                    }
                };
            },

            __showErrors : function(errorMap){
                function refocus(el){
                    el.focus();
                }

                if(typeof errorMap.email !== 'undefined'){
                    this.trigger("invalid","email",errorMap.email);
                    refocus(this.$("input[name='email']"));
                    return;
                }

                if(typeof errorMap.domain !== 'undefined'){
                    this.trigger("invalid","domain",errorMap.domain);
                    refocus(this.$("input[name='email']"));
                    return;
                }

                if(typeof errorMap.password !== 'undefined'){
                    this.trigger("invalid","password",errorMap.password);
                    refocus(this.$("input[name='password']"));
                    return;
                }

                if(typeof errorMap.password1 !== 'undefined'){
                    this.trigger("invalid","password1",errorMap.password1);
                    refocus(this.$("input[name='password1']"));
                    return;
                }
            },

            __restoreForm : function(){
                this.$("input[type='submit']").removeAttr("disabled");
            },

            __showProgress : function(){
                this.$("input[type='submit']").attr("disabled","disabled");
            }
        });

        return AccountFormBase;
    }
);
<commit_msg>include jquery explicitly<commit_after>define(["jquery","models/account","views/form","validation"],

    function($,Account,Form){

        /*
            Custom validator for .codenvy.com tenant names
        */

        jQuery.validator.addMethod("validDomain", function(value) {
            return Account.isValidDomain(value+".codenvy.com");
        });

        var AccountFormBase = Form.extend({

            settings : {
                noDomainErrorMessage : "Please specify a domain name.",
                noEmailErrorMessage : "Please provide an email address.",
                noPasswordErrorMessage : "Please provide your account password.",
                noConfirmPasswordErrorMessage : "Please type your new password again. Both passwords must match.",
                invalidEmailErrorMessage : "You must provide a valid email address.",
                invalidDomainNameErrorMessage : "Your domain name cannot start with a number and must only contain digits and characters. "
            },

            __validationRules : function(){
                return {
                    domain: {
                        required : true,
                        validDomain : true
                    },
                    email: {
                        required: true,
                        email: true
                    }
                };
            },

            __validationMessages : function(){
                return {
                    domain: {
                        required : this.settings.noDomainErrorMessage,
                        validDomain : this.settings.invalidDomainNameErrorMessage
                    },
                    email: {
                        required: this.settings.noEmailErrorMessage,
                        email: this.settings.invalidEmailErrorMessage
                    },
                    password: {
                        required: this.settings.noPasswordErrorMessage
                    },
                    password1: {
                        required: this.settings.noConfirmPasswordErrorMessage,
                        equalTo: this.settings.noConfirmPasswordErrorMessage
                    }
                };
            },

            __showErrors : function(errorMap){
                function refocus(el){
                    el.focus();
                }

                if(typeof errorMap.email !== 'undefined'){
                    this.trigger("invalid","email",errorMap.email);
                    refocus(this.$("input[name='email']"));
                    return;
                }

                if(typeof errorMap.domain !== 'undefined'){
                    this.trigger("invalid","domain",errorMap.domain);
                    refocus(this.$("input[name='email']"));
                    return;
                }

                if(typeof errorMap.password !== 'undefined'){
                    this.trigger("invalid","password",errorMap.password);
                    refocus(this.$("input[name='password']"));
                    return;
                }

                if(typeof errorMap.password1 !== 'undefined'){
                    this.trigger("invalid","password1",errorMap.password1);
                    refocus(this.$("input[name='password1']"));
                    return;
                }
            },

            __restoreForm : function(){
                this.$("input[type='submit']").removeAttr("disabled");
            },

            __showProgress : function(){
                this.$("input[type='submit']").attr("disabled","disabled");
            }
        });

        return AccountFormBase;
    }
);
<|endoftext|>


<commit_before>/**
    TimeSlot view model (aka: CalendarSlot) for use
    as part of the template/component time-slot-tile or activities
    providing data for the template.
**/
'use strict';

var getObservable = require('../utils/getObservable');

function TimeSlotViewModel(params) {
    /*jshint maxcomplexity:9*/

    this.startTime = getObservable(params.startTime || null);
    this.endTime = getObservable(params.endTime || null);
    this.subject = getObservable(params.subject || null);
    this.description = getObservable(params.description || null);
    this.link = getObservable(params.link || null);
    this.actionIcon = getObservable(params.actionIcon || null);
    this.actionText = getObservable(params.actionText || null);
    this.classNames = getObservable(params.classNames || null);
}

module.exports = TimeSlotViewModel;

var numeral = require('numeral'),
    Appointment = require('../models/Appointment');

/**
    Static constructor to convert an Appointment model into
    a TimeSlot instance following UI criteria for preset values/setup.
**/
TimeSlotViewModel.fromAppointment = function fromAppointment(apt) {
    /*jshint maxcomplexity:10 */

    // Commented the option to detect and not link unavail slots:
    //var unavail = Appointment.specialIds.unavailable === apt.id();
    //var link = null;
    //if (!unavail)
    var link = '#!appointment/' + apt.startTime().toISOString() + '/' + apt.id();

    if (apt.id() === Appointment.specialIds.preparationTime) {
        // Special link case: it goes to scheduling preferences to allow quick edit
        // the preparation time slots
        link = '#!schedulingPreferences?mustReturn=1';
    }

    var classNames = null;
    if (Appointment.specialIds.free === apt.id()) {
        classNames = 'Tile--tag-gray-lighter ';
    }
    else if (apt.id() > 0 && apt.sourceBooking()) {
        if (apt.sourceBooking().serviceDateID())
            classNames = 'Tile--tag-primary ' ;
        else
            // is a request:
            classNames = 'Tile--tag-warning ';

        classNames += 'ItemAddonTile--largerContent ';
    }
    else {
        // any block event, preparation time slots
        classNames = 'Tile--tag-danger ';
    }

    return new TimeSlotViewModel({
        startTime: apt.startTime,
        endTime: apt.endTime,
        subject: apt.summary,
        description: apt.description,
        link: link,
        actionIcon: (apt.sourceBooking() ? null : apt.sourceEvent() ? 'fa ion ion-ios-arrow-right' : !apt.id() ? 'fa ion ion-plus' : null),
        actionText: (
            apt.sourceBooking() &&
            apt.sourceBooking().pricingSummary() ?
            numeral(apt.sourceBooking().pricingSummary().totalPrice() || 0).format('$0.00') :
            null
        ),
        classNames: classNames
    });
};
<commit_msg>Fixed code that set a different Tile color class for booking requests #735<commit_after>/**
    TimeSlot view model (aka: CalendarSlot) for use
    as part of the template/component time-slot-tile or activities
    providing data for the template.
**/
'use strict';

var getObservable = require('../utils/getObservable');

function TimeSlotViewModel(params) {
    /*jshint maxcomplexity:9*/

    this.startTime = getObservable(params.startTime || null);
    this.endTime = getObservable(params.endTime || null);
    this.subject = getObservable(params.subject || null);
    this.description = getObservable(params.description || null);
    this.link = getObservable(params.link || null);
    this.actionIcon = getObservable(params.actionIcon || null);
    this.actionText = getObservable(params.actionText || null);
    this.classNames = getObservable(params.classNames || null);
}

module.exports = TimeSlotViewModel;

var numeral = require('numeral'),
    Appointment = require('../models/Appointment');

/**
    Static constructor to convert an Appointment model into
    a TimeSlot instance following UI criteria for preset values/setup.
**/
TimeSlotViewModel.fromAppointment = function fromAppointment(apt) {
    /*jshint maxcomplexity:10 */

    // Commented the option to detect and not link unavail slots:
    //var unavail = Appointment.specialIds.unavailable === apt.id();
    //var link = null;
    //if (!unavail)
    var link = '#!appointment/' + apt.startTime().toISOString() + '/' + apt.id();

    if (apt.id() === Appointment.specialIds.preparationTime) {
        // Special link case: it goes to scheduling preferences to allow quick edit
        // the preparation time slots
        link = '#!schedulingPreferences?mustReturn=1';
    }

    var classNames = null;
    if (Appointment.specialIds.free === apt.id()) {
        classNames = 'Tile--tag-gray-lighter ';
    }
    else if (apt.id() > 0 && apt.sourceBooking()) {
        if (apt.sourceBooking().isRequest())
            classNames = 'Tile--tag-warning ';
        else
            classNames = 'Tile--tag-primary ';

        classNames += 'ItemAddonTile--largerContent ';
    }
    else {
        // any block event, preparation time slots
        classNames = 'Tile--tag-danger ';
    }

    return new TimeSlotViewModel({
        startTime: apt.startTime,
        endTime: apt.endTime,
        subject: apt.summary,
        description: apt.description,
        link: link,
        actionIcon: (apt.sourceBooking() ? null : apt.sourceEvent() ? 'fa ion ion-ios-arrow-right' : !apt.id() ? 'fa ion ion-plus' : null),
        actionText: (
            apt.sourceBooking() &&
            apt.sourceBooking().pricingSummary() ?
            numeral(apt.sourceBooking().pricingSummary().totalPrice() || 0).format('$0.00') :
            null
        ),
        classNames: classNames
    });
};
<|endoftext|>


<commit_before>import i18n from '@/i18n'
import store from '@/store'
import * as validators from '@/helpers/validators'
import { isObjectLiteral, isEmptyValue, flattenObjectLiteral } from '@/helpers/commons'

/**
 * Tries to find a translation corresponding to the user's locale/fallback locale in a
 * Yunohost argument or simply return the string if it's not an object literal.
 *
 * @param {(Object|String)} field - A field value containing a translation object or string
 * @return {String}
 */
export function formatI18nField (field) {
  if (typeof field === 'string') return field
  const { locale, fallbackLocale } = store.state
  return field[locale] || field[fallbackLocale] || field.en
}

/**
 * Returns a string size declaration to a M value.
 *
 * @param {String} sizeStr - A size declared like '500M' or '56k'
 * @return {Number}
 */
export function sizeToM (sizeStr) {
  const unit = sizeStr.slice(-1)
  const value = sizeStr.slice(0, -1)
  if (unit === 'M') return parseInt(value)
  if (unit === 'b') return Math.ceil(value / (1024 * 1024))
  if (unit === 'k') return Math.ceil(value / 1024)
  if (unit === 'G') return Math.ceil(value * 1024)
  if (unit === 'T') return Math.ceil(value * 1024 * 1024)
}

/**
 * Returns a formatted address element to be used by AdressInputSelect component.
 *
 * @param {String} address - A string representing an adress (subdomain or email)
 * @return {Object} - `{ localPart, separator, domain }`.
 */
export function adressToFormValue (address) {
  const separator = address.includes('@') ? '@' : '.'
  const [localPart, domain] = address.split(separator)
  return { localPart, separator, domain }
}

/**
 * Format app install, actions and config panel argument into a data structure that
 * will be automaticly transformed into a component on screen.
 *
 * @param {Object} arg - a yunohost arg options written by a packager.
 * @return {Object} an formated argument containing formItem props, validation and base value.
 */
export function formatYunoHostArgument (arg) {
  let value = (arg.value !== undefined) ? arg.value : (arg.current_value !== undefined) ? arg.current_value : null
  const validation = {}
  const error = { message: null }
  arg.ask = formatI18nField(arg.ask)
  const field = {
    component: undefined,
    label: arg.ask,
    props: {}
  }
  const defaultProps = ['id:name', 'placeholder:example']
  const components = [
    {
      types: [undefined, 'string', 'path'],
      name: 'InputItem',
      props: defaultProps.concat(['autocomplete', 'trim', 'choices']),
      callback: function () {
        if (arg.choices) {
            arg.type = 'select'
            this.name = 'SelectItem'
        }
      }
    },
    {
      types: ['email', 'url', 'date', 'time', 'color'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'trim'])
    },
    {
      types: ['password'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'autocomplete', 'trim']),
      callback: function () {
        if (!arg.help) {
          arg.help = 'good_practices_about_admin_password'
        }
        arg.example = '••••••••••••'
        validation.passwordLenght = validators.minLength(8)
      }
    },
    {
      types: ['number', 'range'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'min', 'max', 'step']),
      callback: function () {
        if (!isNaN(parseInt(arg.min))) {
          validation.minValue = validators.minValue(parseInt(arg.min))
        }
        if (!isNaN(parseInt(arg.max))) {
          validation.maxValue = validators.maxValue(parseInt(arg.max))
        }
        validation.numValue = validators.helpers.regex('Please provide an integer', new RegExp('^-?[0-9]+$'))
      }
    },
    {
      types: ['select'],
      name: 'SelectItem',
      props: ['id:name', 'choices']
    },
    {
      types: ['user', 'domain'],
      name: 'SelectItem',
      props: ['id:name', 'choices'],
      callback: function () {
        field.link = { name: arg.type + '-list', text: i18n.t(`manage_${arg.type}s`) }
        field.props.choices = store.getters[arg.type + 'sAsChoices']
        if (value) {
          return
        }
        if (arg.type === 'domain') {
          value = store.getters.mainDomain
        } else {
          value = field.props.choices.length ? field.props.choices[0].value : null
        }
      }
    },
    {
      types: ['file'],
      name: 'FileItem',
      props: defaultProps.concat(['accept']),
      callback: function () {
        if (value) {
          value = new File([''], value)
          value.currentfile = true
        }
      }
    },
    {
      types: ['text'],
      name: 'TextAreaItem',
      props: defaultProps
    },
    {
      types: ['tags'],
      name: 'TagsItem',
      props: defaultProps.concat(['limit', 'placeholder', 'options:choices', 'tagIcon:icon']),
      callback: function () {
        if (arg.choices) {
            this.name = 'TagsSelectizeItem'
            field.props.auto = true
            field.props.itemsName = ''
            field.props.label = arg.placeholder
        }
        if (typeof value === 'string') {
          value = value.split(',')
        } else if (!value) {
          value = []
        }
      }
    },
    {
      types: ['boolean'],
      name: 'CheckboxItem',
      props: ['id:name', 'choices'],
      callback: function () {
        if (value !== null && value !== undefined) {
          value = ['1', 'yes', 'y', 'true'].includes(String(value).toLowerCase())
        } else if (arg.default !== null && arg.default !== undefined) {
          value = ['1', 'yes', 'y', 'true'].includes(String(arg.default).toLowerCase())
        }
      }
    },
    {
      types: ['alert'],
      name: 'ReadOnlyAlertItem',
      props: ['type:style', 'label:ask', 'icon'],
      readonly: true
    },
    {
      types: ['markdown', 'display_text'],
      name: 'MarkdownItem',
      props: ['label:ask'],
      readonly: true
    }
  ]

  // Default type management if no one is filled
  if (arg.type === undefined) {
    arg.type = (arg.choices === undefined) ? 'string' : 'select'
  }
  // Search the component bind to the type
  const component = components.find(element => element.types.includes(arg.type))
  if (component === undefined) throw new TypeError('Unknown type: ' + arg.type)
  // Callback use for specific behaviour
  if (component.callback) component.callback()
  field.component = component.name
  // Affect properties to the field Item
  for (let prop of component.props) {
    prop = prop.split(':')
    const propName = prop[0]
    const argName = prop.slice(-1)[0]
    if (argName in arg) {
      field.props[propName] = arg[argName]
    }
  }
  // We don't want to display a label html item as this kind or field contains
  // already the text to display
  if (component.readonly) delete field.label
  // Required (no need for checkbox its value can't be null)
  else if (field.component !== 'CheckboxItem' && arg.optional !== true) {
    validation.required = validators.required
  }
  if (arg.pattern && arg.type !== 'tags') {
    // validation.pattern = validators.helpers.withMessage(arg.pattern.error,
    validation.pattern = validators.helpers.regex(arg.pattern.error, new RegExp(arg.pattern.regexp))
  }
  validation.remote = validators.helpers.withParams(error, (v) => {
    const result = !error.message
    error.message = null
    return result
  })

  // field.props['title'] = field.pattern.error
  // Default value if still `null`
  if (value === null && arg.current_value) {
    value = arg.current_value
  }
  if (value === null && arg.default) {
    value = arg.default
  }

  // Help message
  if (arg.help) {
    field.description = formatI18nField(arg.help)
  }

  // Help message
  if (arg.helpLink) {
    field.link = { href: arg.helpLink.href, text: i18n.t(arg.helpLink.text) }
  }

  if (arg.visible) {
    field.visible = arg.visible
    // Temporary value to wait visible expression to be evaluated
    field.isVisible = true
  }

  return {
    value,
    field,
    // Return null instead of empty object if there's no validation
    validation: Object.keys(validation).length === 0 ? null : validation,
    error
  }
}

/**
 * Format app install, actions and config panel manifest args into a form that can be used
 * as v-model values, fields that can be passed to a FormField component and validations.
 *
 * @param {Array} args - a yunohost arg array written by a packager.
 * @param {String} name - (temp) an app name to build a label field in case of manifest install args
 * @return {Object} an object containing all parsed values to be used in vue views.
 */
export function formatYunoHostArguments (args, name = null) {
  const form = {}
  const fields = {}
  const validations = {}
  const errors = {}

  // FIXME yunohost should add the label field by default
  if (name) {
    args.unshift({
      ask: i18n.t('label_for_manifestname', { name }),
      default: name,
      name: 'label'
    })
  }

  for (const arg of args) {
    const { value, field, validation, error } = formatYunoHostArgument(arg)
    fields[arg.name] = field
    form[arg.name] = value
    if (validation) validations[arg.name] = validation
    errors[arg.name] = error
  }

  return { form, fields, validations, errors }
}

export function pFileReader (file, output, key, base64 = true) {
    return new Promise((resolve, reject) => {
        const fr = new FileReader()
        fr.onerror = reject
        fr.onload = () => {
          output[key] = fr.result
          if (base64) {
            output[key] = fr.result.replace(/data:[^;]*;base64,/, '')
          }
          output[key + '[name]'] = file.name
          resolve()
        }
        if (base64) {
          fr.readAsDataURL(file)
        } else {
          fr.readAsText(file)
        }
    })
}
/**
 * Format helper for a form value.
 * Convert Boolean to (1|0) and concatenate adresses.
 *
 * @param {*} value
 * @return {*}
 */
export function formatFormDataValue (value) {
  if (typeof value === 'boolean') {
    return value ? 1 : 0
  } else if (isObjectLiteral(value) && 'separator' in value) {
    return Object.values(value).join('')
  }
  return value
}

/**
 * Format a form produced by a vue view to be sent to the server.
 *
 * @param {Object} formData - An object literal containing form values.
 * @param {Object} [extraParams] - Optionnal params
 * @param {Array} [extraParams.extract] - An array of keys that should be extracted from the form.
 * @param {Boolean} [extraParams.flatten=false] - Flattens or not the passed formData.
 * @param {Boolean} [extraParams.removeEmpty=true] - Removes "empty" values from the object.
 * @return {Object} the parsed data to be sent to the server, with extracted values if specified.
 */
export function formatFormData (
  formData,
  { extract = null, flatten = false, removeEmpty = true, removeNull = false, promise = false } = {}
) {
  const output = {
    data: {},
    extracted: {}
  }
  const promises = []
  for (const key in formData) {
    const type = extract && extract.includes(key) ? 'extracted' : 'data'
    const value = Array.isArray(formData[key])
      ? formData[key].map(item => formatFormDataValue(item))
      : formatFormDataValue(formData[key])

    if (removeEmpty && isEmptyValue(value)) {
      continue
    } else if (removeNull && (value === null || value === undefined)) {
      continue
    } else if (value instanceof File) {
      if (value.currentfile) {
          continue
      } else if (value._removed) {
          output[type][key] = ''
          continue
      }
      promises.push(pFileReader(value, output[type], key))
    } else if (flatten && isObjectLiteral(value)) {
      flattenObjectLiteral(value, output[type])
    } else {
      output[type][key] = value
    }
  }
  const { data, extracted } = output
  if (promises.length > 0 || promise) {
    return new Promise((resolve, reject) => {
      Promise.all(promises).then((value) => {
        resolve(data)
      })
    })
  } else {
    return extract ? { data, ...extracted } : data
  }
}
<commit_msg>Avoid formatI18nField to fail if field is undefined<commit_after>import i18n from '@/i18n'
import store from '@/store'
import * as validators from '@/helpers/validators'
import { isObjectLiteral, isEmptyValue, flattenObjectLiteral } from '@/helpers/commons'

/**
 * Tries to find a translation corresponding to the user's locale/fallback locale in a
 * Yunohost argument or simply return the string if it's not an object literal.
 *
 * @param {(Object|String|undefined)} field - A field value containing a translation object or string
 * @return {String}
 */
export function formatI18nField (field) {
  if (typeof field === 'string') return field
  const { locale, fallbackLocale } = store.state
  return field ? field[locale] || field[fallbackLocale] || field.en : ''
}

/**
 * Returns a string size declaration to a M value.
 *
 * @param {String} sizeStr - A size declared like '500M' or '56k'
 * @return {Number}
 */
export function sizeToM (sizeStr) {
  const unit = sizeStr.slice(-1)
  const value = sizeStr.slice(0, -1)
  if (unit === 'M') return parseInt(value)
  if (unit === 'b') return Math.ceil(value / (1024 * 1024))
  if (unit === 'k') return Math.ceil(value / 1024)
  if (unit === 'G') return Math.ceil(value * 1024)
  if (unit === 'T') return Math.ceil(value * 1024 * 1024)
}

/**
 * Returns a formatted address element to be used by AdressInputSelect component.
 *
 * @param {String} address - A string representing an adress (subdomain or email)
 * @return {Object} - `{ localPart, separator, domain }`.
 */
export function adressToFormValue (address) {
  const separator = address.includes('@') ? '@' : '.'
  const [localPart, domain] = address.split(separator)
  return { localPart, separator, domain }
}

/**
 * Format app install, actions and config panel argument into a data structure that
 * will be automaticly transformed into a component on screen.
 *
 * @param {Object} arg - a yunohost arg options written by a packager.
 * @return {Object} an formated argument containing formItem props, validation and base value.
 */
export function formatYunoHostArgument (arg) {
  let value = (arg.value !== undefined) ? arg.value : (arg.current_value !== undefined) ? arg.current_value : null
  const validation = {}
  const error = { message: null }
  arg.ask = formatI18nField(arg.ask)
  const field = {
    component: undefined,
    label: arg.ask,
    props: {}
  }
  const defaultProps = ['id:name', 'placeholder:example']
  const components = [
    {
      types: [undefined, 'string', 'path'],
      name: 'InputItem',
      props: defaultProps.concat(['autocomplete', 'trim', 'choices']),
      callback: function () {
        if (arg.choices) {
            arg.type = 'select'
            this.name = 'SelectItem'
        }
      }
    },
    {
      types: ['email', 'url', 'date', 'time', 'color'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'trim'])
    },
    {
      types: ['password'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'autocomplete', 'trim']),
      callback: function () {
        if (!arg.help) {
          arg.help = 'good_practices_about_admin_password'
        }
        arg.example = '••••••••••••'
        validation.passwordLenght = validators.minLength(8)
      }
    },
    {
      types: ['number', 'range'],
      name: 'InputItem',
      props: defaultProps.concat(['type', 'min', 'max', 'step']),
      callback: function () {
        if (!isNaN(parseInt(arg.min))) {
          validation.minValue = validators.minValue(parseInt(arg.min))
        }
        if (!isNaN(parseInt(arg.max))) {
          validation.maxValue = validators.maxValue(parseInt(arg.max))
        }
        validation.numValue = validators.helpers.regex('Please provide an integer', new RegExp('^-?[0-9]+$'))
      }
    },
    {
      types: ['select'],
      name: 'SelectItem',
      props: ['id:name', 'choices']
    },
    {
      types: ['user', 'domain'],
      name: 'SelectItem',
      props: ['id:name', 'choices'],
      callback: function () {
        field.link = { name: arg.type + '-list', text: i18n.t(`manage_${arg.type}s`) }
        field.props.choices = store.getters[arg.type + 'sAsChoices']
        if (value) {
          return
        }
        if (arg.type === 'domain') {
          value = store.getters.mainDomain
        } else {
          value = field.props.choices.length ? field.props.choices[0].value : null
        }
      }
    },
    {
      types: ['file'],
      name: 'FileItem',
      props: defaultProps.concat(['accept']),
      callback: function () {
        if (value) {
          value = new File([''], value)
          value.currentfile = true
        }
      }
    },
    {
      types: ['text'],
      name: 'TextAreaItem',
      props: defaultProps
    },
    {
      types: ['tags'],
      name: 'TagsItem',
      props: defaultProps.concat(['limit', 'placeholder', 'options:choices', 'tagIcon:icon']),
      callback: function () {
        if (arg.choices) {
            this.name = 'TagsSelectizeItem'
            field.props.auto = true
            field.props.itemsName = ''
            field.props.label = arg.placeholder
        }
        if (typeof value === 'string') {
          value = value.split(',')
        } else if (!value) {
          value = []
        }
      }
    },
    {
      types: ['boolean'],
      name: 'CheckboxItem',
      props: ['id:name', 'choices'],
      callback: function () {
        if (value !== null && value !== undefined) {
          value = ['1', 'yes', 'y', 'true'].includes(String(value).toLowerCase())
        } else if (arg.default !== null && arg.default !== undefined) {
          value = ['1', 'yes', 'y', 'true'].includes(String(arg.default).toLowerCase())
        }
      }
    },
    {
      types: ['alert'],
      name: 'ReadOnlyAlertItem',
      props: ['type:style', 'label:ask', 'icon'],
      readonly: true
    },
    {
      types: ['markdown', 'display_text'],
      name: 'MarkdownItem',
      props: ['label:ask'],
      readonly: true
    }
  ]

  // Default type management if no one is filled
  if (arg.type === undefined) {
    arg.type = (arg.choices === undefined) ? 'string' : 'select'
  }
  // Search the component bind to the type
  const component = components.find(element => element.types.includes(arg.type))
  if (component === undefined) throw new TypeError('Unknown type: ' + arg.type)
  // Callback use for specific behaviour
  if (component.callback) component.callback()
  field.component = component.name
  // Affect properties to the field Item
  for (let prop of component.props) {
    prop = prop.split(':')
    const propName = prop[0]
    const argName = prop.slice(-1)[0]
    if (argName in arg) {
      field.props[propName] = arg[argName]
    }
  }
  // We don't want to display a label html item as this kind or field contains
  // already the text to display
  if (component.readonly) delete field.label
  // Required (no need for checkbox its value can't be null)
  else if (field.component !== 'CheckboxItem' && arg.optional !== true) {
    validation.required = validators.required
  }
  if (arg.pattern && arg.type !== 'tags') {
    // validation.pattern = validators.helpers.withMessage(arg.pattern.error,
    validation.pattern = validators.helpers.regex(arg.pattern.error, new RegExp(arg.pattern.regexp))
  }
  validation.remote = validators.helpers.withParams(error, (v) => {
    const result = !error.message
    error.message = null
    return result
  })

  // field.props['title'] = field.pattern.error
  // Default value if still `null`
  if (value === null && arg.current_value) {
    value = arg.current_value
  }
  if (value === null && arg.default) {
    value = arg.default
  }

  // Help message
  if (arg.help) {
    field.description = formatI18nField(arg.help)
  }

  // Help message
  if (arg.helpLink) {
    field.link = { href: arg.helpLink.href, text: i18n.t(arg.helpLink.text) }
  }

  if (arg.visible) {
    field.visible = arg.visible
    // Temporary value to wait visible expression to be evaluated
    field.isVisible = true
  }

  return {
    value,
    field,
    // Return null instead of empty object if there's no validation
    validation: Object.keys(validation).length === 0 ? null : validation,
    error
  }
}

/**
 * Format app install, actions and config panel manifest args into a form that can be used
 * as v-model values, fields that can be passed to a FormField component and validations.
 *
 * @param {Array} args - a yunohost arg array written by a packager.
 * @param {String} name - (temp) an app name to build a label field in case of manifest install args
 * @return {Object} an object containing all parsed values to be used in vue views.
 */
export function formatYunoHostArguments (args, name = null) {
  const form = {}
  const fields = {}
  const validations = {}
  const errors = {}

  // FIXME yunohost should add the label field by default
  if (name) {
    args.unshift({
      ask: i18n.t('label_for_manifestname', { name }),
      default: name,
      name: 'label'
    })
  }

  for (const arg of args) {
    const { value, field, validation, error } = formatYunoHostArgument(arg)
    fields[arg.name] = field
    form[arg.name] = value
    if (validation) validations[arg.name] = validation
    errors[arg.name] = error
  }

  return { form, fields, validations, errors }
}

export function pFileReader (file, output, key, base64 = true) {
    return new Promise((resolve, reject) => {
        const fr = new FileReader()
        fr.onerror = reject
        fr.onload = () => {
          output[key] = fr.result
          if (base64) {
            output[key] = fr.result.replace(/data:[^;]*;base64,/, '')
          }
          output[key + '[name]'] = file.name
          resolve()
        }
        if (base64) {
          fr.readAsDataURL(file)
        } else {
          fr.readAsText(file)
        }
    })
}
/**
 * Format helper for a form value.
 * Convert Boolean to (1|0) and concatenate adresses.
 *
 * @param {*} value
 * @return {*}
 */
export function formatFormDataValue (value) {
  if (typeof value === 'boolean') {
    return value ? 1 : 0
  } else if (isObjectLiteral(value) && 'separator' in value) {
    return Object.values(value).join('')
  }
  return value
}

/**
 * Format a form produced by a vue view to be sent to the server.
 *
 * @param {Object} formData - An object literal containing form values.
 * @param {Object} [extraParams] - Optionnal params
 * @param {Array} [extraParams.extract] - An array of keys that should be extracted from the form.
 * @param {Boolean} [extraParams.flatten=false] - Flattens or not the passed formData.
 * @param {Boolean} [extraParams.removeEmpty=true] - Removes "empty" values from the object.
 * @return {Object} the parsed data to be sent to the server, with extracted values if specified.
 */
export function formatFormData (
  formData,
  { extract = null, flatten = false, removeEmpty = true, removeNull = false, promise = false } = {}
) {
  const output = {
    data: {},
    extracted: {}
  }
  const promises = []
  for (const key in formData) {
    const type = extract && extract.includes(key) ? 'extracted' : 'data'
    const value = Array.isArray(formData[key])
      ? formData[key].map(item => formatFormDataValue(item))
      : formatFormDataValue(formData[key])

    if (removeEmpty && isEmptyValue(value)) {
      continue
    } else if (removeNull && (value === null || value === undefined)) {
      continue
    } else if (value instanceof File) {
      if (value.currentfile) {
          continue
      } else if (value._removed) {
          output[type][key] = ''
          continue
      }
      promises.push(pFileReader(value, output[type], key))
    } else if (flatten && isObjectLiteral(value)) {
      flattenObjectLiteral(value, output[type])
    } else {
      output[type][key] = value
    }
  }
  const { data, extracted } = output
  if (promises.length > 0 || promise) {
    return new Promise((resolve, reject) => {
      Promise.all(promises).then((value) => {
        resolve(data)
      })
    })
  } else {
    return extract ? { data, ...extracted } : data
  }
}
<|endoftext|>


<commit_before>Ext.define('devilry.extjshelpers.assignmentgroup.AssignmentGroupTodoListWindow', {
    extend: 'Ext.window.Window',
    title: 'To-do list (Open groups on this assignment)',
    height: 370,
    width: 750,
    modal: true,
    layout: 'fit',

    config: {
        assignmentgroupstore: undefined,
        assignmentgroup_recordcontainer: undefined
    },

    constructor: function(config) {
        this.initConfig(config);
        this.callParent([config]);
    },

    initComponent: function() {
        Ext.apply(this, {
            items: {
                xtype: 'assignmentgrouptodolist',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                store: this.assignmentgroupstore
            }
        });
        this.callParent(arguments);
    }
});
<commit_msg>apps.extjshelpers...AssignmentGroupTodoListWindow: Updated for changes in AssignmentGroupTodoList.<commit_after>Ext.define('devilry.extjshelpers.assignmentgroup.AssignmentGroupTodoListWindow', {
    extend: 'Ext.window.Window',
    title: 'To-do list (Open groups on this assignment)',
    height: 370,
    width: 750,
    modal: true,
    layout: 'fit',
    requires: [
        'devilry.extjshelpers.assignmentgroup.AssignmentGroupTodoList'
    ],

    /**
     * @cfg
     */
    assignmentgroupmodelname: undefined,

    /**
     * @cfg
     */
    assignmentgroup_recordcontainer: undefined,

    initComponent: function() {
        Ext.apply(this, {
            items: {
                xtype: 'assignmentgrouptodolist',
                assignmentgroup_recordcontainer: this.assignmentgroup_recordcontainer,
                assignmentgroupmodelname: this.assignmentgroupmodelname
            }
        });
        this.callParent(arguments);
    }
});
<|endoftext|>


<commit_before>/*
2013 sapient gmbh

 This javascript code is part of the DDBridge api. It is used to communicate
 data to Objective-C code through a UIWebView/webview.
 */

// Counts the number of objects communicated to the Objective-C code.
// It is used to index data in the DDBridge_objArray.
var DDBridge_objCount = 0;

// Keeps the objects that should be communicated to the Objective-C code.
var DDBridge_objArray = new Array();

/*
	Builds an empty instance of a DDBridge object.
 */
function DDBridgeObj()
{
	this.objectJson = "";
	this.addObject = DDBridgeObj_AddObject;
	this.sendBridgeObject = DDBridgeObj_SendObject;
}

/*
	The addObject method implementation for the DDBridge object.
    obj must be compatible with JSON or... an img element in which case ill take its data
 */
function DDBridgeObj_AddObject(id, obj)
{
    if(typeof(obj) == "object" && obj.nodeName == "IMG")
    {
        obj = helper_getBase64Image(obj);
    }

    var result = JSON.stringify(obj);

	if(result != "")
	{
		if(this.objectJson != "")
		{
			this.objectJson += ", ";
		}
		this.objectJson += '"' + id + '":' + result;
	}
}

/*
	This method sends the object to the Objective-C code. Basically,
	it tries to load a special URL, which passes the object id.
 */
function DDBridgeObj_SendObject(component, method)
{
	DDBridge_objArray[DDBridge_objCount] = this.objectJson;

    var url = "";
    if(method)
        url = "DDBridge://"+component+"/"+method+"?"+"ReadNotificationWithId=" + DDBridge_objCount;
	else
        url = "DDBridge://"+component+"/" + DDBridge_objCount;

	if ( window.DDBridgableWebView ) {
		/* calls our Objective-C console logging function */
		window.DDBridgableWebView.bridgeUrl(url);
	}
    else {
        window.location.href = url;
    }

	DDBridge_objCount++;
}

/*
	This method is invoked by the Objective-C code. It retrieves the json string representation
	of a DDBridge object given its id.
 */
function DDBridge_getJsonStringForObjectWithId(objId)
{
	var jsonStr = DDBridge_objArray[objId];

	DDBridge_objArray[objId] = null;

	return "{" + jsonStr + "}";
}

/*
 Receives as input an image object and returns its data
 encoded in a base64 string.

 This piece of code was based on Matthew Crumley's post
 at http://stackoverflow.com/questions/934012/get-image-data-in-javascript.
 */
function helper_getBase64Image(img) {
    // Create an empty canvas element
    var canvas = document.createElement("canvas");

	var newImg = new Image();
	newImg.src = img.src;
    canvas.width = newImg.width;
    canvas.height = newImg.height;

    // Copy the image contents to the canvas
    var ctx = canvas.getContext("2d");
    ctx.drawImage(newImg, 0, 0);

    // Get the data-URL formatted image
    var dataURL = canvas.toDataURL("image/png");

    return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}<commit_msg>Update DDEnhancedJSBridge.js<commit_after>
<|endoftext|>


<commit_before>// constants
import { RETRIEVE_ACCESS_TOKEN } from '../../constants/actionTypes';

const initialState = {
  accessToken: '',
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case RETRIEVE_ACCESS_TOKEN:

      return Object.assign({}, state, {
        accessToken: action.accessToken,
      });

        // ...other actions

    default:
      return state;
  }
}
<commit_msg>use spread operator.<commit_after>// constants
import { RETRIEVE_ACCESS_TOKEN } from '../../constants/actionTypes';

const initialState = {
  accessToken: '',
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case RETRIEVE_ACCESS_TOKEN:
      return {
        ...state,
        accessToken: action.accessToken,
      };

        // ...other actions

    default:
      return state;
  }
}
<|endoftext|>


<commit_before>'use strict'

var gulp = require('gulp');
var del = require('del');
var imagemin = require('gulp-imagemin');

gulp.task('clean', function(cb) {
  del(['<%= paths.dist.base %>'], cb);
});

gulp.task('static:images', function() {
  return gulp.src('<%= paths.src.images %>/**/*')
    .pipe(imagemin({
      optimizationLevel: 5
    }))
    .pipe(gulp.dest('<%= paths.dist.images %>'));
});

gulp.task('static:fonts', function() {
  return gulp.src('<%= paths.src.fonts %>/**/*')
    .pipe(gulp.dest('<%= paths.dist.fonts %>'));
});

gulp.task('static', ['static:images', 'static:fonts']);
<commit_msg>fix static shit<commit_after>'use strict'

var gulp = require('gulp');
var del = require('del');
var imagemin = require('gulp-imagemin');

gulp.task('clean', function(cb) {
  del(['<%= paths.dist.base %>'], cb);
});

gulp.task('images:main', function() {
  return gulp.src('<%= paths.src.images %>/**/*')
    .pipe(imagemin({
      optimizationLevel: 5
    }))
    .pipe(gulp.dest('<%= paths.dist.images %>'));
});

gulp.task('fonts:main', function() {
  return gulp.src('<%= paths.src.fonts %>/**/*')
    .pipe(gulp.dest('<%= paths.dist.fonts %>'));
});

gulp.task('icons:main', function() {
  return gulp.src('<%= paths.src.icons %>/**/*')
    .pipe(gulp.dest('<%= paths.dist.icons %>'));
});

gulp.task('static', ['images:main', 'fonts:main']);
<|endoftext|>


<commit_before>var Backbone = require('backbone');
var _ = require('underscore');
var NestedForm = require('../../nested-form-custom');

Backbone.Form.editors.List.MeasurementModel = Backbone.Form.editors.NestedModel.extend({

  initialize: function (options) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, options);
    Backbone.Form.editors.Base.prototype._setOptions.call(this, options); // Options

    if (!this.form) throw new Error('Missing required option "form"');
    if (!options.schema.model) throw new Error('Missing required "schema.model" option for NestedModel editor');
  },

  render: function () {
    var data = this.value || {};
    var NestedModel = this.schema.model;

    // Wrap the data in a model if it isn't already a model instance
    var modelInstance = (data.constructor === NestedModel) ? data : new NestedModel(data, this.options);

    this.nestedForm = new NestedForm({
      model: modelInstance,
      idPrefix: this.cid + '_',
      fieldTemplate: 'nestedField',
      template: _.template('<form data-fields="*"></form>')
    });

    this._observeFormEvents();

    // Render form
    this.$el.html(this.nestedForm.render().el);

    if (this.hasFocus) this.trigger('blur', this);

    return this;
  }

});
<commit_msg>Commit changes to parent form.<commit_after>var Backbone = require('backbone');
var _ = require('underscore');
var NestedForm = require('../../nested-form-custom');

Backbone.Form.editors.List.MeasurementModel = Backbone.Form.editors.NestedModel.extend({

  initialize: function (options) {
    Backbone.Form.editors.Base.prototype.initialize.call(this, options);
    Backbone.Form.editors.Base.prototype._setOptions.call(this, options); // Options

    if (!this.form) throw new Error('Missing required option "form"');
    if (!options.schema.model) throw new Error('Missing required "schema.model" option for NestedModel editor');
  },

  render: function () {
    var data = this.value || {};
    var NestedModel = this.schema.model;

    // Wrap the data in a model if it isn't already a model instance
    var modelInstance = (data.constructor === NestedModel) ? data : new NestedModel(data, this.options);

    this.nestedForm = new NestedForm({
      model: modelInstance,
      idPrefix: this.cid + '_',
      fieldTemplate: 'nestedField',
      template: _.template('<form data-fields="*"></form>')
    });

    this._observeFormEvents();

    this.listenTo(this.nestedForm, 'change', this._onChangeForm, this);
    // Render form
    this.$el.html(this.nestedForm.render().el);

    if (this.hasFocus) this.trigger('blur', this);

    return this;
  },

  _onChangeForm: function () {
    // To translate values to parent form
    this.nestedForm.commit();
  }

});
<|endoftext|>