<commit_before>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2017 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined
            };
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                }
            }

            this._state.prcontrolsdisable = paragraph_locked || header_locked;

            var need_disable = paragraph_locked || in_equation || in_image || in_header;
            _.each (this.view.btnsNotes, function(item){
                item.setDisabled(need_disable);
            }, this);
        },

        onApiCanAddHyperlink: function(value) {
            var need_disable = !value || this._state.prcontrolsdisable;

            if ( this.toolbar.editMode ) {
                _.each (this.view.btnsHyperlink, function(item){
                    item.setDisabled(need_disable);
                }, this);
            }
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type){
            switch (type) {
                case 0:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 1:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    (hasTable) ? this.api.asc_SetTableOfContentsPr(props) : this.api.asc_AddTableOfContents(null, props);
                    break;
                case 'settings':
                    var props = this.api.asc_GetTableOfContentsPr(),
                        me = this;
                    var win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(null, value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    this.api.asc_RemoveTableOfContents();
                    break;
            }
        },

        onTableContentsUpdate: function(type){
            this.api.asc_UpdateTableOfContents(type == 'pages');
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    me.api.asc_AddFootnote(settings.custom);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        }

    }, DE.Controllers.Links || {}));
});<commit_msg>[DE] Fix adding table of contents.<commit_after>/*
 *
 * (c) Copyright Ascensio System Limited 2010-2017
 *
 * This program is a free software product. You can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License (AGPL)
 * version 3 as published by the Free Software Foundation. In accordance with
 * Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
 * that Ascensio System SIA expressly excludes the warranty of non-infringement
 * of any third-party rights.
 *
 * This program is distributed WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR  PURPOSE. For
 * details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
 *
 * You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
 * EU, LV-1021.
 *
 * The  interactive user interfaces in modified source and object code versions
 * of the Program must display Appropriate Legal Notices, as required under
 * Section 5 of the GNU AGPL version 3.
 *
 * Pursuant to Section 7(b) of the License you must retain the original Product
 * logo when distributing the program. Pursuant to Section 7(e) we decline to
 * grant you any rights under trademark law for use of our trademarks.
 *
 * All the Product's GUI elements, including illustrations and icon sets, as
 * well as technical writing content are licensed under the terms of the
 * Creative Commons Attribution-ShareAlike 4.0 International. See the License
 * terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
 *
 */

/**
 *  Links.js
 *
 *  Created by Julia Radzhabova on 22.12.2017
 *  Copyright (c) 2017 Ascensio System SIA. All rights reserved.
 *
 */

define([
    'core',
    'documenteditor/main/app/view/Links',
    'documenteditor/main/app/view/NoteSettingsDialog',
    'documenteditor/main/app/view/HyperlinkSettingsDialog',
    'documenteditor/main/app/view/TableOfContentsSettings'
], function () {
    'use strict';

    DE.Controllers.Links = Backbone.Controller.extend(_.extend({
        models : [],
        collections : [
        ],
        views : [
            'Links'
        ],
        sdkViewName : '#id_main',

        initialize: function () {

            this.addListeners({
                'Links': {
                    'links:contents': this.onTableContents,
                    'links:update': this.onTableContentsUpdate,
                    'links:notes': this.onNotesClick,
                    'links:hyperlink': this.onHyperlinkClick
                }
            });
        },
        onLaunch: function () {
            this._state = {
                prcontrolsdisable:undefined
            };
        },

        setApi: function (api) {
            if (api) {
                this.api = api;
                this.api.asc_registerCallback('asc_onFocusObject', this.onApiFocusObject.bind(this));
                this.api.asc_registerCallback('asc_onCanAddHyperlink',      _.bind(this.onApiCanAddHyperlink, this));
                this.api.asc_registerCallback('asc_onCoAuthoringDisconnect',_.bind(this.onCoAuthoringDisconnect, this));
                Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this));
            }
            return this;
        },

        setConfig: function(config) {
            this.toolbar = config.toolbar;
            this.view = this.createView('Links', {
                toolbar: this.toolbar.toolbar
            });
        },

        SetDisabled: function(state) {
            this.view && this.view.SetDisabled(state);
        },

        getView: function(name) {
            return !name && this.view ?
                this.view : Backbone.Controller.prototype.getView.call(this, name);
        },

        onCoAuthoringDisconnect: function() {
            this.SetDisabled(true);
        },

        onApiFocusObject: function(selectedObjects) {
            if (!this.toolbar.editMode) return;

            var pr, i = -1, type,
                paragraph_locked = false,
                header_locked = false,
                in_header = false,
                in_equation = false,
                in_image = false;

            while (++i < selectedObjects.length) {
                type = selectedObjects[i].get_ObjectType();
                pr   = selectedObjects[i].get_ObjectValue();

                if (type === Asc.c_oAscTypeSelectElement.Paragraph) {
                    paragraph_locked = pr.get_Locked();
                } else if (type === Asc.c_oAscTypeSelectElement.Header) {
                    header_locked = pr.get_Locked();
                    in_header = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Image) {
                    in_image = true;
                } else if (type === Asc.c_oAscTypeSelectElement.Math) {
                    in_equation = true;
                }
            }

            this._state.prcontrolsdisable = paragraph_locked || header_locked;

            var need_disable = paragraph_locked || in_equation || in_image || in_header;
            _.each (this.view.btnsNotes, function(item){
                item.setDisabled(need_disable);
            }, this);
        },

        onApiCanAddHyperlink: function(value) {
            var need_disable = !value || this._state.prcontrolsdisable;

            if ( this.toolbar.editMode ) {
                _.each (this.view.btnsHyperlink, function(item){
                    item.setDisabled(need_disable);
                }, this);
            }
        },

        onHyperlinkClick: function(btn) {
            var me = this,
                win, props, text;

            if (me.api){

                var handlerDlg = function(dlg, result) {
                    if (result == 'ok') {
                        props = dlg.getSettings();
                        (text!==false)
                            ? me.api.add_Hyperlink(props)
                            : me.api.change_Hyperlink(props);
                    }

                    Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                };

                text = me.api.can_AddHyperlink();

                if (text !== false) {
                    win = new DE.Views.HyperlinkSettingsDialog({
                        api: me.api,
                        handler: handlerDlg
                    });

                    props = new Asc.CHyperlinkProperty();
                    props.put_Text(text);

                    win.show();
                    win.setSettings(props);
                } else {
                    var selectedElements = me.api.getSelectedElements();
                    if (selectedElements && _.isArray(selectedElements)){
                        _.each(selectedElements, function(el, i) {
                            if (selectedElements[i].get_ObjectType() == Asc.c_oAscTypeSelectElement.Hyperlink)
                                props = selectedElements[i].get_ObjectValue();
                        });
                    }
                    if (props) {
                        win = new DE.Views.HyperlinkSettingsDialog({
                            api: me.api,
                            handler: handlerDlg
                        });
                        win.show();
                        win.setSettings(props);
                    }
                }
            }

            Common.component.Analytics.trackEvent('ToolBar', 'Add Hyperlink');
        },

        onTableContents: function(type){
            switch (type) {
                case 0:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(true);
                    props.put_RightAlignTab(true);
                    props.put_TabLeader( Asc.c_oAscTabLeader.Dot);
                    this.api.asc_AddTableOfContents(null, props);
                    break;
                case 1:
                    var props = this.api.asc_GetTableOfContentsPr(),
                        hasTable = !!props;
                    if (!props) {
                        props = new Asc.CTableOfContentsPr();
                        props.put_OutlineRange(1, 9);
                    }
                    props.put_Hyperlink(true);
                    props.put_ShowPageNumbers(false);
                    props.put_TabLeader( Asc.c_oAscTabLeader.None);
                    this.api.asc_AddTableOfContents(null, props);
                    break;
                case 'settings':
                    var props = this.api.asc_GetTableOfContentsPr(),
                        me = this;
                    var win = new DE.Views.TableOfContentsSettings({
                        api: this.api,
                        props: props,
                        handler: function(result, value) {
                            if (result == 'ok') {
                                (props) ? me.api.asc_SetTableOfContentsPr(value) : me.api.asc_AddTableOfContents(null, value);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        }
                    });
                    win.show();
                    break;
                case 'remove':
                    this.api.asc_RemoveTableOfContents();
                    break;
            }
        },

        onTableContentsUpdate: function(type){
            this.api.asc_UpdateTableOfContents(type == 'pages');
        },

        onNotesClick: function(type) {
            var me = this;
            switch (type) {
                case 'ins_footnote':
                    this.api.asc_AddFootnote();
                    break;
                case 'delele':
                    Common.UI.warning({
                        msg: this.view.confirmDeleteFootnotes,
                        buttons: ['yes', 'no'],
                        primary: 'yes',
                        callback: _.bind(function (btn) {
                            if (btn == 'yes') {
                                this.api.asc_RemoveAllFootnotes();
                            }
                            Common.NotificationCenter.trigger('edit:complete', this.toolbar);
                        }, this)
                    });
                    break;
                case 'settings':
                    (new DE.Views.NoteSettingsDialog({
                        api: me.api,
                        handler: function (result, settings) {
                            if (settings) {
                                me.api.asc_SetFootnoteProps(settings.props, settings.applyToAll);
                                if (result == 'insert')
                                    me.api.asc_AddFootnote(settings.custom);
                            }
                            Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                        },
                        props: me.api.asc_GetFootnoteProps()
                    })).show();
                    break;
                case 'prev':
                    this.api.asc_GotoFootnote(false);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
                case 'next':
                    this.api.asc_GotoFootnote(true);
                    setTimeout(function() {
                        Common.NotificationCenter.trigger('edit:complete', me.toolbar);
                    }, 50);
                    break;
            }
        }

    }, DE.Controllers.Links || {}));
});<|endoftext|>


<commit_before>/* eslint-disable import/no-named-as-default */
import { Component, PropTypes } from "react";
import { connect } from "react-redux";

import { actions as audioActions } from "../../../store/audio";

import Audio from "../../../libraries/players/audio";

// We only care about the audio state
function mapStateToProps(state) {
  return {
    audio: state.audio,
  };
}

@connect(mapStateToProps, audioActions)
export default class AudioPlayerUtility extends Component {

  static propTypes = {
    audio: PropTypes.object,
    loading: PropTypes.func,
    ready: PropTypes.func,
    play: PropTypes.func,
    setProgress: PropTypes.func,
    next: PropTypes.func,
    restart: PropTypes.func,
    seek: PropTypes.func,
    setPlaying: PropTypes.func,
  };

  componentWillUpdate(nextProps) {
    const nextAudio = nextProps.audio;
    const { audio } = this.props;

    // change of track to play
    if (audio.playing.track.title !== nextAudio.playing.track.title) {
      this.player = this.createPlayer(nextAudio.playing.track, nextAudio.state === "playing");
    }

    // change of state
    if (audio.state !== nextAudio.state && audio.state !== "default") {
      // play || pause
      if (
        (audio.state !== "next" && audio.state !== "previous") &&
        (nextAudio.state === "playing" || nextAudio.state === "paused")
      ) {
        this.toggle(nextAudio.state);
      }

      if (nextAudio.state === "next") {
        this.next();
        return;
      }

      if (nextAudio.state === "previous") {
        this.previous();
        return;
      }
    }

    // seeking
    if (audio.seek !== nextAudio.seek) {
      this.seek(nextAudio.seek);
    }
  }

  tracksWithFiles = () => {
    const { playlist } = this.props.audio;
    return _.filter(playlist, (track) => (
      track.file
    ));
  }

  createPlayer = (track, autoload) => {
    // only make sweet jams on client side
    if (typeof window === "undefined" || window === null) {
      return null;
    }

    if (!track.file) {
      return null;
    }

    if (this.player instanceof Audio && this.player.stop) {
      this.player.stop();
    }

    if (track.file.indexOf("http") === -1) {
      // eslint-disable-next-line no-param-reassign
      track.file = `https:${track.file}`;
    }

    // set loading state
    this.props.loading();

    // eslint-disable-next-line
    const Player = (Meteor.isCordova && cordova.platformId === "ios") ? Media : Audio;
    const getProps = () => this.props;

    const player = new Player(track.file, () => {
      // set ready state
      this.props.ready();

      if (autoload) {
        this.props.play();
        if (Meteor.isCordova) return;
        player.play();
        return;
      }
    }, () => {}, function audioEventStream(STATUS) {
      if (this.done) return;
      // this === Media object
      if (STATUS === Media.MEDIA_STOPPED) {
        const length = this.getDuration();
        if (length === player.getDuration()) { // reached the end of the song
          const { audio } = getProps();
          if (audio.repeat === "repeat-one") {
            this.seekTo(0);
            this.play();
            return;
          }
          for (const cb of this.endedCallbacks) cb();
          delete this.endedCallbacks;
          this.done = true;
        }
      }
    });

    if (autoload && Meteor.isCordova) {
      this.props.play();
      player.play();
    }

    player.timeupdate((pos) => {
      const [durMin, durSec] = track.duration.split(":");
      const length = Number((durMin * 60)) + Number(durSec);

      const [min, sec] = pos.split(":");
      const seekLength = Number((min * 60)) + Number(sec);
      const width = Number((length - (length - seekLength)) / length);

      // ensure seconds are not greater than 60
      const realSec = Number(sec) % 60;
      // if there is a minute, return that. else, caclulate minutes from seconds
      const realMin = Number(min) > 0 ? Number(min) : Math.floor(Number(sec) / 60);

      // ensure minutes and seconds are 0 padded
      const formatSec = realSec < 10 ? `0${realSec}` : realSec;
      const formatMin = realMin < 10 ? `0${realMin}` : realMin;

      const formatPos = `${formatMin}:${formatSec}`;

      if (this.file !== track.file) return;
      this.props.setProgress(width * 100, formatPos);
    });

    this.file = track.file;

    player.ended(() => {
      this.props.next();
    });

    return player;
  }

  toggle = (playerState) => {
    if (!this.player || !this.player.playPause) { return; }
    if (playerState === "playing") {
      this.player.play();
      return;
    }
    if (playerState === "paused") {
      this.player.pause();
      return;
    }
    this.player.playPause();
  }

  seek = (value) => {
    // value is percent of how far to scrub

    if (!this.player || !this.player.seekTo) { return; }

    const [min, sec] = this.props.audio.playing.track.duration.split(":");

    // duration in milliseconds
    const duration = (Number((min * 60)) + Number(sec)) * 1000;
    const newPos = duration * (value / 100);

    this.player.seekTo(newPos);
  }

  next = () => {
    // XXX Warning: leaving this out may break
    // this.player.release();

    const { playing, order, repeat } = this.props.audio;
    const playlist = this.tracksWithFiles();

    const currentTrack = playing.track.title;

    for (const track of playlist) {
      const index = playlist.indexOf(track);
      if (track.title === currentTrack) {
        let next;

        switch (order) {
          // eslint-disable-next-line no-case-declarations
          case "shuffle":
            let randomId = Math.floor(Math.random() * playlist.length);
            while (randomId === index) {
              randomId = Math.floor(Math.random() * playlist.length);
            }

            next = playlist[randomId];
            break;
          default:
            if ((playlist.length - 1) === index) {
              next = playlist[0];
            } else {
              next = playlist[index + 1];
            }
        }

        // This file needs cleanup
        // repeat one handled in creation of player above
        if (repeat === "repeat-one") {
          return;
        }

        this.props.setPlaying({
          track: next,
        });

        break;
      }
    }
  }

  previous = () => {
    // this.player.release();

    const { playing, order, repeat } = this.props.audio;
    const playlist = this.tracksWithFiles();

    const currentTrack = playing.track.title;

    for (const track of playlist) {
      const index = playlist.indexOf(track);
      if (track.title === currentTrack) {
        let prev;

        this.setState({ force: false });

        switch (order) {
          // eslint-disable-next-line no-case-declarations
          case "shuffle":

            let randomId = Math.floor(Math.random() * playlist.length);
            while (randomId === index) {
              randomId = Math.floor(Math.random() * playlist.length);
            }

            prev = playlist[randomId];
            break;
          default:
            if (index === 0) {
              prev = playlist[playlist.length - 1];
            } else {
              prev = playlist[index - 1];
            }
        }

        if (repeat === "repeat-one") {
          this.props.restart();
          this.props.play();
          if (this.player && Meteor.isCordova) {
            this.props.seek(0);
            this.player.play();
          }
          return;
        }

        this.props.setPlaying({
          track: prev,
        });
        break;
      }
    }
  };

  render() {
    return <span />;
  }
}
<commit_msg>Fixing repeat one issue when you come to the end of the track, or click next<commit_after>/* eslint-disable import/no-named-as-default */
import { Component, PropTypes } from "react";
import { connect } from "react-redux";

import { actions as audioActions } from "../../../store/audio";

import Audio from "../../../libraries/players/audio";

// We only care about the audio state
function mapStateToProps(state) {
  return {
    audio: state.audio,
  };
}

@connect(mapStateToProps, audioActions)
export default class AudioPlayerUtility extends Component {

  static propTypes = {
    audio: PropTypes.object,
    loading: PropTypes.func,
    ready: PropTypes.func,
    play: PropTypes.func,
    setProgress: PropTypes.func,
    next: PropTypes.func,
    restart: PropTypes.func,
    seek: PropTypes.func,
    setPlaying: PropTypes.func,
  };

  componentWillUpdate(nextProps) {
    const nextAudio = nextProps.audio;
    const { audio } = this.props;

    // change of track to play
    if (audio.playing.track.title !== nextAudio.playing.track.title) {
      this.player = this.createPlayer(nextAudio.playing.track, nextAudio.state === "playing");
    }

    // change of state
    if (audio.state !== nextAudio.state && audio.state !== "default") {
      // play || pause
      if (
        (audio.state !== "next" && audio.state !== "previous") &&
        (nextAudio.state === "playing" || nextAudio.state === "paused")
      ) {
        this.toggle(nextAudio.state);
      }

      if (nextAudio.state === "next") {
        this.next();
        return;
      }

      if (nextAudio.state === "previous") {
        this.previous();
        return;
      }
    }

    // seeking
    if (audio.seek !== nextAudio.seek) {
      this.seek(nextAudio.seek);
    }
  }

  tracksWithFiles = () => {
    const { playlist } = this.props.audio;
    return _.filter(playlist, (track) => (
      track.file
    ));
  }

  createPlayer = (track, autoload) => {
    // only make sweet jams on client side
    if (typeof window === "undefined" || window === null) {
      return null;
    }

    if (!track.file) {
      return null;
    }

    if (this.player instanceof Audio && this.player.stop) {
      this.player.stop();
    }

    if (track.file.indexOf("http") === -1) {
      // eslint-disable-next-line no-param-reassign
      track.file = `https:${track.file}`;
    }

    // set loading state
    this.props.loading();

    // eslint-disable-next-line
    const Player = (Meteor.isCordova && cordova.platformId === "ios") ? Media : Audio;
    const getProps = () => this.props;

    const player = new Player(track.file, () => {
      // set ready state
      this.props.ready();

      if (autoload) {
        this.props.play();
        if (Meteor.isCordova) return;
        player.play();
        return;
      }
    }, () => {}, function audioEventStream(STATUS) {
      if (this.done) return;
      // this === Media object
      if (STATUS === Media.MEDIA_STOPPED) {
        const length = this.getDuration();
        if (length === player.getDuration()) { // reached the end of the song
          const { audio } = getProps();
          if (audio.repeat === "repeat-one") {
            this.seekTo(0);
            this.play();
            return;
          }
          for (const cb of this.endedCallbacks) cb();
          delete this.endedCallbacks;
          this.done = true;
        }
      }
    });

    if (autoload && Meteor.isCordova) {
      this.props.play();
      player.play();
    }

    player.timeupdate((pos) => {
      const [durMin, durSec] = track.duration.split(":");
      const length = Number((durMin * 60)) + Number(durSec);

      const [min, sec] = pos.split(":");
      const seekLength = Number((min * 60)) + Number(sec);
      const width = Number((length - (length - seekLength)) / length);

      // ensure seconds are not greater than 60
      const realSec = Number(sec) % 60;
      // if there is a minute, return that. else, caclulate minutes from seconds
      const realMin = Number(min) > 0 ? Number(min) : Math.floor(Number(sec) / 60);

      // ensure minutes and seconds are 0 padded
      const formatSec = realSec < 10 ? `0${realSec}` : realSec;
      const formatMin = realMin < 10 ? `0${realMin}` : realMin;

      const formatPos = `${formatMin}:${formatSec}`;

      if (this.file !== track.file) return;
      this.props.setProgress(width * 100, formatPos);
    });

    this.file = track.file;

    player.ended(() => {
      this.props.next();
    });

    return player;
  }

  toggle = (playerState) => {
    if (!this.player || !this.player.playPause) { return; }
    if (playerState === "playing") {
      this.player.play();
      return;
    }
    if (playerState === "paused") {
      this.player.pause();
      return;
    }
    this.player.playPause();
  }

  seek = (value) => {
    // value is percent of how far to scrub

    if (!this.player || !this.player.seekTo) { return; }

    const [min, sec] = this.props.audio.playing.track.duration.split(":");

    // duration in milliseconds
    const duration = (Number((min * 60)) + Number(sec)) * 1000;
    const newPos = duration * (value / 100);

    this.player.seekTo(newPos);
  }

  next = () => {
    // XXX Warning: leaving this out may break
    // this.player.release();

    const { playing, order, repeat } = this.props.audio;
    const playlist = this.tracksWithFiles();

    const currentTrack = playing.track.title;

    for (const track of playlist) {
      const index = playlist.indexOf(track);
      if (track.title === currentTrack) {
        let next;

        switch (order) {
          // eslint-disable-next-line no-case-declarations
          case "shuffle":
            let randomId = Math.floor(Math.random() * playlist.length);
            while (randomId === index) {
              randomId = Math.floor(Math.random() * playlist.length);
            }

            next = playlist[randomId];
            break;
          default:
            if ((playlist.length - 1) === index) {
              next = playlist[0];
            } else {
              next = playlist[index + 1];
            }
        }

        // This file needs cleanup
        // repeat one handled in creation of player above
        if (repeat === "repeat-one") {
          this.props.restart();
          this.props.play();
          if (this.player && Meteor.isCordova) {
            this.props.seek(0);
            this.player.play();
          }
          return;
        }

        this.props.setPlaying({
          track: next,
        });

        break;
      }
    }
  }

  previous = () => {
    // this.player.release();

    const { playing, order, repeat } = this.props.audio;
    const playlist = this.tracksWithFiles();

    const currentTrack = playing.track.title;

    for (const track of playlist) {
      const index = playlist.indexOf(track);
      if (track.title === currentTrack) {
        let prev;

        this.setState({ force: false });

        switch (order) {
          // eslint-disable-next-line no-case-declarations
          case "shuffle":

            let randomId = Math.floor(Math.random() * playlist.length);
            while (randomId === index) {
              randomId = Math.floor(Math.random() * playlist.length);
            }

            prev = playlist[randomId];
            break;
          default:
            if (index === 0) {
              prev = playlist[playlist.length - 1];
            } else {
              prev = playlist[index - 1];
            }
        }

        if (repeat === "repeat-one") {
          this.props.restart();
          this.props.play();
          if (this.player && Meteor.isCordova) {
            this.props.seek(0);
            this.player.play();
          }
          return;
        }

        this.props.setPlaying({
          track: prev,
        });
        break;
      }
    }
  };

  render() {
    return <span />;
  }
}
<|endoftext|>


<commit_before>define(['app/geometry', 'app/view', 'data/pubs'],
function (geo, View, pubsData) {

    function createLink(url, text)
    {
        return "" + text + "";
    }

    function bubbleHtml(pub)
    {
        var text = "" + pub.name + "";
        if ("link" in pub && pub.link != "")
            text = createLink(pub.link, text);
        if ("comment" in pub && pub.comment)
            text += "
" + pub.comment + "";
        if ("price" in pub && pub.price > 0)
            text += "
Price: £" + pub.price.toFixed(2);
        if ("previous" in pub) {
            var previous = [];
            for (var i = pub.previous.length-1; i >= 0; --i) {
                previous.push(createLink(pub.previous[i].link, pub.previous[i].name));
            }
            text += "
Previously known as " + previous.join(', ') + ".";
        }
        if ("tags" in pub && pub.tags.length > 0) {
            text += "
Tags: " + pub.tags.join(', ');
        }
        return text;
    }

    function setStatusMessage(html)
    {
        document.getElementById('message').innerHTML = html;
    }

    function displayStats(stats)
    {
        setStatusMessage(
            stats.name + ":
"
            + "Low (green): " + stats.format(stats.low) + "
"
            + "Median (blue): " + stats.format(stats.median) + "
"
            + "High (red): " + stats.format(stats.high)
        );
    }

    function round(value)
    {
        return Math.floor(value + 0.5);
    }

    /**
     * @param {number} r - Red component in range 0 to 255.
     * @param {number} g - Green component in range 0 to 255.
     * @param {number} b - Blue component in range 0 to 255.
     * @constructor
     */
    function Colour(r, g, b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    function interpolate(t, a, b)
    {
        return a + t * (b - a);
    }

    /**
     * Interpolate between two colours.
     *
     * @param {number} t - Interpolation value in range 0 to 1.
     * @param {Colour} low - Colour for t = 0.
     * @param {Colour} high - Colour for t = 1.
     */
    function interpolateColour(t, low, high)
    {
        return new Colour(
            interpolate(t, low.r, high.r),
            interpolate(t, low.g, high.g),
            interpolate(t, low.b, high.b)
        );
    }

    /**
     * Convert to HTML #RRGGBB format.
     *
     * @returns {string}
     */
    Colour.prototype.toString = function()
    {
        var intColour = (round(this.r) << 16) + (round(this.g) << 8) + round(this.b);
        return '#' + ('000000' + intColour.toString(16)).slice(-6);
    };

    /**
     * Maps three colours to the low, median and high value range.
     *
     * @param {number} value
     * @param {{value:number, colour:Colour}} low
     * @param {{value:number, colour:Colour}} median
     * @param {{value:number, colour:Colour}} high
     * @param {Colour} outOfRange
     * @returns {Colour}
     */
    function colourDualLinear(value, low, median, high, outOfRange)
    {
        var colour;
        if (value < low.value || value > high.value) {
            colour = outOfRange;
        } else if (value < median.value) {
            colour = interpolateColour(
                (value - low.value) / (median.value-low.value),
                low.colour,
                median.colour
            );
        } else {
            colour = interpolateColour(
                (value - median.value) / (high.value-median.value),
                median.colour,
                high.colour
            );
        }
        return colour;
    }

    function priceStats(pubs)
    {
        var low = Number.MAX_VALUE;
        var high = Number.MIN_VALUE;
        var prices = [];
        pubs.forEach(function(pub)
        {
            if ("price" in pub && pub.price > 0) {
                low = Math.min(low, pub.price);
                high = Math.max(high, pub.price);
                prices.push(pub.price);
            }
        });
        prices.sort();
        return {
            name: 'Prices',
            low: low,
            high: high,
            median: prices[Math.floor(prices.length/2)],
            format:function(value) { return "£" + value.toFixed(2); }
        }
    }

    function hasTag(pub, tags)
    {
        return "tags" in pub && pub.tags.some(function(pubTag) {
            return tags.some(function(tag) {
                return tag == pubTag;
            });
        });
    }

    function formatPubForView(pub, stats)
    {
        return {
            name: pub.name,
            lat: pub.lat,
            lon: pub.lon,
            html: bubbleHtml(pub),
            colour: colourDualLinear(
                pub.price,
                {value:stats.low, colour:new Colour(0,255,0)},
                {value:stats.median, colour:new Colour(0,0,255)},
                {value:stats.high, colour:new Colour(255,0,0)},
                new Colour(64,64,64)
            ).toString()
        };
    }

    function initialiseMap()
    {
        setStatusMessage("Calculating...");

        var origin = new geo.GeoCoord(55.94816654144937, -3.1994622945785522);
        var circleRadiusMetres = 1609;

        var view = new View("map");
        view.setTarget(origin, circleRadiusMetres);

        var stats = priceStats(pubsData);

        var todo = [];
        var blogged = [];
        var excluded = [];

        pubsData.forEach(function(pub) {
            if (pub.link) {
                blogged.push(formatPubForView(pub, stats));
            } else if (hasTag(pub, ['Disqualified','Closed','Student union','Club','Restaurant'])) {
                excluded.push(formatPubForView(pub, stats));
            } else {
                todo.push(formatPubForView(pub, stats));
            }
        });

        view.addPinsLayer(todo, "Todo (yellow)", "gold", true);
        view.addPinsLayer(blogged, "Visited (green)", "green", true);
        view.addPinsLayer(excluded, "Excluded (red)", "red", false);

        displayStats(stats);

        var sites = geo.earthSurfaceVoronoi(blogged, origin, circleRadiusMetres);
        view.addVoronoiCellsLayer(
            sites.map(function(site) {
                return {
                    polygon: site.polygon,
                    colour: site.loc.colour
                };
            }),
            stats.name
        );
    }

    initialiseMap();
});
<commit_msg>Tidy up statstics a bit.<commit_after>define(['app/geometry', 'app/view', 'data/pubs'],
function (geo, View, pubsData) {

    function createLink(url, text)
    {
        return "" + text + "";
    }

    function bubbleHtml(pub)
    {
        var text = "" + pub.name + "";
        if ("link" in pub && pub.link != "")
            text = createLink(pub.link, text);
        if ("comment" in pub && pub.comment)
            text += "
" + pub.comment + "";
        if ("price" in pub && pub.price > 0)
            text += "
Price: £" + pub.price.toFixed(2);
        if ("previous" in pub) {
            var previous = [];
            for (var i = pub.previous.length-1; i >= 0; --i) {
                previous.push(createLink(pub.previous[i].link, pub.previous[i].name));
            }
            text += "
Previously known as " + previous.join(', ') + ".";
        }
        if ("tags" in pub && pub.tags.length > 0) {
            text += "
Tags: " + pub.tags.join(', ');
        }
        return text;
    }

    function setStatusMessage(html)
    {
        document.getElementById('message').innerHTML = html;
    }

    function displayStats(stats)
    {
        setStatusMessage(
            stats.name + ":
"
            + "Low (green): " + stats.lowString + "
"
            + "Median (blue): " + stats.medianString + "
"
            + "High (red): " + stats.highString
        );
    }

    function round(value)
    {
        return Math.floor(value + 0.5);
    }

    /**
     * @param {number} r - Red component in range 0 to 255.
     * @param {number} g - Green component in range 0 to 255.
     * @param {number} b - Blue component in range 0 to 255.
     * @constructor
     */
    function Colour(r, g, b) {
        this.r = r;
        this.g = g;
        this.b = b;
    }

    function interpolate(t, a, b)
    {
        return a + t * (b - a);
    }

    /**
     * Interpolate between two colours.
     *
     * @param {number} t - Interpolation value in range 0 to 1.
     * @param {Colour} low - Colour for t = 0.
     * @param {Colour} high - Colour for t = 1.
     */
    function interpolateColour(t, low, high)
    {
        return new Colour(
            interpolate(t, low.r, high.r),
            interpolate(t, low.g, high.g),
            interpolate(t, low.b, high.b)
        );
    }

    /**
     * Convert to HTML #RRGGBB format.
     *
     * @returns {string}
     */
    Colour.prototype.toString = function()
    {
        var intColour = (round(this.r) << 16) + (round(this.g) << 8) + round(this.b);
        return '#' + ('000000' + intColour.toString(16)).slice(-6);
    };

    /**
     * Maps three colours to the low, median and high value range.
     *
     * @param {number} value
     * @param {{value:number, colour:Colour}} low
     * @param {{value:number, colour:Colour}} median
     * @param {{value:number, colour:Colour}} high
     * @param {Colour} outOfRange
     * @returns {Colour}
     */
    function colourDualLinear(value, low, median, high, outOfRange)
    {
        var colour;
        if (value < low.value || value > high.value) {
            colour = outOfRange;
        } else if (value < median.value) {
            colour = interpolateColour(
                (value - low.value) / (median.value-low.value),
                low.colour,
                median.colour
            );
        } else {
            colour = interpolateColour(
                (value - median.value) / (high.value-median.value),
                median.colour,
                high.colour
            );
        }
        return colour;
    }

    /**
     * Extract some statistics about some values within a list of objects.
     *
     * @param {Object[]} objects
     * @param {string} key
     * @param {string} name
     * @param {function} format
     * @returns {{low:*, high:*, median:*, name:string, lowString:string, medianString:string, highString:string}}
     */
    function createStatistics(objects, key, name, format)
    {
        var values = objects
            .filter(function(pub){return (key in pub) && (pub[key] > 0);})
            .map(function(x){return x[key];})
            .sort();

        var low = values[0];
        var high = values[values.length-1];
        var median = values[Math.floor(values.length/2)];

        return {
            low: low,
            high: high,
            median: median,
            name: name,
            lowString: format(low),
            medianString: format(median),
            highString: format(high)
        };
    }

    function hasTag(pub, tags)
    {
        return "tags" in pub && pub.tags.some(function(pubTag) {
            return tags.some(function(tag) {
                return tag == pubTag;
            });
        });
    }

    function formatPubForView(pub, stats)
    {
        return {
            name: pub.name,
            lat: pub.lat,
            lon: pub.lon,
            html: bubbleHtml(pub),
            colour: colourDualLinear(
                pub.price,
                {value:stats.low, colour:new Colour(0,255,0)},
                {value:stats.median, colour:new Colour(0,0,255)},
                {value:stats.high, colour:new Colour(255,0,0)},
                new Colour(64,64,64)
            ).toString()
        };
    }

    function initialiseMap()
    {
        setStatusMessage("Calculating...");

        var origin = new geo.GeoCoord(55.94816654144937, -3.1994622945785522);
        var circleRadiusMetres = 1609;

        var view = new View("map");
        view.setTarget(origin, circleRadiusMetres);

        var stats = createStatistics(pubsData, 'price', 'Prices', function(x){ return "£"+x.toFixed(2); });

        var todo = [];
        var blogged = [];
        var excluded = [];

        pubsData.forEach(function(pub) {
            if (pub.link) {
                blogged.push(formatPubForView(pub, stats));
            } else if (hasTag(pub, ['Disqualified','Closed','Student union','Club','Restaurant'])) {
                excluded.push(formatPubForView(pub, stats));
            } else {
                todo.push(formatPubForView(pub, stats));
            }
        });

        view.addPinsLayer(todo, "Todo (yellow)", "gold", true);
        view.addPinsLayer(blogged, "Visited (green)", "green", true);
        view.addPinsLayer(excluded, "Excluded (red)", "red", false);

        displayStats(stats);

        var sites = geo.earthSurfaceVoronoi(blogged, origin, circleRadiusMetres);
        view.addVoronoiCellsLayer(
            sites.map(function(site) {
                return {
                    polygon: site.polygon,
                    colour: site.loc.colour
                };
            }),
            stats.name
        );
    }

    initialiseMap();
});
<|endoftext|>


<commit_before>'use strict';
angular.module('bucketlist.controllers', ['ngMaterial'])
.controller('AuthController', ['$rootScope', '$scope', '$state', '$localStorage', 'Toast', 'BucketListService',
    function AuthController($rootScope, $scope, $state, $localStorage, Toast, BucketListService) {
        $scope.user = {};
        $scope.user.signup = true;

        $scope.navLogin = function () {
            $scope.user.login = true;
            $scope.user.signup = false;
        }
        $scope.navSignup = function () {
            $scope.user.login = false;
            $scope.user.signup = true;
        }

        /**
         * This function handles the a user login using the bucketlist service
         *
         */
        $scope.login = function () {
            var data = {username: $scope.user.username, password: $scope.user.password};
            BucketListService.auth.login(data).$promise.then(function(response){
                $localStorage.token = response.token;
                $localStorage.authenticated = true;
                $localStorage.currentUser = $scope.user.username;
                $localStorage.currentUserid = response.id;

                $state.go('dashboard');
                Toast.show('Welcome ' + $scope.user.username);
            })
            .catch(function(responseError){
                Toast.show('Incorrect credentials! Please try again.')
            });

        };

        /**
         * This function handles a new user registration using a create promise
         *
         */
        $scope.register = function () {
            var data = {username: $scope.user.username, password: $scope.user.password};
            BucketListService.users.create(data).$promise.then($scope.login)
            .catch(function(responseError){
                Toast.show('Oops! User can\'t be registered')
            });
        };
    }])

.controller('BucketListController', ['$rootScope', '$scope', '$state', '$localStorage', '$stateParams', 'Toast', '$mdSidenav', '$timeout', 'BucketListService', '$mdDialog', 'Menu',
    function BucketListController($rootScope, $scope, $state, $localStorage, $stateParams, Toast, $mdSidenav, $timeout, BucketListService, $mdDialog, Menu) {

    $scope.selectedBucket = {};
    $scope.editbucket = {};

    $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
    $scope.$on('updateBucketList', function() {
        $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
    });

    $scope.selectBucketlist = function (bucketlist) {
        $scope.selectedBucket = bucketlist
        $scope.editbucket.enabled = false;
    };

    $scope.toggleLeft = Menu.toggle('left');
    $scope.close = function () {
        $mdSidenav('left').close()
            .then(function () {});
    }
    // create a bucketlist using the provided name
    $scope.createBucketlist = function () {
        var data = { name: $scope.newbucket.name };
        BucketListService.Bucketlists.createBucket(data).$promise.then(
            function(response) {
                // emit the trigger to a fresh UI update
                $scope.$emit('updateBucketList');
                console.log(JSON.stringify(response));
                Toast.show('Yeiy! Bucketlist created successfully!')
                // nullify the new bucketlist object
                $scope.newbucket.name = null;
            }, function() {
                Toast.show('Oops! There is a bucket with the same name.')
            })
        }

    $scope.toggleUpdate = function () {
        if (!$scope.editbucket.enabled) {
            $scope.editbucket.enabled = true;
        }
        else{
            $scope.editbucket.enabled = undefined;
        }
    }

    $scope.cancelEdit = function () {
        $scope.editbucket.enabled = false;
    }
    $scope.updateBucket = function (bucketlist) {
        var data = { name: $scope.editbucket.name, id: bucketlist.id}
        BucketListService.Bucketlists.updateBucket(data).$promise
        .then(function (response) {
            Toast.show("Your bucketlist has been updated");
            $scope.$emit('updateBucketList');
            $scope.editbucket.enabled = false;
            delete $scope.selectedBucket;
            $scope.selectBucketlist(bucketlist);
        })
    };

    $scope.showConfirm = function(ev, bucketlist) {
        // Appending dialog to document.body to cover sidenav in docs app
        var confirm = $mdDialog.confirm()
              .title('DELETE BUCKETLIST')
              .textContent('Are you sure you want to delete this bucketlist? This cannot be undone.')
              .ariaLabel('Lucky day')
              .targetEvent(ev)
              .ok('YES, DELETE IT')
              .cancel('CANCEL');
        $mdDialog.show(confirm).then(function() {
            bucketlist.$deleteBucket().then(function() {
                delete $scope.selectedBucket;
                $scope.$emit('updateBucketList');
                Toast.show('Bucketlist deleted successfully')
            });
        }, function() {});
    };
}])

.controller('BucketListViewController', ['$rootScope', '$scope', '$state', '$localStorage', '$stateParams', 'Toast', 'Menu', 'BucketListService', '$mdSidenav', '$mdDialog',
    function BucketListViewController ($rootScope, $scope, $state, $localStorage, $stateParams, Toast, Menu, BucketListService, $mdSidenav, $mdDialog) {

        $scope.selectedBucket = {};
        $scope.newitem = {};
        $scope.selectBucketlist = function (bucketlist) {
            $state.go('viewBucket', {id: bucketlist.id});
        };

        $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
        $scope.$on('updateBucketlistItems', function () {
            console.log("HERE IS THE STATE PARAM " + $stateParams.id);
            $scope.bucket = BucketListService.Bucketlists.getOneBucket({
                id: $stateParams.id
            });
        });

        $scope.bucket = BucketListService.Bucketlists.getOneBucket({
            id: $stateParams.id
        });
        console.log(JSON.stringify($scope.bucket));

        $scope.createBucketItem = function (params) {
            var data = angular.extend({}, params);
            data.name = $scope.newitem.name;
            BucketListService.BucketlistItems.createBucketItem(data)
            .$promise.then(function(response) {
                    console.log(JSON.stringify(response));
                    $scope.newitem.name = null
                    $scope.$emit('updateBucketlistItems');
                    Toast.show('Item created successfully');
                }, function(error) {
                    //creating an item failed
                    console.log(JSON.stringify(error));
                    if (error.status == 400) {
                        Toast.show('An item with the same name already exists');
                    }
                    else if (error.status == -1) {
                        Toast.show('You are disconnected from the server. Please ensure you have an internet connection.')
                    }
                    else {
                        Toast.show('Unable to create item. Please try again')
                    }
                })
        };

        $scope.toggleDone = function (item, itemId, bucketlistId) {
            var data = {
                done: item.done, name: item.name,
                id: itemId, bucketlist: bucketlistId, bid: bucketlistId
            };
            BucketListService.BucketlistItems.updateBucketItem(data)
            .$promise.then(function(response) {
                Toast.show("Your item has been updated.");
            })
        };

        $scope.deleteBucketItem = function(ev, bucketlist) {
            // Appending dialog to document.body to cover sidenav in docs app
            var confirm = $mdDialog.confirm()
                  .title('DELETE BUCKETLIST')
                  .textContent('Are you sure you want to delete this bucketlist item? This cannot be undone.')
                  .ariaLabel('Lucky day')
                  .targetEvent(ev)
                  .ok('YES, DELETE ITEM')
                  .cancel('CANCEL');
            $mdDialog.show(confirm).then(function() {
                BucketListService.BucketlistItems.deleteBucketItem(bucketlist)
                .$promise.then(function (response) {
                    $scope.$emit('updateBucketlistItems');
                    Toast.show('Bucketlist Item Deleted successfully.');
                }, function (response_error) {
                    // Failed to delete item
                    console.log(JSON.stringify(response_error));
                    Toast.show('Could not delete item. Please try again.');
                });
            }, function() {});
        };

        $scope.toggleLeft = Menu.toggle('left');
        $scope.close = function () {
        $mdSidenav('left').close()
            .then(function () {});
    }
    }])
<commit_msg>(chore) document the bucketlist update function<commit_after>'use strict';
angular.module('bucketlist.controllers', ['ngMaterial'])
.controller('AuthController', ['$rootScope', '$scope', '$state', '$localStorage', 'Toast', 'BucketListService',
    function AuthController($rootScope, $scope, $state, $localStorage, Toast, BucketListService) {
        $scope.user = {};
        $scope.user.signup = true;

        $scope.navLogin = function () {
            $scope.user.login = true;
            $scope.user.signup = false;
        }
        $scope.navSignup = function () {
            $scope.user.login = false;
            $scope.user.signup = true;
        }

        /**
         * This function handles the a user login using the bucketlist service
         *
         */
        $scope.login = function () {
            var data = {username: $scope.user.username, password: $scope.user.password};
            BucketListService.auth.login(data).$promise.then(function(response){
                $localStorage.token = response.token;
                $localStorage.authenticated = true;
                $localStorage.currentUser = $scope.user.username;
                $localStorage.currentUserid = response.id;

                $state.go('dashboard');
                Toast.show('Welcome ' + $scope.user.username);
            })
            .catch(function(responseError){
                Toast.show('Incorrect credentials! Please try again.')
            });

        };

        /**
         * This function handles a new user registration using a create promise
         *
         */
        $scope.register = function () {
            var data = {username: $scope.user.username, password: $scope.user.password};
            BucketListService.users.create(data).$promise.then($scope.login)
            .catch(function(responseError){
                Toast.show('Oops! User can\'t be registered')
            });
        };
    }])

.controller('BucketListController', ['$rootScope', '$scope', '$state', '$localStorage', '$stateParams', 'Toast', '$mdSidenav', '$timeout', 'BucketListService', '$mdDialog', 'Menu',
    function BucketListController($rootScope, $scope, $state, $localStorage, $stateParams, Toast, $mdSidenav, $timeout, BucketListService, $mdDialog, Menu) {

    $scope.selectedBucket = {};
    $scope.editbucket = {};

    $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
    $scope.$on('updateBucketList', function() {
        $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
    });

    $scope.selectBucketlist = function (bucketlist) {
        $scope.selectedBucket = bucketlist
        $scope.editbucket.enabled = false;
    };

    $scope.toggleLeft = Menu.toggle('left');
    $scope.close = function () {
        $mdSidenav('left').close()
            .then(function () {});
    }
    // create a bucketlist using the provided name
    $scope.createBucketlist = function () {
        var data = { name: $scope.newbucket.name };
        BucketListService.Bucketlists.createBucket(data).$promise.then(
            function(response) {
                // emit the trigger to a fresh UI update
                $scope.$emit('updateBucketList');
                console.log(JSON.stringify(response));
                Toast.show('Yeiy! Bucketlist created successfully!')
                // nullify the new bucketlist object
                $scope.newbucket.name = null;
            }, function() {
                Toast.show('Oops! There is a bucket with the same name.')
            })
        }

    $scope.toggleUpdate = function () {
        if (!$scope.editbucket.enabled) {
            $scope.editbucket.enabled = true;
        }
        else{
            $scope.editbucket.enabled = undefined;
        }
    }

    $scope.cancelEdit = function () {
        $scope.editbucket.enabled = false;
    }

    /**
     * This function updates an existing bucketlist.
     * @param  {object} bucketlist The bucketlist object
     */
    $scope.updateBucket = function (bucketlist) {
        var data = { name: $scope.editbucket.name, id: bucketlist.id}
        BucketListService.Bucketlists.updateBucket(data).$promise
        .then(function (response) {
            Toast.show("Your bucketlist has been updated");
            $scope.$emit('updateBucketList');
            $scope.editbucket.enabled = false;
            delete $scope.selectedBucket;
            $scope.selectBucketlist(bucketlist);
        })
    };

    $scope.showConfirm = function(ev, bucketlist) {
        // Appending dialog to document.body to cover sidenav in docs app
        var confirm = $mdDialog.confirm()
              .title('DELETE BUCKETLIST')
              .textContent('Are you sure you want to delete this bucketlist? This cannot be undone.')
              .ariaLabel('Lucky day')
              .targetEvent(ev)
              .ok('YES, DELETE IT')
              .cancel('CANCEL');
        $mdDialog.show(confirm).then(function() {
            bucketlist.$deleteBucket().then(function() {
                delete $scope.selectedBucket;
                $scope.$emit('updateBucketList');
                Toast.show('Bucketlist deleted successfully')
            });
        }, function() {});
    };
}])

.controller('BucketListViewController', ['$rootScope', '$scope', '$state', '$localStorage', '$stateParams', 'Toast', 'Menu', 'BucketListService', '$mdSidenav', '$mdDialog',
    function BucketListViewController ($rootScope, $scope, $state, $localStorage, $stateParams, Toast, Menu, BucketListService, $mdSidenav, $mdDialog) {

        $scope.selectedBucket = {};
        $scope.newitem = {};
        $scope.selectBucketlist = function (bucketlist) {
            $state.go('viewBucket', {id: bucketlist.id});
        };

        $scope.bucketlists = BucketListService.Bucketlists.getAllBuckets();
        $scope.$on('updateBucketlistItems', function () {
            console.log("HERE IS THE STATE PARAM " + $stateParams.id);
            $scope.bucket = BucketListService.Bucketlists.getOneBucket({
                id: $stateParams.id
            });
        });

        $scope.bucket = BucketListService.Bucketlists.getOneBucket({
            id: $stateParams.id
        });
        console.log(JSON.stringify($scope.bucket));

        $scope.createBucketItem = function (params) {
            var data = angular.extend({}, params);
            data.name = $scope.newitem.name;
            BucketListService.BucketlistItems.createBucketItem(data)
            .$promise.then(function(response) {
                    console.log(JSON.stringify(response));
                    $scope.newitem.name = null
                    $scope.$emit('updateBucketlistItems');
                    Toast.show('Item created successfully');
                }, function(error) {
                    //creating an item failed
                    console.log(JSON.stringify(error));
                    if (error.status == 400) {
                        Toast.show('An item with the same name already exists');
                    }
                    else if (error.status == -1) {
                        Toast.show('You are disconnected from the server. Please ensure you have an internet connection.')
                    }
                    else {
                        Toast.show('Unable to create item. Please try again')
                    }
                })
        };

        $scope.toggleDone = function (item, itemId, bucketlistId) {
            var data = {
                done: item.done, name: item.name,
                id: itemId, bucketlist: bucketlistId, bid: bucketlistId
            };
            BucketListService.BucketlistItems.updateBucketItem(data)
            .$promise.then(function(response) {
                Toast.show("Your item has been updated.");
            })
        };

        $scope.deleteBucketItem = function(ev, bucketlist) {
            // Appending dialog to document.body to cover sidenav in docs app
            var confirm = $mdDialog.confirm()
                  .title('DELETE BUCKETLIST')
                  .textContent('Are you sure you want to delete this bucketlist item? This cannot be undone.')
                  .ariaLabel('Lucky day')
                  .targetEvent(ev)
                  .ok('YES, DELETE ITEM')
                  .cancel('CANCEL');
            $mdDialog.show(confirm).then(function() {
                BucketListService.BucketlistItems.deleteBucketItem(bucketlist)
                .$promise.then(function (response) {
                    $scope.$emit('updateBucketlistItems');
                    Toast.show('Bucketlist Item Deleted successfully.');
                }, function (response_error) {
                    // Failed to delete item
                    console.log(JSON.stringify(response_error));
                    Toast.show('Could not delete item. Please try again.');
                });
            }, function() {});
        };

        $scope.toggleLeft = Menu.toggle('left');
        $scope.close = function () {
        $mdSidenav('left').close()
            .then(function () {});
    }
    }])
<|endoftext|>


<commit_before>// util.js
// This contains various utilities used throughout the code.

import dnt from '/imports/lib/dnt';
import rx from '/imports/common/rx';
import select2 from '/imports/lib/select2';
import userMsg from '/imports/common/userMsg';

exports.get_username = function (callback) {

    // Callback will be called with one parameter: the username or undefined
    Meteor.call('get_username', function (error, results) {
        if (error || !results) {
            callback(undefined);
        } else {
            callback(results);
        }
    });
}

exports.isValidFileName = function (name) {
    var index = name.search(/[^A-Za-z0-9_\-\.]/g);
    return (index < 0);
}

exports.clean_file_name = function (dirty) {

    // Make a directory or file name out of some string
    // Valid characters:
    //     a-z, A-Z, 0-9, dash (-), dot (.), underscore (_)
    // All other characters are replaced with underscores.

    if (!dirty) { return undefined; }

    return dirty.replace(/[^A-Za-z0-9_\-\.]/g, "_");
}

exports.getDataType = function (layerName) {
    let dataType = layers[layerName.dataType];
    if (dataType === undefined) {
        if (exports.is_binary(layerName)){
            dataType = "binary"
        } else if (exports.is_continuous(layerName)){
            dataType = "continuous"
        } else if (exports.is_categorical(layerName)){
            dataType = "categorical"
        } else {
            throw 'No dataType for attribute: ' + layerName;
        }
    }
    return dataType;
}

exports.is_continuous = function (layer_name) {
    return (ctx.cont_layers.indexOf(layer_name.toString()) > -1);
}

exports.is_categorical = function (layer_name) {
        var is_cat;
        if ( _.isUndefined(layer_name) ){
            is_cat = false
        } else {
            is_cat = (ctx.cat_layers.indexOf(layer_name.toString()) > -1)
}
        return is_cat;
    }

exports.is_cat_or_bin = function (layer_name){
        return (exports.is_categorical(layer_name) ||
            exports.is_binary(layer_name));
    }

exports.is_binary = function (layer_name) {
        var is_bin;
        if ( _.isUndefined(layer_name) ){
            is_bin = false
        } else {
            is_bin = (ctx.bin_layers.indexOf(layer_name.toString()) > -1)
}
        return is_bin;
    }

exports.round = function (x, n) {
    if (!n) {
        n = 0;
    }
    var m = Math.pow(10, n);
    return Math.round(x * m) / m;
}

exports.getHumanProject = function (project) {

    // Transform a project from dir structure to display for humans
    return project.slice(0, -1);
}

export function hideAllSnakes () {
    rx.set('snake.map.hide');
    rx.set('snake.project.hide');
    rx.set('snake.shortlist.hide');
}

exports.mapNotFoundNotify = function (more, stackTrace) {
    if (!ctx.mapNotFoundNotified) {
        ctx.mapNotFoundNotified = true;

        var name = exports.getHumanProject(ctx.project),
            msg = 'Map "' + name + '" not found.\nPlease select another';
        if (Meteor.user() === null) {
            msg += ' or sign in'
        }
        msg += '. ' + more;
        userMsg.error(msg, {logStr: stackTrace});
        hideAllSnakes();
    }
}

exports.parseTsv = function (data) {

    if (data.length < 1) {
        return [];
    }

    // Separate the data into an array of rows
    var rows = data.split('\n'),

    // Separate each row into an array of values
    parsed = _.map(rows, function(row) {
        return row.split('\t');
    });

    // Remove any empty row left from the new-line split
    if (parsed[parsed.length-1].length === 1 &&
            parsed[parsed.length-1][0] === '') {
        parsed.pop();
    }
    return parsed;
}

exports.removeFromDataTypeList = function (layer_name) {

    // Remove this layer from the appropriate data type list
    var index = ctx.bin_layers.indexOf(layer_name);
    if (index > -1) {
        ctx.bin_layers.splice(index, 1);
    } else {
        index = ctx.cat_layers.indexOf(layer_name);
        if (index > -1) {
            ctx.cat_layers.splice(index, 1);
        } else {
            index = ctx.cont_layers.indexOf(layer_name);
            if (index > -1) {
                ctx.cont_layers.splice(index, 1);
            }
        }
    }
}

function setHeightSelect2 ($el) {

    // Make the bottom of the list no longer than the main window
    $el.parent().on('select2-open', function () {
        var results = $('#select2-drop .select2-results');
        results.css(
            'max-height', $(window).height() - results.offset().top - 15);
    });
}

exports.createOurSelect2 = function ($el, optsIn, defaultSelection) {

    // Create a select2 drop-down.

    // Including our favorite options
    var opts = {
        dropdownAutoWidth: true,
        minimumResultsForSearch: -1,
    };

    // The caller's options override our favorite options
    for (var key in optsIn) {
        if (optsIn.hasOwnProperty(key)) {
            opts[key] = optsIn[key];
        }
    }

    // Create the select2 object
    $el.select2(opts);

    // Set the default selection
    if (defaultSelection) {
        $el.select2('val', defaultSelection);
    }

    setHeightSelect2($el);
}

exports.addToDataTypeList = function (layer_name, dataType) {
    if (dataType === 'binary') {
        ctx.bin_layers.push(layer_name);
    } else if (dataType === 'categorical') {
        ctx.cat_layers.push(layer_name);
    } else {
        ctx.cont_layers.push(layer_name);
    }
}

exports.googleAnalytics = function () {

    // Before including google analytics, respect the user's wish not to be
    // tracked if she set this in her browser preferences.
    let url = document.location
    if (!dnt._dntEnabled() || (window.ga && ga.loaded)) {
        /* eslint-disable */
        window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
        ga('create', 'UA-76132249-2', 'auto');
        ga('send', 'pageview');
        /* eslint-enable */
    } else {
        document.location = url;
    }
}

<commit_msg>fix page reloading on firefox with tracking protection on<commit_after>// util.js
// This contains various utilities used throughout the code.

import dnt from '/imports/lib/dnt';
import rx from '/imports/common/rx';
import select2 from '/imports/lib/select2';
import userMsg from '/imports/common/userMsg';

exports.get_username = function (callback) {

    // Callback will be called with one parameter: the username or undefined
    Meteor.call('get_username', function (error, results) {
        if (error || !results) {
            callback(undefined);
        } else {
            callback(results);
        }
    });
}

exports.isValidFileName = function (name) {
    var index = name.search(/[^A-Za-z0-9_\-\.]/g);
    return (index < 0);
}

exports.clean_file_name = function (dirty) {

    // Make a directory or file name out of some string
    // Valid characters:
    //     a-z, A-Z, 0-9, dash (-), dot (.), underscore (_)
    // All other characters are replaced with underscores.

    if (!dirty) { return undefined; }

    return dirty.replace(/[^A-Za-z0-9_\-\.]/g, "_");
}

exports.getDataType = function (layerName) {
    let dataType = layers[layerName.dataType];
    if (dataType === undefined) {
        if (exports.is_binary(layerName)){
            dataType = "binary"
        } else if (exports.is_continuous(layerName)){
            dataType = "continuous"
        } else if (exports.is_categorical(layerName)){
            dataType = "categorical"
        } else {
            throw 'No dataType for attribute: ' + layerName;
        }
    }
    return dataType;
}

exports.is_continuous = function (layer_name) {
    return (ctx.cont_layers.indexOf(layer_name.toString()) > -1);
}

exports.is_categorical = function (layer_name) {
        var is_cat;
        if ( _.isUndefined(layer_name) ){
            is_cat = false
        } else {
            is_cat = (ctx.cat_layers.indexOf(layer_name.toString()) > -1)
}
        return is_cat;
    }

exports.is_cat_or_bin = function (layer_name){
        return (exports.is_categorical(layer_name) ||
            exports.is_binary(layer_name));
    }

exports.is_binary = function (layer_name) {
        var is_bin;
        if ( _.isUndefined(layer_name) ){
            is_bin = false
        } else {
            is_bin = (ctx.bin_layers.indexOf(layer_name.toString()) > -1)
}
        return is_bin;
    }

exports.round = function (x, n) {
    if (!n) {
        n = 0;
    }
    var m = Math.pow(10, n);
    return Math.round(x * m) / m;
}

exports.getHumanProject = function (project) {

    // Transform a project from dir structure to display for humans
    return project.slice(0, -1);
}

export function hideAllSnakes () {
    rx.set('snake.map.hide');
    rx.set('snake.project.hide');
    rx.set('snake.shortlist.hide');
}

exports.mapNotFoundNotify = function (more, stackTrace) {
    if (!ctx.mapNotFoundNotified) {
        ctx.mapNotFoundNotified = true;

        var name = exports.getHumanProject(ctx.project),
            msg = 'Map "' + name + '" not found.\nPlease select another';
        if (Meteor.user() === null) {
            msg += ' or sign in'
        }
        msg += '. ' + more;
        userMsg.error(msg, {logStr: stackTrace});
        hideAllSnakes();
    }
}

exports.parseTsv = function (data) {

    if (data.length < 1) {
        return [];
    }

    // Separate the data into an array of rows
    var rows = data.split('\n'),

    // Separate each row into an array of values
    parsed = _.map(rows, function(row) {
        return row.split('\t');
    });

    // Remove any empty row left from the new-line split
    if (parsed[parsed.length-1].length === 1 &&
            parsed[parsed.length-1][0] === '') {
        parsed.pop();
    }
    return parsed;
}

exports.removeFromDataTypeList = function (layer_name) {

    // Remove this layer from the appropriate data type list
    var index = ctx.bin_layers.indexOf(layer_name);
    if (index > -1) {
        ctx.bin_layers.splice(index, 1);
    } else {
        index = ctx.cat_layers.indexOf(layer_name);
        if (index > -1) {
            ctx.cat_layers.splice(index, 1);
        } else {
            index = ctx.cont_layers.indexOf(layer_name);
            if (index > -1) {
                ctx.cont_layers.splice(index, 1);
            }
        }
    }
}

function setHeightSelect2 ($el) {

    // Make the bottom of the list no longer than the main window
    $el.parent().on('select2-open', function () {
        var results = $('#select2-drop .select2-results');
        results.css(
            'max-height', $(window).height() - results.offset().top - 15);
    });
}

exports.createOurSelect2 = function ($el, optsIn, defaultSelection) {

    // Create a select2 drop-down.

    // Including our favorite options
    var opts = {
        dropdownAutoWidth: true,
        minimumResultsForSearch: -1,
    };

    // The caller's options override our favorite options
    for (var key in optsIn) {
        if (optsIn.hasOwnProperty(key)) {
            opts[key] = optsIn[key];
        }
    }

    // Create the select2 object
    $el.select2(opts);

    // Set the default selection
    if (defaultSelection) {
        $el.select2('val', defaultSelection);
    }

    setHeightSelect2($el);
}

exports.addToDataTypeList = function (layer_name, dataType) {
    if (dataType === 'binary') {
        ctx.bin_layers.push(layer_name);
    } else if (dataType === 'categorical') {
        ctx.cat_layers.push(layer_name);
    } else {
        ctx.cont_layers.push(layer_name);
    }
}

exports.googleAnalytics = function () {

    // Before including google analytics, respect the user's wish not to be
    // tracked if she set this in her browser preferences.
    if (!dnt._dntEnabled() || (window.ga && ga.loaded)) {
        /* eslint-disable */
        window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
        ga('create', 'UA-76132249-2', 'auto');
        ga('send', 'pageview');
        /* eslint-enable */
    }
}

<|endoftext|>