<commit_before>/*! videojs-wavesurfer
 * Copyright (c) 2014 Collab
 * Licensed under the MIT license. */
(function(root, factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['wavesurfer'], factory);
    }
    else
    {
        root.WaveSurfer.Videojs = factory(root.WaveSurfer);
    }
}(this, function(WaveSurfer)
{
    /**
     * Use waveform for audio files in video.js player.
     */
    videojs.Waveform = videojs.Component.extend({

        /**
         * The constructor function for the class.
         *
         * @param {videojs.Player|Object} player
         * @param {Object} options Player options.
         * @param {Function} ready Ready callback function.
         */
        init: function(player, options, ready)
        {
            // run base component initializing with new options.
            videojs.Component.call(this, player, options, ready);

            this.waveReady = false;
            this.waveFinished = false;
            this.liveMode = false;

            if (this.options().options.src == 'live')
            {
                // check if the Microphone plugin can be enabled
                try
                {
                    this.microphone = Object.create(WaveSurfer.Microphone);

                    // enable audio input from a microphone
                    this.liveMode = true;
                    this.waveReady = true;
                }
                catch (TypeError)
                {
                    console.warn('Could not find Microphone plugin!');
                }
            }

            if (this.options().options.msDisplayMax !== undefined)
            {
            	// msDisplayMax indicates the number of seconds that is
                // considered the boundary value for displaying milliseconds
                // in the time controls. An audio clip with a total length of
                // 2 seconds and a msDisplayMax of 3 will use the format
                // M:SS:MMM. Clips longer than msDisplayMax will be displayed
            	// as M:SS or HH:MM:SS.
            	this.msDisplayMax = parseFloat(this.options().options.msDisplayMax);
            }
            else
            {
            	// default
            	this.msDisplayMax = 3;
            }

            // customize controls
            this.player().bigPlayButton.hide();
            if (this.player().options().controls)
            {
            	// progress control isn't used by this plugin
                this.player().controlBar.progressControl.hide();

                // prevent controlbar fadeout
                this.player().on('userinactive', function(event)
                {
                   this.player().userActive(true);
                });

                // videojs automatically hides the controls when no valid 'source'
                // element is included in the 'audio' tag. Don't.
                this.player().controlBar.show();

                // disable play button until waveform is ready
                // (except when in live mode)
                if (!this.liveMode)
                {
                    this.player().controlBar.playToggle.hide();
                }

                // disable currentTimeDisplay's 'timeupdate' event listener that
                // constantly tries to reset the current time value to 0
                this.player().off('timeupdate');
            }

            // waveform events
            this.surfer = Object.create(WaveSurfer);
            this.surfer.on('error', this.onWaveError.bind(this));
            this.surfer.on('finish', this.onWaveFinish.bind(this));

            // only listen to these events when we're not in live mode
            if (!this.liveMode)
            {
                this.setupPlaybackEvents();
            }

            // player events
            this.player().on('play', this.onPlay.bind(this));
            this.player().on('pause', this.onPause.bind(this));
            this.player().on('volumechange', this.onVolumeChange.bind(this));
            this.player().on('fullscreenchange', this.onScreenChange.bind(this));

            // kick things off
            this.startPlayers();
        },

        /**
         *
         */
        setupPlaybackEvents: function()
        {
            this.surfer.on('ready', this.onWaveReady.bind(this));
            this.surfer.on('audioprocess', this.onWaveProgress.bind(this));
            this.surfer.on('seek', this.onWaveSeek.bind(this));
        },

        /**
         * Start the players.
         */
        startPlayers: function()
        {
            var options = this.options().options;

            // init waveform
            this.initialize(options);

            if (options.src !== undefined)
            {
                if (this.microphone === undefined)
                {
                    // show loading spinner
                    this.player().loadingSpinner.show();

                    // start loading file
                    this.load(options.src);
                }
                else
                {
                    // hide loading spinner
                    this.player().loadingSpinner.hide();

                    // connect microphone input to our waveform
                    this.microphone.init({
                        wavesurfer: this.surfer
                    });
                }
            }
            else
            {
                // no valid src found, hide loading spinner
                this.player().loadingSpinner.hide();
            }
        },

        /**
         * Initializes the waveform.
         *
         * @param {Object} opts Plugin options.
         */
        initialize: function(opts)
        {
            this.originalHeight = this.player().options().height;

            // set waveform element and dimensions
            opts.container = this.el();
            opts.height = this.player().height() - this.player().controlBar.height();

            // customize waveform appearance
            this.surfer.init(opts);
        },

        /**
         * Start loading waveform data.
         *
         * @param {String|Blob|File} url Either the URL of the audio file,
         *     or a Blob or File object.
         */
        load: function(url)
        {
            // XXX:
            if (this.liveMode)
            {
                this.setupPlaybackEvents();
            }

            if (url instanceof Blob || url instanceof File)
            {
                this.surfer.loadBlob(url);
            }
            else
            {
                this.surfer.load(url);
            }
        },

        /**
         * Start/resume playback or microphone.
         */
        play: function()
        {
            if (this.liveMode)
            {
                // toggle microphone
                this.microphone.togglePlay();
            }
            else
            {
                // start playback
                this.surfer.play();
            }
        },

        /**
         * Pauses playback or microphone.
         */
        pause: function()
        {
            if (this.liveMode)
            {
                // toggle microphone
                this.microphone.togglePlay();
            }
            else
            {
                // pause playback
                if (!this.waveFinished)
                {
                    this.surfer.pause();
                }
                else
                {
                    this.waveFinished = false;
                }

                this.setCurrentTime();
            }
        },

        /**
         * Remove the player and waveform.
         */
        destroy: function()
        {
            this.surfer.destroy();
            this.player().dispose();
        },

        /**
         * Set the current volume.
         *
         * @param {Number} volume The new volume level.
         */
        setVolume: function(volume)
        {
            if (volume !== undefined)
            {
                this.surfer.setVolume(volume);
            }
        },

        /**
         * Updates the player's element displaying the current time.
         *
         * @param {Number} currentTime (optional) Current position of the
         *    playhead (in seconds).
         * @param {Number} duration (optional) Duration of the waveform
         *    (in seconds).
         */
        setCurrentTime: function(currentTime, duration)
        {
            if (currentTime === undefined)
            {
                currentTime = this.surfer.getCurrentTime();
            }

            if (duration === undefined)
            {
                duration = this.surfer.getDuration();
            }
            var time = Math.min(currentTime, duration);

            // update control
            this.player().controlBar.currentTimeDisplay.el(
                ).firstChild.innerHTML = this.formatTime(
                time, duration);
        },

        /**
         * Updates the player's element displaying the duration time.
         *
         * @param {Number} duration (optional) Duration of the waveform
         *    (in seconds).
         */
        setDuration: function(duration)
        {
            if (duration === undefined)
            {
                duration = this.surfer.getDuration();
            }

            // update control
            this.player().controlBar.durationDisplay.el(
                ).firstChild.innerHTML = this.formatTime(
                duration, duration);
        },

        /**
         * Audio is loaded, decoded and the waveform is drawn.
         */
        onWaveReady: function()
        {
            this.waveReady = true;
            this.waveFinished = false;
            this.liveMode = false;

            // make sure the size of time controls is large enough to
            // display milliseconds
            if (this.surfer.getDuration() < this.msDisplayMax)
            {
                this.player().controlBar.durationDisplay.el().style.width =
                    this.player().controlBar.currentTimeDisplay.el().style.width = '6em';
            }

            // update time display
            this.setCurrentTime();
            this.setDuration();

            // enable and show play button
            this.player().controlBar.playToggle.show();

            // remove loading spinner
            this.player().removeChild(this.player().loadingSpinner);

            // auto-play when ready (if enabled)
            if (this.player().options().autoplay)
            {
                this.play();
            }
        },

        /**
         * Fires when audio playback completed.
         */
        onWaveFinish: function()
        {
            // check if player isn't paused already
            if (!this.player().paused())
            {
                // check if loop is enabled
                if (this.player().options().loop)
                {
                    // reset waveform
                    this.surfer.stop();
                    this.play();
                }
                else
                {
                	// finished
                	this.waveFinished = true;

                    // pause player
                    this.player().pause();
                }
            }
        },

        /**
         * Fires continuously during audio playback.
         *
         * @param {Number} percent Percentage played so far.
         */
        onWaveProgress: function(percent)
        {
            this.setCurrentTime();
        },

        /**
         * Fires during seeking of the waveform.
         */
        onWaveSeek: function()
        {
            this.setCurrentTime();
        },

        /**
         * Fired whenever the media in the player begins or resumes playback.
         */
        onPlay: function()
        {
            // don't start playing until waveform's ready
            if (this.waveReady)
            {
                this.play();
            }
        },

        /**
         * Fired whenever the media in the player has been paused.
         */
        onPause: function()
        {
            this.pause();
        },

        /**
         * Fired when the volume in the player changes.
         */
        onVolumeChange: function()
        {
            var volume = this.player().volume();
            if (this.player().muted())
            {
                // muted volume
                volume = 0;
            }

            this.setVolume(volume);
        },

        /**
         * Fired when the player switches in or out of fullscreen mode.
         */
        onScreenChange: function()
        {
            var isFullscreen = this.player().isFullscreen();
            var newHeight;

            if (!isFullscreen)
            {
                // restore original height
                newHeight = this.originalHeight;
            }
            else
            {
                // fullscreen height
                newHeight = window.outerHeight;
            }

            if (this.waveReady)
            {
                if (this.liveMode && !this.microphone.active)
                {
                    // we're in live mode but the microphone hasn't been
                    // started yet
                    return;
                }

                // destroy old drawing
                this.surfer.drawer.destroy();

                // set new height
                this.surfer.params.height = newHeight - this.player().controlBar.height();
                this.surfer.createDrawer();

                // redraw
                this.surfer.drawBuffer();

                // make sure playhead is restored at right position
                this.surfer.drawer.progress(this.surfer.backend.getPlayedPercents());
            }
        },

        /**
         * Waveform error.
         *
         * @param {String} error The wavesurfer error.
         */
        onWaveError: function(error)
        {
            console.warn(error);
        },

        /**
        * Format seconds as a time string, H:MM:SS, M:SS or M:SS:MMM.
        *
        * Supplying a guide (in seconds) will force a number of leading zeros
        * to cover the length of the guide.
        *
        * @param {Number} seconds Number of seconds to be turned into a string
        * @param {Number} guide Number (in seconds) to model the string after
        * @return {String} Time formatted as H:MM:SS, M:SS or M:SS:MMM.
        */
        formatTime: function(seconds, guide)
        {
            // Default to using seconds as guide
            guide = guide || seconds;
            var s = Math.floor(seconds % 60),
                m = Math.floor(seconds / 60 % 60),
                h = Math.floor(seconds / 3600),
                gm = Math.floor(guide / 60 % 60),
                gh = Math.floor(guide / 3600),
                ms = Math.floor((seconds - s) * 1000);

            // handle invalid times
            if (isNaN(seconds) || seconds === Infinity)
            {
                // '-' is false for all relational operators (e.g. <, >=) so this
                // setting will add the minimum number of fields specified by the
                // guide
                h = m = s = ms = '-';
            }

            // Check if we need to show milliseconds
            if (guide > 0 && guide < this.msDisplayMax)
            {
                if (ms < 100)
                {
                    if (ms < 10)
                    {
                        ms = '00' + ms;
                    }
                    else
                    {
                        ms = '0' + ms;
                    }
                }
                ms = ":" + ms;
            }
            else
            {
                ms = '';
            }

            // Check if we need to show hours
            h = (h > 0 || gh > 0) ? h + ':' : '';

            // If hours are showing, we may need to add a leading zero.
            // Always show at least one digit of minutes.
            m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':';

            // Check if leading zero is need for seconds
            s = ((s < 10) ? '0' + s : s);

            return h + m + s + ms;
        }

    });

    var createWaveform = function()
    {
        var props = {
            className: 'vjs-waveform',
            tabIndex: 0
        };
        return videojs.Component.prototype.createEl(null, props);
    };

    // plugin defaults
    var defaults = {};

    /**
     * Initialize the plugin.
     * @param options (optional) {object} configuration for the plugin
     */
    var wavesurferPlugin = function(options)
    {
        var settings = videojs.util.mergeOptions(defaults, options);
        var player = this;

        // create new waveform
        player.waveform = new videojs.Waveform(player,
        {
            'el': createWaveform(),
            'options': settings
        });

        // add waveform to dom
        player.el().appendChild(player.waveform.el());
    }

    // register the plugin
    videojs.plugin('wavesurfer', wavesurferPlugin);

    return wavesurferPlugin;

}));
<commit_msg>add 'enable' param to setupPlaybackEvents<commit_after>/*! videojs-wavesurfer
 * Copyright (c) 2014 Collab
 * Licensed under the MIT license. */
(function(root, factory)
{
    if (typeof define === 'function' && define.amd)
    {
        define(['wavesurfer'], factory);
    }
    else
    {
        root.WaveSurfer.Videojs = factory(root.WaveSurfer);
    }
}(this, function(WaveSurfer)
{
    /**
     * Use waveform for audio files in video.js player.
     */
    videojs.Waveform = videojs.Component.extend({

        /**
         * The constructor function for the class.
         *
         * @param {videojs.Player|Object} player
         * @param {Object} options Player options.
         * @param {Function} ready Ready callback function.
         */
        init: function(player, options, ready)
        {
            // run base component initializing with new options.
            videojs.Component.call(this, player, options, ready);

            this.waveReady = false;
            this.waveFinished = false;
            this.liveMode = false;

            if (this.options().options.src == 'live')
            {
                // check if the Microphone plugin can be enabled
                try
                {
                    this.microphone = Object.create(WaveSurfer.Microphone);

                    // enable audio input from a microphone
                    this.liveMode = true;
                    this.waveReady = true;
                }
                catch (TypeError)
                {
                    console.warn('Could not find Microphone plugin!');
                }
            }

            if (this.options().options.msDisplayMax !== undefined)
            {
            	// msDisplayMax indicates the number of seconds that is
                // considered the boundary value for displaying milliseconds
                // in the time controls. An audio clip with a total length of
                // 2 seconds and a msDisplayMax of 3 will use the format
                // M:SS:MMM. Clips longer than msDisplayMax will be displayed
            	// as M:SS or HH:MM:SS.
            	this.msDisplayMax = parseFloat(this.options().options.msDisplayMax);
            }
            else
            {
            	// default
            	this.msDisplayMax = 3;
            }

            // customize controls
            this.player().bigPlayButton.hide();
            if (this.player().options().controls)
            {
            	// progress control isn't used by this plugin
                this.player().controlBar.progressControl.hide();

                // prevent controlbar fadeout
                this.player().on('userinactive', function(event)
                {
                   this.player().userActive(true);
                });

                // videojs automatically hides the controls when no valid 'source'
                // element is included in the 'audio' tag. Don't.
                this.player().controlBar.show();

                // disable play button until waveform is ready
                // (except when in live mode)
                if (!this.liveMode)
                {
                    this.player().controlBar.playToggle.hide();
                }

                // disable currentTimeDisplay's 'timeupdate' event listener that
                // constantly tries to reset the current time value to 0
                this.player().off('timeupdate');
            }

            // waveform events
            this.surfer = Object.create(WaveSurfer);
            this.surfer.on('error', this.onWaveError.bind(this));
            this.surfer.on('finish', this.onWaveFinish.bind(this));

            this.surferReady = this.onWaveReady.bind(this);
            this.surferProgress = this.onWaveProgress.bind(this);
            this.surferSeek = this.onWaveSeek.bind(this);

            // only listen to these events when we're not in live mode
            if (!this.liveMode)
            {
                this.setupPlaybackEvents();
            }

            // player events
            this.player().on('play', this.onPlay.bind(this));
            this.player().on('pause', this.onPause.bind(this));
            this.player().on('volumechange', this.onVolumeChange.bind(this));
            this.player().on('fullscreenchange', this.onScreenChange.bind(this));

            // kick things off
            this.startPlayers();
        },

        /**
         * Starts or stops listening to events related to audio-playback.
         *
         * @param {boolean} enable Start or stop listening to playback related
         *     events.
         */
        setupPlaybackEvents: function(enable)
        {
            if (enable === false)
            {
                this.surfer.un('ready', this.surferReady);
                this.surfer.un('audioprocess', this.surferProgress);
                this.surfer.un('seek', this.surferSeek);
            }
            else if (enable === true)
            {
                this.surfer.on('ready', this.surferReady);
                this.surfer.on('audioprocess', this.surferProgress);
                this.surfer.on('seek', this.surferSeek);
            }
        },

        /**
         * Start the players.
         */
        startPlayers: function()
        {
            var options = this.options().options;

            // init waveform
            this.initialize(options);

            if (options.src !== undefined)
            {
                if (this.microphone === undefined)
                {
                    // show loading spinner
                    this.player().loadingSpinner.show();

                    // start loading file
                    this.load(options.src);
                }
                else
                {
                    // hide loading spinner
                    this.player().loadingSpinner.hide();

                    // connect microphone input to our waveform
                    this.microphone.init({
                        wavesurfer: this.surfer
                    });
                }
            }
            else
            {
                // no valid src found, hide loading spinner
                this.player().loadingSpinner.hide();
            }
        },

        /**
         * Initializes the waveform.
         *
         * @param {Object} opts Plugin options.
         */
        initialize: function(opts)
        {
            this.originalHeight = this.player().options().height;

            // set waveform element and dimensions
            opts.container = this.el();
            opts.height = this.player().height() - this.player().controlBar.height();

            // customize waveform appearance
            this.surfer.init(opts);
        },

        /**
         * Start loading waveform data.
         *
         * @param {String|Blob|File} url Either the URL of the audio file,
         *     or a Blob or File object.
         */
        load: function(url)
        {
            if (url instanceof Blob || url instanceof File)
            {
                this.surfer.loadBlob(url);
            }
            else
            {
                this.surfer.load(url);
            }
        },

        /**
         * Start/resume playback or microphone.
         */
        play: function()
        {
            if (this.liveMode)
            {
                // toggle microphone
                this.microphone.togglePlay();
            }
            else
            {
                // start playback
                this.surfer.play();
            }
        },

        /**
         * Pauses playback or microphone.
         */
        pause: function()
        {
            if (this.liveMode)
            {
                // toggle microphone
                this.microphone.togglePlay();
            }
            else
            {
                // pause playback
                if (!this.waveFinished)
                {
                    this.surfer.pause();
                }
                else
                {
                    this.waveFinished = false;
                }

                this.setCurrentTime();
            }
        },

        /**
         * Remove the player and waveform.
         */
        destroy: function()
        {
            this.surfer.destroy();
            this.player().dispose();
        },

        /**
         * Set the current volume.
         *
         * @param {Number} volume The new volume level.
         */
        setVolume: function(volume)
        {
            if (volume !== undefined)
            {
                this.surfer.setVolume(volume);
            }
        },

        /**
         * Updates the player's element displaying the current time.
         *
         * @param {Number} currentTime (optional) Current position of the
         *    playhead (in seconds).
         * @param {Number} duration (optional) Duration of the waveform
         *    (in seconds).
         */
        setCurrentTime: function(currentTime, duration)
        {
            if (currentTime === undefined)
            {
                currentTime = this.surfer.getCurrentTime();
            }

            if (duration === undefined)
            {
                duration = this.surfer.getDuration();
            }
            var time = Math.min(currentTime, duration);

            // update control
            this.player().controlBar.currentTimeDisplay.el(
                ).firstChild.innerHTML = this.formatTime(
                time, duration);
        },

        /**
         * Updates the player's element displaying the duration time.
         *
         * @param {Number} duration (optional) Duration of the waveform
         *    (in seconds).
         */
        setDuration: function(duration)
        {
            if (duration === undefined)
            {
                duration = this.surfer.getDuration();
            }

            // update control
            this.player().controlBar.durationDisplay.el(
                ).firstChild.innerHTML = this.formatTime(
                duration, duration);
        },

        /**
         * Audio is loaded, decoded and the waveform is drawn.
         */
        onWaveReady: function()
        {
            this.waveReady = true;
            this.waveFinished = false;
            this.liveMode = false;

            // make sure the size of time controls is large enough to
            // display milliseconds
            if (this.surfer.getDuration() < this.msDisplayMax)
            {
                this.player().controlBar.durationDisplay.el().style.width =
                    this.player().controlBar.currentTimeDisplay.el().style.width = '6em';
            }

            // update time display
            this.setCurrentTime();
            this.setDuration();

            // enable and show play button
            this.player().controlBar.playToggle.show();

            // remove loading spinner
            this.player().removeChild(this.player().loadingSpinner);

            // auto-play when ready (if enabled)
            if (this.player().options().autoplay)
            {
                this.play();
            }
        },

        /**
         * Fires when audio playback completed.
         */
        onWaveFinish: function()
        {
            // check if player isn't paused already
            if (!this.player().paused())
            {
                // check if loop is enabled
                if (this.player().options().loop)
                {
                    // reset waveform
                    this.surfer.stop();
                    this.play();
                }
                else
                {
                	// finished
                	this.waveFinished = true;

                    // pause player
                    this.player().pause();
                }
            }
        },

        /**
         * Fires continuously during audio playback.
         *
         * @param {Number} time Current time/location of the playhead.
         */
        onWaveProgress: function(time)
        {
            this.setCurrentTime();
        },

        /**
         * Fires during seeking of the waveform.
         */
        onWaveSeek: function()
        {
            this.setCurrentTime();
        },

        /**
         * Fired whenever the media in the player begins or resumes playback.
         */
        onPlay: function()
        {
            // don't start playing until waveform's ready
            if (this.waveReady)
            {
                this.play();
            }
        },

        /**
         * Fired whenever the media in the player has been paused.
         */
        onPause: function()
        {
            this.pause();
        },

        /**
         * Fired when the volume in the player changes.
         */
        onVolumeChange: function()
        {
            var volume = this.player().volume();
            if (this.player().muted())
            {
                // muted volume
                volume = 0;
            }

            this.setVolume(volume);
        },

        /**
         * Fired when the player switches in or out of fullscreen mode.
         */
        onScreenChange: function()
        {
            var isFullscreen = this.player().isFullscreen();
            var newHeight;

            if (!isFullscreen)
            {
                // restore original height
                newHeight = this.originalHeight;
            }
            else
            {
                // fullscreen height
                newHeight = window.outerHeight;
            }

            if (this.waveReady)
            {
                if (this.liveMode && !this.microphone.active)
                {
                    // we're in live mode but the microphone hasn't been
                    // started yet
                    return;
                }

                // destroy old drawing
                this.surfer.drawer.destroy();

                // set new height
                this.surfer.params.height = newHeight - this.player().controlBar.height();
                this.surfer.createDrawer();

                // redraw
                this.surfer.drawBuffer();

                // make sure playhead is restored at right position
                this.surfer.drawer.progress(this.surfer.backend.getPlayedPercents());
            }
        },

        /**
         * Waveform error.
         *
         * @param {String} error The wavesurfer error.
         */
        onWaveError: function(error)
        {
            console.warn(error);
        },

        /**
        * Format seconds as a time string, H:MM:SS, M:SS or M:SS:MMM.
        *
        * Supplying a guide (in seconds) will force a number of leading zeros
        * to cover the length of the guide.
        *
        * @param {Number} seconds Number of seconds to be turned into a string
        * @param {Number} guide Number (in seconds) to model the string after
        * @return {String} Time formatted as H:MM:SS, M:SS or M:SS:MMM.
        */
        formatTime: function(seconds, guide)
        {
            // Default to using seconds as guide
            guide = guide || seconds;
            var s = Math.floor(seconds % 60),
                m = Math.floor(seconds / 60 % 60),
                h = Math.floor(seconds / 3600),
                gm = Math.floor(guide / 60 % 60),
                gh = Math.floor(guide / 3600),
                ms = Math.floor((seconds - s) * 1000);

            // handle invalid times
            if (isNaN(seconds) || seconds === Infinity)
            {
                // '-' is false for all relational operators (e.g. <, >=) so this
                // setting will add the minimum number of fields specified by the
                // guide
                h = m = s = ms = '-';
            }

            // Check if we need to show milliseconds
            if (guide > 0 && guide < this.msDisplayMax)
            {
                if (ms < 100)
                {
                    if (ms < 10)
                    {
                        ms = '00' + ms;
                    }
                    else
                    {
                        ms = '0' + ms;
                    }
                }
                ms = ":" + ms;
            }
            else
            {
                ms = '';
            }

            // Check if we need to show hours
            h = (h > 0 || gh > 0) ? h + ':' : '';

            // If hours are showing, we may need to add a leading zero.
            // Always show at least one digit of minutes.
            m = (((h || gm >= 10) && m < 10) ? '0' + m : m) + ':';

            // Check if leading zero is need for seconds
            s = ((s < 10) ? '0' + s : s);

            return h + m + s + ms;
        }

    });

    var createWaveform = function()
    {
        var props = {
            className: 'vjs-waveform',
            tabIndex: 0
        };
        return videojs.Component.prototype.createEl(null, props);
    };

    // plugin defaults
    var defaults = {};

    /**
     * Initialize the plugin.
     * @param options (optional) {object} configuration for the plugin
     */
    var wavesurferPlugin = function(options)
    {
        var settings = videojs.util.mergeOptions(defaults, options);
        var player = this;

        // create new waveform
        player.waveform = new videojs.Waveform(player,
        {
            'el': createWaveform(),
            'options': settings
        });

        // add waveform to dom
        player.el().appendChild(player.waveform.el());
    };

    // register the plugin
    videojs.plugin('wavesurfer', wavesurferPlugin);

    return wavesurferPlugin;

}));
<|endoftext|>


<commit_before>// macros ----------------------------------------

let async = macro {
  case {_ function $name ($params ...) { $body ...} } => {
    return #{ var $name = require('q').async(function * $name ($params ...) { $body ... }) }
  }
  case {_ function ($params ...) { $body ...} } => {
    return #{ require('q').async(function * ($params ...) { $body ... }) }
  }
}

macro await {
  case {_ $e:expr } => {
    return #{ yield $e }
  }
}

let var = macro {
  rule { $name:ident = $value:expr } => {
    var $name = $value
  }

  rule { {$name:ident (,) ...} = $value:expr } => {
    var object = $value
    $(, $name = object.$name) ...
  }

  rule { [$name:ident (,) ...] = $value:expr } => {
    var array = $value, index = 0
    $(, $name = array[index++]) ...
  }
}

// app ------------------------------------------------

var http = require('http');
var Q = require('q');
var request = require('./request.js');
var headers = { 'User-Agent': 'lukehoban', 'Authorization': 'token 665021d813ad67942206d94c47d7947716d27f66' };

async function getCollaboratorImages(full_name) {
  // promise-returning async HTTP GET
  // note - if any exceptions are thrown here they will propogate into try/catch in callers
  var url = 'https://api.github.com/repos/' + full_name + '/collaborators';
  var [response, body] = await request({url: url, headers: headers});
  return JSON.parse(body).map(function(collab) {
    return collab.avatar_url;
  });
}

// can use a `async function` here because createServer doesn't care what this returns
http.createServer(async function (req, res) {
  console.log('starting...')
  var url = 'https://api.github.com/search/repositories?per_page=100&q=' + 'tetris';
  var items = [];
  // write a normal 'synchronous' while loop
  while(true) {
    console.log('Got ' + items.length + ' items total.  Next: ' + url);
    // use normal exception handling
    try {
      // promise-returning async HTTP GET
      var [response, body] = await request({url: url, headers: headers});
      var newItems = Q.all(JSON.parse(body).items.map(async function(item) {
        return {
          full_name: item.full_name,
          collabs_images: await getCollaboratorImages(item.full_name)
        };
      }));
      items = items.concat(await newItems);
      url = (/<(.*)>; rel="next"/.exec(response.headers.link) || [])[1];
      // break once there is no 'next' link
      if(!url) break;
    } catch(err) {
      console.log('backing off... ' + err)
      // backoff on any error
      await Q.timeout(1000);
      // then try again
      continue;
    }
  }
  // when done, write response - appears in the usual synchronous 'at the end'
  console.log('Done. Got ' + items.length + ' items total.');
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(items));
}).listen(process.env.port || 1337);
<commit_msg>Sample updates<commit_after>// macros ----------------------------------------

let async = macro {
  case {_ function $name ($params ...) { $body ...} } => {
    return #{ var $name = require('q').async(function * $name ($params ...) { $body ... }) }
  }
  case {_ function ($params ...) { $body ...} } => {
    return #{ require('q').async(function * ($params ...) { $body ... }) }
  }
}

macro await {
  case {_ $e:expr } => {
    return #{ yield $e }
  }
}

let var = macro {
  rule { $name:ident = $value:expr } => {
    var $name = $value
  }

  rule { {$name:ident (,) ...} = $value:expr } => {
    var object = $value
    $(, $name = object.$name) ...
  }

  rule { [$name:ident (,) ...] = $value:expr } => {
    var array = $value, index = 0
    $(, $name = array[index++]) ...
  }
}

// app ------------------------------------------------

var http = require('http');
var Q = require('q');
var request = require('./request.js');
var headers = { 'User-Agent': 'lukehoban', 'Authorization': 'token 665021d813ad67942206d94c47d7947716d27f66' };

// Promise-returning asynchronous function
async function getCollaboratorImages(full_name) {
  // any exceptions thrown here will propogate into try/catch in callers - same as synchronous
  var url = 'https://api.github.com/repos/' + full_name + '/collaborators';
  // await a promise-returning async HTTP GET - same as synchronous
  var [response, body] = await request({url: url, headers: headers});
  return JSON.parse(body).map(function(collab) {
    return collab.avatar_url;
  });
}

// can use a `async function` here because createServer doesn't care what this returns
http.createServer(async function (req, res) {
  console.log('starting...')
  var url = 'https://api.github.com/search/repositories?per_page=100&q=' + 'tetris';
  var items = [];
  // write a normal 'synchronous' while loop
  while(true) {
    console.log('Got ' + items.length + ' items total.  Next: ' + url);
    // use normal exception handling
    try {
      // promise-returning async HTTP GET
      var [response, body] = await request({url: url, headers: headers});
      // nested parallel work is still possible with Q.all (could be future await* ?)
      var newItems = Q.all(JSON.parse(body).items.map(async function(item) {
        return {
          full_name: item.full_name,
          collabs_images: await getCollaboratorImages(item.full_name)
        };
      }));
      items = items.concat(await newItems);
      url = (/<(.*)>; rel="next"/.exec(response.headers.link) || [])[1];
      // break once there is no 'next' link
      if(!url) break;
    } catch(err) {
      console.log('backing off... ' + err)
      // backoff on any error
      await Q.timeout(1000);
      // then try again
      continue;
    }
  }
  // when done, write response - appears in the usual synchronous 'at the end'
  console.log('Done. Got ' + items.length + ' items total.');
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.end(JSON.stringify(items));
}).listen(process.env.port || 1337);
<|endoftext|>


<commit_before>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';

moduleForComponent('week-glance-session-attrs', 'Integration | Component | week glance session attrs', {
  integration: true
});

test('it renders', function(assert) {

  // Set any properties with this.set('myProperty', 'value');
  // Handle any actions with this.on('myAction', function(val) { ... });

  this.render(hbs`{{week-glance-session-attrs}}`);

  assert.equal(this.$().text().trim(), '');

  // Template block usage:
  this.render(hbs`
    {{#week-glance-session-attrs}}
      template block text
    {{/week-glance-session-attrs}}
  `);

  assert.equal(this.$().text().trim(), 'template block text');
});
<commit_msg>integration test.<commit_after>import { moduleForComponent, test } from 'ember-qunit';
import hbs from 'htmlbars-inline-precompile';
import Ember from 'ember';
import initializer from "ilios/instance-initializers/ember-i18n";
import wait from 'ember-test-helpers/wait';

const { RSVP, Service, Object } = Ember;
const { resolve } = RSVP;

moduleForComponent('week-glance-session-attrs', 'Integration | Component | week glance session attrs', {
  integration: true,
  setup(){
    initializer.initialize(this);
  },
});

test('it renders', async function(assert) {
  assert.expect(4);

  const mockEvent = Object.create({});
  const userEventsMock = Service.extend({
    getSessionForEvent(event){
      assert.equal(event, mockEvent);
      let mockSession = Object.create({
        attireRequired: true,
        equipmentRequired: true,
        attendanceRequired: true,
      });
      return new resolve(mockSession);
    }
  });

  this.register('service:user-events', userEventsMock);
  this.set('event', mockEvent);

  this.render(hbs`{{week-glance-session-attrs event=event}}`);

  await wait();

  assert.equal(this.$('.fa-black-tie').attr('title'), 'Whitecoats / special attire');
  assert.equal(this.$('.fa-flask').attr('title'), 'Special Equipment');
  assert.equal(this.$('.fa-calendar-check-o').attr('title'), 'Attendance is required');
});

test('only show applicable session attributes', async function(assert) {
  assert.expect(4);

  const mockEvent = Object.create({});
  const userEventsMock = Service.extend({
    getSessionForEvent(event){
      assert.equal(event, mockEvent);
      let mockSession = Object.create({
        attireRequired: true,
        equipmentRequired: false,
        attendanceRequired: false,
      });
      return new resolve(mockSession);
    }
  });

  this.register('service:user-events', userEventsMock);
  this.set('event', mockEvent);

  this.render(hbs`{{week-glance-session-attrs event=event}}`);

  await wait();

  assert.equal(this.$('.fa-black-tie').attr('title'), 'Whitecoats / special attire');
  assert.notOk(this.$('.fa-flask').length);
  assert.notOk(this.$('.fa-calendar-check-o').notOk);
});
<|endoftext|>


<commit_before>App.ProjectListController = Em.ArrayController.extend({
    needs: ['projectSearchForm']
});

App.ProjectSearchFormController = Em.ObjectController.extend({
    needs: ['projectList'],

    // Number of results to show on one page
    pageSize: 8,

    // Have a property for this so we can easily use another list controller if we extend this.
    listController: function(){
        return this.get('controllers.projectList');
    }.property(),

    init: function(){
        // Make sure this record is in it's own transaction so it will never pollute other commits.
        var transaction = this.get('store').transaction();
        var form =  transaction.createRecord(App.ProjectSearch);
        this.set('model', form);
        this.updateSearch();
    },

    rangeStart: function(){
        return this.get('page') * this.get('pageSize') - this.get('pageSize') + 1;
    }.property('listController.model.length'),

    rangeEnd: function(){
        return this.get('page') * this.get('pageSize') - this.get('pageSize') + this.get('listController.model.length');
    }.property('listController.model.length'),

    hasNextPage: function(){
        var next = this.get('page') * this.get('pageSize');
        var total = this.get('listController.model.meta.total');
        return (next < total);
    }.property('listController.model.meta.total'),

    hasPreviousPage: function(){
        return (this.get('page') > 1);
    }.property('page'),

    orderedByPopularity: function(){
        return (this.get('ordering') == 'popularity');
    }.property('ordering'),
    orderedByTitle: function(){
        return (this.get('ordering') == 'title');
    }.property('ordering'),
    orderedByNewest: function(){
        return (this.get('ordering') == 'newest');
    }.property('ordering'),
    orderedByNeeded: function(){
        return (this.get('ordering') == 'needed');
    }.property('ordering'),
    orderedByDeadline: function(){
        return (this.get('ordering') == 'deadline');
    }.property('ordering'),

    updateSearch: function(sender, key){
        if (key != 'page') {
            // If the query changes we should jump back to page 1
            this.set('page', 1);
        }
        if (this.get('model.isDirty') ) {
            var list = this.get('listController');
            var controller = this;

            var query = {
                'page_size': this.get('pageSize'),
                'page': this.get('page'),
                'ordering': this.get('ordering'),
                'phase': this.get('phase'),
                'country': this.get('country'),
                'text': this.get('text'),
                'theme': this.get('theme')
            };
            var projects = App.ProjectPreview.find(query);
            list.set('model', projects);
        }
    }.observes('text', 'country', 'theme', 'phase', 'page', 'ordering'),

    actions: {
        nextPage: function(){
            this.incrementProperty('page');
        },

        previousPage: function(){
            this.decrementProperty('page');
        },

        sortOrder: function(order) {
            this.set('ordering', order);
        },
        clearForm: function(sender, key) {
            this.set('model.text', '');
            this.set('model.country', null);
            this.set('model.theme', null);
            this.set('model.phase', null);
        }

    }
});

App.ProjectController = Em.ObjectController.extend({
    isFundable: function(){
       return (this.get('phase') == 'campaign' && this.get('campaign.money_asked'));
    }.property('phase', 'campaign'),

    allTags: function() {
        var tags = this.get('plan.tags');
        return tags.reduce(function(previousValue, tag, index) {
            var separator = (index == 0 ? " " : ", ");
            return previousValue + separator + tag.id;
        }, "");
    }.property('tags.@each')

});

App.ProjectSupporterListController = Em.ArrayController.extend({
    supportersLoaded: function(sender, key) {
        if (this.get(key)) {
            this.set('model', this.get('supporters').toArray());
        } else {
            // Don't show old content when new content is being retrieved.
            this.set('model', null);
        }
    }.observes('supporters.isLoaded')

});

/*
 Project Manage Controllers
 */

App.MyProjectController = Em.ObjectController.extend({
    needs: ['currentUser']
});

App.MyPitchNewController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser'],
    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });

            model.one('didCreate', function(record){
                controller.transitionToRoute('myProjectPitch', record);
            });

            model.save();
        }
    }
});

App.MyProjectListController = Em.ArrayController.extend({
    needs: ['currentUser'],
    canPitchNew: function(){
        var can = true;
        this.get('model').forEach(function(project){
            if (project.get('inProgress')) {
                can = false;
            }
        });
        return can;
    }.property('model.@each.phase')

});

App.MyProjectPitchController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser']
});

App.MyProjectPitchBasicsController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.location'
});
App.MyProjectPitchLocationController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.media'
});
App.MyProjectPitchMediaController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.submit',

    actions: {
        save: function(record) {
            this._super();
            this.get('model').reload();
        }
    }
});

App.MyProjectPitchSubmitController = Em.ObjectController.extend(App.Editable, {
    actions: {
        submitPitch: function(e){
            var controller = this;
            var model = this.get('model');
            model.set('status', 'submitted');
            model.on('didUpdate', function(){
                controller.transitionToRoute('myProjectPitchReview');
            });
            model.save();
        }
    },
    exit: function(){
        this.set('model.status', 'new');
        this._super();
    }
});

App.MyProjectPitchReviewController = Em.ObjectController.extend({});

App.MyProjectPlanController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser']

});

App.MyProjectPlanBasicsController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.description'
});
App.MyProjectPlanDescriptionController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.location'
});
App.MyProjectPlanLocationController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.media'
});
App.MyProjectPlanSubmitController = Em.ObjectController.extend(App.Editable, {});
App.MyProjectPlanMediaController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.organisation'
});
App.MyProjectPlanCampaignController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.budget'
});

/*
App.MyProjectPlanAmbassadorsController = Em.ObjectController.extend(App.Editable, {
    nextStep: function(){
        if (this.get('need') == 'skills') {
            return 'myProjectPlan.submit'
        } else {
            return 'myProjectPlan.campaign'
        }
    }.property('need'),

    shouldSave: function(){
        // Determine if any part is dirty, project plan or any of the ambassadors
        if (this.get('isDirty')) {
            return true;
        }
        var ambassadors = this.get('ambassadors');
        var dirty = false;
        ambassadors.forEach(function(ad){
             if (ad.get('isDirty')) {
                 dirty = true;
             }

        });
        return dirty;
    }.property('isDirty', 'ambassadors.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.transaction.commit();

            // The minimum number of ambassadors requirement was dropped, plus there was no visual feedback if not
            // enough ambassadors
            //if (model.get('validAmbassadors')) {
            controller.transitionToRoute(controller.get('nextStep'));
            //}

        },

        addAmbassador: function(){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            var ambassador = transaction.createRecord(App.MyProjectAmbassador, {});
            this.get('ambassadors').pushObject(ambassador);
        },

        removeAmbassador: function(ambassador){
            ambassador.deleteRecord();
        }
    }

});
*/

App.MyProjectPlanOrganisationController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.legal',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
        if (this.get('address.isDirty')) {
            return true;
        }
        return false;
    }.property('organization.isLoaded', 'organization.addresses.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            var organization = model.get('organization');
            var address = this.get('address');
            model.one('didUpdate', function(){
                // Connected an organization to ProjectPlan.
                controller.transitionToRoute(controller.get('nextStep'));
            });
            organization.one('didUpdate', function(){
                // Updated organization info.
                controller.transitionToRoute(controller.get('nextStep'));
            });
            address.one('didUpdate', function(){
                // Updated address info.
                controller.transitionToRoute(controller.get('nextStep'));
            });
            model.transaction.commit();
        },

        addAddress: function(){
            // Use the same transaction as the projectplan
            var organization =  this.get('model.organization');
            var transaction =  organization.transaction;
            var address = transaction.createRecord(App.MyOrganizationAddress);
            organization.get('addresses').pushObject(address);
        },

        removeAddress: function(address){
            address.deleteRecord();
        },

        selectOrganization: function(org){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            transaction.add(org);
            this.set('model.organization', org);
            if (this.get('model.organization.addresses.length') == 0) {
                this.send('addAddress');
            }
        },

        createNewOrganization: function() {
            var controller = this;
            var transaction = this.get('store').transaction();
            var org = transaction.createRecord(App.MyOrganization, {name: controller.get('model.title')});
            this.set('model.organization', org);

            transaction.commit();
            // TODO: This does not work.
            this.send('addAddress');
        }
    }
});

App.MyProjectPlanBankController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.submit',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
        return false;
    }.property('organization.isLoaded', 'isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model.organization');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });
            model.one('didUpdate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });
            model.one('didCreate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });

            model.save();
        }
    }
});

App.MyProjectPlanBudgetController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.bank',

    shouldSave: function(){
        // Determine if any part is dirty, project plan or any of the budget_lines
        if (this.get('isDirty')) {
            return true;
        }
        var budgetLines = this.get('budgetLines');
        var dirty = false;
        budgetLines.forEach(function(ad){
             if (ad.get('isDirty')) {
                 dirty = true;
             }

        });
        return dirty;
    }.property('isDirty', 'budgetLines.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.transaction.commit();
            if (model.get('validBudget')) {
                controller.transitionToRoute(controller.get('nextStep'));
            }
        },

        addBudgetLine: function(){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            var line = transaction.createRecord(App.MyProjectBudgetLine, {});
            this.get('budgetLines').pushObject(line);
        },

        removeBudgetLine: function(line){
            line.deleteRecord();
        }
    }

});

App.MyProjectPlanLegalController = Em.ObjectController.extend(App.Editable, {

    /*nextStep: 'myProjectPlan.ambassadors', */
    nextStep: 'myProjectPlan.campaign',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
    }.property('organization.isLoaded'),

    actions: {
        updateRecordOnServer: function() {
            var controller = this;
            var model = this.get('model.organization');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });
            model.one('didUpdate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });
            model.one('didCreate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });

            model.save();
        },

        removeFile: function(doc) {
            var transaction = this.get('model').transaction;
            transaction.add(doc);
            doc.deleteRecord();
            transaction.commit();
        }
    },

    addFile: function(file) {
        var store = this.get('store');
        var doc = store.createRecord(App.MyOrganizationDocument);
        doc.set('file', file);
        var org = this.get('organization');
        doc.set('organization', org);
        doc.save();
    }
});

App.MyProjectPlanSubmitController = Em.ObjectController.extend(App.Editable, {

    actions: {
        submitPlan: function(e){
            var controller = this;
            var model = this.get('model');
            model.set('status', 'submitted');
            model.on('didUpdate', function(){
                controller.transitionToRoute('myProjectPlanReview');
            });
            model.save();
        }
    },

    exit: function(){
        this.set('model.status', 'new');
        this._super();
    }
});

<commit_msg>If no address exists yet, cannot assign update action to it.<commit_after>App.ProjectListController = Em.ArrayController.extend({
    needs: ['projectSearchForm']
});

App.ProjectSearchFormController = Em.ObjectController.extend({
    needs: ['projectList'],

    // Number of results to show on one page
    pageSize: 8,

    // Have a property for this so we can easily use another list controller if we extend this.
    listController: function(){
        return this.get('controllers.projectList');
    }.property(),

    init: function(){
        // Make sure this record is in it's own transaction so it will never pollute other commits.
        var transaction = this.get('store').transaction();
        var form =  transaction.createRecord(App.ProjectSearch);
        this.set('model', form);
        this.updateSearch();
    },

    rangeStart: function(){
        return this.get('page') * this.get('pageSize') - this.get('pageSize') + 1;
    }.property('listController.model.length'),

    rangeEnd: function(){
        return this.get('page') * this.get('pageSize') - this.get('pageSize') + this.get('listController.model.length');
    }.property('listController.model.length'),

    hasNextPage: function(){
        var next = this.get('page') * this.get('pageSize');
        var total = this.get('listController.model.meta.total');
        return (next < total);
    }.property('listController.model.meta.total'),

    hasPreviousPage: function(){
        return (this.get('page') > 1);
    }.property('page'),

    orderedByPopularity: function(){
        return (this.get('ordering') == 'popularity');
    }.property('ordering'),
    orderedByTitle: function(){
        return (this.get('ordering') == 'title');
    }.property('ordering'),
    orderedByNewest: function(){
        return (this.get('ordering') == 'newest');
    }.property('ordering'),
    orderedByNeeded: function(){
        return (this.get('ordering') == 'needed');
    }.property('ordering'),
    orderedByDeadline: function(){
        return (this.get('ordering') == 'deadline');
    }.property('ordering'),

    updateSearch: function(sender, key){
        if (key != 'page') {
            // If the query changes we should jump back to page 1
            this.set('page', 1);
        }
        if (this.get('model.isDirty') ) {
            var list = this.get('listController');
            var controller = this;

            var query = {
                'page_size': this.get('pageSize'),
                'page': this.get('page'),
                'ordering': this.get('ordering'),
                'phase': this.get('phase'),
                'country': this.get('country'),
                'text': this.get('text'),
                'theme': this.get('theme')
            };
            var projects = App.ProjectPreview.find(query);
            list.set('model', projects);
        }
    }.observes('text', 'country', 'theme', 'phase', 'page', 'ordering'),

    actions: {
        nextPage: function(){
            this.incrementProperty('page');
        },

        previousPage: function(){
            this.decrementProperty('page');
        },

        sortOrder: function(order) {
            this.set('ordering', order);
        },
        clearForm: function(sender, key) {
            this.set('model.text', '');
            this.set('model.country', null);
            this.set('model.theme', null);
            this.set('model.phase', null);
        }

    }
});

App.ProjectController = Em.ObjectController.extend({
    isFundable: function(){
       return (this.get('phase') == 'campaign' && this.get('campaign.money_asked'));
    }.property('phase', 'campaign'),

    allTags: function() {
        var tags = this.get('plan.tags');
        return tags.reduce(function(previousValue, tag, index) {
            var separator = (index == 0 ? " " : ", ");
            return previousValue + separator + tag.id;
        }, "");
    }.property('tags.@each')

});

App.ProjectSupporterListController = Em.ArrayController.extend({
    supportersLoaded: function(sender, key) {
        if (this.get(key)) {
            this.set('model', this.get('supporters').toArray());
        } else {
            // Don't show old content when new content is being retrieved.
            this.set('model', null);
        }
    }.observes('supporters.isLoaded')

});

/*
 Project Manage Controllers
 */

App.MyProjectController = Em.ObjectController.extend({
    needs: ['currentUser']
});

App.MyPitchNewController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser'],
    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });

            model.one('didCreate', function(record){
                controller.transitionToRoute('myProjectPitch', record);
            });

            model.save();
        }
    }
});

App.MyProjectListController = Em.ArrayController.extend({
    needs: ['currentUser'],
    canPitchNew: function(){
        var can = true;
        this.get('model').forEach(function(project){
            if (project.get('inProgress')) {
                can = false;
            }
        });
        return can;
    }.property('model.@each.phase')

});

App.MyProjectPitchController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser']
});

App.MyProjectPitchBasicsController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.location'
});
App.MyProjectPitchLocationController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.media'
});
App.MyProjectPitchMediaController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPitch.submit',

    actions: {
        save: function(record) {
            this._super();
            this.get('model').reload();
        }
    }
});

App.MyProjectPitchSubmitController = Em.ObjectController.extend(App.Editable, {
    actions: {
        submitPitch: function(e){
            var controller = this;
            var model = this.get('model');
            model.set('status', 'submitted');
            model.on('didUpdate', function(){
                controller.transitionToRoute('myProjectPitchReview');
            });
            model.save();
        }
    },
    exit: function(){
        this.set('model.status', 'new');
        this._super();
    }
});

App.MyProjectPitchReviewController = Em.ObjectController.extend({});

App.MyProjectPlanController = Em.ObjectController.extend(App.Editable, {
    needs: ['currentUser']

});

App.MyProjectPlanBasicsController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.description'
});
App.MyProjectPlanDescriptionController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.location'
});
App.MyProjectPlanLocationController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.media'
});
App.MyProjectPlanSubmitController = Em.ObjectController.extend(App.Editable, {});
App.MyProjectPlanMediaController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.organisation'
});
App.MyProjectPlanCampaignController = Em.ObjectController.extend(App.Editable, {
    nextStep: 'myProjectPlan.budget'
});

/*
App.MyProjectPlanAmbassadorsController = Em.ObjectController.extend(App.Editable, {
    nextStep: function(){
        if (this.get('need') == 'skills') {
            return 'myProjectPlan.submit'
        } else {
            return 'myProjectPlan.campaign'
        }
    }.property('need'),

    shouldSave: function(){
        // Determine if any part is dirty, project plan or any of the ambassadors
        if (this.get('isDirty')) {
            return true;
        }
        var ambassadors = this.get('ambassadors');
        var dirty = false;
        ambassadors.forEach(function(ad){
             if (ad.get('isDirty')) {
                 dirty = true;
             }

        });
        return dirty;
    }.property('isDirty', 'ambassadors.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.transaction.commit();

            // The minimum number of ambassadors requirement was dropped, plus there was no visual feedback if not
            // enough ambassadors
            //if (model.get('validAmbassadors')) {
            controller.transitionToRoute(controller.get('nextStep'));
            //}

        },

        addAmbassador: function(){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            var ambassador = transaction.createRecord(App.MyProjectAmbassador, {});
            this.get('ambassadors').pushObject(ambassador);
        },

        removeAmbassador: function(ambassador){
            ambassador.deleteRecord();
        }
    }

});
*/

App.MyProjectPlanOrganisationController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.legal',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
        if (this.get('address.isDirty')) {
            return true;
        }
        return false;
    }.property('organization.isLoaded', 'organization.addresses.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            var organization = model.get('organization');
            var address = this.get('address');
            model.one('didUpdate', function(){
                // Connected an organization to ProjectPlan.
                controller.transitionToRoute(controller.get('nextStep'));
            });
            organization.one('didUpdate', function(){
                // Updated organization info.
                controller.transitionToRoute(controller.get('nextStep'));
            });
            if (address) {
                address.one('didUpdate', function(){
                    // Updated address info.
                    controller.transitionToRoute(controller.get('nextStep'));
                });
            }
            model.transaction.commit();
        },

        addAddress: function(){
            // Use the same transaction as the projectplan
            var organization =  this.get('model.organization');
            var transaction =  organization.transaction;
            var address = transaction.createRecord(App.MyOrganizationAddress);
            organization.get('addresses').pushObject(address);
        },

        removeAddress: function(address){
            address.deleteRecord();
        },

        selectOrganization: function(org){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            transaction.add(org);
            this.set('model.organization', org);
            if (this.get('model.organization.addresses.length') == 0) {
                this.send('addAddress');
            }
        },

        createNewOrganization: function() {
            var controller = this;
            var transaction = this.get('store').transaction();
            var org = transaction.createRecord(App.MyOrganization, {name: controller.get('model.title')});
            this.set('model.organization', org);

            transaction.commit();
            // TODO: This does not work.
            this.send('addAddress');
        }
    }
});

App.MyProjectPlanBankController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.submit',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
        return false;
    }.property('organization.isLoaded', 'isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model.organization');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });
            model.one('didUpdate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });
            model.one('didCreate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });

            model.save();
        }
    }
});

App.MyProjectPlanBudgetController = Em.ObjectController.extend(App.Editable, {

    nextStep: 'myProjectPlan.bank',

    shouldSave: function(){
        // Determine if any part is dirty, project plan or any of the budget_lines
        if (this.get('isDirty')) {
            return true;
        }
        var budgetLines = this.get('budgetLines');
        var dirty = false;
        budgetLines.forEach(function(ad){
             if (ad.get('isDirty')) {
                 dirty = true;
             }

        });
        return dirty;
    }.property('isDirty', 'budgetLines.@each.isDirty'),

    actions: {
        updateRecordOnServer: function(){
            var controller = this;
            var model = this.get('model');
            model.transaction.commit();
            if (model.get('validBudget')) {
                controller.transitionToRoute(controller.get('nextStep'));
            }
        },

        addBudgetLine: function(){
            // Use the same transaction as the projectplan
            var transaction =  this.get('model').transaction;
            var line = transaction.createRecord(App.MyProjectBudgetLine, {});
            this.get('budgetLines').pushObject(line);
        },

        removeBudgetLine: function(line){
            line.deleteRecord();
        }
    }

});

App.MyProjectPlanLegalController = Em.ObjectController.extend(App.Editable, {

    /*nextStep: 'myProjectPlan.ambassadors', */
    nextStep: 'myProjectPlan.campaign',

    shouldSave: function(){
        // Determine if any part is dirty, project plan, org or any of the org addresses
        if (this.get('isDirty')) {
            return true;
        }
        if (this.get('organization.isDirty')) {
            return true;
        }
    }.property('organization.isLoaded'),

    actions: {
        updateRecordOnServer: function() {
            var controller = this;
            var model = this.get('model.organization');
            model.one('becameInvalid', function(record){
                model.set('errors', record.get('errors'));
            });
            model.one('didUpdate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });
            model.one('didCreate', function(){
                controller.transitionToRoute(controller.get('nextStep'));
                window.scrollTo(0);
            });

            model.save();
        },

        removeFile: function(doc) {
            var transaction = this.get('model').transaction;
            transaction.add(doc);
            doc.deleteRecord();
            transaction.commit();
        }
    },

    addFile: function(file) {
        var store = this.get('store');
        var doc = store.createRecord(App.MyOrganizationDocument);
        doc.set('file', file);
        var org = this.get('organization');
        doc.set('organization', org);
        doc.save();
    }
});

App.MyProjectPlanSubmitController = Em.ObjectController.extend(App.Editable, {

    actions: {
        submitPlan: function(e){
            var controller = this;
            var model = this.get('model');
            model.set('status', 'submitted');
            model.on('didUpdate', function(){
                controller.transitionToRoute('myProjectPlanReview');
            });
            model.save();
        }
    },

    exit: function(){
        this.set('model.status', 'new');
        this._super();
    }
});

<|endoftext|>


<commit_before>const express = require('express');
const passport = require('passport');

const auth = express.Router();

auth.get('/signin', passport.authenticate('signIn', { scope: ['user-read-email', 'user-read-private', 'playlist-modify-public', 'playlist-modify-private', 'user-top-read', 'playlist-read-private'], showDialog: true }));

auth.get('/callback', passport.authenticate('signIn', { failureRedirect: '/', successRedirect: '/api/home' }));

module.exports = auth;
<commit_msg>Resolved<commit_after>const express = require('express');
const passport = require('passport');

const auth = express.Router();

auth.get('/signin', passport.authenticate('signIn', { scope: ['user-read-email', 'user-read-private', 'playlist-modify-public', 'playlist-modify-private', 'user-top-read', 'playlist-read-private'], showDialog: true }));

auth.get('/callback', passport.authenticate('signIn', { failureRedirect: '/', successRedirect: '/api/home' }));

auth.get('/logout', (req, res) => {
  req.logOut();
  req.session.destroy(() => {
    res.send('logout');
  });
});

module.exports = auth;
<|endoftext|>


<commit_before>define([
    'knockout',
    'jquery',
    'viewmodels/widget',
    'arches',
], function(ko, $, WidgetViewModel, arches) {
    var nameLookup = {};
    require(['views/components/workflows/new-tile-step']);
    var ResourceInstanceSelectViewModel = function(params) {
        var self = this;
        params.configKeys = ['placeholder'];
        this.multiple = params.multiple || false;
        this.value = params.value || undefined;
        this.disable = params.disable || function() {
            return false;
        };
        this.disableMessage = params.disableMessage || '';

        WidgetViewModel.apply(this, [params]);

        var displayName = ko.observable('');
        self.newTileStep = ko.observable();

        this.valueList = ko.computed(function() {
            var valueList = self.value();
            displayName();
            if (!self.multiple && valueList) {
                valueList = [valueList];
            }
            if (Array.isArray(valueList)) {
                return valueList;
            }
            return [];
        });

        this.valueObjects = ko.computed(function() {
            displayName();
            return self.valueList().map(function(value) {
                return {
                    id: value,
                    name: nameLookup[value],
                    reportUrl: arches.urls.resource_report + value
                };
            }).filter(function(item) {
                return item.name;
            });
        });

        var updateName = function() {
            var names = [];
            self.valueList().forEach(function(val) {
                if (val) {
                    if (nameLookup[val]) {
                        names.push(nameLookup[val]);
                        displayName(names.join(', '));
                    } else {
                        $.ajax(arches.urls.resource_descriptors + val, {
                            dataType: "json"
                        }).done(function(data) {
                            nameLookup[val] = data.displayname;
                            names.push(data.displayname);
                            displayName(names.join(', '));
                        });
                    }
                }
            });
        };
        this.value.subscribe(updateName);

        this.displayValue = ko.computed(function() {
            var val = self.value();
            var name = displayName();
            var displayVal = null;

            if (val) {
                displayVal = name;
            }

            return displayVal;
        });
        updateName();

        var relatedResourceModels = ko.computed(function() {
            if (params.node) {
                var ids = ko.unwrap(params.node.config.graphid);
                return arches.resources.filter(function(graph) {
                    return ids.indexOf(graph.graphid) >= 0;
                }).map(function(g) {
                    return {
                        name: g.name,
                        _id: g.graphid,
                        isGraph: true
                    };
                });
            }
        }, this);

        var url = ko.observable(arches.urls.search_results);
        this.url = url;
        this.select2Config = {
            value: this.value,
            clickBubble: true,
            multiple: this.multiple,
            placeholder: this.placeholder,
            closeOnSelect: false,
            allowClear: true,
            disabled: this.disabled,
            ajax: {
                url: function() {
                    return url();
                },
                dataType: 'json',
                quietMillis: 250,
                data: function(term, page) {
                    //TODO This regex isn't working, but it would nice fix it so that we can do more robust url checking
                    // var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
                    // var regex = new RegExp(expression);
                    // var isUrl = val.target.value.match(regex)
                    var isUrl = term.startsWith('http');
                    if (isUrl) {
                        url(term.replace('search', 'search/resources'));
                        return {};
                    } else {
                        url(arches.urls.search_results);
                        var graphid = params.node ? ko.unwrap(params.node.config.graphid) : undefined;
                        var data = {
                            'paging-filter': page
                        };
                        if (graphid && graphid.length > 0) {
                            data['resource-type-filter'] = JSON.stringify(
                                graphid.map(function(id) {
                                    return {
                                        "graphid": id,
                                        "inverted": false
                                    };
                                })
                            );
                        }
                        if (term) {
                            data['term-filter'] = JSON.stringify([{
                                "inverted": false,
                                "type": "string",
                                "context": "",
                                "context_label": "",
                                "id": term,
                                "text": term,
                                "value": term
                            }]);
                        }
                        return data;
                    }
                },

                results: function(data, page) {
                    if (!data['paging-filter'].paginator.has_next) {
                        if (relatedResourceModels()) {
                            relatedResourceModels().forEach(function(val) {
                                data.results.hits.hits.push(val);
                            });
                        }
                    }
                    return {
                        results: data.results.hits.hits,
                        more: data['paging-filter'].paginator.has_next
                    };
                }
            },
            id: function(item) {
                return item._id;
            },
            formatResult: function(item) {
                if (self.disable(item) === false) {
                    if (item._source) {
                        return item._source.displayname;
                    } else {
                        return ' Create a new ' + item.name + ' . . . ';
                    }
                } else {
                    return '' + item._source.displayname + ' ' + self.disableMessage + '';
                }
            },
            formatResultCssClass: function(item) {
                if (self.disable(item) === false) {
                    return '';
                } else {
                    return 'disabled';
                }
            },
            formatSelection: function(item) {
                if (item._source) {
                    return item._source.displayname;
                } else {
                    return item.name;
                }
            },
            initSelection: function(el, callback) {
                var valueList = self.valueList();
                var setSelectionData = function() {
                    var valueData = self.valueObjects().map(function(item) {
                        return {
                            _id: item.id,
                            _source: {
                                displayname: item.name
                            }
                        };
                    });
                    valueData = self.multiple ? valueData : valueData[0];
                    if (valueData) {
                        callback(valueData);
                    }
                };
                valueList.forEach(function(value) {
                    if (value) {
                        var modelIds = relatedResourceModels().map(function(model) {
                            return model._id;
                        });
                        if (!(modelIds.indexOf(value) > -1)) {
                            if (nameLookup[value]) {
                                setSelectionData();
                            } else {
                                $.ajax(arches.urls.resource_descriptors + value, {
                                    dataType: "json"
                                }).done(function(data) {
                                    nameLookup[value] = data.displayname;
                                    setSelectionData();
                                });
                            }
                        } else {
                            var params = {
                                graphid: value,
                                complete: ko.observable(false),
                                resourceid: ko.observable(),
                                tileid: ko.observable()
                            };
                            self.newTileStep(params);
                            params.complete.subscribe(function() {
                                self.value(params.resourceid());
                                self.newTileStep(null);
                            });
                        }
                    }
                });
            }
        };
    };

    return ResourceInstanceSelectViewModel;
});
<commit_msg>Handles for error when `params.node.config.graphid` is undefined.<commit_after>define([
    'knockout',
    'jquery',
    'viewmodels/widget',
    'arches',
], function(ko, $, WidgetViewModel, arches) {
    var nameLookup = {};
    require(['views/components/workflows/new-tile-step']);
    var ResourceInstanceSelectViewModel = function(params) {
        var self = this;
        params.configKeys = ['placeholder'];
        this.multiple = params.multiple || false;
        this.value = params.value || undefined;
        this.disable = params.disable || function() {
            return false;
        };
        this.disableMessage = params.disableMessage || '';

        WidgetViewModel.apply(this, [params]);

        var displayName = ko.observable('');
        self.newTileStep = ko.observable();

        this.valueList = ko.computed(function() {
            var valueList = self.value();
            displayName();
            if (!self.multiple && valueList) {
                valueList = [valueList];
            }
            if (Array.isArray(valueList)) {
                return valueList;
            }
            return [];
        });

        this.valueObjects = ko.computed(function() {
            displayName();
            return self.valueList().map(function(value) {
                return {
                    id: value,
                    name: nameLookup[value],
                    reportUrl: arches.urls.resource_report + value
                };
            }).filter(function(item) {
                return item.name;
            });
        });

        var updateName = function() {
            var names = [];
            self.valueList().forEach(function(val) {
                if (val) {
                    if (nameLookup[val]) {
                        names.push(nameLookup[val]);
                        displayName(names.join(', '));
                    } else {
                        $.ajax(arches.urls.resource_descriptors + val, {
                            dataType: "json"
                        }).done(function(data) {
                            nameLookup[val] = data.displayname;
                            names.push(data.displayname);
                            displayName(names.join(', '));
                        });
                    }
                }
            });
        };
        this.value.subscribe(updateName);

        this.displayValue = ko.computed(function() {
            var val = self.value();
            var name = displayName();
            var displayVal = null;

            if (val) {
                displayVal = name;
            }

            return displayVal;
        });
        updateName();

        var relatedResourceModels = ko.computed(function() {
            if (params.node) {
                var res = [];
                var ids = ko.unwrap(params.node.config.graphid);
                if (ids) {
                    res = arches.resources.filter(function(graph) {
                        return ids.indexOf(graph.graphid) >= 0;
                    }).map(function(g) {
                        return {
                            name: g.name,
                            _id: g.graphid,
                            isGraph: true
                        };
                    });
                }
                return res;
            }
        }, this);

        var url = ko.observable(arches.urls.search_results);
        this.url = url;
        this.select2Config = {
            value: this.value,
            clickBubble: true,
            multiple: this.multiple,
            placeholder: this.placeholder,
            closeOnSelect: false,
            allowClear: true,
            disabled: this.disabled,
            ajax: {
                url: function() {
                    return url();
                },
                dataType: 'json',
                quietMillis: 250,
                data: function(term, page) {
                    //TODO This regex isn't working, but it would nice fix it so that we can do more robust url checking
                    // var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
                    // var regex = new RegExp(expression);
                    // var isUrl = val.target.value.match(regex)
                    var isUrl = term.startsWith('http');
                    if (isUrl) {
                        url(term.replace('search', 'search/resources'));
                        return {};
                    } else {
                        url(arches.urls.search_results);
                        var graphid = params.node ? ko.unwrap(params.node.config.graphid) : undefined;
                        var data = {
                            'paging-filter': page
                        };
                        if (graphid && graphid.length > 0) {
                            data['resource-type-filter'] = JSON.stringify(
                                graphid.map(function(id) {
                                    return {
                                        "graphid": id,
                                        "inverted": false
                                    };
                                })
                            );
                        }
                        if (term) {
                            data['term-filter'] = JSON.stringify([{
                                "inverted": false,
                                "type": "string",
                                "context": "",
                                "context_label": "",
                                "id": term,
                                "text": term,
                                "value": term
                            }]);
                        }
                        return data;
                    }
                },

                results: function(data, page) {
                    if (!data['paging-filter'].paginator.has_next) {
                        if (relatedResourceModels()) {
                            relatedResourceModels().forEach(function(val) {
                                data.results.hits.hits.push(val);
                            });
                        }
                    }
                    return {
                        results: data.results.hits.hits,
                        more: data['paging-filter'].paginator.has_next
                    };
                }
            },
            id: function(item) {
                return item._id;
            },
            formatResult: function(item) {
                if (self.disable(item) === false) {
                    if (item._source) {
                        return item._source.displayname;
                    } else {
                        return ' Create a new ' + item.name + ' . . . ';
                    }
                } else {
                    return '' + item._source.displayname + ' ' + self.disableMessage + '';
                }
            },
            formatResultCssClass: function(item) {
                if (self.disable(item) === false) {
                    return '';
                } else {
                    return 'disabled';
                }
            },
            formatSelection: function(item) {
                if (item._source) {
                    return item._source.displayname;
                } else {
                    return item.name;
                }
            },
            initSelection: function(el, callback) {
                var valueList = self.valueList();
                var setSelectionData = function() {
                    var valueData = self.valueObjects().map(function(item) {
                        return {
                            _id: item.id,
                            _source: {
                                displayname: item.name
                            }
                        };
                    });
                    valueData = self.multiple ? valueData : valueData[0];
                    if (valueData) {
                        callback(valueData);
                    }
                };
                valueList.forEach(function(value) {
                    if (value) {
                        var modelIds = relatedResourceModels().map(function(model) {
                            return model._id;
                        });
                        if (!(modelIds.indexOf(value) > -1)) {
                            if (nameLookup[value]) {
                                setSelectionData();
                            } else {
                                $.ajax(arches.urls.resource_descriptors + value, {
                                    dataType: "json"
                                }).done(function(data) {
                                    nameLookup[value] = data.displayname;
                                    setSelectionData();
                                });
                            }
                        } else {
                            var params = {
                                graphid: value,
                                complete: ko.observable(false),
                                resourceid: ko.observable(),
                                tileid: ko.observable()
                            };
                            self.newTileStep(params);
                            params.complete.subscribe(function() {
                                self.value(params.resourceid());
                                self.newTileStep(null);
                            });
                        }
                    }
                });
            }
        };
    };

    return ResourceInstanceSelectViewModel;
});
<|endoftext|>