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

    /**
     * Reference file: https://github.com/photonstorm/phaser-examples/blob/master/projects/rox/src/Player.js
     */

    var textureKey = 'player1';
    var maxSpeed = 500;
    var fullJumpMeter = 20000;
    var jumpFactor = 0.7;
    var acc = 2000;
    var maxJumps = 2;
    var maxVelocity = new Phaser.Point(3000, 2000);
    var maxWalkingVelocity = new Phaser.Point(500, 0);
    var drag = {x: 1000, y: 0};

    Game.Player = function(game, x, y) {

        Phaser.Sprite.call(this, game, x, y, textureKey);

        // var desiredHeight = 20;
        // var scaleFactor = desiredHeight / this.height;
        // this.scale.setTo(scaleFactor)

        this.inventory = [];

        this.anchor.set(0.5);
        this.game.physics.enable(this, Phaser.Physics.ARCADE);

        this.body.collideWorldBounds = true;
        this.body.maxVelocity = maxVelocity;

        this.controller = new Game.Controller(this.game);

        // this.animations.add('running', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 10, true);
        // this.animations.add('jump', [3], 20, true);
        // this.animations.add('falling', [2], 20, true);
        this.animations.add('still', [0], 20, true);
        // this.animations.add('duck', [0], 20, true);

        this.currAnim = '';

        this.jumpMeter = fullJumpMeter;
        this.currentJumps = 0;

        this.ctrlKey = this.game.input.keyboard.addKey(17);

        this.godMode = false;

        this.jumpSFX = this.game.add.audio('jump');

        return this;
    };

    // Clone the Phaser.Sprite prototype, and use it as our own.
    // This is how inheritance works in JavaScript btw
    Game.Player.prototype = Object.create(Phaser.Sprite.prototype);
    Game.Player.prototype.constructor = Game.Player;

    Game.Player.prototype.updateBodySize = function() {
        // var currentFrame = this.animations.currentAnim.currentFrame;
        // if (!currentFrame) return;

        // if (this.body.height !== currentFrame.height) {
        //     this.body.height = currentFrame.height;
        // }
        // if (this.body.width !== currentFrame.width) {
        //     this.body.width = currentFrame.width;
        // }
    };

    Game.Player.prototype.resetJump = function() {
        this.currentJumps = 0;
        this.jumpMeter = fullJumpMeter;
    };

    Game.Player.prototype.jump = function() {
        if (this.controller.jump.isDown) {
            if (!this.jumpWasDown) {
                this.currentJumps += 1;

                if (this.currentJumps < maxJumps) {
                    this.jumpSFX.play();
                    this.body.velocity.y = 0;
                }
            }

            if (Math.floor(this.jumpMeter) > 0 && this.currentJumps < maxJumps) {
                this.body.acceleration.y -= this.jumpMeter;
                this.jumpMeter *= jumpFactor;

            }
        } else {
            if (this.jumpWasDown) {
                if (maxJumps >= this.currentJumps) {
                    this.jumpMeter = fullJumpMeter;
                } else {
                    this.jumpMeter = 0;
                }
            }
        }

        this.jumpWasDown = this.controller.jump.isDown;
    };

    Game.Player.prototype.animate = function() {
        var airborn = !this.body.onFloor();
        var walking = (this.controller.right.isDown || this.controller.left.isDown) && !airborn;
        var falling = airborn && this.body.velocity.y > 0;
        var rising = airborn && this.body.velocity.y < 0;
        var still = (this.body.touching.down || this.body.onFloor()) && Math.abs(this.body.velocity.x) < 40;
        var ducking = this.controller.down.isDown;

        // if (ducking) {
        //     this.animations.play('duck');
        //     return;
        // }

        // if (walking) {
        //     this.animations.play('running');
        //     return;
        // }

        // if (falling) {
        //     this.animations.play('falling');
        //     return;
        // }

        // if (rising) {
        //     this.animations.play('jump');
        //     return;
        // }

        // if (still) {
        //     this.animations.play('still');
        //     return;
        // }
    };

    Game.Player.prototype.update = function() {
        this.updateBodySize();
        this.body.acceleration.y = 0;

        if (this.controller.right.isDown) {
            this.body.acceleration.x = acc;

            if (!this.ctrlKey.isDown && !this.godMode) {
                this.body.velocity.x = Math.clamp(this.body.velocity.x, -maxWalkingVelocity.x, maxWalkingVelocity.x);
            }

            this.animations.play('running');

            if (this.scale.x < 0) {
                this.scale.x *= -1;
            }
        } else if (this.controller.left.isDown) {
            this.body.acceleration.x = -acc;

            if (!this.ctrlKey.isDown && !this.godMode) {
                this.body.velocity.x = Math.clamp(this.body.velocity.x, -maxWalkingVelocity.x, maxWalkingVelocity.x);
            }

            this.animations.play('running');

            if (this.scale.x > 0) {
                this.scale.x *= -1;
            }
        } else {
            this.body.acceleration.setTo(0);
            if (this.godMode) {
                this.body.velocity.x *= 0.95;
            }
        }

        if (!this.godMode) {
            this.jump();
        } else {
            if (this.controller.jump.isDown) {
                this.body.acceleration.y = -2000;
            } else if (this.controller.down.isDown) {
                this.body.acceleration.y = 2000;
            } else {
                this.body.velocity.y *= 0.95;
            }
        }

        if (this.body.onFloor() || this.body.touching.down) {
            this.resetJump();
            this.body.drag.setTo(drag.x, drag.y);
        } else {
            this.body.drag.setTo(0);
        }

        this.animate();
    };

})();
<commit_msg>Changed how the jump sfx triggers because holding down the jump button causing consecutive jumps did not trigger jump sfx before<commit_after>(function() {
    'use strict';

    /**
     * Reference file: https://github.com/photonstorm/phaser-examples/blob/master/projects/rox/src/Player.js
     */

    var textureKey = 'player1';
    var maxSpeed = 500;
    var fullJumpMeter = 20000;
    var jumpFactor = 0.7;
    var acc = 2000;
    var maxJumps = 2;
    var maxVelocity = new Phaser.Point(3000, 2000);
    var maxWalkingVelocity = new Phaser.Point(500, 0);
    var drag = {x: 1000, y: 0};

    Game.Player = function(game, x, y) {

        Phaser.Sprite.call(this, game, x, y, textureKey);

        // var desiredHeight = 20;
        // var scaleFactor = desiredHeight / this.height;
        // this.scale.setTo(scaleFactor)

        this.inventory = [];

        this.anchor.set(0.5);
        this.game.physics.enable(this, Phaser.Physics.ARCADE);

        this.body.collideWorldBounds = true;
        this.body.maxVelocity = maxVelocity;

        this.controller = new Game.Controller(this.game);

        // this.animations.add('running', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], 10, true);
        // this.animations.add('jump', [3], 20, true);
        // this.animations.add('falling', [2], 20, true);
        this.animations.add('still', [0], 20, true);
        // this.animations.add('duck', [0], 20, true);

        this.currAnim = '';

        this.jumpMeter = fullJumpMeter;
        this.currentJumps = 0;

        this.ctrlKey = this.game.input.keyboard.addKey(17);

        this.godMode = false;

        this.jumpSFX = this.game.add.audio('jump');

        return this;
    };

    // Clone the Phaser.Sprite prototype, and use it as our own.
    // This is how inheritance works in JavaScript btw
    Game.Player.prototype = Object.create(Phaser.Sprite.prototype);
    Game.Player.prototype.constructor = Game.Player;

    Game.Player.prototype.updateBodySize = function() {
        // var currentFrame = this.animations.currentAnim.currentFrame;
        // if (!currentFrame) return;

        // if (this.body.height !== currentFrame.height) {
        //     this.body.height = currentFrame.height;
        // }
        // if (this.body.width !== currentFrame.width) {
        //     this.body.width = currentFrame.width;
        // }
    };

    Game.Player.prototype.resetJump = function() {
        this.currentJumps = 0;
        this.jumpMeter = fullJumpMeter;
    };

    Game.Player.prototype.jump = function() {
        if (this.controller.jump.isDown) {
            if (!this.jumpWasDown) {
                this.currentJumps += 1;

                if (this.currentJumps < maxJumps) {
                    this.body.velocity.y = 0;
                }
            }

            if (Math.floor(this.jumpMeter) > 0 && this.currentJumps < maxJumps) {
                if (this.body.onFloor() || !this.jumpWasDown) {
                    this.jumpSFX.play();
                }

                this.body.acceleration.y -= this.jumpMeter;
                this.jumpMeter *= jumpFactor;
            }
        } else {
            if (this.jumpWasDown) {
                if (maxJumps >= this.currentJumps) {
                    this.jumpMeter = fullJumpMeter;
                } else {
                    this.jumpMeter = 0;
                }
            }
        }

        this.jumpWasDown = this.controller.jump.isDown;
    };

    Game.Player.prototype.animate = function() {
        var airborn = !this.body.onFloor();
        var walking = (this.controller.right.isDown || this.controller.left.isDown) && !airborn;
        var falling = airborn && this.body.velocity.y > 0;
        var rising = airborn && this.body.velocity.y < 0;
        var still = (this.body.touching.down || this.body.onFloor()) && Math.abs(this.body.velocity.x) < 40;
        var ducking = this.controller.down.isDown;

        // if (ducking) {
        //     this.animations.play('duck');
        //     return;
        // }

        // if (walking) {
        //     this.animations.play('running');
        //     return;
        // }

        // if (falling) {
        //     this.animations.play('falling');
        //     return;
        // }

        // if (rising) {
        //     this.animations.play('jump');
        //     return;
        // }

        // if (still) {
        //     this.animations.play('still');
        //     return;
        // }
    };

    Game.Player.prototype.update = function() {
        this.updateBodySize();
        this.body.acceleration.y = 0;

        if (this.controller.right.isDown) {
            this.body.acceleration.x = acc;

            if (!this.ctrlKey.isDown && !this.godMode) {
                this.body.velocity.x = Math.clamp(this.body.velocity.x, -maxWalkingVelocity.x, maxWalkingVelocity.x);
            }

            this.animations.play('running');

            if (this.scale.x < 0) {
                this.scale.x *= -1;
            }
        } else if (this.controller.left.isDown) {
            this.body.acceleration.x = -acc;

            if (!this.ctrlKey.isDown && !this.godMode) {
                this.body.velocity.x = Math.clamp(this.body.velocity.x, -maxWalkingVelocity.x, maxWalkingVelocity.x);
            }

            this.animations.play('running');

            if (this.scale.x > 0) {
                this.scale.x *= -1;
            }
        } else {
            this.body.acceleration.setTo(0);
            if (this.godMode) {
                this.body.velocity.x *= 0.95;
            }
        }

        if (!this.godMode) {
            this.jump();
        } else {
            if (this.controller.jump.isDown) {
                this.body.acceleration.y = -2000;
            } else if (this.controller.down.isDown) {
                this.body.acceleration.y = 2000;
            } else {
                this.body.velocity.y *= 0.95;
            }
        }

        if (this.body.onFloor() || this.body.touching.down) {
            this.resetJump();
            this.body.drag.setTo(drag.x, drag.y);
        } else {
            this.body.drag.setTo(0);
        }

        this.animate();
    };

})();
<|endoftext|>


<commit_before>import AppDispatcher from '../dispatcher/AppDispatcher';
import AppConstants  from '../constants/AppConstants';

import app from '../constants/app.js';

import mmd      from 'musicmetadata';
import fs       from 'fs';
import path     from 'path';
import mime     from 'mime';
import walkSync from 'walk-sync';

import remote from 'remote';

var globalShortcut = remote.require('global-shortcut');
var dialog         = remote.require('dialog');

var AppActions = {

    init: function() {

        // Usual tasks
        this.app.start();
        this.getTracks();
        this.settings.checkTheme();
        this.settings.checkDevMode();
        this.app.initShortcuts();

        // Prevent some events
        window.addEventListener('dragover', function (e) {
            e.preventDefault();
        }, false);

        window.addEventListener('drop', function (e) {
            e.preventDefault();
        }, false);

        // Remember dimensions and positionning
        var currentWindow = app.browserWindows.main;

        currentWindow.on('resize', function() {
            AppActions.app.saveBounds();
        });

        currentWindow.on('move', function() {
            AppActions.app.saveBounds();
        });
    },

    /**
     * Refresh the library
     */
    getTracks: function() {
        app.db.find({}).sort({ 'lArtist': 1, 'year': 1, 'album': 1, 'disk': 1, 'track.no': 1 }).exec(function (err, tracks) {
            if (err) throw err;
            else {
                AppDispatcher.dispatch({
                    actionType : AppConstants.APP_REFRESH_LIBRARY,
                    tracks     : tracks
                });
            }
        });
    },

    /**
     * Select and play a track
     */
    selectAndPlay: function(id) {

        AppDispatcher.dispatch({
            actionType : AppConstants.APP_SELECT_AND_PLAY,
            id         : id
        });
    },

    /**
     * Search
     */
    filterSearch: function(search) {

        AppDispatcher.dispatch({
            actionType : AppConstants.APP_FILTER_SEARCH,
            search     : search
        });
    },

    app: {

        start: function() {
            // nothing here anymore
        },

        close: function() {
            app.browserWindows.main.close();
        },

        saveBounds: function() {

            var self = AppActions;
            var now = window.performance.now();

            if (now - self.lastFilterSearch < 250) {
                clearTimeout(self.filterSearchTimeOut);
            }

            self.lastFilterSearch = now;

            self.filterSearchTimeOut = setTimeout(() => {

                app.config.set('bounds', app.browserWindows.main.getBounds());
                app.config.saveSync();

            }, 250);
        },

        initShortcuts: function() {

            // Global shortcuts
            globalShortcut.register('MediaPlayPause', function () {
                AppActions.player.playToggle();
            });

            globalShortcut.register('MediaPreviousTrack', function () {
                AppActions.player.previous();
            });

            globalShortcut.register('MediaNextTrack', function () {
                AppActions.player.next();
            });
        }
    },

    player: {

        toggle: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_TOGGLE
            });
        },

        play: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PLAY
            });
        },

        pause: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PAUSE
            });
        },

        stop: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_STOP
            });
        },

        next: function(e) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_NEXT,
                e          : e
            });
        },

        previous: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PREVIOUS
            });
        },

        shuffle: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_SHUFFLE
            });
        },

        setVolume: function(volume) {
            app.audio.volume = volume;
            app.config.set('volume', volume);
            app.config.saveSync();
        },

        repeat: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_REPEAT
            });
        },

        jumpTo: function(to) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_JUMP_TO,
                to         : to
            });
        }
    },

    queue: {

        clear: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_CLEAR
            });
        },

        remove: function(index) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_REMOVE,
                index      : index
            });
        },

        add: function(selected) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_ADD,
                selected   : selected
            });
        },

        addNext: function(selected) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_ADD_NEXT,
                selected   : selected
            });
        },

        setPlaylist: function(playlist) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_SET_PLAYLIST,
                playlist   : playlist
            });
        }
    },

    library: {

        addFolders(folders) {

            dialog.showOpenDialog({ properties: ['openDirectory', 'multiSelections']}, (folders) => {
                if(folders !== undefined) {
                    AppDispatcher.dispatch({
                        actionType : AppConstants.APP_LIBRARY_ADD_FOLDERS,
                        folders    : folders
                    });
                }
            });
        },

        removeFolder(index) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REMOVE_FOLDER,
                index      : index
            });
        },

        reset() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_START,
            });

            app.db.remove({ }, { multi: true }, function (err, numRemoved) {
                app.db.loadDatabase(function (err) {
                    if(err) {
                        throw err
                    } else {
                        AppActions.getTracks();
                        AppDispatcher.dispatch({
                            actionType : AppConstants.APP_LIBRARY_REFRESH_END,
                        });
                    }
                });
            });
        },

        refresh() {

            var folders = app.config.get('musicFolders');

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_START
            });

            // Start the big thing
            app.db.remove({ }, { multi: true }, function (err, numRemoved) {
                app.db.loadDatabase(function (err) {
                    if(err) throw err;
                    else {

                        var filesList = [];

                        // Loop through folders
                        folders.forEach(function(folder, index, folders) {
                            // Get the list of files
                            filesList = filesList.concat(walkSync(folder, { directories: false }).map((d) =>  path.join(folder, d)));
                        });

                        var filesListFiltered = [];

                        // Get the metadatas of all the files
                        filesList.forEach((file, i) => {
                            if(app.supportedFormats.indexOf(mime.lookup(file)) > -1) filesListFiltered.push(file);
                        });

                        if(filesListFiltered.length > 0) {
                            // Fake sync async loop
                            (function forloop(i){
                                if(i < filesListFiltered.length) {

                                    var file   = filesListFiltered[i];
                                    var stream = fs.createReadStream(file);

                                    // store in DB here
                                    mmd(stream, { duration: true }, function (err, metadata) {

                                        AppActions.settings.refreshProgress(parseInt(i * 100 / filesListFiltered.length));

                                        forloop(i + 1);
                                        if (err) console.warn('An error occured while reading ' + file + ' id3 tags: ' + err);

                                        delete metadata.picture;
                                        metadata.path = file;
                                        metadata.lArtist = metadata.artist.length === 0 ? ['unknown artist'] : metadata.artist[0].toLowerCase();

                                        if(metadata.artist.length === 0) metadata.artist = ['Unknown artist'];
                                        if(metadata.album === null || metadata.album === '') metadata.album = 'Unknown';
                                        if(metadata.title === null || metadata.title === '') metadata.title = path.parse(file).base;
                                        if(metadata.duration == '') metadata.duration = 0;

                                        // Let's insert in the data
                                        app.db.insert(metadata, function (err, newDoc) {
                                            if(err) throw err;
                                            if(i === filesListFiltered.length - 1) {
                                                AppActions.getTracks();
                                                AppDispatcher.dispatch({
                                                    actionType : AppConstants.APP_LIBRARY_REFRESH_END
                                                });
                                            }
                                        });
                                    });
                                }
                            })(0);
                        } else {
                            AppDispatcher.dispatch({
                                actionType : AppConstants.APP_LIBRARY_REFRESH_END
                            });
                        }
                    }
                });
            });
        }
    },

    settings: {

        checkTheme: function() {
            var themeName = app.config.get('theme');
            document.querySelector('body').classList.add('theme-' + themeName);
        },

        toggleDarkTheme: function() {

            var oldTheme = app.config.get('theme');
            var newTheme = oldTheme === 'light' ? 'dark' : 'light';

            document.querySelector('body').classList.remove('theme-' + oldTheme);
            document.querySelector('body').classList.add('theme-' + newTheme);

            app.config.set('theme', newTheme);
            app.config.saveSync();

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_REFRESH_CONFIG
            });
        },

        checkDevMode: function() {
            if(app.config.get('devMode')) app.browserWindows.main.openDevTools();
        },

        toggleDevMode: function() {

            app.config.set('devMode', !app.config.get('devMode'));

            // Open dev tools if needed
            if(app.config.get('devMode')) app.browserWindows.main.openDevTools();
            else app.browserWindows.main.closeDevTools();

            app.config.saveSync();

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_REFRESH_CONFIG
            });
        },

        refreshProgress: function(percentage) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_PROGRESS,
                percentage : percentage
            });
        }
    }
};

export default AppActions;
<commit_msg>Check if document exists in library before inserting it (#59)<commit_after>import AppDispatcher from '../dispatcher/AppDispatcher';
import AppConstants  from '../constants/AppConstants';

import app from '../constants/app.js';

import mmd      from 'musicmetadata';
import fs       from 'fs';
import path     from 'path';
import mime     from 'mime';
import walkSync from 'walk-sync';

import remote from 'remote';

var globalShortcut = remote.require('global-shortcut');
var dialog         = remote.require('dialog');

var AppActions = {

    init: function() {

        // Usual tasks
        this.app.start();
        this.getTracks();
        this.settings.checkTheme();
        this.settings.checkDevMode();
        this.app.initShortcuts();

        // Prevent some events
        window.addEventListener('dragover', function (e) {
            e.preventDefault();
        }, false);

        window.addEventListener('drop', function (e) {
            e.preventDefault();
        }, false);

        // Remember dimensions and positionning
        var currentWindow = app.browserWindows.main;

        currentWindow.on('resize', function() {
            AppActions.app.saveBounds();
        });

        currentWindow.on('move', function() {
            AppActions.app.saveBounds();
        });
    },

    /**
     * Refresh the library
     */
    getTracks: function() {
        app.db.find({}).sort({ 'lArtist': 1, 'year': 1, 'album': 1, 'disk': 1, 'track.no': 1 }).exec(function (err, tracks) {
            if (err) throw err;
            else {
                AppDispatcher.dispatch({
                    actionType : AppConstants.APP_REFRESH_LIBRARY,
                    tracks     : tracks
                });
            }
        });
    },

    /**
     * Select and play a track
     */
    selectAndPlay: function(id) {

        AppDispatcher.dispatch({
            actionType : AppConstants.APP_SELECT_AND_PLAY,
            id         : id
        });
    },

    /**
     * Search
     */
    filterSearch: function(search) {

        AppDispatcher.dispatch({
            actionType : AppConstants.APP_FILTER_SEARCH,
            search     : search
        });
    },

    app: {

        start: function() {
            // nothing here anymore
        },

        close: function() {
            app.browserWindows.main.close();
        },

        saveBounds: function() {

            var self = AppActions;
            var now = window.performance.now();

            if (now - self.lastFilterSearch < 250) {
                clearTimeout(self.filterSearchTimeOut);
            }

            self.lastFilterSearch = now;

            self.filterSearchTimeOut = setTimeout(() => {

                app.config.set('bounds', app.browserWindows.main.getBounds());
                app.config.saveSync();

            }, 250);
        },

        initShortcuts: function() {

            // Global shortcuts
            globalShortcut.register('MediaPlayPause', function () {
                AppActions.player.playToggle();
            });

            globalShortcut.register('MediaPreviousTrack', function () {
                AppActions.player.previous();
            });

            globalShortcut.register('MediaNextTrack', function () {
                AppActions.player.next();
            });
        }
    },

    player: {

        toggle: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_TOGGLE
            });
        },

        play: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PLAY
            });
        },

        pause: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PAUSE
            });
        },

        stop: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_STOP
            });
        },

        next: function(e) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_NEXT,
                e          : e
            });
        },

        previous: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_PREVIOUS
            });
        },

        shuffle: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_SHUFFLE
            });
        },

        setVolume: function(volume) {
            app.audio.volume = volume;
            app.config.set('volume', volume);
            app.config.saveSync();
        },

        repeat: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_REPEAT
            });
        },

        jumpTo: function(to) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_PLAYER_JUMP_TO,
                to         : to
            });
        }
    },

    queue: {

        clear: function() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_CLEAR
            });
        },

        remove: function(index) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_REMOVE,
                index      : index
            });
        },

        add: function(selected) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_ADD,
                selected   : selected
            });
        },

        addNext: function(selected) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_ADD_NEXT,
                selected   : selected
            });
        },

        setPlaylist: function(playlist) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_QUEUE_SET_PLAYLIST,
                playlist   : playlist
            });
        }
    },

    library: {

        addFolders(folders) {

            dialog.showOpenDialog({ properties: ['openDirectory', 'multiSelections']}, (folders) => {
                if(folders !== undefined) {
                    AppDispatcher.dispatch({
                        actionType : AppConstants.APP_LIBRARY_ADD_FOLDERS,
                        folders    : folders
                    });
                }
            });
        },

        removeFolder(index) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REMOVE_FOLDER,
                index      : index
            });
        },

        reset() {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_START,
            });

            app.db.remove({ }, { multi: true }, function (err, numRemoved) {
                app.db.loadDatabase(function (err) {
                    if(err) {
                        throw err
                    } else {
                        AppActions.getTracks();
                        AppDispatcher.dispatch({
                            actionType : AppConstants.APP_LIBRARY_REFRESH_END,
                        });
                    }
                });
            });
        },

        refresh() {

            console.time('lala');

            var folders = app.config.get('musicFolders');

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_START
            });

            // Start the big thing
            app.db.remove({ }, { multi: true }, function (err, numRemoved) {
                app.db.loadDatabase(function (err) {
                    if(err) throw err;
                    else {

                        var filesList = [];

                        // Loop through folders
                        folders.forEach(function(folder, index, folders) {
                            // Get the list of files
                            filesList = filesList.concat(walkSync(folder, { directories: false }).map((d) =>  path.join(folder, d)));
                        });

                        var filesListFiltered = [];

                        // Get the metadatas of all the files
                        filesList.forEach((file, i) => {
                            if(app.supportedFormats.indexOf(mime.lookup(file)) > -1) filesListFiltered.push(file);
                        });

                        if(filesListFiltered.length > 0) {
                            // Fake sync async loop
                            (function forloop(i){
                                if(i < filesListFiltered.length) {

                                    var file   = filesListFiltered[i];
                                    var stream = fs.createReadStream(file);

                                    // store in DB here
                                    mmd(stream, { duration: true }, function (err, metadata) {

                                        AppActions.settings.refreshProgress(parseInt(i * 100 / filesListFiltered.length));

                                        forloop(i + 1);
                                        if (err) console.warn('An error occured while reading ' + file + ' id3 tags: ' + err);

                                        delete metadata.picture;

                                        fs.realpath(file, (err, realpath) => {

                                            if(err) console.warn(err);

                                            metadata.path = realpath
                                            metadata.lArtist = metadata.artist.length === 0 ? ['unknown artist'] : metadata.artist[0].toLowerCase();

                                            if(metadata.artist.length === 0) metadata.artist = ['Unknown artist'];
                                            if(metadata.album === null || metadata.album === '') metadata.album = 'Unknown';
                                            if(metadata.title === null || metadata.title === '') metadata.title = path.parse(file).base;
                                            if(metadata.duration == '') metadata.duration = 0;

                                            app.db.find({ path: metadata.path }, function (err, docs) {
                                                if(err) console.warn(err);
                                                if(docs.length === 0) { // Track is not already in database
                                                    // Let's insert in the data
                                                    app.db.insert(metadata, function (err, newDoc) {
                                                        if(err) console.warn(err);
                                                        if(i === filesListFiltered.length - 1) {
                                                            console.timeEnd('lala');
                                                            AppActions.getTracks();
                                                            AppDispatcher.dispatch({
                                                                actionType : AppConstants.APP_LIBRARY_REFRESH_END
                                                            });
                                                        }
                                                    });
                                                } else {
                                                    if(i === filesListFiltered.length - 1) {
                                                        console.log('no insert');
                                                        console.timeEnd('lala');
                                                        AppActions.getTracks();
                                                        AppDispatcher.dispatch({
                                                            actionType : AppConstants.APP_LIBRARY_REFRESH_END
                                                        });
                                                    }
                                                }
                                            }); // db.find
                                        }); // fs.realpath
                                    }); // mmd
                                }
                            })(0);
                        } else {
                            AppDispatcher.dispatch({
                                actionType : AppConstants.APP_LIBRARY_REFRESH_END
                            });
                        }
                    }
                });
            });
        }
    },

    settings: {

        checkTheme: function() {
            var themeName = app.config.get('theme');
            document.querySelector('body').classList.add('theme-' + themeName);
        },

        toggleDarkTheme: function() {

            var oldTheme = app.config.get('theme');
            var newTheme = oldTheme === 'light' ? 'dark' : 'light';

            document.querySelector('body').classList.remove('theme-' + oldTheme);
            document.querySelector('body').classList.add('theme-' + newTheme);

            app.config.set('theme', newTheme);
            app.config.saveSync();

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_REFRESH_CONFIG
            });
        },

        checkDevMode: function() {
            if(app.config.get('devMode')) app.browserWindows.main.openDevTools();
        },

        toggleDevMode: function() {

            app.config.set('devMode', !app.config.get('devMode'));

            // Open dev tools if needed
            if(app.config.get('devMode')) app.browserWindows.main.openDevTools();
            else app.browserWindows.main.closeDevTools();

            app.config.saveSync();

            AppDispatcher.dispatch({
                actionType : AppConstants.APP_REFRESH_CONFIG
            });
        },

        refreshProgress: function(percentage) {
            AppDispatcher.dispatch({
                actionType : AppConstants.APP_LIBRARY_REFRESH_PROGRESS,
                percentage : percentage
            });
        }
    }
};

export default AppActions;
<|endoftext|>


<commit_before>var truncate = require('html-truncate');

module.exports = function ($stateProvider) {

  $stateProvider
  .state('newProject', {
    url: '/new-project',
    parent: 'main',
    resolve: {
      project: function() { return null }
    },
    views: {
      main: {
        templateUrl: '/ui/project/edit.tpl.html',
        controller: 'ProjectEditCtrl'
      }
    }
  })
  .state('editProject', {
    url: '/project/:id/edit',
    parent: 'main',
    resolve: {
      project: /*@ngInject*/ function(Project, $stateParams) {
        return Project.get({id: $stateParams.id}).$promise;
      }
    },
    views: {
      main: {
        templateUrl: '/ui/project/edit.tpl.html',
        controller: 'ProjectEditCtrl'
      }
    }
  })
  .state('project', /*@ngInject*/ {
    url: '/project/:id',
    parent: 'main',
    abstract: true,
    resolve: {
      project: function(Project, $stateParams) {
        return Project.get({id: $stateParams.id}).$promise;
      }
    },
    views: {
      main: {
        templateUrl: '/ui/project/show.tpl.html',
        controller: function($scope, project, currentUser, growl) {
          $scope.project = project;
          $scope.isCreator = currentUser && project.user_id === currentUser.id;

          $scope.details = truncate(project.details || '', 420);
          $scope.truncatedDetails = project.details && $scope.details !== project.details;
          $scope.showFullDetails = function() {
            $scope.details = project.details;
            $scope.truncatedDetails = false;
          };

          $scope.publish = function() {
            project.publish(function(resp) {
              project.published_at = resp.published_at;
              growl.addSuccessMessage('Published! You can unpublish from the Edit Project screen if you change your mind.');
            });
          };

        }
      }
    }
  })
  .state('project.requests', /*@ngInject*/ {
    url: '',
    resolve: {
      requests: function(Seed, project) {
        return Seed.queryForProject({projectId: project.id}).$promise;
      }
    },
    views: {
      tab: {
        templateUrl: '/ui/project/requests.tpl.html',
        controller: function($scope, project, Seed, growl, Cache, UserCache, $analytics, requests, currentUser) {

          $scope.posts = requests;
          var newRequest = $scope.newRequest = {};

          $scope.addRequest = function() {
            new Seed({
              name: newRequest.name,
              projectId: project.id,
              communityId: project.community.id,
              type: 'request'
            }).$save(function() {
              $analytics.eventTrack('Add Post', {
                has_mention: $scope.hasMention,
                community_name: project.community.name,
                community_id: project.community.id,
                project_id: project.id
              });

              // FIXME this is copied from SeedEditCtrl
              Cache.drop('community.seeds:' + project.community.id);
              UserCache.seeds.clear(currentUser.id);
              UserCache.allSeeds.clear(currentUser.id);

              // FIXME this is slow
              $scope.posts = Seed.queryForProject({projectId: project.id});
              newRequest.name = null;

            }, function(err) {
              growl.addErrorMessage(err.data);
              $analytics.eventTrack('Add Post Failed');
            });

          };

          $scope.removePost = function(post) {
            growl.addSuccessMessage("Post has been removed: " + post.name, {ttl: 5000});
            $analytics.eventTrack('Post: Remove', {post_name: post.name, post_id: post.id});
            $scope.posts.splice($scope.posts.indexOf(post), 1);
          };

        }
      }
    }
  })
  .state('project.contributors', /*@ngInject*/ {
    url: '/contributors',
    views: {
      tab: {
        template: 'contributors tab placeholder. {{foo}}',
        controller: function($scope) {
          $scope.foo = 'controller stub is working.';
        }
      }
    }
  });

};<commit_msg>make project details rich text<commit_after>var RichText = require('../services/RichText');

module.exports = function ($stateProvider) {

  $stateProvider
  .state('newProject', {
    url: '/new-project',
    parent: 'main',
    resolve: {
      project: function() { return null }
    },
    views: {
      main: {
        templateUrl: '/ui/project/edit.tpl.html',
        controller: 'ProjectEditCtrl'
      }
    }
  })
  .state('editProject', {
    url: '/project/:id/edit',
    parent: 'main',
    resolve: {
      project: /*@ngInject*/ function(Project, $stateParams) {
        return Project.get({id: $stateParams.id}).$promise;
      }
    },
    views: {
      main: {
        templateUrl: '/ui/project/edit.tpl.html',
        controller: 'ProjectEditCtrl'
      }
    }
  })
  .state('project', /*@ngInject*/ {
    url: '/project/:id',
    parent: 'main',
    abstract: true,
    resolve: {
      project: function(Project, $stateParams) {
        return Project.get({id: $stateParams.id}).$promise;
      }
    },
    views: {
      main: {
        templateUrl: '/ui/project/show.tpl.html',
        controller: function($scope, project, currentUser, growl) {
          $scope.project = project;
          $scope.isCreator = currentUser && project.user_id === currentUser.id;

          $scope.details = RichText.present(project.details, {maxlength: 420});
          $scope.truncatedDetails = project.details && project.details.length > 420;

          $scope.showFullDetails = function() {
            $scope.details = RichText.present(project.details);
            $scope.truncatedDetails = false;
          };

          $scope.publish = function() {
            project.publish(function(resp) {
              project.published_at = resp.published_at;
              growl.addSuccessMessage('Published! You can unpublish from the Edit Project screen if you change your mind.');
            });
          };

        }
      }
    }
  })
  .state('project.requests', /*@ngInject*/ {
    url: '',
    resolve: {
      requests: function(Seed, project) {
        return Seed.queryForProject({projectId: project.id}).$promise;
      }
    },
    views: {
      tab: {
        templateUrl: '/ui/project/requests.tpl.html',
        controller: function($scope, project, Seed, growl, Cache, UserCache, $analytics, requests, currentUser) {

          $scope.posts = requests;
          var newRequest = $scope.newRequest = {};

          $scope.addRequest = function() {
            new Seed({
              name: newRequest.name,
              projectId: project.id,
              communityId: project.community.id,
              type: 'request'
            }).$save(function() {
              $analytics.eventTrack('Add Post', {
                has_mention: $scope.hasMention,
                community_name: project.community.name,
                community_id: project.community.id,
                project_id: project.id
              });

              // FIXME this is copied from SeedEditCtrl
              Cache.drop('community.seeds:' + project.community.id);
              UserCache.seeds.clear(currentUser.id);
              UserCache.allSeeds.clear(currentUser.id);

              // FIXME this is slow
              $scope.posts = Seed.queryForProject({projectId: project.id});
              newRequest.name = null;

            }, function(err) {
              growl.addErrorMessage(err.data);
              $analytics.eventTrack('Add Post Failed');
            });

          };

          $scope.removePost = function(post) {
            growl.addSuccessMessage("Post has been removed: " + post.name, {ttl: 5000});
            $analytics.eventTrack('Post: Remove', {post_name: post.name, post_id: post.id});
            $scope.posts.splice($scope.posts.indexOf(post), 1);
          };

        }
      }
    }
  })
  .state('project.contributors', /*@ngInject*/ {
    url: '/contributors',
    views: {
      tab: {
        template: 'contributors tab placeholder. {{foo}}',
        controller: function($scope) {
          $scope.foo = 'controller stub is working.';
        }
      }
    }
  });

};<|endoftext|>


<commit_before>// Auto Generated on 2022-05-05T22:11:10.917Z
 module.exports = [
  {
    "event": {
      "title": "data.MAY <- Provisional: MLens and Cubist",
      "description": "CALL FOR SPEAKERS:\n\nDo you have an interesting data story to tell?\n\nWould you like to give this month's presentation?\n\nFollow the link below to our Google form to express your interest and we'll contact you soon!\n\nhttps://forms.gle/y57b2seehSgvtGgn7\n\n##-------------------------------------------------------------------##\n\nVENUE:\n\nNEW LOCATION!\n\nThe university's brand new Q building in Honeysuckle!\n\nhttps://goo.gl/maps/35LwcLhKV1nvoFAh7\n\nIMPORTANT this is NOT the NewSpace building where we met last year.\n\nWe will meet in the Level 2 Seminar Space. You'll need to enter through the main doors on Worth Place and will be directed to the elevators to join the meetup.\n\nPARKING: The closest parking is Wright Lane Carpark.\nhttps://goo.gl/maps/rj6Yg8EkpjYzUmbi7\n\nAfter 5pm it is free and is a 400m - 5 minute walk.\n\n##-------------------------------------------------------------------##\n\nTIME: 5.30pm - 7.30pm\n\nFOOD: 🍕 + 🥤\n- Wood fired pizza, sponsored by Newy Tech People\n- Please advise dietary requirements in RSVP\n- Soft drinks\n\nSCHEDULE:\n- 5:30pm: Networking + Food + Drinks\n(Don't stress if you are held up in traffic)\n- 6:15pm: Presentation\n- 7:15pm: Packing up + Bonus questions\n- 7:30pm: Close",
      "dateTime": "2022-05-19T17:30+10:00",
      "eventUrl": "https://www.meetup.com/Hunter-Data-Analytics/events/283702245",
      "going": 12,
      "maxTickets": 40,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/501789003/676x380.webp",
      "venue": {
        "name": "Q Building, The University of Newcastle",
        "lat": -32.926277,
        "lng": 151.7683,
        "address": "16B Honeysuckle Dr",
        "city": "Newcastle"
      }
    },
    "group": {
      "name": "Hunter Data Analytics",
      "urlname": "Hunter-Data-Analytics",
      "groupPhoto": {
        "id": "483576656",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "483576656",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  },
  {
    "event": {
      "title": "Beer and chats",
      "description": "It's been a while... time for a beer (or drink of choice) and a chat, whether you work in the design space, are studying design or just interested in design.\n\nPlease stay at home if you're unwell, you know the drill by now!\n\nLooking forward to catching up in person.",
      "dateTime": "2022-05-25T18:00+10:00",
      "eventUrl": "https://www.meetup.com/IxDA-Newcastle/events/285605518",
      "going": 11,
      "maxTickets": 0,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/468275527/676x380.webp",
      "venue": {
        "name": "The Edwards",
        "lat": -32.92747,
        "lng": 151.75963,
        "address": "148 Parry St",
        "city": "Newcastle West"
      }
    },
    "group": {
      "name": "IxDA Newcastle",
      "urlname": "IxDA-Newcastle",
      "groupPhoto": {
        "id": "468275527",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "468275527",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  },
  {
    "event": {
      "title": "Innovation in the Water Industry using IoT",
      "description": "IoT Pioneers in June, hear from Sydney Water's IoT Manager Christoph Prackwieser as he walks us through innovation in the water Industry.\n\nWith a PHD in business informatics and a degree in IT from Vienna, Christoph has had more than 20 years of experience in Aligning information technology with business needs to create solutions that drive change.\n\nAs always, after the presentation, there will be opportunities to network with other attendees and enjoy some of the fine food on offer.\n\nJoin us on June 2nd from 6:30 pm - 8:30 pm at The Stag & Hunter Hotel. Feel free to grab a drink at the bar and come on up; dinner will be provided.\n\nPlease make sure to RSVP\n\n#meetup #networking #engineering #startup #IoT",
      "dateTime": "2022-06-02T18:30+10:00",
      "eventUrl": "https://www.meetup.com/Newcastle-IoT-Pioneers/events/285707120",
      "going": 2,
      "maxTickets": 0,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/503884007/676x380.webp",
      "venue": {
        "name": "The Stag and Hunter hotel ",
        "lat": -32.897346,
        "lng": 151.73651,
        "address": "cnr hanbury and maitland rd  mayfield",
        "city": "Newcastle"
      }
    },
    "group": {
      "name": "Newcastle IoT Pioneers",
      "urlname": "Newcastle-IoT-Pioneers",
      "groupPhoto": {
        "id": "465862831",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "465862831",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  }
]<commit_msg>Get latest set of Meetup events<commit_after>// Auto Generated on 2022-05-05T22:12:10.583Z
 module.exports = [
  {
    "event": {
      "title": "data.MAY <- Provisional: MLens and Cubist",
      "description": "CALL FOR SPEAKERS:\n\nDo you have an interesting data story to tell?\n\nWould you like to give this month's presentation?\n\nFollow the link below to our Google form to express your interest and we'll contact you soon!\n\nhttps://forms.gle/y57b2seehSgvtGgn7\n\n##-------------------------------------------------------------------##\n\nVENUE:\n\nNEW LOCATION!\n\nThe university's brand new Q building in Honeysuckle!\n\nhttps://goo.gl/maps/35LwcLhKV1nvoFAh7\n\nIMPORTANT this is NOT the NewSpace building where we met last year.\n\nWe will meet in the Level 2 Seminar Space. You'll need to enter through the main doors on Worth Place and will be directed to the elevators to join the meetup.\n\nPARKING: The closest parking is Wright Lane Carpark.\nhttps://goo.gl/maps/rj6Yg8EkpjYzUmbi7\n\nAfter 5pm it is free and is a 400m - 5 minute walk.\n\n##-------------------------------------------------------------------##\n\nTIME: 5.30pm - 7.30pm\n\nFOOD: 🍕 + 🥤\n- Wood fired pizza, sponsored by Newy Tech People\n- Please advise dietary requirements in RSVP\n- Soft drinks\n\nSCHEDULE:\n- 5:30pm: Networking + Food + Drinks\n(Don't stress if you are held up in traffic)\n- 6:15pm: Presentation\n- 7:15pm: Packing up + Bonus questions\n- 7:30pm: Close",
      "dateTime": "2022-05-19T17:30+10:00",
      "eventUrl": "https://www.meetup.com/Hunter-Data-Analytics/events/283702245",
      "going": 12,
      "maxTickets": 40,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/501789003/676x380.webp",
      "venue": {
        "name": "Q Building, The University of Newcastle",
        "lat": -32.926277,
        "lng": 151.7683,
        "address": "16B Honeysuckle Dr",
        "city": "Newcastle"
      }
    },
    "group": {
      "name": "Hunter Data Analytics",
      "urlname": "Hunter-Data-Analytics",
      "groupPhoto": {
        "id": "483576656",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "483576656",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  },
  {
    "event": {
      "title": "Beer and chats",
      "description": "It's been a while... time for a beer (or drink of choice) and a chat, whether you work in the design space, are studying design or just interested in design.\n\nPlease stay at home if you're unwell, you know the drill by now!\n\nLooking forward to catching up in person.",
      "dateTime": "2022-05-25T18:00+10:00",
      "eventUrl": "https://www.meetup.com/IxDA-Newcastle/events/285605518",
      "going": 11,
      "maxTickets": 0,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/468275527/676x380.webp",
      "venue": {
        "name": "The Edwards",
        "lat": -32.92747,
        "lng": 151.75963,
        "address": "148 Parry St",
        "city": "Newcastle West"
      }
    },
    "group": {
      "name": "IxDA Newcastle",
      "urlname": "IxDA-Newcastle",
      "groupPhoto": {
        "id": "468275527",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "468275527",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  },
  {
    "event": {
      "title": "Innovation in the Water Industry using IoT",
      "description": "IoT Pioneers in June, hear from Sydney Water's IoT Manager Christoph Prackwieser as he walks us through innovation in the water Industry.\n\nWith a PHD in business informatics and a degree in IT from Vienna, Christoph has had more than 20 years of experience in Aligning information technology with business needs to create solutions that drive change.\n\nAs always, after the presentation, there will be opportunities to network with other attendees and enjoy some of the fine food on offer.\n\nJoin us on June 2nd from 6:30 pm - 8:30 pm at The Stag & Hunter Hotel. Feel free to grab a drink at the bar and come on up; dinner will be provided.\n\nPlease make sure to RSVP\n\n#meetup #networking #engineering #startup #IoT",
      "dateTime": "2022-06-02T18:30+10:00",
      "eventUrl": "https://www.meetup.com/Newcastle-IoT-Pioneers/events/285707120",
      "going": 2,
      "maxTickets": 0,
      "duration": "PT2H",
      "imageUrl": "https://secure-content.meetupstatic.com/images/classic-events/503884007/676x380.webp",
      "venue": {
        "name": "The Stag and Hunter hotel ",
        "lat": -32.897346,
        "lng": 151.73651,
        "address": "cnr hanbury and maitland rd  mayfield",
        "city": "Newcastle"
      }
    },
    "group": {
      "name": "Newcastle IoT Pioneers",
      "urlname": "Newcastle-IoT-Pioneers",
      "groupPhoto": {
        "id": "465862831",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      },
      "logo": {
        "id": "465862831",
        "baseUrl": "https://secure-content.meetupstatic.com/images/classic-events/",
        "preview": null
      }
    }
  }
]<|endoftext|>


<commit_before>(function($) {
	MediaElementPlayer.prototype.buildvolume = function(player, controls, layers, media) {
		var mute =
			$('
'+
				''+
				'
'+ // outer background
					'

'+ // line background
					'

'+ // current volume
					'

'+ // handle
				'
'+
			'
')
			.appendTo(controls),
		volumeSlider = mute.find('.mejs-volume-slider'),
		volumeTotal = mute.find('.mejs-volume-total'),
		volumeCurrent = mute.find('.mejs-volume-current'),
		volumeHandle = mute.find('.mejs-volume-handle'),

		positionVolumeHandle = function(volume) {

			var
				top = volumeTotal.height() - (volumeTotal.height() * volume);

			// handle
			volumeHandle.css('top', top - (volumeHandle.height() / 2));

			// show the current visibility
			volumeCurrent.height(volumeTotal.height() - top + parseInt(volumeTotal.css('top').replace(/px/,''),10));
			volumeCurrent.css('top',  top);
		},
		handleVolumeMove = function(e) {
			var
				railHeight = volumeTotal.height(),
				totalOffset = volumeTotal.offset(),
				totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
				newY = e.pageY - totalOffset.top,
				volume = (railHeight - newY) / railHeight

			// TODO: handle vertical and horizontal CSS
			// only allow it to move within the rail
			if (newY < 0)
				newY = 0;
			else if (newY > railHeight)
				newY = railHeight;

			// move the handle to match the mouse
			volumeHandle.css('top', newY - (volumeHandle.height() / 2) + totalTop );

			// show the current visibility
			volumeCurrent.height(railHeight-newY);
			volumeCurrent.css('top',newY+totalTop);

			// set mute status
			if (volume == 0) {
				media.setMuted(true);
				mute.removeClass('mejs-mute').addClass('mejs-unmute');
			} else {
				media.setMuted(false);
				mute.removeClass('mejs-unmute').addClass('mejs-mute');
			}

			volume = Math.max(0,volume);
			volume = Math.min(volume,1);

			// set the volume
			media.setVolume(volume);
		},
		mouseIsDown = false;

		// SLIDER
		volumeSlider
			.bind('mousedown', function (e) {
				handleVolumeMove(e);
				mouseIsDown = true;
				return false;
			});
		$(document)
			.bind('mouseup', function (e) {
				mouseIsDown = false;
			})
			.bind('mousemove', function (e) {
				if (mouseIsDown) {
					handleVolumeMove(e);
				}
			});

		// MUTE button
		mute.find('span').click(function() {
			if (media.muted) {
				media.setMuted(false);
				mute.removeClass('mejs-unmute').addClass('mejs-mute');
				positionVolumeHandle(1);
			} else {
				media.setMuted(true);
				mute.removeClass('mejs-mute').addClass('mejs-unmute');
				positionVolumeHandle(0);
			}
		});

		// listen for volume change events from other sources
		media.addEventListener('volumechange', function(e) {
			if (!mouseIsDown) {
				positionVolumeHandle(e.target.volume);
			}
		}, true);

		// set initial volume
		//player.options.startVolume = Math.min(Math.max(0,player.options.startVolume),1);
		media.setVolume(player.options.startVolume);
	}

})(jQuery);<commit_msg>- initial volume fix<commit_after>(function($) {
	MediaElementPlayer.prototype.buildvolume = function(player, controls, layers, media) {
		var mute =
			$('
'+
				''+
				'
'+ // outer background
					'

'+ // line background
					'

'+ // current volume
					'

'+ // handle
				'
'+
			'
')
			.appendTo(controls),
		volumeSlider = mute.find('.mejs-volume-slider'),
		volumeTotal = mute.find('.mejs-volume-total'),
		volumeCurrent = mute.find('.mejs-volume-current'),
		volumeHandle = mute.find('.mejs-volume-handle'),

		positionVolumeHandle = function(volume) {

			var
				top = volumeTotal.height() - (volumeTotal.height() * volume);

			// handle
			volumeHandle.css('top', top - (volumeHandle.height() / 2));

			// show the current visibility
			volumeCurrent.height(volumeTotal.height() - top + parseInt(volumeTotal.css('top').replace(/px/,''),10));
			volumeCurrent.css('top',  top);
		},
		handleVolumeMove = function(e) {
			var
				railHeight = volumeTotal.height(),
				totalOffset = volumeTotal.offset(),
				totalTop = parseInt(volumeTotal.css('top').replace(/px/,''),10),
				newY = e.pageY - totalOffset.top,
				volume = (railHeight - newY) / railHeight

			// TODO: handle vertical and horizontal CSS
			// only allow it to move within the rail
			if (newY < 0)
				newY = 0;
			else if (newY > railHeight)
				newY = railHeight;

			// move the handle to match the mouse
			volumeHandle.css('top', newY - (volumeHandle.height() / 2) + totalTop );

			// show the current visibility
			volumeCurrent.height(railHeight-newY);
			volumeCurrent.css('top',newY+totalTop);

			// set mute status
			if (volume == 0) {
				media.setMuted(true);
				mute.removeClass('mejs-mute').addClass('mejs-unmute');
			} else {
				media.setMuted(false);
				mute.removeClass('mejs-unmute').addClass('mejs-mute');
			}

			volume = Math.max(0,volume);
			volume = Math.min(volume,1);

			// set the volume
			media.setVolume(volume);
		},
		mouseIsDown = false;

		// SLIDER
		volumeSlider
			.bind('mousedown', function (e) {
				handleVolumeMove(e);
				mouseIsDown = true;
				return false;
			});
		$(document)
			.bind('mouseup', function (e) {
				mouseIsDown = false;
			})
			.bind('mousemove', function (e) {
				if (mouseIsDown) {
					handleVolumeMove(e);
				}
			});

		// MUTE button
		mute.find('span').click(function() {
			if (media.muted) {
				media.setMuted(false);
				mute.removeClass('mejs-unmute').addClass('mejs-mute');
				positionVolumeHandle(1);
			} else {
				media.setMuted(true);
				mute.removeClass('mejs-mute').addClass('mejs-unmute');
				positionVolumeHandle(0);
			}
		});

		// listen for volume change events from other sources
		media.addEventListener('volumechange', function(e) {
			if (!mouseIsDown) {
				positionVolumeHandle(e.target.volume);
			}
		}, true);

		// set initial volume
		//player.options.startVolume = Math.min(Math.max(0,player.options.startVolume),1);
		positionVolumeHandle(player.options.startVolume);
		media.setVolume(player.options.startVolume);
	}

})(jQuery);<|endoftext|>


<commit_before>/* global history */

import './common'

let Awesomplete = require('awesomplete') // eslint-disable-line
let geolib = require('geolib')
import List from 'list.js'
import Holder from 'holderjs'
import Bricks from 'bricks.js'
import Find from 'lodash/collection/find'
import ForEach from 'lodash/collection/forEach'
import moment from 'moment'

let apiRoutes = require('./api')
let browser = require('./browser')
let getApiData = require('./get-api-data')
let getLocation = require('./get-location')
let templating = require('./template-render')
let getUrlParams = require('./get-url-parameter')
let accordion = require('./accordion')
let socialShare = require('./social-share')
import listToSelect from './list-to-dropdown'

let formatDate = (needs) => {
  // Change to relative date
  ForEach(needs, function (need) {
    need.formattedCreationDate = moment(need.creationDate).fromNow()
  })

  return needs
}

let renderNeeds = (needs) => {
  let theData = { card: needs }

  let listCallback = () => {
    buildList()
    buildCard(needs)
    browser.loaded()
  }

  templating.renderTemplate('js-card-list-tpl', theData, 'js-card-list-output', listCallback)
}

let initAutoComplete = (needs) => {
  let keywords = needs
    .map((n) => n.keywords)
    .filter((k) => k.length > 0)
    .join(',')

  let input = document.querySelector('.search')
  let awesomplete = new Awesomplete(input, {list: keywords}) // eslint-disable-line
}

let useDistanceForLocation = (position, needs) => {
  let latitude = position.coords.latitude
  let longitude = position.coords.longitude
  needs.forEach((n) => {
    let distanceInMetres = geolib.getDistance(
      { latitude: latitude, longitude: longitude },
      { latitude: n.latitude, longitude: n.longitude }
    )
    n.distanceAwayInMetres = distanceInMetres
    n.locationDescription = (distanceInMetres * 0.00062137).toFixed(2) + ' miles away'
  })

  return needs
}

let usePostcodeForLocation = (needs) => {
  needs.forEach((n) => {
    n.locationDescription = n.postcode
  })
  return needs
}

let init = () => {
  browser.loading()
  listToSelect.init()
  getApiData.data(apiRoutes.needs)
    .then(function (result) {
      let needsFromApi = formatDate(result.data)

      initAutoComplete(needsFromApi)

      if (navigator.geolocation) {
        getLocation.location().then(function (position) {
          renderNeeds(useDistanceForLocation(position, needsFromApi))
        }, () => {
          renderNeeds(usePostcodeForLocation(needsFromApi))
        })
      } else {
        renderNeeds(usePostcodeForLocation(needsFromApi))
      }
    })
}

let initBricks = () => {
  const sizes = [
   { columns: 1, gutter: 20 }, // assumed to be mobile, because of the missing mq property
   { mq: '360px', columns: 1, gutter: 20 },
   { mq: '480px', columns: 2, gutter: 20 },
   { mq: '600px', columns: 2, gutter: 20 },
   { mq: '800px', columns: 3, gutter: 20 }
  ]

  const cardLayout = Bricks({
    container: '#js-card-list',
    packed: 'data-packed', // if not prefixed with 'data-', it will be added
    sizes: sizes
  })

  return cardLayout
}

let initList = (cardLayout) => {
   // List.js
  let options = {
    valueNames: [ 'type', 'serviceProviderName', 'creationDate', 'description', 'keywords', 'distanceAwayInMetres' ],
    plugins: []
  }

  let theList = new List('js-card-search', options)
  theList.sort('creationDate', { order: 'desc' })
  cardLayout.resize(true).pack()

  // List.js Triggers
  theList.on('sortStart', () =>
   cardLayout.pack()
  )

  theList.on('searchComplete', () =>
   cardLayout.pack()
  )

  return theList
}

let initFiltering = (theList, cardLayout) => {
  let runFiltering = () => {
    if (activeFilters.length === 0) {
      resetFiltering()
      return
    }

    theList.filter(function (item) {
      if (activeFilters.length > 0) {
        return (activeFilters.indexOf(item.values().type)) > -1
      }
      return true
    })

    cardLayout.pack()
  }

  let resetFiltering = () => {
    // Reset active states
    let c
    let filters = document.querySelectorAll('.js-filter-item')

    activeFilters = []

    for (c = 0; c < filters.length; c++) {
      filters[c].classList.remove('is-active')
    }

    document.querySelector('.js-filter-item-all').classList.add('is-active')

    // Reset filter & layout
    theList.filter()
    cardLayout.pack()
  }

  let filters = document.querySelectorAll('.js-filter-item')
  let activeFilters = []

  // Add click listener to each item
  for (let b = 0; b < filters.length; b++) {
    filters[b].addEventListener('click', function (event) {
      let getFilter = this.getAttribute('data-filter')
      event.preventDefault()

      if (getFilter === 'all') {
        resetFiltering()
      } else {
        if (this.classList.contains('is-active')) {
          this.classList.remove('is-active')
          activeFilters.splice(activeFilters.indexOf(getFilter), 1)
          runFiltering()
        } else {
          document.querySelector('.js-filter-item-all').classList.remove('is-active')
          this.classList.add('is-active')
          activeFilters.push(getFilter)
          runFiltering()
        }
      }
    })
  }

  // Add change listener to `<select>` for small screens
  let filterList = document.querySelectorAll('.js-filter-list.list-to-dropdown__select')
  for (let c = 0; c < filterList.length; c++) {
    filterList[c].addEventListener('change', () => {
    })
  }
}

let initSorting = (theList, cardLayout) => {
  document.querySelector('.js-sort-dropdown')
    .addEventListener('change', function (event) {
      let sortFields = []
      sortFields['organisation'] = 'serviceProviderName'
      sortFields['date'] = 'creationDate'
      sortFields['distance'] = 'distanceAwayInMetres'

      let selectedSort = this.options[this.selectedIndex].value
      let [field, direction] = selectedSort.split('-')
      theList.sort(sortFields[field], { order: direction })
      cardLayout.pack()
    })
}

let buildList = () => {
  let cardLayout = initBricks()
  let theList = initList(cardLayout)

  initFiltering(theList, cardLayout)
  initSorting(theList, cardLayout)
}

// Full detail view
let buildCard = function (data) {
  let openCard = function (el, cardBackOnClick) {
    let cardCallback = () => {
      document.querySelector('.js-card-detail').classList.remove('is-hidden')
      document.querySelector('.js-card-detail').classList.add('is-active')

      window.scrollTo(0, 0)

      Holder.run({})

      // TODO: Proper URL support
      let state = { test: 'TBA' }
      let theId = el.getAttribute('data-id')
      history.pushState(state, 'TEST', '?id=' + theId)

      let d
      let cardBack = document.querySelectorAll('.js-card-back')

      // Add click listener to each item
      for (d = 0; d < cardBack.length; d++) {
        cardBack[d].addEventListener('click', function (event) {
          event.preventDefault()
          cardBackOnClick()
        })
      }

      accordion.init(false)
      socialShare.updateSharePageHrefs()
    }

    let init = () => {
      let theId = el.getAttribute('data-id')
      let cardData = Find(data, function (o) { return o.id === theId })

      // hide search
      document.querySelector('#js-card-search').classList.remove('is-active')
      document.querySelector('#js-card-search').classList.add('is-hidden')

      // Append object name for Hogan
      let theCardTemplateData = { card: cardData }

      templating.renderTemplate('js-card-detail-tpl', theCardTemplateData, 'js-card-detail-output', cardCallback)
    }

    init()
  }

  let openIfCardRequested = () => {
    let cardId = getUrlParams.parameter('id')
    if (cardId) {
      let card = Find(document.querySelectorAll('.requests-listing__item'), (c) => {
        return c.getAttribute('data-id') === cardId
      })
      openCard(card, () => {
        history.pushState({}, 'from openIfCardRequested', '?')
        closeCard()
      })
    }
  }

  let rewindHistory = () => {
    window.history.back()
  }

  let closeCard = () => {
    document.querySelector('#js-card-search').classList.remove('is-hidden')
    document.querySelector('#js-card-search').classList.add('is-active')

    document.querySelector('.js-card-detail').classList.remove('is-active')
    document.querySelector('.js-card-detail').classList.add('is-hidden')
  }

  let addClickEvents = () => {
    let addEventListener = (card) => {
      card.addEventListener('click', function (event) {
        event.preventDefault()
        openCard(this, rewindHistory)
      })
    }
    ForEach(document.querySelectorAll('.requests-listing__item'), addEventListener)
  }

  window.onpopstate = () => {
    closeCard()
    openIfCardRequested()
  }

  addClickEvents()
  openIfCardRequested()
}

init()
<commit_msg>reafactorin'<commit_after>/* global history */

import './common'

let Awesomplete = require('awesomplete') // eslint-disable-line
let geolib = require('geolib')
import List from 'list.js'
import Holder from 'holderjs'
import Bricks from 'bricks.js'
import Find from 'lodash/collection/find'
import ForEach from 'lodash/collection/forEach'
import moment from 'moment'

let apiRoutes = require('./api')
let browser = require('./browser')
let getApiData = require('./get-api-data')
let getLocation = require('./get-location')
let templating = require('./template-render')
let getUrlParams = require('./get-url-parameter')
let accordion = require('./accordion')
let socialShare = require('./social-share')
import listToSelect from './list-to-dropdown'

const activeClass = 'is-active'

let formatDate = (needs) => {
  // Change to relative date
  ForEach(needs, function (need) {
    need.formattedCreationDate = moment(need.creationDate).fromNow()
  })

  return needs
}

let renderNeeds = (needs) => {
  let theData = { card: needs }

  let listCallback = () => {
    buildList()
    buildCard(needs)
    browser.loaded()
  }

  templating.renderTemplate('js-card-list-tpl', theData, 'js-card-list-output', listCallback)
}

let initAutoComplete = (needs) => {
  let keywords = needs
    .map((n) => n.keywords)
    .filter((k) => k.length > 0)
    .join(',')

  let input = document.querySelector('.search')
  let awesomplete = new Awesomplete(input, {list: keywords}) // eslint-disable-line
}

let useDistanceForLocation = (position, needs) => {
  let latitude = position.coords.latitude
  let longitude = position.coords.longitude
  needs.forEach((n) => {
    let distanceInMetres = geolib.getDistance(
      { latitude: latitude, longitude: longitude },
      { latitude: n.latitude, longitude: n.longitude }
    )
    n.distanceAwayInMetres = distanceInMetres
    n.locationDescription = (distanceInMetres * 0.00062137).toFixed(2) + ' miles away'
  })

  return needs
}

let usePostcodeForLocation = (needs) => {
  needs.forEach((n) => {
    n.locationDescription = n.postcode
  })
  return needs
}

let init = () => {
  browser.loading()
  listToSelect.init()
  getApiData.data(apiRoutes.needs)
    .then(function (result) {
      let needsFromApi = formatDate(result.data)

      initAutoComplete(needsFromApi)

      if (navigator.geolocation) {
        getLocation.location().then(function (position) {
          renderNeeds(useDistanceForLocation(position, needsFromApi))
        }, () => {
          renderNeeds(usePostcodeForLocation(needsFromApi))
        })
      } else {
        renderNeeds(usePostcodeForLocation(needsFromApi))
      }
    })
}

let initBricks = () => {
  const sizes = [
   { columns: 1, gutter: 20 }, // assumed to be mobile, because of the missing mq property
   { mq: '360px', columns: 1, gutter: 20 },
   { mq: '480px', columns: 2, gutter: 20 },
   { mq: '600px', columns: 2, gutter: 20 },
   { mq: '800px', columns: 3, gutter: 20 }
  ]

  const cardLayout = Bricks({
    container: '#js-card-list',
    packed: 'data-packed', // if not prefixed with 'data-', it will be added
    sizes: sizes
  })

  return cardLayout
}

let initList = (cardLayout) => {
   // List.js
  let options = {
    valueNames: [ 'type', 'serviceProviderName', 'creationDate', 'description', 'keywords', 'distanceAwayInMetres' ],
    plugins: []
  }

  let theList = new List('js-card-search', options)
  theList.sort('creationDate', { order: 'desc' })
  cardLayout.resize(true).pack()

  // List.js Triggers
  theList.on('sortStart', () =>
   cardLayout.pack()
  )

  theList.on('searchComplete', () =>
   cardLayout.pack()
  )

  return theList
}

let initFiltering = (theList, cardLayout) => {
  let runFiltering = () => {
    if (activeFilters.length === 0) {
      resetFiltering()
      return
    }

    theList.filter(function (item) {
      if (activeFilters.length > 0) {
        return (activeFilters.indexOf(item.values().type)) > -1
      }
      return true
    })

    cardLayout.pack()
  }

  let resetFiltering = () => {
    // Reset active states
    let c
    let filters = document.querySelectorAll('.js-filter-item')

    activeFilters = []

    for (c = 0; c < filters.length; c++) {
      filters[c].classList.remove(activeClass)
    }

    document.querySelector('.js-filter-item-all').classList.add(activeClass)

    // Reset filter & layout
    theList.filter()
    cardLayout.pack()
  }

  let filters = document.querySelectorAll('.js-filter-item')
  let activeFilters = []

  // Add click listener to each item
  for (let b = 0; b < filters.length; b++) {
    filters[b].addEventListener('click', function (event) {
      let getFilter = this.getAttribute('data-filter')
      event.preventDefault()

      if (getFilter === 'all') {
        resetFiltering()
      } else {
        if (this.classList.contains(activeClass)) {
          this.classList.remove(activeClass)
          activeFilters.splice(activeFilters.indexOf(getFilter), 1)
          runFiltering()
        } else {
          document.querySelector('.js-filter-item-all').classList.remove(activeClass)
          this.classList.add(activeClass)
          activeFilters.push(getFilter)
          runFiltering()
        }
      }
    })
  }

  // Add change listener to `<select>` for small screens
  let filterList = document.querySelectorAll('.js-filter-list.list-to-dropdown__select')
  for (let c = 0; c < filterList.length; c++) {
    filterList[c].addEventListener('change', () => {
    })
  }
}

let initSorting = (theList, cardLayout) => {
  document.querySelector('.js-sort-dropdown')
    .addEventListener('change', function (event) {
      let sortFields = []
      sortFields['organisation'] = 'serviceProviderName'
      sortFields['date'] = 'creationDate'
      sortFields['distance'] = 'distanceAwayInMetres'

      let selectedSort = this.options[this.selectedIndex].value
      let [field, direction] = selectedSort.split('-')
      theList.sort(sortFields[field], { order: direction })
      cardLayout.pack()
    })
}

let buildList = () => {
  let cardLayout = initBricks()
  let theList = initList(cardLayout)

  initFiltering(theList, cardLayout)
  initSorting(theList, cardLayout)
}

// Full detail view
let buildCard = function (data) {
  const searchSelector = '#js-card-search'
  const cardDetailSelector = '.js-card-detail'
  const hiddenClass = 'is-hidden'

  let openCard = function (el, callback) {
    let cardCallback = () => {
      document.querySelector(cardDetailSelector).classList.remove(hiddenClass)
      document.querySelector(cardDetailSelector).classList.add(activeClass)

      window.scrollTo(0, 0)

      Holder.run({})

      // TODO: Proper URL support
      let state = { test: 'TBA' }
      let theId = el.getAttribute('data-id')
      history.pushState(state, 'TEST', '?id=' + theId)

      let d
      let cardBack = document.querySelectorAll('.js-card-back')

      // Add click listener to each item
      for (d = 0; d < cardBack.length; d++) {
        cardBack[d].addEventListener('click', function (event) {
          event.preventDefault()
          callback()
        })
      }

      accordion.init(false)
      socialShare.updateSharePageHrefs()
    }

    let init = () => {
      let theId = el.getAttribute('data-id')
      let cardData = Find(data, function (o) { return o.id === theId })

      // hide search
      document.querySelector(searchSelector).classList.remove(activeClass)
      document.querySelector(searchSelector).classList.add(hiddenClass)

      // Append object name for Hogan
      let theCardTemplateData = { card: cardData }

      templating.renderTemplate('js-card-detail-tpl', theCardTemplateData, 'js-card-detail-output', cardCallback)
    }

    init()
  }

  let openIfCardRequested = () => {
    let cardId = getUrlParams.parameter('id')
    if (cardId) {
      let card = Find(document.querySelectorAll('.requests-listing__item'), (c) => {
        return c.getAttribute('data-id') === cardId
      })
      openCard(card, () => {
        history.pushState({}, 'from openIfCardRequested', '?')
        closeCard()
      })
    }
  }

  let rewindHistory = () => {
    window.history.back()
  }

  let closeCard = () => {
    document.querySelector(searchSelector).classList.remove(hiddenClass)
    document.querySelector(searchSelector).classList.add(activeClass)

    document.querySelector(cardDetailSelector).classList.remove(activeClass)
    document.querySelector(cardDetailSelector).classList.add(hiddenClass)
  }

  let addClickEvents = () => {
    let addEventListener = (card) => {
      card.addEventListener('click', function (event) {
        event.preventDefault()
        openCard(this, rewindHistory)
      })
    }
    ForEach(document.querySelectorAll('.requests-listing__item'), addEventListener)
  }

  window.onpopstate = () => {
    closeCard()
    openIfCardRequested()
  }

  addClickEvents()
  openIfCardRequested()
}

init()
<|endoftext|>