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

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import Button from '../../../../components/Button';
import {
	SETUP_FLOW_MODE_LEGACY,
	SETUP_FLOW_MODE_UA,
	SETUP_FLOW_MODE_GA4,
	SETUP_FLOW_MODE_GA4_TRANSITIONAL,
	MODULES_ANALYTICS,
	FORM_SETUP,
	EDIT_SCOPE,
} from '../../datastore/constants';
import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants';
import { CORE_FORMS } from '../../../../googlesitekit/datastore/forms/constants';
import { trackEvent } from '../../../../util';
import { isPermissionScopeError } from '../../../../util/errors';
import SetupFormLegacy from './SetupFormLegacy';
import SetupFormUA from './SetupFormUA';
import SetupFormGA4 from './SetupFormGA4';
import SetupFormGA4Transitional from './SetupFormGA4Transitional';
const { useSelect, useDispatch } = Data;

export default function SetupForm( { finishSetup } ) {
	const canSubmitChanges = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).canSubmitChanges()
	);
	const hasEditScope = useSelect( ( select ) =>
		select( CORE_USER ).hasScope( EDIT_SCOPE )
	);

	const autoSubmit = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( FORM_SETUP, 'autoSubmit' )
	);
	const setupFlowMode = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getSetupFlowMode()
	);

	const { setValues } = useDispatch( CORE_FORMS );
	const { submitChanges } = useDispatch( MODULES_ANALYTICS );
	const submitForm = useCallback(
		async ( event ) => {
			event.preventDefault();

			const { error } = await submitChanges();
			if ( isPermissionScopeError( error ) ) {
				setValues( FORM_SETUP, { autoSubmit: true } );
			}

			if ( ! error ) {
				setValues( FORM_SETUP, { autoSubmit: false } );
				await trackEvent( 'analytics_setup', 'analytics_configured' );
				finishSetup();
			}
		},
		[ finishSetup, setValues, submitChanges ]
	);

	// If the user lands back on this component with autoSubmit and the edit scope,
	// resubmit the form.
	useEffect( () => {
		if ( autoSubmit && hasEditScope ) {
			submitForm( { preventDefault: () => {} } );
		}
	}, [ hasEditScope, autoSubmit, submitForm ] );

	return (
		<form
			className="googlesitekit-analytics-setup__form"
			onSubmit={ submitForm }
		>
			{ setupFlowMode === SETUP_FLOW_MODE_LEGACY && <SetupFormLegacy /> }
			{ setupFlowMode === SETUP_FLOW_MODE_UA && <SetupFormUA /> }
			{ setupFlowMode === SETUP_FLOW_MODE_GA4 && <SetupFormGA4 /> }
			{ setupFlowMode === SETUP_FLOW_MODE_GA4_TRANSITIONAL && (
				<SetupFormGA4Transitional />
			) }

				<Button disabled={ ! canSubmitChanges }>
					{ __( 'Configure Analytics', 'google-site-kit' ) }
				</Button>

		</form>
	);
}

SetupForm.propTypes = {
	finishSetup: PropTypes.func,
};

SetupForm.defaultProps = {
	finishSetup: () => {},
};
<commit_msg>Revert whitespace changes.<commit_after>/**
 * Analytics Setup form.
 *
 * Site Kit by Google, Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import PropTypes from 'prop-types';

/**
 * WordPress dependencies
 */
import { useCallback, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';

/**
 * Internal dependencies
 */
import Data from 'googlesitekit-data';
import Button from '../../../../components/Button';
import {
	SETUP_FLOW_MODE_LEGACY,
	SETUP_FLOW_MODE_UA,
	SETUP_FLOW_MODE_GA4,
	SETUP_FLOW_MODE_GA4_TRANSITIONAL,
	MODULES_ANALYTICS,
	FORM_SETUP,
	EDIT_SCOPE,
} from '../../datastore/constants';
import { CORE_USER } from '../../../../googlesitekit/datastore/user/constants';
import { CORE_FORMS } from '../../../../googlesitekit/datastore/forms/constants';
import { trackEvent } from '../../../../util';
import { isPermissionScopeError } from '../../../../util/errors';
import SetupFormLegacy from './SetupFormLegacy';
import SetupFormUA from './SetupFormUA';
import SetupFormGA4 from './SetupFormGA4';
import SetupFormGA4Transitional from './SetupFormGA4Transitional';
const { useSelect, useDispatch } = Data;

export default function SetupForm( { finishSetup } ) {
	const canSubmitChanges = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).canSubmitChanges()
	);
	const hasEditScope = useSelect( ( select ) =>
		select( CORE_USER ).hasScope( EDIT_SCOPE )
	);
	const autoSubmit = useSelect( ( select ) =>
		select( CORE_FORMS ).getValue( FORM_SETUP, 'autoSubmit' )
	);
	const setupFlowMode = useSelect( ( select ) =>
		select( MODULES_ANALYTICS ).getSetupFlowMode()
	);

	const { setValues } = useDispatch( CORE_FORMS );
	const { submitChanges } = useDispatch( MODULES_ANALYTICS );
	const submitForm = useCallback(
		async ( event ) => {
			event.preventDefault();
			const { error } = await submitChanges();
			if ( isPermissionScopeError( error ) ) {
				setValues( FORM_SETUP, { autoSubmit: true } );
			}
			if ( ! error ) {
				setValues( FORM_SETUP, { autoSubmit: false } );
				await trackEvent( 'analytics_setup', 'analytics_configured' );
				finishSetup();
			}
		},
		[ finishSetup, setValues, submitChanges ]
	);

	// If the user lands back on this component with autoSubmit and the edit scope,
	// resubmit the form.
	useEffect( () => {
		if ( autoSubmit && hasEditScope ) {
			submitForm( { preventDefault: () => {} } );
		}
	}, [ hasEditScope, autoSubmit, submitForm ] );

	return (
		<form
			className="googlesitekit-analytics-setup__form"
			onSubmit={ submitForm }
		>
			{ setupFlowMode === SETUP_FLOW_MODE_LEGACY && <SetupFormLegacy /> }
			{ setupFlowMode === SETUP_FLOW_MODE_UA && <SetupFormUA /> }
			{ setupFlowMode === SETUP_FLOW_MODE_GA4 && <SetupFormGA4 /> }
			{ setupFlowMode === SETUP_FLOW_MODE_GA4_TRANSITIONAL && (
				<SetupFormGA4Transitional />
			) }

				<Button disabled={ ! canSubmitChanges }>
					{ __( 'Configure Analytics', 'google-site-kit' ) }
				</Button>

		</form>
	);
}

SetupForm.propTypes = {
	finishSetup: PropTypes.func,
};

SetupForm.defaultProps = {
	finishSetup: () => {},
};
<|endoftext|>


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

/** @class  The base class for plot adornments.

  @extends SC.Object
*/
DG.PlotAdornment = SC.Object.extend(
/** @scope DG.PlotAdornment.prototype */
{
  /**
    The model containing the data to display.
    @property { Depends on adornment }
  */
  model: null,

  /**
   * @property {DG.PlotBackgroundView}
  */
  paperSource: null,

  /**
   * @property( Raphael }
   */
  paper: function() {
    return this.getPath('paperSource.paper');
  }.property(),

  paperDidChange: function() {
    this.notifyPropertyChange('paper');
  }.observes('*paperSource.paper'),

  /**
    Provides x-coordinates
    @property { DG.AxisView }
  */
  xAxisView: function() {
    return this.getPath('paperSource.xAxisView');
  }.property('paperSource').cacheable(),

  xAxisViewDidChange: function() {
    this.notifyPropertyChange('xAxisView');
  }.observes('*paperSource.xAxisView'),

  /**
    Provides y-coordinates
    @property { DG.AxisView }
  */
  yAxisView: function() {
    return this.getPath('paperSource.yAxisView');
  }.property('paperSource').cacheable(),

  yAxisViewDidChange: function() {
    this.notifyPropertyChange('yAxisView');
  }.observes('*paperSource.yAxisView'),

  /**
    We need to know this view in order to transform from window to view coordinates.
    @property { SC.View }
  */
  parentView: null,

  /**
    These are the Raphael objects I draw, kept together for ease of showing and hiding.
    @property { SC.Array }
  */
  myElements: null,

  /**
   * The key into the layerManager to get the layer we use to display
   * @property { String }
   */
  layerName: null,

  /**
   * All my Raphael elements go in this layer
   * @property { DG.RaphaelLayer }
   */
  layer: function() {
    var tLayerManager = this.getPath('paperSource.layerManager');
    return (tLayerManager && this.layerName) ? tLayerManager[ this.layerName] :null;
  }.property('paperSource', 'layerName' ),

  /**
    Contains an array of elements, each of which corresponds to a pair of strings
    indicating a model property that should be observed and the handler that should
    be called when that property changes. Adding 'modelPropertiesToObserve' to the
    'concatenatedProperties' array allows the contents of the property from multiple
    levels of the 'class hierarchy' to be concatenated automatically taking advantage
    of a built-in SproutCore behavior. AddObserver/removeObserver will be called for
    each of the ['PropertyName','ObserverMethod'] pairs as appropriate whenever the
    model changes.

    @property   {Array of [{String},{String}]}  Elements are ['PropertyName','ObserverMethod']
   */
  modelPropertiesToObserve: [ ['willDestroy', 'detachModel'],
    ['computingNeeded', 'updateToModel'],
    ['isVisible', 'updateVisibility'] ],
  concatenatedProperties: [ 'modelPropertiesToObserve' ],

  /**
    Initialization method
   */
  init: function() {
    sc_super();

    this.myElements = [];
    // Trigger manually and then addObserver
    this.modelDidChange();
  },

  /**
    Detaches all model references in preparation for destruction of
    either the model or the adornment.
   */
  detachModel: function() {
    // Reset the model (triggering notification), then removeObserver
    this.set('model', null);
  },

  /**
    Destruction method
   */
  destroy: function() {
    this.detachModel();
    this.removeElements();
    sc_super();
  },

  removeElements: function() {
    var tLayerManager = this.getPath('paperSource.layerManager');
    this.myElements.forEach( function( iElement) {
      tLayerManager.removeElement( iElement);
      iElement.remove();
    });
    this.myElements = null;
  },

  /**
    Observer method called when the adornment model changes.
    Guarantees that observers are added/removed for any relevant model properties.
    List ['Property','Handler'] pairs in 'modelPropertiesToObserve' property to
    have observers added/removed automatically.
   */
  _observedModel: null,
  modelDidChange: function() {
    // Get the concatenated list of model properties to observe
    var modelProps = this.get('modelPropertiesToObserve');

    // Detach from previous model
    if( this._observedModel) {
      if( modelProps) {
        // Remove observers for all listed properties
        modelProps.forEach( function( iModelProp) {
                              this._observedModel.removeObserver( iModelProp[0], this, iModelProp[1]);
                            }.bind( this));
      }
      this._observedModel = null;
    }

    // Attach to new model
    var model = this.get('model');
    if( model) {
      if( modelProps) {
        // Add observers for all listed properties
        modelProps.forEach( function( iModelProp) {
                              model.addObserver( iModelProp[0], this, iModelProp[1]);
                            }.bind( this));
      }
      this._observedModel = model;
    }
  }.observes('model'),

  showElements: function() {
    var tOpacity = this.get('opacity') || 1,
        tStrokeOpacity = this.get('strokeOpacity') || 1;
    this.myElements.forEach(function (iElement) {
      iElement.show();
      if (iElement.animatable) {
        iElement.animate({'stroke-opacity': tStrokeOpacity, opacity: tOpacity},
            DG.PlotUtilities.kDefaultAnimationTime, '<>');
      }
    });
    this.set('isHidden', false);
  },

  hideElements: function () {
    this.myElements.forEach(function (iElement) {
      if (iElement.animatable) {
        iElement.animate({'stroke-opacity': 0, opacity: 0}, DG.PlotUtilities.kDefaultAnimationTime, '<>',
            function () {
              iElement.hide();
            });
      }
      else
        iElement.hide();
    });
    this.set('isHidden', true);
  },

  /**
    My model's visibility has changed.
  */
  updateVisibility: function() {
    if( this.getPath('model.isVisible')) {
      this.updateToModel();
      this.showElements();
    }
    else if( !SC.none( this.myElements))
    {
      this.hideElements();
    }
  },

  updateToModel: function() {
    // Subclasses will override
  }

});

<commit_msg>[#177570372] Bug fix: Closing graph with movable value crashes<commit_after>// ==========================================================================
//                          DG.PlotAdornment
//
//  Author:   William Finzer
//
//  Copyright (c) 2014 by The Concord Consortium, Inc. All rights reserved.
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// ==========================================================================

/** @class  The base class for plot adornments.

  @extends SC.Object
*/
DG.PlotAdornment = SC.Object.extend(
/** @scope DG.PlotAdornment.prototype */
{
  /**
    The model containing the data to display.
    @property { Depends on adornment }
  */
  model: null,

  /**
   * @property {DG.PlotBackgroundView}
  */
  paperSource: null,

  /**
   * @property( Raphael }
   */
  paper: function() {
    return this.getPath('paperSource.paper');
  }.property(),

  paperDidChange: function() {
    this.notifyPropertyChange('paper');
  }.observes('*paperSource.paper'),

  /**
    Provides x-coordinates
    @property { DG.AxisView }
  */
  xAxisView: function() {
    return this.getPath('paperSource.xAxisView');
  }.property('paperSource').cacheable(),

  xAxisViewDidChange: function() {
    this.notifyPropertyChange('xAxisView');
  }.observes('*paperSource.xAxisView'),

  /**
    Provides y-coordinates
    @property { DG.AxisView }
  */
  yAxisView: function() {
    return this.getPath('paperSource.yAxisView');
  }.property('paperSource').cacheable(),

  yAxisViewDidChange: function() {
    this.notifyPropertyChange('yAxisView');
  }.observes('*paperSource.yAxisView'),

  /**
    We need to know this view in order to transform from window to view coordinates.
    @property { SC.View }
  */
  parentView: null,

  /**
    These are the Raphael objects I draw, kept together for ease of showing and hiding.
    @property { SC.Array }
  */
  myElements: null,

  /**
   * The key into the layerManager to get the layer we use to display
   * @property { String }
   */
  layerName: null,

  /**
   * All my Raphael elements go in this layer
   * @property { DG.RaphaelLayer }
   */
  layer: function() {
    var tLayerManager = this.getPath('paperSource.layerManager');
    return (tLayerManager && this.layerName) ? tLayerManager[ this.layerName] :null;
  }.property('paperSource', 'layerName' ),

  /**
    Contains an array of elements, each of which corresponds to a pair of strings
    indicating a model property that should be observed and the handler that should
    be called when that property changes. Adding 'modelPropertiesToObserve' to the
    'concatenatedProperties' array allows the contents of the property from multiple
    levels of the 'class hierarchy' to be concatenated automatically taking advantage
    of a built-in SproutCore behavior. AddObserver/removeObserver will be called for
    each of the ['PropertyName','ObserverMethod'] pairs as appropriate whenever the
    model changes.

    @property   {Array of [{String},{String}]}  Elements are ['PropertyName','ObserverMethod']
   */
  modelPropertiesToObserve: [ ['willDestroy', 'detachModel'],
    ['computingNeeded', 'updateToModel'],
    ['isVisible', 'updateVisibility'] ],
  concatenatedProperties: [ 'modelPropertiesToObserve' ],

  /**
    Initialization method
   */
  init: function() {
    sc_super();

    this.myElements = [];
    // Trigger manually and then addObserver
    this.modelDidChange();
  },

  /**
    Detaches all model references in preparation for destruction of
    either the model or the adornment.
   */
  detachModel: function() {
    // Reset the model (triggering notification), then removeObserver
    this.set('model', null);
  },

  /**
    Destruction method
   */
  destroy: function() {
    this.detachModel();
    this.removeElements();
    sc_super();
  },

  removeElements: function() {
    if( this.myElements) {
      var tLayerManager = this.getPath('paperSource.layerManager');
      this.myElements.forEach(function (iElement) {
        tLayerManager.removeElement(iElement);
        iElement.remove();
      });
      this.myElements = null;
    }
  },

  /**
    Observer method called when the adornment model changes.
    Guarantees that observers are added/removed for any relevant model properties.
    List ['Property','Handler'] pairs in 'modelPropertiesToObserve' property to
    have observers added/removed automatically.
   */
  _observedModel: null,
  modelDidChange: function() {
    // Get the concatenated list of model properties to observe
    var modelProps = this.get('modelPropertiesToObserve');

    // Detach from previous model
    if( this._observedModel) {
      if( modelProps) {
        // Remove observers for all listed properties
        modelProps.forEach( function( iModelProp) {
                              this._observedModel.removeObserver( iModelProp[0], this, iModelProp[1]);
                            }.bind( this));
      }
      this._observedModel = null;
    }

    // Attach to new model
    var model = this.get('model');
    if( model) {
      if( modelProps) {
        // Add observers for all listed properties
        modelProps.forEach( function( iModelProp) {
                              model.addObserver( iModelProp[0], this, iModelProp[1]);
                            }.bind( this));
      }
      this._observedModel = model;
    }
  }.observes('model'),

  showElements: function() {
    var tOpacity = this.get('opacity') || 1,
        tStrokeOpacity = this.get('strokeOpacity') || 1;
    this.myElements.forEach(function (iElement) {
      iElement.show();
      if (iElement.animatable) {
        iElement.animate({'stroke-opacity': tStrokeOpacity, opacity: tOpacity},
            DG.PlotUtilities.kDefaultAnimationTime, '<>');
      }
    });
    this.set('isHidden', false);
  },

  hideElements: function () {
    this.myElements.forEach(function (iElement) {
      if (iElement.animatable) {
        iElement.animate({'stroke-opacity': 0, opacity: 0}, DG.PlotUtilities.kDefaultAnimationTime, '<>',
            function () {
              iElement.hide();
            });
      }
      else
        iElement.hide();
    });
    this.set('isHidden', true);
  },

  /**
    My model's visibility has changed.
  */
  updateVisibility: function() {
    if( this.getPath('model.isVisible')) {
      this.updateToModel();
      this.showElements();
    }
    else if( !SC.none( this.myElements))
    {
      this.hideElements();
    }
  },

  updateToModel: function() {
    // Subclasses will override
  }

});

<|endoftext|>


<commit_before>/* jshint undef: true, strict:false, trailing:false, unused:false */
/* global require, exports, console, process, module, L, angular */

angular
	.module('wellbeing', ['ng','indx','infinite-scroll'])
	.directive('dayContainer', function() {
		return {
			restrict:'E',
			scope:{ day:'=' },
			templateUrl:'/apps/aditl/templates/day-container.html',
			controller:function($scope) {

			}
		};
	}).directive('locmap', function() {
		return {
			restrict:'E',
			replace:true,
			scope:{ location:'=' },
			template:'

',
			link:function(scope, element, attribute) {
				var lat = scope.location.peek('latitude'), lon = scope.location.peek('longitude');
				scope.map = L.map(element[0]).setView([lat, lon], 18);

				element.attr('data-latitude', lat);
				element.attr('data-longitude', lon);
				element.attr('data-id', scope.location.id);

				L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '' }).addTo(scope.map);

					// add a marker in the given location, attach some popup content to it and open the popup
				L.marker([lat, lon]).addTo(scope.map);
					    // .bindPopup(scope.location.peek('name') ? scope.location.peek('name') : scope.location.id)
					    // .openPopup();
			},
			controller:function($scope) {
			}
		};
	}).controller('main', function($scope, client, utils, entities) {
		var u = utils;
		$scope.days = [];
		// $scope.locations = [];
		var box, all_locs = [];
		var sa = function(fn) { return u.safeApply($scope, fn); };
		var weekday=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
		var months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];

		var createDay = function(date) {
			if (!box) return;

			var dstart = new Date(date.valueOf()); dstart.setHours(0,0,0,0);
			var dend = new Date(date.valueOf()); dend.setHours(23,59,59,999);

			var day = {
				date:date,dstart:dstart,dend:dend,
				dow: weekday[dstart.getDay()].toLowerCase(),
				dom:dstart.getDate(),
				month:months[dstart.getMonth()].toLowerCase().slice(0,3),
				segments:[]
			};

			// segment the day by activity
			entities.activities.getByActivityType(	box, dstart, dend, ['walk','run','stay','transport'] ).then(
				function(acts) {
					console.log('got activities > ', acts);

				}).fail(function(bail) {
					console.error('error getting activities >> ', bail);
				});
			return day;
		};
		$scope.generatePast = function(start_date, num_days) {
			console.log('generatePast >> ', start_date);
			var i = 1;
			if (start_date === undefined) {
				start_date = new Date(new Date().valueOf() + i*24*3600*1000);
			}
			while (i <= num_days) {
				var date = new Date(start_date.valueOf() - i*24*3600*1000);
				var cd = createDay(date);
				if (cd) { $scope.days.push(cd); }
				i++;
			}
		};
		$scope.$watch('user + box', function() {
			if (!$scope.user) { console.log('no user :( '); return; }
			$scope.days = [];
			client.store.getBox($scope.box).then(function(_box) {
				window.box = _box;
				box = _box;
				// box.query({type:'location'}).then(function(things) {
				// 	u.safeApply($scope, function() {
				// 		all_locs = things.concat();
				// 		$scope.locations = things.slice(0,6);
				// 	});
				// }).fail(function(bail) { console.log('fail querying ', bail); });
				// $scope.genDay();
				u.safeApply($scope, function() { $scope.generatePast(undefined,4); });
			}).fail(function(bail) { console.error(bail); });
		});
		window._s = $scope;
	});<commit_msg>starting segmentation<commit_after>/* jshint undef: true, strict:false, trailing:false, unused:false */
/* global require, exports, console, process, module, L, angular */

angular
	.module('wellbeing', ['ng','indx','infinite-scroll'])
	.directive('dayContainer', function() {
		return {
			restrict:'E',
			scope:{ day:'=' },
			templateUrl:'/apps/aditl/templates/day-container.html',
			controller:function($scope) {

			}
		};
	}).directive('locmap', function() {
		return {
			restrict:'E',
			replace:true,
			scope:{ location:'=' },
			template:'

',
			link:function(scope, element, attribute) {
				var lat = scope.location.peek('latitude'), lon = scope.location.peek('longitude');
				scope.map = L.map(element[0]).setView([lat, lon], 18);

				element.attr('data-latitude', lat);
				element.attr('data-longitude', lon);
				element.attr('data-id', scope.location.id);

				L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '' }).addTo(scope.map);

					// add a marker in the given location, attach some popup content to it and open the popup
				L.marker([lat, lon]).addTo(scope.map);
					    // .bindPopup(scope.location.peek('name') ? scope.location.peek('name') : scope.location.id)
					    // .openPopup();
			},
			controller:function($scope) {
			}
		};
	}).controller('main', function($scope, client, utils, entities) {
		var u = utils;
		$scope.days = [];
		// $scope.locations = [];
		var box, all_locs = [];
		var sa = function(fn) { return u.safeApply($scope, fn); };
		var weekday=['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
		var months = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ];

		var makeSegment = function(tstart, tend, segname, location) {
			var seg = {
				tstart: tstart,
				tend: tend,
				name : segname,
				location : location,
				nike : [],
				fitbit : [],
				browsing: []
			};

			return seg;
		};

		var createDay = function(date) {
			if (!box) return;

			var dstart = new Date(date.valueOf()); dstart.setHours(0,0,0,0);
			var dend = new Date(date.valueOf()); dend.setHours(23,59,59,999);

			var day = {
				date:date,dstart:dstart,dend:dend,
				dow: weekday[dstart.getDay()].toLowerCase(),
				dom:dstart.getDate(),
				month:months[dstart.getMonth()].toLowerCase().slice(0,3),
				segments:[]
			};

			// segment the day by activity
			entities.activities.getByActivityType(	box, dstart, dend, ['walk','run','stay','transport'] ).then(
				function(acts) {
					console.log('got activities > ', acts);
					var sorted = acts.concat();
					acts.sort(function(x,y) { return x.peek('tstart') - y.peek('tstart'); });
					console.log('sorted >> ', acts.map(function(act) { return act.peek('tstart'); }));

					// filter for activities that have at least 1 waypoint
					acts = acts.filter(function(x) { return x.peek('waypoints'); });
					console.log('post waypoints acts >> ', acts.length);

					// filter out activities that are shorter than 1 minute
					acts = acts.filter(function(x) { return x.peek('tend') - x.peek('tstart') < 60000; });

					// now have activities that have at least one segment and that are at least 1 minute
					acts.map(function(act) {
						var newseg = makeSegment(act.peek('tstart'), act.peek('tend'), act.peek('activity'), act.peek('waypoints') );
						console.log('made segment >> ', newseg);
						sa(function() { day.segments.push(newseg);	});
					});
				}).fail(function(bail) {
					console.error('error getting activities >> ', bail);
				});
			return day;
		};
		$scope.generatePast = function(start_date, num_days) {
			console.log('generatePast >> ', start_date);
			var i = 1;
			if (start_date === undefined) {
				start_date = new Date(new Date().valueOf() + i*24*3600*1000);
			}
			while (i <= num_days) {
				var date = new Date(start_date.valueOf() - i*24*3600*1000);
				var cd = createDay(date);
				if (cd) { $scope.days.push(cd); }
				i++;
			}
		};
		$scope.$watch('user + box', function() {
			if (!$scope.user) { console.log('no user :( '); return; }
			$scope.days = [];
			client.store.getBox($scope.box).then(function(_box) {
				window.box = _box;
				box = _box;
				// box.query({type:'location'}).then(function(things) {
				// 	u.safeApply($scope, function() {
				// 		all_locs = things.concat();
				// 		$scope.locations = things.slice(0,6);
				// 	});
				// }).fail(function(bail) { console.log('fail querying ', bail); });
				// $scope.genDay();
				u.safeApply($scope, function() { $scope.generatePast(undefined,4); });
			}).fail(function(bail) { console.error(bail); });
		});
		window._s = $scope;
	});<|endoftext|>


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

const { computed, isEmpty, isPresent } = 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: Ember.on('init', 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);
  }),

  // 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) {
        // ignore errors
      }
    }, 600));
  }.observes('quickSearch'),

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

  additionalColumnsForFilter: [],

  // 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,

  selectedRows: [],

  /**
   * @method
   * @public
   *
   * allows serialize the controller state to be able to send it to storage
   */
  toTableSettingsState () {
    // We need to return a json object
    // that can be used to re-hydrate the controller
    // after a cold refresh to use saved filters
    // sorting, page, perPage etc that are not part of
    // the queryParams

    const mapColumn = (col) => {
      return {
        fieldName: col.get('fieldName'),
        filterValue: col.get('filterValue'),
        showFilter: col.get('showFilter'),
        advFilterValue: col.get('advFilterValue'),
        advFilterValue2: col.get('advFilterValue2'),
        advFilterOperator: col.get('advFilterOperator')
      }
    }

    return {
      page: this.get('page'),
      perPage: this.get('perPage'),
      sortField: this.get('sortField'),
      quickSearch: this.get('quickSearch'),
      extraParams: this.get('extraParams'),
      columns: this.get('columns').map(mapColumn),
      additionalColumnsForFilter: this.get('additionalColumnsForFilter').map(mapColumn)
    }
  },

  /**
   * @method
   * @public
   *
   * allows deserialize the controller state from a storage
   */
  fromTableSettingsState (state) {
    if (isEmpty(state)) return
    this.set('page', state.page)
    this.set('perPage', state.perPage)
    this.set('sortField', state.sortField)
    this.set('quickSearch', state.quickSearch)
    this.set('extraParams', state.extraParams)
  },

  updateColumnsFromTableSettingState (state) {
    const updateColumn = function (list) {
      return (col) => {
        const tableColumn = list.findBy('fieldName', col.fieldName)
        if (isPresent(tableColumn)) {
          tableColumn.set('filterValue', col.filterValue)
          tableColumn.set('showFilter', col.showFilter)
          tableColumn.set('advFilterValue', col.advFilterValue)
          tableColumn.set('advFilterValue2', col.advFilterValue2)
          if (isPresent(col.advFilterOperator)) {
            tableColumn.set('advFilterOperator', Ember.Object.create(col.advFilterOperator))
          }
        }
      }
    }

    if (state) {
      state.columns.forEach(updateColumn(this.get('columns')))
      state.additionalColumnsForFilter.forEach(updateColumn(this.get('additionalColumnsForFilter')))
    }
  },

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

    editFlag (row) {
      this.set('isEditingFlagNote', true);
      this.set('flaggingRecord', row.content);
    },

    saveFlag(){
      let row = this.get('flaggingRecord');

      let subject = this.store.peekRecord('subject', row.id);

      subject.set('flagNote', row.flagNote);

      if (Ember.isEmpty(row.get('flagNote'))) {
        subject.set('flagStatus', 'unflagged');
      } else {
        subject.set('flagStatus', 'flagged');
      }
      subject.save().then(() => {
        this.get('notify').success('Saved successfully');
      }).then(() => {
        if (Ember.isEmpty(row.get('flagNote'))) {
          row.set('flagStatus', 'unflagged');
        } else {
          row.set('flagStatus', 'flagged');
        }

        this.set('isEditingFlagNote', false);
        this.set('flaggingRecord', null);
      });
    },

    cancelFlag () {
      this.set('isEditingFlagNote', false);
      this.set('flaggingRecord', null);
    },
  }
});
<commit_msg>Replace usage of content in controller mixin<commit_after>import Ember from 'ember';
import Column from './column';
import pagedArray from 'ember-cli-pagination/computed/paged-array';

const { computed, isEmpty, isPresent } = 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: Ember.on('init', 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);
  }),

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

  pageList: [
    50, 100, 250
  ],

  totalPages: alias("model.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('model', {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) {
        // ignore errors
      }
    }, 600));
  }.observes('quickSearch'),

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

  additionalColumnsForFilter: [],

  // 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,

  selectedRows: [],

  /**
   * @method
   * @public
   *
   * allows serialize the controller state to be able to send it to storage
   */
  toTableSettingsState () {
    // We need to return a json object
    // that can be used to re-hydrate the controller
    // after a cold refresh to use saved filters
    // sorting, page, perPage etc that are not part of
    // the queryParams

    const mapColumn = (col) => {
      return {
        fieldName: col.get('fieldName'),
        filterValue: col.get('filterValue'),
        showFilter: col.get('showFilter'),
        advFilterValue: col.get('advFilterValue'),
        advFilterValue2: col.get('advFilterValue2'),
        advFilterOperator: col.get('advFilterOperator')
      }
    }

    return {
      page: this.get('page'),
      perPage: this.get('perPage'),
      sortField: this.get('sortField'),
      quickSearch: this.get('quickSearch'),
      extraParams: this.get('extraParams'),
      columns: this.get('columns').map(mapColumn),
      additionalColumnsForFilter: this.get('additionalColumnsForFilter').map(mapColumn)
    }
  },

  /**
   * @method
   * @public
   *
   * allows deserialize the controller state from a storage
   */
  fromTableSettingsState (state) {
    if (isEmpty(state)) return
    this.set('page', state.page)
    this.set('perPage', state.perPage)
    this.set('sortField', state.sortField)
    this.set('quickSearch', state.quickSearch)
    this.set('extraParams', state.extraParams)
  },

  updateColumnsFromTableSettingState (state) {
    const updateColumn = function (list) {
      return (col) => {
        const tableColumn = list.findBy('fieldName', col.fieldName)
        if (isPresent(tableColumn)) {
          tableColumn.set('filterValue', col.filterValue)
          tableColumn.set('showFilter', col.showFilter)
          tableColumn.set('advFilterValue', col.advFilterValue)
          tableColumn.set('advFilterValue2', col.advFilterValue2)
          if (isPresent(col.advFilterOperator)) {
            tableColumn.set('advFilterOperator', Ember.Object.create(col.advFilterOperator))
          }
        }
      }
    }

    if (state) {
      state.columns.forEach(updateColumn(this.get('columns')))
      state.additionalColumnsForFilter.forEach(updateColumn(this.get('additionalColumnsForFilter')))
    }
  },

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

    editFlag (row) {
      this.set('isEditingFlagNote', true);
      this.set('flaggingRecord', row.content);
    },

    saveFlag(){
      let row = this.get('flaggingRecord');

      let subject = this.store.peekRecord('subject', row.id);

      subject.set('flagNote', row.flagNote);

      if (Ember.isEmpty(row.get('flagNote'))) {
        subject.set('flagStatus', 'unflagged');
      } else {
        subject.set('flagStatus', 'flagged');
      }
      subject.save().then(() => {
        this.get('notify').success('Saved successfully');
      }).then(() => {
        if (Ember.isEmpty(row.get('flagNote'))) {
          row.set('flagStatus', 'unflagged');
        } else {
          row.set('flagStatus', 'flagged');
        }

        this.set('isEditingFlagNote', false);
        this.set('flaggingRecord', null);
      });
    },

    cancelFlag () {
      this.set('isEditingFlagNote', false);
      this.set('flaggingRecord', null);
    },
  }
});
<|endoftext|>


<commit_before>// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.

/* ------------------ IMPORTS ------------------ */

const remote = require('electron').remote
const main = remote.require('./main.js')
const shell = require('electron').shell
const fs = require('fs')
const os = require('os')

// Those are needed to download the torrents
const path = require('path')
const request = require('request')
const findRemoveSync = require('find-remove')

// Mal API
const mal = require('malapi').Anime
// Nyaa API
const Nyaa = require('node-nyaa-api')
// Scraping the news from Mal
const malScraper = require('mal-scraper')

/* ----------------- END IMPORTS ----------------- */

const DIR = path.join(os.userInfo().homedir, '.KawAnime')

const VueMaterial = require('vue-material')

Vue.use(VueMaterial)

Vue.material.theme.register('default', {
    primary: 'white',
    accent: 'indigo',
    warn: 'deep-orange',
    background: 'grey'
})

/* -----------------  FUNCTIONS  ----------------- */

function reduceString(string, wanted) {
    if (string.length > wanted)
        return string.substring(0, wanted) + ('...')
    return string
}

// I like HorribleSubs
function horribleSubsFilter(name) {
    if ('[HorribleSubs]' === name.split(' ')[0])
    {
        if ('[720p].mkv' === name.split(' ').reverse()[0])
            return true
    }
    return false
}

function getNameOnly(name) {
    let tmp = name.split(' ')
    tmp.pop()
    tmp.shift()
    return tmp.join(' ')
}

function getNameForResearch(name) {
    let tmp = getNameOnly(name).split(' ')
    tmp.pop()   // Episode number

    return tmp.join(' ')
}

function byProperty(prop) {
    return function(a, b) {
        if (typeof a[prop] == "number") {
            return (a[prop] - b[prop])
        }
        return ((a[prop] < b[prop]) ? -1 : ((a[prop] > b[prop]) ? 1 : 0))
    }
}

// Make the research for the latest animes
function getLatest () {
    let sorted = false

    Nyaa.search('[HorribleSubs]', function (err, animes) {
        // Initialize
        releases.releases = []
        releases.show = false
        loader.show = true
        if (err) throw err

        for(let anime in animes)
        {
            if (horribleSubsFilter(animes[anime].title))
            {
                let tmp = animes[anime].title.split(' ')
                tmp.pop()   // Remove the extension
                tmp.pop()   // Remove the episode number
                tmp.shift() // Remove the Horrible Subs tag

                // Make the actual research
                try
                {
                    mal.fromName(tmp.join(' ')).then(result => {
                        releases.releases.push({
                            realTitle: animes[anime].title,
                            title: getNameOnly(animes[anime].title),
                            link: animes[anime].link,
                            synopsis: reduceString(result.synopsis, 100),
                            picture: result.image,
                            published: animes[anime].published
                        })
                    }).then(() => {
                        if (releases.releases.length > 29) {
                            releases.releases.sort(byProperty('published'))
                            releases.releases.reverse()
                            sorted = true
                        }
                    }).then(() => {
                        // 35 elements is too much, reducing to 18
                        if (sorted)
                            while (releases.releases.length > 18)
                                releases.releases.pop()
                    }).then(() => {
                        setTimeout(() => {
                            if (loader.show)
                            {
                                loader.show = false
                                releases.show = true
                            }
                        }, 1200)
                    })
                }
                catch (e)
                {
                    console.log("There was a 'Too many request' error.")
                }
            }
        }
    })
}

function downloadFile (file_url, name){
    let req = request({
        method: 'GET',
        uri: file_url
    })

    let out = fs.createWriteStream(path.join(DIR, `${name}.torrent`))
    req.pipe(out)
}

function startTorrent (file_url, name) {
    // Removing old torrents
    findRemoveSync(DIR, {extensions: ['.torrent']})

    // Downloading the new ones
    downloadFile(file_url, name)

    // Opening them
    main.openTorrents()
}

function makeResearchOnMal (name) {

    mal.fromName(name).then(anime => {
        info.infos.title = anime.title
        info.infos.japTitle = anime.alternativeTitles.japanese[0].slice(10)
        info.infos.image = anime.image
        info.infos.synopsis = anime.synopsis
        info.infos.episodes = anime.episodes
        info.infos.studios = anime.studios
        info.infos.stats = anime.statistics
        info.infos.genres = anime.genres
        info.infos.type = anime.type.split(' ').slice(0, 3).join(' ')
        info.infos.characters = anime.characters
        info.infos.staff = anime.staff
        info.infos.aired = anime.aired
        info.infos.status = anime.status

        releases.show = false
        loader.show = false
        info.display = 'block'
        info.show = true
    })
}

function getNews () {
    let tmp = malScraper.getNewsNoDetails( () => {
        news.news = tmp
    })
}

function getCurrentSeason () {
    const date = new Date()

    // Get current year
    const year = 1900 + date.getYear()

    // Get current month
    const month = 1 + date.getMonth()   // I am a weak person that like 1-indexed things

    if (0 < month && month < 4)  // Winter
        return { season: 'winter', year: year}
    else if (3 < month && month < 7)  // Spring
        return { season: 'spring', year: year}
    else if (6 < month && month < 10)  // Summer
        return { season: 'summer', year: year}
    else if (9 < month && month < 13)  // Fall
        return { season: 'fall', year: year}
}

function fillSeason(seasonalInfo) {
    seasonalInfo.info.forEach( (elem) => {
        switch (elem.type)
        {
            case 'TV':
                season.TVs.push(elem)
                break

            case 'ONA':
                season.ONAs.push(elem)
                break

            case 'OVA':
                season.OVAs.push(elem)
                break

            case 'Movie':
                season.Movies.push(elem)
                break

            case 'Special':
                season.Specials.push(elem)
                break

            default:
                break
        }
    })
    season.infoWatcher = false
}

/* -------------------- END FUNCTIONS ----------------- */

// First research at kawAnime's start
getLatest()

// Getting the news in advance
getNews()

// Getting the current season's information
let seasonalInfo = malScraper.getSeason(getCurrentSeason().year, getCurrentSeason().season, () => {
    fillSeason(seasonalInfo)
})

/* ------------------- VUE.JS OBJECTS ----------------- */

let lastPage = 'release'

let releases = new Vue({
    el: '#releases',
    data: {
        releases: [],
        show: false,
        styleButton: {
            minWidth: '0px',
            marginBottom: '0px',
            marginTop: '0px',
            marginLeft: '2px',
            marginRight: '2px'
        }
    },
    methods: {
        download: function (url, name) {
            startTorrent(url, name)
        },
        refresh: function () {
            getLatest()
        },
        searchThis: function (arg) {
            info.infos = {}
            this.show = false
            loader.show = true
            makeResearchOnMal(getNameForResearch(arg))

            lastPage = 'release'
        }
    }
})

let loader = new Vue({
    el: '#loader-container',
    data: {
        show: true
    },
    methods: {
        test: function () {
            this.show = false
            releases.show = true
        }
    }
})

let info = new Vue({
    el: '#info-container',
    data: {
        infos: {
            stats: {
                score: {value: ''},
                ranking: ''
            }
        },
        display: 'none',
        show: false
    },
    methods: {
        downlaodThis: function () {

        },
        show: function () {
            this.display = 'block'
            this.show = true
        },
        hide: function () {
            this.show = false
        },
        back: function () {
            this.hide()
            switch (lastPage)
            {
                case 'release':
                    releases.show = true
                    break

                case 'season':
                    season.show = true
                    break

                default:
                    break
            }

        }
    }
})

let news = new Vue({
    el: '#news-container',
    data: {
        show: false,
        news: [],
        display: 'none',
        buttonStyle: {
            position: 'absolute'
        }
    },
    methods: {
        openLink: function (link) {
            event.preventDefault()
            shell.openExternal(link)
        }
    }
})

let season = new Vue({
    el: '#season-info-container',
    data: {
        display: 'none',
        show: false,
        searching: false,
        season: getCurrentSeason().season,
        year: getCurrentSeason().year,
        infoWatcher: false,
        TVs: [],
        ONAs: [],
        OVAs: [],
        Movies: [],
        Specials: [],
        link: {
            marginTop: 0,
            marginBottom: 0,
            float: 'right'
        },
        scoreStyle: {
            margin: '0 0 0 0',
        },
        textStyle: {
            marginLeft: '30%',
            height: '50%'
        },
        synopsisStyle: {
            paddingRight: '0',
            textAlign: 'justify',
            paddingTop: '5px'
        }
    },
    watch: {
        infoWatcher: function (bool) {
            if (bool)
            {
                this.TVs = []
                this.ONAs = []
                this.OVAs = []
                this.Movies = []
                this.Specials = []
                console.log("ERASED", this.TVs.length)
            }
        }
    },
    methods: {
        reduced: function (text, nb) {
            return reduceString(text, nb)
        },
        getGenres: function (genres) {
            let result = ''

            genres.forEach( (elem) => {
                result += `${elem}, `
            })

            return result.slice(0, -2)
        },
        searchThis: function (arg) {
            info.infos = {}
            this.show = false
            loader.show = true
            makeResearchOnMal(arg)
            lastPage = 'season'
        },
        getThisSeason: function (year, season) {
            this.infoWatcher = true
            self = this
            let newSeasonalInfo = malScraper.getSeason(year, season, function () {
                fillSeason(newSeasonalInfo)
            })
        }
    },
})

// Vue object to open the other pages
new Vue({
    el: '.mdl-navigation',
    methods: {
        getDownloader: function () {
            main.openDownloader()
        },
        getMainPage: function () {
            if (!loader.show)
            {
                releases.show = true
                news.show = false
                info.show = false
                season.show = false
            }
        },
        getNewsPage: function () {
            // For first time
            if (news.display === 'none')
                news.display = 'block'

            releases.show = false
            news.show = true
            loader.show = false
            season.show = false
            info.show = false
        },
        getSeasonPage: function () {
            // For first time
            if (season.display === 'none')
                season.display = 'block'

            releases.show = false
            news.show = false
            loader.show = false
            season.show = true
            info.show = false
        }
    }
})

// For the greeting's message
new Vue({
    el: '.greetings',
    data: {
        username: os.userInfo().username
    }
})

// Research button on top right
let searchButton = document.getElementById('fixed-header-drawer-exp')

searchButton.addEventListener('keydown', (key) => {
    if (key.keyCode === 13)
    {
        info.show = false
        if (searchButton.value.length > 3)
        {
            releases.show = false
            loader.show = true
            makeResearchOnMal(searchButton.value.toString())
        }
        searchButton.value = ''
    }
})<commit_msg>Minor correct, forgot to hide news and season windows on research<commit_after>// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// All of the Node.js APIs are available in this process.

/* ------------------ IMPORTS ------------------ */

const remote = require('electron').remote
const main = remote.require('./main.js')
const shell = require('electron').shell
const fs = require('fs')
const os = require('os')

// Those are needed to download the torrents
const path = require('path')
const request = require('request')
const findRemoveSync = require('find-remove')

// Mal API
const mal = require('malapi').Anime
// Nyaa API
const Nyaa = require('node-nyaa-api')
// Scraping the news from Mal
const malScraper = require('mal-scraper')

/* ----------------- END IMPORTS ----------------- */

const DIR = path.join(os.userInfo().homedir, '.KawAnime')

const VueMaterial = require('vue-material')

Vue.use(VueMaterial)

Vue.material.theme.register('default', {
    primary: 'white',
    accent: 'indigo',
    warn: 'deep-orange',
    background: 'grey'
})

/* -----------------  FUNCTIONS  ----------------- */

function reduceString(string, wanted) {
    if (string.length > wanted)
        return string.substring(0, wanted) + ('...')
    return string
}

// I like HorribleSubs
function horribleSubsFilter(name) {
    if ('[HorribleSubs]' === name.split(' ')[0])
    {
        if ('[720p].mkv' === name.split(' ').reverse()[0])
            return true
    }
    return false
}

function getNameOnly(name) {
    let tmp = name.split(' ')
    tmp.pop()
    tmp.shift()
    return tmp.join(' ')
}

function getNameForResearch(name) {
    let tmp = getNameOnly(name).split(' ')
    tmp.pop()   // Episode number

    return tmp.join(' ')
}

function byProperty(prop) {
    return function(a, b) {
        if (typeof a[prop] == "number") {
            return (a[prop] - b[prop])
        }
        return ((a[prop] < b[prop]) ? -1 : ((a[prop] > b[prop]) ? 1 : 0))
    }
}

// Make the research for the latest animes
function getLatest () {
    let sorted = false

    Nyaa.search('[HorribleSubs]', function (err, animes) {
        // Initialize
        releases.releases = []
        releases.show = false
        loader.show = true
        if (err) throw err

        for(let anime in animes)
        {
            if (horribleSubsFilter(animes[anime].title))
            {
                let tmp = animes[anime].title.split(' ')
                tmp.pop()   // Remove the extension
                tmp.pop()   // Remove the episode number
                tmp.shift() // Remove the Horrible Subs tag

                // Make the actual research
                try
                {
                    mal.fromName(tmp.join(' ')).then(result => {
                        releases.releases.push({
                            realTitle: animes[anime].title,
                            title: getNameOnly(animes[anime].title),
                            link: animes[anime].link,
                            synopsis: reduceString(result.synopsis, 100),
                            picture: result.image,
                            published: animes[anime].published
                        })
                    }).then(() => {
                        if (releases.releases.length > 29) {
                            releases.releases.sort(byProperty('published'))
                            releases.releases.reverse()
                            sorted = true
                        }
                    }).then(() => {
                        // 35 elements is too much, reducing to 18
                        if (sorted)
                            while (releases.releases.length > 18)
                                releases.releases.pop()
                    }).then(() => {
                        setTimeout(() => {
                            if (loader.show)
                            {
                                loader.show = false
                                releases.show = true
                            }
                        }, 1200)
                    })
                }
                catch (e)
                {
                    console.log("There was a 'Too many request' error.")
                }
            }
        }
    })
}

function downloadFile (file_url, name){
    let req = request({
        method: 'GET',
        uri: file_url
    })

    let out = fs.createWriteStream(path.join(DIR, `${name}.torrent`))
    req.pipe(out)
}

function startTorrent (file_url, name) {
    // Removing old torrents
    findRemoveSync(DIR, {extensions: ['.torrent']})

    // Downloading the new ones
    downloadFile(file_url, name)

    // Opening them
    main.openTorrents()
}

function makeResearchOnMal (name) {

    mal.fromName(name).then(anime => {
        info.infos.title = anime.title
        info.infos.japTitle = anime.alternativeTitles.japanese[0].slice(10)
        info.infos.image = anime.image
        info.infos.synopsis = anime.synopsis
        info.infos.episodes = anime.episodes
        info.infos.studios = anime.studios
        info.infos.stats = anime.statistics
        info.infos.genres = anime.genres
        info.infos.type = anime.type.split(' ').slice(0, 3).join(' ')
        info.infos.characters = anime.characters
        info.infos.staff = anime.staff
        info.infos.aired = anime.aired
        info.infos.status = anime.status

        releases.show = false
        loader.show = false
        info.display = 'block'
        info.show = true
    })
}

function getNews () {
    let tmp = malScraper.getNewsNoDetails( () => {
        news.news = tmp
    })
}

function getCurrentSeason () {
    const date = new Date()

    // Get current year
    const year = 1900 + date.getYear()

    // Get current month
    const month = 1 + date.getMonth()   // I am a weak person that like 1-indexed things

    if (0 < month && month < 4)  // Winter
        return { season: 'winter', year: year}
    else if (3 < month && month < 7)  // Spring
        return { season: 'spring', year: year}
    else if (6 < month && month < 10)  // Summer
        return { season: 'summer', year: year}
    else if (9 < month && month < 13)  // Fall
        return { season: 'fall', year: year}
}

function fillSeason(seasonalInfo) {
    seasonalInfo.info.forEach( (elem) => {
        switch (elem.type)
        {
            case 'TV':
                season.TVs.push(elem)
                break

            case 'ONA':
                season.ONAs.push(elem)
                break

            case 'OVA':
                season.OVAs.push(elem)
                break

            case 'Movie':
                season.Movies.push(elem)
                break

            case 'Special':
                season.Specials.push(elem)
                break

            default:
                break
        }
    })
    season.infoWatcher = false
}

/* -------------------- END FUNCTIONS ----------------- */

// First research at kawAnime's start
getLatest()

// Getting the news in advance
getNews()

// Getting the current season's information
let seasonalInfo = malScraper.getSeason(getCurrentSeason().year, getCurrentSeason().season, () => {
    fillSeason(seasonalInfo)
})

/* ------------------- VUE.JS OBJECTS ----------------- */

let lastPage = 'release'

let releases = new Vue({
    el: '#releases',
    data: {
        releases: [],
        show: false,
        styleButton: {
            minWidth: '0px',
            marginBottom: '0px',
            marginTop: '0px',
            marginLeft: '2px',
            marginRight: '2px'
        }
    },
    methods: {
        download: function (url, name) {
            startTorrent(url, name)
        },
        refresh: function () {
            getLatest()
        },
        searchThis: function (arg) {
            info.infos = {}
            this.show = false
            loader.show = true
            makeResearchOnMal(getNameForResearch(arg))

            lastPage = 'release'
        }
    }
})

let loader = new Vue({
    el: '#loader-container',
    data: {
        show: true
    },
    methods: {
        test: function () {
            this.show = false
            releases.show = true
        }
    }
})

let info = new Vue({
    el: '#info-container',
    data: {
        infos: {
            stats: {
                score: {value: ''},
                ranking: ''
            }
        },
        display: 'none',
        show: false
    },
    methods: {
        downlaodThis: function () {

        },
        show: function () {
            this.display = 'block'
            this.show = true
        },
        hide: function () {
            this.show = false
        },
        back: function () {
            this.hide()
            switch (lastPage)
            {
                case 'release':
                    releases.show = true
                    break

                case 'season':
                    season.show = true
                    break

                default:
                    break
            }

        }
    }
})

let news = new Vue({
    el: '#news-container',
    data: {
        show: false,
        news: [],
        display: 'none',
        buttonStyle: {
            position: 'absolute'
        }
    },
    methods: {
        openLink: function (link) {
            event.preventDefault()
            shell.openExternal(link)
        }
    }
})

let season = new Vue({
    el: '#season-info-container',
    data: {
        display: 'none',
        show: false,
        searching: false,
        season: getCurrentSeason().season,
        year: getCurrentSeason().year,
        infoWatcher: false,
        TVs: [],
        ONAs: [],
        OVAs: [],
        Movies: [],
        Specials: [],
        link: {
            marginTop: 0,
            marginBottom: 0,
            float: 'right'
        },
        scoreStyle: {
            margin: '0 0 0 0',
        },
        textStyle: {
            marginLeft: '30%',
            height: '50%'
        },
        synopsisStyle: {
            paddingRight: '0',
            textAlign: 'justify',
            paddingTop: '5px'
        }
    },
    watch: {
        infoWatcher: function (bool) {
            if (bool)
            {
                this.TVs = []
                this.ONAs = []
                this.OVAs = []
                this.Movies = []
                this.Specials = []
                console.log("ERASED", this.TVs.length)
            }
        }
    },
    methods: {
        reduced: function (text, nb) {
            return reduceString(text, nb)
        },
        getGenres: function (genres) {
            let result = ''

            genres.forEach( (elem) => {
                result += `${elem}, `
            })

            return result.slice(0, -2)
        },
        searchThis: function (arg) {
            info.infos = {}
            this.show = false
            loader.show = true
            makeResearchOnMal(arg)
            lastPage = 'season'
        },
        getThisSeason: function (year, season) {
            this.infoWatcher = true
            self = this
            let newSeasonalInfo = malScraper.getSeason(year, season, function () {
                fillSeason(newSeasonalInfo)
            })
        }
    },
})

// Vue object to open the other pages
new Vue({
    el: '.mdl-navigation',
    methods: {
        getDownloader: function () {
            main.openDownloader()
        },
        getMainPage: function () {
            if (!loader.show)
            {
                releases.show = true
                news.show = false
                info.show = false
                season.show = false
            }
        },
        getNewsPage: function () {
            // For first time
            if (news.display === 'none')
                news.display = 'block'

            releases.show = false
            news.show = true
            loader.show = false
            season.show = false
            info.show = false
        },
        getSeasonPage: function () {
            // For first time
            if (season.display === 'none')
                season.display = 'block'

            releases.show = false
            news.show = false
            loader.show = false
            season.show = true
            info.show = false
        }
    }
})

// For the greeting's message
new Vue({
    el: '.greetings',
    data: {
        username: os.userInfo().username
    }
})

// Research button on top right
let searchButton = document.getElementById('fixed-header-drawer-exp')

searchButton.addEventListener('keydown', (key) => {
    if (key.keyCode === 13)
    {
        info.show = false
        if (searchButton.value.length > 3)
        {
            releases.show = false
            news.show = false
            season.show = false
            loader.show = true
            makeResearchOnMal(searchButton.value.toString())
        }
        searchButton.value = ''
    }
})<|endoftext|>