<commit_before>import { delay, eventChannel } from 'redux-saga';
import { fork, take, call, put } from 'redux-saga/effects';

export const FETCHED_JOBS = 'FETCHED_JOBS';
export const FETCHED_JOB = 'FETCHED_JOB';
export const WATCH_JOB = 'WATCH_JOB';
export const UNWATCH_JOB = 'UNWATCH_JOB';

export const FETCHED_MEMBERS = 'FETCHED_MEMBERS';
export const FETCHED_MEMBER = 'FETCHED_MEMBER';
export const FETCH_MEMBER = 'FETCH_MEMBER';
export const WATCH_MEMBER = 'WATCH_MEMBER';
export const UNWATCH_MEMBER = 'UNWATCH_MEMBER';

export const FETCHED_NODES = 'FETCHED_NODES';
export const FETCHED_NODE = 'FETCHED_NODE';
export const FETCH_NODE = 'FETCH_NODE';
export const WATCH_NODE = 'WATCH_NODE';
export const UNWATCH_NODE = 'UNWATCH_NODE';

export const FETCHED_EVALS = 'FETCHED_EVALS';
export const FETCHED_EVAL = 'FETCHED_EVAL';
export const WATCH_EVAL = 'WATCH_EVAL';
export const UNWATCH_EVAL = 'UNWATCH_EVAL';

export const FETCHED_ALLOCS = 'FETCHED_ALLOCS';
export const FETCHED_ALLOC = 'FETCHED_ALLOC';
export const WATCH_ALLOC = 'WATCH_ALLOC';
export const UNWATCH_ALLOC = 'UNWATCH_ALLOC';

export const FETCH_DIR = 'FETCH_DIR'
export const FETCHED_DIR = 'FETCHED_DIR'

export const WATCH_FILE = 'WATCH_FILE'
export const UNWATCH_FILE = 'UNWATCH_FILE'
export const FETCHED_FILE = 'FETCHED_FILE'

function subscribe(socket) {
    return eventChannel(emit => {
        socket.onmessage = (event) => {
            const data = JSON.parse(event.data)
            emit({
                type: data.Type,
                payload: data.Payload
            })
        }
        return () => {};
    });
}

function* read(socket) {
    const channel = yield call(subscribe, socket);
    while (true) {
         let action = yield take(channel);
         yield put(action);
    }
}

function* write(socket) {
    while (true) {
        const action = yield take([
            WATCH_JOB,
            UNWATCH_JOB,
            WATCH_ALLOC,
            UNWATCH_ALLOC,
            WATCH_EVAL,
            UNWATCH_EVAL,
            WATCH_NODE,
            UNWATCH_NODE,
            FETCH_NODE,
            WATCH_MEMBER,
            UNWATCH_MEMBER,
            FETCH_MEMBER,
            FETCH_DIR,
            WATCH_FILE,
            UNWATCH_FILE,
        ]);
        socket.send(JSON.stringify(action))
    }
}

function* transport(socket) {
    yield fork(read, socket);
    yield fork(write, socket);
}

function connectTo(url) {
    const socket = new WebSocket(url);

    const resolver = (resolve, reject) => {
        const timeout = setTimeout(() => {
            reject("Unable to connect to the backend...");
        }, 2000)

        socket.onopen = () => {
            console.log("Connected to the backend.")
            resolve(socket)
            clearTimeout(timeout)
        }
    }

    return new Promise(resolver.bind(socket))
}

function* events() {
    const protocol = location.protocol === "https:" ? "wss:" : "ws:";
    const url = `${protocol}//${location.host}/ws`;

    while (true) {
        try {
            console.log(`Connecting to backend: ${url}`)
            const socket = yield call(connectTo, url)
            yield call(transport, socket)
        } catch(e) {
            console.log(e)
        }
        yield delay(5000)
    }
}

export default function* eventSaga() {
    yield fork(events)
}
<commit_msg>Code style fix<commit_after>import { delay, eventChannel } from 'redux-saga';
import { fork, take, call, put } from 'redux-saga/effects';

export const FETCHED_JOBS = 'FETCHED_JOBS';
export const FETCHED_JOB = 'FETCHED_JOB';
export const WATCH_JOB = 'WATCH_JOB';
export const UNWATCH_JOB = 'UNWATCH_JOB';

export const FETCHED_MEMBERS = 'FETCHED_MEMBERS';
export const FETCHED_MEMBER = 'FETCHED_MEMBER';
export const FETCH_MEMBER = 'FETCH_MEMBER';
export const WATCH_MEMBER = 'WATCH_MEMBER';
export const UNWATCH_MEMBER = 'UNWATCH_MEMBER';

export const FETCHED_NODES = 'FETCHED_NODES';
export const FETCHED_NODE = 'FETCHED_NODE';
export const FETCH_NODE = 'FETCH_NODE';
export const WATCH_NODE = 'WATCH_NODE';
export const UNWATCH_NODE = 'UNWATCH_NODE';

export const FETCHED_EVALS = 'FETCHED_EVALS';
export const FETCHED_EVAL = 'FETCHED_EVAL';
export const WATCH_EVAL = 'WATCH_EVAL';
export const UNWATCH_EVAL = 'UNWATCH_EVAL';

export const FETCHED_ALLOCS = 'FETCHED_ALLOCS';
export const FETCHED_ALLOC = 'FETCHED_ALLOC';
export const WATCH_ALLOC = 'WATCH_ALLOC';
export const UNWATCH_ALLOC = 'UNWATCH_ALLOC';

export const FETCH_DIR = 'FETCH_DIR';
export const FETCHED_DIR = 'FETCHED_DIR';

export const WATCH_FILE = 'WATCH_FILE';
export const UNWATCH_FILE = 'UNWATCH_FILE';
export const FETCHED_FILE = 'FETCHED_FILE';

function subscribe(socket) {
    return eventChannel(emit => {
        socket.onmessage = (event) => {
            const data = JSON.parse(event.data)
            emit({
                type: data.Type,
                payload: data.Payload
            })
        }
        return () => {};
    });
}

function* read(socket) {
    const channel = yield call(subscribe, socket);
    while (true) {
         let action = yield take(channel);
         yield put(action);
    }
}

function* write(socket) {
    while (true) {
        const action = yield take([
            WATCH_JOB,
            UNWATCH_JOB,
            WATCH_ALLOC,
            UNWATCH_ALLOC,
            WATCH_EVAL,
            UNWATCH_EVAL,
            WATCH_NODE,
            UNWATCH_NODE,
            FETCH_NODE,
            WATCH_MEMBER,
            UNWATCH_MEMBER,
            FETCH_MEMBER,
            FETCH_DIR,
            WATCH_FILE,
            UNWATCH_FILE,
        ]);
        socket.send(JSON.stringify(action))
    }
}

function* transport(socket) {
    yield fork(read, socket);
    yield fork(write, socket);
}

function connectTo(url) {
    const socket = new WebSocket(url);

    const resolver = (resolve, reject) => {
        const timeout = setTimeout(() => {
            reject("Unable to connect to the backend...");
        }, 2000)

        socket.onopen = () => {
            console.log("Connected to the backend.")
            resolve(socket)
            clearTimeout(timeout)
        }
    }

    return new Promise(resolver.bind(socket))
}

function* events() {
    const protocol = location.protocol === "https:" ? "wss:" : "ws:";
    const url = `${protocol}//${location.host}/ws`;

    while (true) {
        try {
            console.log(`Connecting to backend: ${url}`)
            const socket = yield call(connectTo, url)
            yield call(transport, socket)
        } catch(e) {
            console.log(e)
        }
        yield delay(5000)
    }
}

export default function* eventSaga() {
    yield fork(events)
}
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../utils/Class');
var Systems = require('./Systems');

/**
 * @classdesc
 * A base Phaser.Scene class which you could extend for your own use.
 *
 * @class Scene
 * @memberof Phaser
 * @constructor
 * @since 3.0.0
 *
 * @param {(string|Phaser.Scenes.Settings.Config)} config - Scene specific configuration settings.
 */
var Scene = new Class({

    initialize:

    function Scene (config)
    {
        /**
         * The Scene Systems. You must never overwrite this property, or all hell will break lose.
         *
         * @name Phaser.Scene#sys
         * @type {Phaser.Scenes.Systems}
         * @since 3.0.0
         */
        this.sys = new Systems(this, config);

        /**
         * A reference to the Phaser.Game instance.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#game
         * @type {Phaser.Game}
         * @since 3.0.0
         */
        this.game;

        /**
         * A reference to the global Animation Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#anims
         * @type {Phaser.Animations.AnimationManager}
         * @since 3.0.0
         */
        this.anims;

        /**
         * A reference to the global Cache.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#cache
         * @type {Phaser.Cache.CacheManager}
         * @since 3.0.0
         */
        this.cache;

        /**
         * A reference to the game level Data Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#registry
         * @type {Phaser.Data.DataManager}
         * @since 3.0.0
         */
        this.registry;

        /**
         * A reference to the Sound Manager.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#sound
         * @type {Phaser.Sound.BaseSoundManager}
         * @since 3.0.0
         */
        this.sound;

        /**
         * A reference to the Texture Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#textures
         * @type {Phaser.Textures.TextureManager}
         * @since 3.0.0
         */
        this.textures;

        /**
         * A scene level Event Emitter.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.0.0
         */
        this.events;

        /**
         * A scene level Camera System.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#cameras
         * @type {Phaser.Cameras.Scene2D.CameraManager}
         * @since 3.0.0
         */
        this.cameras;

        /**
         * A scene level Game Object Factory.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#add
         * @type {Phaser.GameObjects.GameObjectFactory}
         * @since 3.0.0
         */
        this.add;

        /**
         * A scene level Game Object Creator.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#make
         * @type {Phaser.GameObjects.GameObjectCreator}
         * @since 3.0.0
         */
        this.make;

        /**
         * A reference to the Scene Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#scene
         * @type {Phaser.Scenes.ScenePlugin}
         * @since 3.0.0
         */
        this.scene;

        /**
         * A scene level Game Object Display List.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#children
         * @type {Phaser.GameObjects.DisplayList}
         * @since 3.0.0
         */
        this.children;

        /**
         * A scene level Lights Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#lights
         * @type {Phaser.GameObjects.LightsManager}
         * @since 3.0.0
         */
        this.lights;

        /**
         * A scene level Data Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#data
         * @type {Phaser.Data.DataManager}
         * @since 3.0.0
         */
        this.data;

        /**
         * A scene level Input Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#input
         * @type {Phaser.Input.InputPlugin}
         * @since 3.0.0
         */
        this.input;

        /**
         * A scene level Loader Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#load
         * @type {Phaser.Loader.LoaderPlugin}
         * @since 3.0.0
         */
        this.load;

        /**
         * A scene level Time and Clock Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#time
         * @type {Phaser.Time.Clock}
         * @since 3.0.0
         */
        this.time;

        /**
         * A scene level Tween Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#tweens
         * @type {Phaser.Tweens.TweenManager}
         * @since 3.0.0
         */
        this.tweens;

        /**
         * A scene level Arcade Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#physics
         * @type {Phaser.Physics.Arcade.ArcadePhysics}
         * @since 3.0.0
         */
        this.physics;

        /**
         * A scene level Impact Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#impact
         * @type {Phaser.Physics.Impact.ImpactPhysics}
         * @since 3.0.0
         */
        this.impact;

        /**
         * A scene level Matter Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#matter
         * @type {Phaser.Physics.Matter.MatterPhysics}
         * @since 3.0.0
         */
        this.matter;

        if (typeof PLUGIN_FBINSTANT)
        {
            /**
             * A scene level Facebook Instant Games Plugin.
             * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
             *
             * @name Phaser.Scene#facebook
             * @type {Phaser.FacebookInstantGamesPlugin}
             * @since 3.12.0
             */
            this.facebook;
        }
    },

    /**
     * Should be overridden by your own Scenes.
     *
     * @method Phaser.Scene#update
     * @override
     * @since 3.0.0
     *
     * @param {number} time - The current time. Either a High Resolution Timer value if it comes from Request Animation Frame, or Date.now if using SetTimeout.
     * @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
     */
    update: function ()
    {
    }

});

module.exports = Scene;
<commit_msg>Added this.scale property to Scene<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2019 Photon Storm Ltd.
 * @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
 */

var Class = require('../utils/Class');
var Systems = require('./Systems');

/**
 * @classdesc
 * A base Phaser.Scene class which you could extend for your own use.
 *
 * @class Scene
 * @memberof Phaser
 * @constructor
 * @since 3.0.0
 *
 * @param {(string|Phaser.Scenes.Settings.Config)} config - Scene specific configuration settings.
 */
var Scene = new Class({

    initialize:

    function Scene (config)
    {
        /**
         * The Scene Systems. You must never overwrite this property, or all hell will break lose.
         *
         * @name Phaser.Scene#sys
         * @type {Phaser.Scenes.Systems}
         * @since 3.0.0
         */
        this.sys = new Systems(this, config);

        /**
         * A reference to the Phaser.Game instance.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#game
         * @type {Phaser.Game}
         * @since 3.0.0
         */
        this.game;

        /**
         * A reference to the global Animation Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#anims
         * @type {Phaser.Animations.AnimationManager}
         * @since 3.0.0
         */
        this.anims;

        /**
         * A reference to the global Cache.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#cache
         * @type {Phaser.Cache.CacheManager}
         * @since 3.0.0
         */
        this.cache;

        /**
         * A reference to the game level Data Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#registry
         * @type {Phaser.Data.DataManager}
         * @since 3.0.0
         */
        this.registry;

        /**
         * A reference to the Sound Manager.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#sound
         * @type {Phaser.Sound.BaseSoundManager}
         * @since 3.0.0
         */
        this.sound;

        /**
         * A reference to the Texture Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#textures
         * @type {Phaser.Textures.TextureManager}
         * @since 3.0.0
         */
        this.textures;

        /**
         * A scene level Event Emitter.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.0.0
         */
        this.events;

        /**
         * A scene level Camera System.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#cameras
         * @type {Phaser.Cameras.Scene2D.CameraManager}
         * @since 3.0.0
         */
        this.cameras;

        /**
         * A scene level Game Object Factory.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#add
         * @type {Phaser.GameObjects.GameObjectFactory}
         * @since 3.0.0
         */
        this.add;

        /**
         * A scene level Game Object Creator.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#make
         * @type {Phaser.GameObjects.GameObjectCreator}
         * @since 3.0.0
         */
        this.make;

        /**
         * A reference to the Scene Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#scene
         * @type {Phaser.Scenes.ScenePlugin}
         * @since 3.0.0
         */
        this.scene;

        /**
         * A scene level Game Object Display List.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#children
         * @type {Phaser.GameObjects.DisplayList}
         * @since 3.0.0
         */
        this.children;

        /**
         * A scene level Lights Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#lights
         * @type {Phaser.GameObjects.LightsManager}
         * @since 3.0.0
         */
        this.lights;

        /**
         * A scene level Data Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#data
         * @type {Phaser.Data.DataManager}
         * @since 3.0.0
         */
        this.data;

        /**
         * A scene level Input Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#input
         * @type {Phaser.Input.InputPlugin}
         * @since 3.0.0
         */
        this.input;

        /**
         * A scene level Loader Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#load
         * @type {Phaser.Loader.LoaderPlugin}
         * @since 3.0.0
         */
        this.load;

        /**
         * A scene level Time and Clock Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#time
         * @type {Phaser.Time.Clock}
         * @since 3.0.0
         */
        this.time;

        /**
         * A scene level Tween Manager Plugin.
         * This property will only be available if defined in the Scene Injection Map and the plugin is installed.
         *
         * @name Phaser.Scene#tweens
         * @type {Phaser.Tweens.TweenManager}
         * @since 3.0.0
         */
        this.tweens;

        /**
         * A scene level Arcade Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#physics
         * @type {Phaser.Physics.Arcade.ArcadePhysics}
         * @since 3.0.0
         */
        this.physics;

        /**
         * A scene level Impact Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#impact
         * @type {Phaser.Physics.Impact.ImpactPhysics}
         * @since 3.0.0
         */
        this.impact;

        /**
         * A scene level Matter Physics Plugin.
         * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
         *
         * @name Phaser.Scene#matter
         * @type {Phaser.Physics.Matter.MatterPhysics}
         * @since 3.0.0
         */
        this.matter;

        if (typeof PLUGIN_FBINSTANT)
        {
            /**
             * A scene level Facebook Instant Games Plugin.
             * This property will only be available if defined in the Scene Injection Map, the plugin is installed and configured.
             *
             * @name Phaser.Scene#facebook
             * @type {Phaser.FacebookInstantGamesPlugin}
             * @since 3.12.0
             */
            this.facebook;
        }

        /**
         * A reference to the global Scale Manager.
         * This property will only be available if defined in the Scene Injection Map.
         *
         * @name Phaser.Scene#scale
         * @type {Phaser.Scale.ScaleManager}
         * @since 3.16.1
         */
        this.scale;
    },

    /**
     * Should be overridden by your own Scenes.
     *
     * @method Phaser.Scene#update
     * @override
     * @since 3.0.0
     *
     * @param {number} time - The current time. Either a High Resolution Timer value if it comes from Request Animation Frame, or Date.now if using SetTimeout.
     * @param {number} delta - The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate.
     */
    update: function ()
    {
    }

});

module.exports = Scene;
<|endoftext|>


<commit_before>'use strict';

var WayPoints = require('collections/google-maps-waypoints-collection');
var WayPointsListView = require('views/waypoint-list-view');

var OriginalMapView = require('views/original-map-view');
var OptimizedMapView = require('views/optimized-map-view');

var StatsView = require('views/stats-view');
var CodeView = require('views/code-view');

var Optimizer = require('models/route-optimizer-model');

$(function() {
    var wp = new WayPoints({});
    wp.fetch().then(function() {

        var optimizer = new Optimizer({
            original: wp,
            optimized: wp.clone()
        });
        window.optimizer = optimizer;

        var wpListView = new WayPointsListView({
            collection: optimizer.get('original'),
            className: 'waypoints'
        });
        $('#content .side').append(wpListView.render().$el);

        var originalMapView = new OriginalMapView({
            collection: optimizer.get('original'),
            className: 'A'
        });
        $('#content .maps').append(originalMapView.render().$el);

        var optimizedMapView = new OptimizedMapView({
            collection: optimizer.get('optimized'),
            model: optimizer,
            className: 'other'
        });
        $('#content .maps').append(optimizedMapView.render().$el);

        var statsView = new StatsView({
            model: optimizer,
            el: $('#stats')
        });
        statsView.render();

        var cv = new CodeView({
            el: $('#how-we-did-it .main')
        });
        cv.render();

        window.wp = wp;
    });
});
<commit_msg>seprated map and loading<commit_after>'use strict';

var WayPoints = require('collections/google-maps-waypoints-collection');
var WayPointsListView = require('views/waypoint-list-view');

var OriginalMapView = require('views/original-map-view');
var OptimizedMapView = require('views/optimized-map-view');

var StatsView = require('views/stats-view');
var CodeView = require('views/code-view');

var Optimizer = require('models/route-optimizer-model');

$(function() {
    var wp = new WayPoints({});
    wp.fetch().then(function() {

        var optimizer = new Optimizer({
            original: wp,
            optimized: wp.clone()
        });
        window.optimizer = optimizer;

        var wpListView = new WayPointsListView({
            collection: optimizer.get('original'),
            className: 'waypoints'
        });
        $('#content .side').append(wpListView.render().$el);

        var originalMapView = new OriginalMapView({
            collection: optimizer.get('original'),
            el: $('.maps .original')
        });
        originalMapView.render();

        var optimizedMapView = new OptimizedMapView({
            collection: optimizer.get('optimized'),
            model: optimizer,
            el: $('.maps .optimized .map')
        });
        optimizedMapView.render();

        var statsView = new StatsView({
            model: optimizer,
            el: $('#stats')
        });
        statsView.render();

        var cv = new CodeView({
            el: $('#how-we-did-it .main')
        });
        cv.render();

        window.wp = wp;
    });
});
<|endoftext|>


<commit_before>/**
 * All links that go through Sailthru get encoded to the following format:
 *
 * http://link.artsy.net/click/9466122.343103/aHR0cHM6Ly93d3cuYXJ0c3kubmV0L2FydGljbGUvYXJ0c3ktZWRpdG9yaWFsLW1ldHMtcm9vZi1ob3N0cy1kaW5uZXItcGFydHktNS0wMDAteWVhcnMtYXJ0LWludml0ZWQ_dXRtX3NvdXJjZT1zYWlsdGhydSZ1dG1fbWVkaXVtPWVtYWlsJnV0bV9jYW1wYWlnbj05NDY2MTIyLUVkaXRvcmlhbC0wNC0yNS0xNyZ1dG1fdGVybT1BcnRzeVRvcFN0b3JpZXNXZWVrbHk/56a63f2b17893fbc408b97e9B85ab6cf0
 *
 * (This specific link redirects to https://www.artsy.net/article/artsy-editorial-mets-roof-hosts-dinner-party-5-000-years-art-invited?utm_source=sailthru&utm_medium=email&utm_campaign=9466122-Editorial-04-25-17&utm_term=ArtsyTopStoriesWeekly)
 *
 * The problem with that is that iOS won’t be able to exclude paths in the normal way. However, it turns out that the
 * encoded URL contains a base64 version of the actual Artsy URL, so we can exlcude based on that. This script takes the
 * patterns specified in the apple-app-site-association.json file and generates the encoded versions of them. Or, if a
 * pattern was removed, it removes the corresponding encoded pattern.
 */

const fs = require("fs")

const filename = "apple-app-site-association.json"

function updateFile(block) {
  const json = JSON.parse(fs.readFileSync(filename, { encoding: "utf8" }))
  block(json)
  fs.writeFileSync(filename, JSON.stringify(json, null, 2) + "\n", { encoding: "utf8" })
}

function unencodedPatterns(patterns) {
  return patterns.filter(pattern => pattern.includes("/"))
}

function encodePath(path) {
  return path
    .split("*")
    .filter(component => component.length)
    .map(component => new Buffer(component).toString("base64"))
    .join("*")
}

function encodePattern(pattern) {
  const path = pattern.match(/^NOT (.+)$/)[1]
  return `NOT *${encodePath(path)}*`
}

updateFile(json => {
  const app = json.applinks.details[0]
  const patterns = unencodedPatterns(app.paths)
  app.paths = [...patterns.concat(patterns.map(encodePattern)), "*"]
})
<commit_msg>Base64 encode patterns without padding.<commit_after>/**
 * All links that go through Sailthru get encoded to the following format:
 *
 * http://link.artsy.net/click/9466122.343103/aHR0cHM6Ly93d3cuYXJ0c3kubmV0L2FydGljbGUvYXJ0c3ktZWRpdG9yaWFsLW1ldHMtcm9vZi1ob3N0cy1kaW5uZXItcGFydHktNS0wMDAteWVhcnMtYXJ0LWludml0ZWQ_dXRtX3NvdXJjZT1zYWlsdGhydSZ1dG1fbWVkaXVtPWVtYWlsJnV0bV9jYW1wYWlnbj05NDY2MTIyLUVkaXRvcmlhbC0wNC0yNS0xNyZ1dG1fdGVybT1BcnRzeVRvcFN0b3JpZXNXZWVrbHk/56a63f2b17893fbc408b97e9B85ab6cf0
 *
 * (This specific link redirects to https://www.artsy.net/article/artsy-editorial-mets-roof-hosts-dinner-party-5-000-years-art-invited?utm_source=sailthru&utm_medium=email&utm_campaign=9466122-Editorial-04-25-17&utm_term=ArtsyTopStoriesWeekly)
 *
 * The problem with that is that iOS won’t be able to exclude paths in the normal way. However, it turns out that the
 * encoded URL contains a base64 version of the actual Artsy URL, so we can exclude based on that. This script takes the
 * patterns specified in the apple-app-site-association.json file and generates the encoded versions of them. Or, if a
 * pattern was removed, it removes the corresponding encoded pattern.
 */

const fs = require("fs")

const filename = "apple-app-site-association.json"

function updateFile(block) {
  const json = JSON.parse(fs.readFileSync(filename, { encoding: "utf8" }))
  block(json)
  fs.writeFileSync(filename, JSON.stringify(json, null, 2) + "\n", { encoding: "utf8" })
}

function unencodedPatterns(patterns) {
  return patterns.filter(pattern => pattern.includes("/"))
}

function encodePath(path) {
  return path
    .split("*")
    .filter(component => component.length)
    .map(component => new Buffer(component).toString("base64"))
    .join("*")
}

/**
 * Only encode upto the largest multiple of 3 so that no padding is required to encode to base64.
 *
 * Match everything before, which is going to be the domain (`https://www.artsy.net`) base64 encoded, and after because
 * Sailthru adds another path component that we don’t care about.
 */
function encodePattern(pattern) {
  const path = pattern.match(/^NOT (.+)$/)[1]
  const encoded = encodePath(path.slice(0, -(path.length % 3)))
  return `NOT *${encoded}*`
}

updateFile(json => {
  const app = json.applinks.details[0]
  const patterns = unencodedPatterns(app.paths)
  app.paths = [...patterns.concat(patterns.map(encodePattern)), "*"]
})
<|endoftext|>


<commit_before>/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */
/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */
/* eslint-disable import/no-mutable-exports */

import oneOfRequiredProperty from '../../utilities/warning/one-of-required-property';
/* import deprecatedPropertyValue from '../../utilities/warning/deprecated-property-value'; */

let checkProps = function () {};

if (process.env.NODE_ENV !== 'production') {
	checkProps = function (COMPONENT, props) {
		/**
		 * If illustration SVGs are added to SLDS in the future, we will deprecate the value
		 * of internalIllustration being true and give a warning.
		 *
		if (props.internalIllustration) {
 			deprecatedPropertyValue(COMPONENT, {
 				propAsString: 'internalIllustration',
 				propValue: props.internalIllustration,
 				deprecatedPropValue: true,
 				replacementPropAsValue: false,
 			},
 			'Using illustration SVGs from inside this repo is deprecated. Please update your assets/images path to use the SVGs found in the @salesforce/design-system npm module.');
 		}
		 */

		if (props.illustration || props.path) {
			// An illustration image must be accompanied with heading text inline
			oneOfRequiredProperty(COMPONENT, {
				heading: props.heading
			});
		}
	};
}

export default checkProps;
<commit_msg>fix one more lint issue<commit_after>/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */
/* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */
/* eslint-disable import/no-mutable-exports */

import oneOfRequiredProperty from '../../utilities/warning/one-of-required-property';
/* import deprecatedPropertyValue from '../../utilities/warning/deprecated-property-value'; */

let checkProps = function () {};

if (process.env.NODE_ENV !== 'production') {
	checkProps = function (COMPONENT, props) {
		/**
		 * If illustration SVGs are added to SLDS in the future, we will deprecate the value
		 * of internalIllustration being true and give a warning.
		 *
		if (props.internalIllustration) {
 			deprecatedPropertyValue(COMPONENT, {
 				propAsString: 'internalIllustration',
 				propValue: props.internalIllustration,
 				deprecatedPropValue: true,
 				replacementPropAsValue: false,
 			},
 			'Using illustration SVGs from inside this repo is deprecated. Please update your assets/images path to use the SVGs found in the @salesforce/design-system npm module.');
 		}
		 */

		if (props.illustration || props.path) {
			// An illustration image must be accompanied with heading text inline
			oneOfRequiredProperty(COMPONENT, {
				heading: props.heading,
			});
		}
	};
}

export default checkProps;
<|endoftext|>


<commit_before>import {assoc, clone, evolve, identity, map, pipe, reduce, repeat, range, trim} from "ramda";
import Class from "classnames";
import Cycle from "cyclejs";
import Stream from "cyclejs-stream";
import {merge, randomObjectValue} from "./helpers";
import Piece from "./piece";
import Board from "./board";

let {Rx} = Cycle;
let {Observable, Subject} = Rx;

// CONSTANTS =======================================================================================
const BOARD_ROWS = 12; // have to be able to contain PIECES
const BOARD_COLS = 8; // ...
const INITIAL_POSITION = [2, 2]; // TODO evaluate best position (middle of cols, top + 1 of rows?!)

/**
 LEGEND
   "0" - empty cell
   "1" - written cell
   "P" - piece cell
   "G" - ghost cell
*/

const PIECES = {
  I: [[-1,  0], [ 0,  0], [ 1, 0], [2, 0]],
  T: [[ 0, -1], [-1,  0], [ 0, 0], [1, 0]],
  O: [[ 0, -1], [ 1, -1], [ 0, 0], [1, 0]],
  J: [[-1, -1], [-1,  0], [ 0, 0], [1, 0]],
  L: [[ 1, -1], [-1,  0], [ 0, 0], [1, 0]],
  S: [[ 0, -1], [ 1, -1], [-1, 0], [0, 0]],
  Z: [[-1, -1], [ 0, -1], [ 0, 0], [1, 0]],
};

const TICK_MS = 1000;

const DRAW_MAP = {
 "0": "\u2B1C",
 "1": "\u2B1B",
 "P": "\u2B1B",
 "G": "\u2B1B",
};

//let randomObjectValue2 = (PIECES) => PIECES.O;

// APP =============================================================================================
function Intent(interactions) {
  // KEYBOARD EVENTS
  let keydown$ = Observable.fromEvent(document, "keydown").debounce(100);
  return {
    // TODO: debounce without delay
    hitEnter$: keydown$.filter(event => event.keyCode == 13),
    hitSpace$: keydown$.filter(event => event.keyCode == 32),
    hitLeftArrow$: keydown$.filter(event => event.keyCode == 37),
    hitUpArrow$: keydown$.filter(event => event.keyCode == 38),
    hitRightArrow$: keydown$.filter(event => event.keyCode == 39),
    hitDownArrow$: keydown$.filter(event => event.keyCode == 40),
  }
}

function Model(intentions) {
  function seedState() {
    return {
      // Game board
      board: Board.create(BOARD_ROWS, BOARD_COLS),

      // Current piece
      piece: randomObjectValue(PIECES),

      // Next piece
      nextPiece: randomObjectValue(PIECES),

      // Current position (of current piece)
      position: INITIAL_POSITION,

      // Live state (game is not lost)
      live: true,

      // User pause
      paused: true,
    };
  }

  function isTicking(state) {
    return state.live && !state.paused;
  }

  // GAME EVENTS
  let moveLeft$ = intentions.hitLeftArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let [cx, cy] = state.position;
        let newPosition = [cx - 1, cy];
        if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
          state = assoc("position", newPosition, state);
        }
      }
      return state;
    }
  });

  let moveRight$ = intentions.hitRightArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let [cx, cy] = state.position;
        let newPosition = [cx + 1, cy];
        if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
          state = assoc("position", newPosition, state);
        }
      }
      return state;
    }
  });

  let rotate$ = intentions.hitUpArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let rotatedPiece = Piece.rotateClockwise(state.piece);
        if (Board.doesPieceFit(state.board, rotatedPiece, state.position)) {
          state = assoc("piece", rotatedPiece, state);
        }
      }
      return state;
    }
  });

  let drop$ = intentions.hitSpace$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let dropPosition = Board.getDropPosition(state.board, state.piece, state.position);
        state = merge({
          board: Board.writePiece(state.board, state.piece, dropPosition),
          piece: undefined,
          position: INITIAL_POSITION
        }, state);
      }
      return state;
    }
  });

  let pause$ = intentions.hitEnter$.map(() => {
    return function (state) {
      state = assoc("paused", !state.paused, state);
      return state;
    }
  });

  let tickRunning$ = new Subject();
  tickRunning$.distinctUntilChanged();

  let tick$ = Observable.interval(TICK_MS).pausable(tickRunning$).take(30);

  let gravity$ = tick$.map(() => {
    return function (state) {
      let [cx, cy] = state.position;
      let newPosition = [cx, cy + 1];
      if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
        state = assoc("position", newPosition, state);
      } else {
        state = merge({
          board: Board.writePiece(state.board, state.piece, state.position),
          piece: undefined,
          position: INITIAL_POSITION
        }, state);
      }
      return state;
    }
  });

  let collapse$ = Observable.merge(drop$, gravity$).map(() => {
    return function (state) {
      let filledRowIndexes = Board.getFilledRowIndexes(state.board);
      if (filledRowIndexes.length) {
        let newBoard = Board.collapseFilledRows(state.board);
        state = assoc("board", newBoard, state);
      }
      return state;
    }
  });

  let destiny$ = Observable.merge(drop$, gravity$).map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        if (state.piece == undefined) {
          let nextPiece = randomObjectValue(PIECES);
          if (Board.doesPieceFit(state.board, nextPiece, INITIAL_POSITION)) {
            // Game continues with next piece
            state = merge({
              piece: state.nextPiece,
              nextPiece: randomObjectValue(PIECES),
              position: INITIAL_POSITION
            }, state);
          } else {
            // Game over
            state = merge({
              piece: undefined,
              position: INITIAL_POSITION,
              live: false
            }, state);
          }
        }
      }
      return state;
    }
  });

  let transform$ = Rx.Observable.merge(
    // Controllable
    pause$, moveLeft$, moveRight$, rotate$, drop$,

    // Uncontrollable
    gravity$, collapse$, destiny$
  );

  let state$ = transform$
    .startWith(seedState())
    .scan((state, transform) => transform(state))
    .distinctUntilChanged()
    .shareReplay(1);

  state$.subscribe(state => {
    tickRunning$.onNext(isTicking(state));
  });

  return {state$};
}

function View(state) {
  function renderRow(row) {
    return row.map(char => {
      return DRAW_MAP[char];
    }).join("");
  }

  function renderBoard(board) {
    return pipe(
      reduce((canvas, row) => canvas + renderRow(row) + "\n", ""),
      trim
    )(board);
  }

  return state.state$.map(({board, piece, nextPiece, position, live, paused}) =>  {
    let primaryBoard = board;
    let secondaryBoard = Board.create(2, 4);
    let primaryBoardWithPiece = Board.drawPiece(primaryBoard, piece, position);
    let secondaryBoardWithPiece = Board.drawPiece(secondaryBoard, nextPiece, [1, 1]);

    let status;
    if (live) {
      if (paused) {
        status =
Paused!
;
      } else {
        status =
Running!
;
      }
    } else {
      status =
Game over!
;
    }
    return (

              {renderBoard(primaryBoardWithPiece)}

          {status}

              {renderBoard(secondaryBoardWithPiece)}

    );
  });
}

Cycle.applyToDOM("#main", interactions => View(Model(Intent(interactions))));
<commit_msg>Fix outdated function name<commit_after>import {assoc, clone, evolve, identity, map, pipe, reduce, repeat, range, trim} from "ramda";
import Class from "classnames";
import Cycle from "cyclejs";
import Stream from "cyclejs-stream";
import {merge, randomObjectValue} from "./helpers";
import Piece from "./piece";
import Board from "./board";

let {Rx} = Cycle;
let {Observable, Subject} = Rx;

// CONSTANTS =======================================================================================
const BOARD_ROWS = 12; // have to be able to contain PIECES
const BOARD_COLS = 8; // ...
const INITIAL_POSITION = [2, 2]; // TODO evaluate best position (middle of cols, top + 1 of rows?!)

/**
 LEGEND
   "0" - empty cell
   "1" - written cell
   "P" - piece cell
   "G" - ghost cell
*/

const PIECES = {
  I: [[-1,  0], [ 0,  0], [ 1, 0], [2, 0]],
  T: [[ 0, -1], [-1,  0], [ 0, 0], [1, 0]],
  O: [[ 0, -1], [ 1, -1], [ 0, 0], [1, 0]],
  J: [[-1, -1], [-1,  0], [ 0, 0], [1, 0]],
  L: [[ 1, -1], [-1,  0], [ 0, 0], [1, 0]],
  S: [[ 0, -1], [ 1, -1], [-1, 0], [0, 0]],
  Z: [[-1, -1], [ 0, -1], [ 0, 0], [1, 0]],
};

const TICK_MS = 1000;

const DRAW_MAP = {
 "0": "\u2B1C",
 "1": "\u2B1B",
 "P": "\u2B1B",
 "G": "\u2B1B",
};

//let randomObjectValue2 = (PIECES) => PIECES.O;

// APP =============================================================================================
function Intent(interactions) {
  // KEYBOARD EVENTS
  let keydown$ = Observable.fromEvent(document, "keydown").debounce(100);
  return {
    // TODO: debounce without delay
    hitEnter$: keydown$.filter(event => event.keyCode == 13),
    hitSpace$: keydown$.filter(event => event.keyCode == 32),
    hitLeftArrow$: keydown$.filter(event => event.keyCode == 37),
    hitUpArrow$: keydown$.filter(event => event.keyCode == 38),
    hitRightArrow$: keydown$.filter(event => event.keyCode == 39),
    hitDownArrow$: keydown$.filter(event => event.keyCode == 40),
  }
}

function Model(intentions) {
  function seedState() {
    return {
      // Game board
      board: Board.create(BOARD_ROWS, BOARD_COLS),

      // Current piece
      piece: randomObjectValue(PIECES),

      // Next piece
      nextPiece: randomObjectValue(PIECES),

      // Current position (of current piece)
      position: INITIAL_POSITION,

      // Live state (game is not lost)
      live: true,

      // User pause
      paused: true,
    };
  }

  function isTicking(state) {
    return state.live && !state.paused;
  }

  // GAME EVENTS
  let moveLeft$ = intentions.hitLeftArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let [cx, cy] = state.position;
        let newPosition = [cx - 1, cy];
        if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
          state = assoc("position", newPosition, state);
        }
      }
      return state;
    };
  });

  let moveRight$ = intentions.hitRightArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let [cx, cy] = state.position;
        let newPosition = [cx + 1, cy];
        if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
          state = assoc("position", newPosition, state);
        }
      }
      return state;
    };
  });

  let rotate$ = intentions.hitUpArrow$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let rotatedPiece = Piece.rotate(state.piece);
        if (Board.doesPieceFit(state.board, rotatedPiece, state.position)) {
          state = assoc("piece", rotatedPiece, state);
        }
      }
      return state;
    };
  });

  let drop$ = intentions.hitSpace$.map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        let dropPosition = Board.getDropPosition(state.board, state.piece, state.position);
        state = merge({
          board: Board.writePiece(state.board, state.piece, dropPosition),
          piece: undefined,
          position: INITIAL_POSITION
        }, state);
      }
      return state;
    };
  });

  let pause$ = intentions.hitEnter$.map(() => {
    return function (state) {
      state = assoc("paused", !state.paused, state);
      return state;
    }
  });

  let tickRunning$ = new Subject();
  tickRunning$.distinctUntilChanged();

  let tick$ = Observable.interval(TICK_MS).pausable(tickRunning$).take(30);

  let gravity$ = tick$.map(() => {
    return function (state) {
      let [cx, cy] = state.position;
      let newPosition = [cx, cy + 1];
      if (Board.doesPieceFit(state.board, state.piece, newPosition)) {
        state = assoc("position", newPosition, state);
      } else {
        state = merge({
          board: Board.writePiece(state.board, state.piece, state.position),
          piece: undefined,
          position: INITIAL_POSITION
        }, state);
      }
      return state;
    }
  });

  let collapse$ = Observable.merge(drop$, gravity$).map(() => {
    return function (state) {
      let filledRowIndexes = Board.getFilledRowIndexes(state.board);
      if (filledRowIndexes.length) {
        let newBoard = Board.collapseFilledRows(state.board);
        state = assoc("board", newBoard, state);
      }
      return state;
    }
  });

  let destiny$ = Observable.merge(drop$, gravity$).map(() => {
    return function (state) {
      if (state.live && !state.paused) {
        if (state.piece == undefined) {
          let nextPiece = randomObjectValue(PIECES);
          if (Board.doesPieceFit(state.board, nextPiece, INITIAL_POSITION)) {
            // Game continues with next piece
            state = merge({
              piece: state.nextPiece,
              nextPiece: randomObjectValue(PIECES),
              position: INITIAL_POSITION
            }, state);
          } else {
            // Game over
            state = merge({
              piece: undefined,
              position: INITIAL_POSITION,
              live: false
            }, state);
          }
        }
      }
      return state;
    }
  });

  let transform$ = Rx.Observable.merge(
    // Controllable
    pause$, moveLeft$, moveRight$, rotate$, drop$,

    // Uncontrollable
    gravity$, collapse$, destiny$
  );

  let state$ = transform$
    .startWith(seedState())
    .scan((state, transform) => transform(state))
    .distinctUntilChanged()
    .shareReplay(1);

  state$.subscribe(state => {
    tickRunning$.onNext(isTicking(state));
  });

  return {state$};
}

function View(state) {
  function renderRow(row) {
    return row.map(char => {
      return DRAW_MAP[char];
    }).join("");
  }

  function renderBoard(board) {
    return pipe(
      reduce((canvas, row) => canvas + renderRow(row) + "\n", ""),
      trim
    )(board);
  }

  return state.state$.map(({board, piece, nextPiece, position, live, paused}) =>  {
    let primaryBoard = board;
    let secondaryBoard = Board.create(2, 4);
    let primaryBoardWithPiece = Board.drawPiece(primaryBoard, piece, position);
    let secondaryBoardWithPiece = Board.drawPiece(secondaryBoard, nextPiece, [1, 1]);

    let status;
    if (live) {
      if (paused) {
        status =
Paused!
;
      } else {
        status =
Running!
;
      }
    } else {
      status =
Game over!
;
    }
    return (

              {renderBoard(primaryBoardWithPiece)}

          {status}

              {renderBoard(secondaryBoardWithPiece)}

    );
  });
}

Cycle.applyToDOM("#main", interactions => View(Model(Intent(interactions))));
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Mixin.create(Ember.Evented, {
    /**
     * The api host, default is current host
     *
     * @property {Ember.String} host
     * @default  ""
     */
    host: '',

    /**
     * The api group url like /v1  /v2
     *
     * @property {Ember.String} rootURL
     * @default  ""
     */
    rootURL: '',

    /**
     * The response data root key
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    rootKey: '',

    /**
     * The api if rootURL ends with slash , the url should not starts with slash
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    url: '',

    /**
     * The model object primary key
     *
     * @property {Ember.String} primaryKey
     * @default  "_id"
     */
    primaryKey: '_id',

    /**
     * The response data root key
     *
     * @property {Ember.String} dataRootKey
     * @default  ""
     */
    dataRootKey: '',

    /**
     * @The model object primary key
     * @function api according to url return the request api
     * @property {Ember.String} primaryKey
     * @return   "api"
     */
    api: function() {
        return this.host + this.rootURL + this.url;
    }.property('url', 'host', 'rootURL'),

    /**
     * @function save save the record to backend
     * @returns  response data
     */
    save: function(model) {
        let $this = this;
        let primaryKey = this.get('primaryKey');
        let record = {};

        //filter model data
        Ember.$.each(Object.keys(model), function(index, key) {
            if ($this.model.hasOwnProperty(key)) {
                record[key] = model[key];
            }
        });

        //check if is new data
        let url = this.get('api');
        let method = 'post';
        if (model[primaryKey]) {
            record[primaryKey] = model[primaryKey];
            url = this.get('api') + '/' + model[primaryKey];
            method = 'put';
        }

        return Ember.$.ajax({
            type: method,
            url: url,
            data: record,
            dataType: 'json',
            traditional: true
        }).then(function(data) {
            return data;
        });
    },

    /**
     * @function createRecord create current model ember object
     * @returns  Object
     */
    createRecord: function() {
        return Ember.Object.create(this.model);
    },

    /**
     * @function deleteRecord delete the record from backend
     * @returns  response data
     */
    deleteRecord: function(model) {
        let $this = this;
        let _id = $this.get('primaryKey');
        return Ember.$.ajax({
            type: 'delete',
            url: $this.get('api') + '/' + model.get(_id),
            dataType: 'json'
        }).then(function(data) {
            return data;
        });
    },

    /**
     * @function find find the records from backend according to params
     * @returns  response data
     */
    find: function(params) {
        let $this = this;
        params = $this._filterParams(params);
        return Ember.$.getJSON(this.get('api'), params || {}).then(function(data) {
            let dataList = [];
            let resp = $this._resp(data);
            Ember.$.each(resp[$this.get('rootKey')] || [], function(index, i) {
                dataList.push(Ember.Object.create(i));
            });
            return dataList;
        });
    },

    /**
     * @function findOne find only one according to primary id
     * @returns  response data
     */
    findOne: function(id) {
        let $this = this;
        return Ember.$.getJSON($this.get('api') + '/' + id).then(function(data) {
            let resp = $this._resp(data);
            return Ember.Object.create(resp[$this.get('rootKey')] || {});
        });
    },

    /**
     * @function _filterParams filter request params
     * @returns  filtered params
     */
    _filterParams: function(params) {
        if (!params) {
            return;
        }
        for (let k in params) {
            if (params.hasOwnProperty(k) && !params[k]) {
                delete params[k];
            }
        }
        return params;
    },

    _resp: function(data) {
        let dataRootKey = this.get('dataRootKey'),
            resp = data;
        if (!Ember.isBlank(dataRootKey)) {
            if(Ember.isNone(data[dataRootKey])){
                Ember.Logger.error(this.get('api')+' response data "'+dataRootKey+'" key is undefined');
            }else{
                return data[dataRootKey];
            }
        }

        return resp;
    }
});<commit_msg>fix mixins model.js<commit_after>import Ember from 'ember';

export default Ember.Mixin.create(Ember.Evented, {
    /**
     * The api host, default is current host
     *
     * @property {Ember.String} host
     * @default  ""
     */
    host: '',

    /**
     * The api group url like /v1  /v2
     *
     * @property {Ember.String} rootURL
     * @default  ""
     */
    rootURL: '',

    /**
     * The response data root key
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    rootKey: '',

    /**
     * The api if rootURL ends with slash , the url should not starts with slash
     *
     * @property {Ember.String} rootKey
     * @default  ""
     */
    url: '',

    /**
     * The model object primary key
     *
     * @property {Ember.String} primaryKey
     * @default  "_id"
     */
    primaryKey: '_id',

    /**
     * The response data root key
     *
     * @property {Ember.String} dataRootKey
     * @default  ""
     */
    dataRootKey: '',

    /**
     * @The model object primary key
     * @function api according to url return the request api
     * @property {Ember.String} primaryKey
     * @return   "api"
     */
    api: function() {
        return this.host + this.rootURL + this.url;
    }.property('url', 'host', 'rootURL'),

    /**
     * @function save save the record to backend
     * @returns  response data
     */
    save: function(model) {
        let $this = this;
        let primaryKey = this.get('primaryKey');
        let record = {};

        //filter model data
        Ember.$.each(Object.keys(model), function(index, key) {
            if ($this.model.hasOwnProperty(key)) {
                record[key] = model[key];
            }
        });

        //check if is new data
        let url = this.get('api');
        let method = 'post';
        if (model[primaryKey]) {
            record[primaryKey] = model[primaryKey];
            url = this.get('api') + '/' + model[primaryKey];
            method = 'put';
        }

        return Ember.$.ajax({
            type: method,
            url: url,
            data: record,
            dataType: 'json',
            traditional: true
        }).then(function(data) {
            return data;
        });
    },

    /**
     * @function createRecord create current model ember object
     * @returns  Object
     */
    createRecord: function() {
        return Ember.Object.create(this.model);
    },

    /**
     * @function deleteRecord delete the record from backend
     * @returns  response data
     */
    deleteRecord: function(model) {
        let $this = this;
        let _id = $this.get('primaryKey');
        return Ember.$.ajax({
            type: 'delete',
            url: $this.get('api') + '/' + model.get(_id),
            dataType: 'json'
        }).then(function(data) {
            return data;
        });
    },

    /**
     * @function find find the records from backend according to params
     * @returns  response data
     */
    find: function(params) {
        let $this = this;
        params = $this._filterParams(params);
        return Ember.$.getJSON(this.get('api'), params || {}).then(Ember.run.bind(this, function(data,  textStatus, jqXHR) {
            let dataList = [];
            let resp = this._resp(data);
            Ember.$.each(resp[this.get('rootKey')] || [], function(index, i) {
                dataList.push(Ember.Object.create(i));
            });
            return dataList;
        }));
    },

    /**
     * @function findOne find only one according to primary id
     * @returns  response data
     */
    findOne: function(id) {
        let $this = this;
        return Ember.$.getJSON($this.get('api') + '/' + id).then(function(data) {
            let resp = $this._resp(data);
            return Ember.Object.create(resp[$this.get('rootKey')] || {});
        });
    },

    /**
     * @function _filterParams filter request params
     * @returns  filtered params
     */
    _filterParams: function(params) {
        if (!params) {
            return;
        }
        for (let k in params) {
            if (params.hasOwnProperty(k) && !params[k]) {
                delete params[k];
            }
        }
        return params;
    },

    _resp: function(data) {
        let dataRootKey = this.get('dataRootKey'),
            resp = data;
        if (!Ember.isBlank(dataRootKey)) {
            if(Ember.isNone(data[dataRootKey])){
                Ember.Logger.error(this.get('api')+' response data "'+dataRootKey+'" key is undefined');
            }else{
                return data[dataRootKey];
            }
        }

        return resp;
    }
});
<|endoftext|>


<commit_before>export default ['$scope', '$injector', '$rootScope', '$window', 'Core', 'hs.map.service', 'hs.geolocation.service', 'hs.layermanager.service', 'gettextCatalog', 'config', '$templateCache', '$timeout', '$interval', 'hs.layout.service',
    function ($scope, $injector, $rootScope, $window, Core, OlMap, Geolocation, LayerManager, gettextCatalog, config, $templateCache, $timeout, $interval, layoutService) {
        if (config.design == 'md')
            require(['bottomSheetCollapsible']);
        $scope.importCss = angular.isDefined(config.importCss) ? config.importCss : true;
        $scope.useIsolatedBootstrap = angular.isDefined(config.useIsolatedBootstrap) ? config.useIsolatedBootstrap : false;
        $scope.Core = Core;
        $scope.geolocation = Geolocation;
        $scope.LM = LayerManager;
        $scope.layoutService = layoutService;

        $scope.location = {
            status: {
                icon: "location_searching",
                class: "off"
            }
        };
        $scope.defaultFab = {
            primary: {
                clickAction: function () {
                    console.log("Primary clicked.");
                },
                classes: "",
                icon: {
                    iconSet: "material-icons",
                    classes: "",
                    text: "add"
                },
                tooltip: {
                    direction: "left",
                    text: "Cancel"
                }
            },
            secondary: [
                {
                    clickAction: function () {
                        console.log("Secondary 1 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "place"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New point"
                    }
                },
                {
                    clickAction: function () {
                        console.log("Secondary 2 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "timeline"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New line"
                    }
                },
                {
                    clickAction: function () {
                        console.log("Secondary 3 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "select_all"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New polygon"
                    }
                }
            ],
            options: {
                isOpen: false,
                tooltipsVisible: false,
                direction: "up",
                location: "md-fab-bottom-right"
            }
        };

        $scope.fab = {
            update: function (primary, secondary, options) {
                this.primary = angular.copy(primary);
                if (secondary) {
                    this.secondary = angular.copy(secondary);
                } else if (this.secondary) {
                    delete this.secondary;
                }
                this.options = angular.copy(options);
            },
            unset: function () {
                this.primary = angular.copy($scope.defaultFab.primary);
                this.secondary = $scope.defaultFab.secondary ? angular.copy($scope.defaultFab.secondary) : undefined;
                this.options = angular.copy($scope.defaultFab.options);
            }
        };

        $scope.$watch('fab.options.isOpen', function (isOpen) {
            if (isOpen) {
                $scope.showTooltips = $timeout(function () {
                    $scope.fab.options.tooltipsVisible = $scope.fab.options.isOpen;
                    $scope.hideTooltips = $timeout(function () {
                        $scope.fab.options.tooltipsVisible = false;
                    }, 2500);
                }, 500);
            } else {
                $timeout.cancel($scope.showTooltips);
                $timeout.cancel($scope.hideTooltips);
                $scope.fab.options.tooltipsVisible = $scope.fab.options.isOpen;
            }
        });

        $scope.$on('scope_loaded', function () {
            $scope.fab.unset();
        });

        function removeLoadingLogo() {
            var el = document.getElementById('hs-loading-logo');
            if (el) {
                el.parentElement.removeChild(el);
            }
        }

        $rootScope.$on('$viewContentLoaded', removeLoadingLogo);
        $timeout(removeLoadingLogo, 100);

        $scope.leftSidenavOpen = false;

        $rootScope.$on('geolocation.started', function () {
            $scope.location.status.icon = "my_location";
            $scope.location.status.class = "searching";
        });

        $rootScope.$on('geolocation.updated', function () {
            $scope.location.status.icon = "my_location";
            $scope.location.status.class = "on";
        });

        $rootScope.$on('geolocation.stopped', function () {
            $scope.location.status.icon = "location_searching";
            $scope.location.status.class = "off";
        });

        $rootScope.$on('geolocation.failed', function () {
            $scope.location.status.icon = "location_disabled";
            $scope.location.status.class = "off";
        });

        var $mdMedia;
        try {
            $mdMedia = $injector.get('$mdMedia')
        } catch (ex) {

        }
        $scope.openPanel = function (panel) {
            Core.setMainPanel(panel.name);
            $scope.bottomSheetTitle = panel.title;
            if ($mdMedia && !$mdMedia('gt-sm') && !$scope.getBottomSheetState) {
                $scope.closeLeftSidenav();
                $scope.openBottomSheet(panel);
            }
        }

        $scope.switchBottomSheetState = function () {
            if ($scope.getBottomSheetState() === "minimized") {
                $scope.setHalfway();
            } else {
                $scope.setMinimized();
            }
        }

        // $scope.bottomSheetState = "halfway";
        // $scope.bottomSheetSwitchStateIcon = "expand_more";

        function resolveScrollPosition(raw) {
            if ($scope.getBottomSheetState === "expanded" && raw.scrollHeight > raw.clientHeight) {
                raw.style["touch-action"] = "pan-y";
                if (raw.scrollTop + raw.offsetHeight > raw.scrollHeight) {
                    raw.style["touch-action"] = "pan-up";
                }
                if (raw.scrollTop == 0) {
                    raw.style["touch-action"] = "pan-down";
                }
            } else {
                raw.style["touch-action"] = "none";
            }
        }

        $scope.openBottomSheet = function (panel) {
            try {
                var $mdBottomSheetCollapsible = $injector.get('$mdBottomSheetCollapsible');
                $mdBottomSheetCollapsible.show({
                    template: require('components/layout/partials/bottom-sheet.html'),
                    scope: $scope,
                    parent: "#layout",
                    preserveScope: true,
                    disableBackdrop: true,
                    // disableParentScroll: false,
                    clickOutsideToClose: true,
                    onLoad: function (e) {
                        $scope.setMinimized = e.setMinimized;
                        $scope.setHalfway = e.setHalfway;
                        $scope.setExpanded = e.setExpanded;
                        $scope.getBottomSheetState = e.getState;
                        $scope.bottomSheet = e.element;
                        // () => {
                        //     var raw = $scope.bottomSheet;
                        //     $scope.$watch(() => raw.scrollHeight,
                        //         resolveScrollPosition(raw));
                        //     $scope.$watch(() => $scope.getBottomSheetState(), resolveScrollPosition(raw));
                        //     element.bind('scroll', function () {
                        //         raw.style["touch-action"] = "pan-y";
                        //         if (raw.scrollTop + raw.offsetHeight > raw.scrollHeight) {
                        //             raw.style["touch-action"] = "pan-up";
                        //         }
                        //         if (raw.scrollTop == 0) {
                        //             raw.style["touch-action"] = "pan-down";
                        //         }
                        //     })
                        // }
                    }
                }).then(function (e) {
                    console.log("Bottom sheet closed", Date.now());
                    $scope.unsetBottomSheet();
                }).catch(function (e) {
                    console.log("Bottom sheet canceled", Date.now());
                    $scope.unsetBottomSheet();
                });
            } catch (e) {
                console.log('Injector does not have mdBottomSheetCollapsible service!');
            }

            // $scope.$watch(function() {
            //     return $scope.getBottomSheetState();
            // }, function() {
            //     $scope.bottomSheetSwitchStateIcon = $scope.getBottomSheetState === "minimized" ? "expand_less" : "expand_more";
            // });
        }

        $scope.closeBottomSheet = function () {
            $scope.bottomSheet.hide();
            $scope.unsetBottomSheet();
        }

        $scope.unsetBottomSheet = function () {
            $scope.setMinimized = undefined;
            $scope.setHalfway = undefined;
            $scope.setExpanded = undefined;
            $scope.getBottomSheetState = undefined;
            $scope.bottomSheet = undefined;
        }

        if (config.layout == "md") {
            try {
                var $mdSidenav = $injector.get('$mdSidenav');

                $scope.openLeftSidenav = function () {
                    $mdSidenav('sidenav-left').open()
                        .then(function () {
                            $scope.leftSidenavOpen = true;
                        });
                }

                $scope.closeLeftSidenav = function () {
                    $mdSidenav('sidenav-left').close();
                }

                $mdSidenav('sidenav-left', true).then(function () {
                    $mdSidenav('sidenav-left').onClose(function () {
                        $scope.leftSidenavOpen = false;
                    });

                    Hammer(document.getElementsByClassName("md-sidenav-left")[0]).on("swipeleft", () => {
                        $scope.closeLeftSidenav();
                    });

                    Hammer(document.getElementById("sidenav-swipe-overlay")).on("swiperight", () => {
                        $scope.openLeftSidenav();
                    });
                });

                $scope.openRightPanel = function () {
                    $mdSidenav('right-panel').open()
                        .then(function () {
                            $scope.rightPanelOpen = true;
                        });
                }

                $scope.closeRightPanel = function () {
                    $mdSidenav('right-panel').close();
                }

                $mdSidenav('right-panel', true).then(function () {
                    $mdSidenav('right-panel').onClose(function () {
                        $scope.rightPanelOpen = false;
                    });
                });
            } catch (ex) {
                console.error("$mdSidenav missing")
            }

            try {
                var $mdDialog = $injector.get('$mdDialog');

                $scope.showRemoveDialog = function (e, layer) {
                    var confirm = $mdDialog.confirm()
                        .title('Remove basemap ' + layer.title)
                        .textContent('Are you sure about layer removal?')
                        .ariaLabel('Confirm layer removal')
                        .targetEvent(e)
                        .ok('Remove')
                        .cancel('Cancel')
                        .hasBackdrop(false);

                    $mdDialog.show(confirm).then(function () {
                        $scope.removeLayer(layer);
                    }, function () {
                    });
                }
            } catch (ex) {

            }
        }

        $scope.defaultBaselayerThumbnail = require(`components/layout/osm.png`);
        $scope.defaultTerrainlayerThumbnail = require(`components/layout/osm.png`);

        let baselayersPanelRef;

        $scope.removeLayer = function (layer) {
            var active = layer.active;
            OlMap.map.removeLayer(layer.layer);
            if (active) {
                if (LM.data.baselayers.length > 0) LM.changeBaseLayerVisibility(true, LM.data.baselayers[0]);
            }
        }

        $scope.hasImage = function (layer) {
            return angular.isDefined(layer.layer.get('img')) ? true : false;
        }

        $scope.getImage = function (layer) {
            return layer.layer.get('img');
        }

        $scope.isRemovable = function (layer) {
            return layer.layer.get('removable');
        }

        try {
            var $mdPanel = $injector.get('$mdPanel');

            $scope.openBaselayersPanel = function ($event) {
                let panelPosition = $mdPanel.newPanelPosition()
                    // .relativeTo($event.srcElement)
                    .relativeTo($event.target)
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_END,
                        $mdPanel.yPosition.ALIGN_TOPS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_START,
                        $mdPanel.yPosition.ALIGN_TOPS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_END,
                        $mdPanel.yPosition.ALIGN_BOTTOMS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_START,
                        $mdPanel.yPosition.ALIGN_BOTTOMS
                    );
                let panelAnimation = $mdPanel.newPanelAnimation()
                    .openFrom($event.target)
                    .closeTo($event.target)
                    // .targetEvent($event)
                    // .defaultAnimation('md-panel-animate-fly')
                    .withAnimation($mdPanel.animation.SCALE);
                let config = {
                    attachTo: angular.element("#gui"),
                    position: panelPosition,
                    animation: panelAnimation,
                    targetEvent: $event,
                    template: require(`components/layout/partials/baselayers.html`),
                    panelClass: 'baselayers-panel md-whiteframe-8dp',
                    scope: this,
                    trapFocus: true,
                    clickOutsideToClose: true,
                    clickEscapeToClose: true,
                    zIndex: 50
                }

                $mdPanel.open(config)
                    .then(function (result) {
                        baselayersPanelRef = result;
                    });

                $scope.closeBaselayersPanel = function (MdPanelRef) {
                    if (MdPanelRef) MdPanelRef.close();
                }
            }

        } catch (ex) {

        }

        $scope.panelSpaceWidth = layoutService.panelSpaceWidth;

        $scope.infoContainerStyle = function () {
            if (!Core.sidebarRight)
                return {
                    marginLeft: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
            else
                return {
                    marginRight: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
        }

        $scope.mapStyle = function () {
            if (!Core.sidebarRight)
                return {
                    marginLeft: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
            else
                return {
                    marginLeft: '-px',
                    width: layoutService.widthWithoutPanelSpace()
                }
        }

        $scope.onlyEnabled = function (item) {
            return item.enabled;
        };

        $scope.$emit('scope_loaded', "Layout");
    }

]<commit_msg>BottomsheetCollapsible should be loaded explicitly<commit_after>export default ['$scope', '$injector', '$rootScope', '$window', 'Core', 'hs.map.service', 'hs.geolocation.service', 'hs.layermanager.service', 'gettextCatalog', 'config', '$templateCache', '$timeout', '$interval', 'hs.layout.service',
    function ($scope, $injector, $rootScope, $window, Core, OlMap, Geolocation, LayerManager, gettextCatalog, config, $templateCache, $timeout, $interval, layoutService) {
        if (config.design == 'md')
            require(['angular-material-bottom-sheet-collapsible/bottomSheetCollapsible']);
        $scope.importCss = angular.isDefined(config.importCss) ? config.importCss : true;
        $scope.useIsolatedBootstrap = angular.isDefined(config.useIsolatedBootstrap) ? config.useIsolatedBootstrap : false;
        $scope.Core = Core;
        $scope.geolocation = Geolocation;
        $scope.LM = LayerManager;
        $scope.layoutService = layoutService;

        $scope.location = {
            status: {
                icon: "location_searching",
                class: "off"
            }
        };
        $scope.defaultFab = {
            primary: {
                clickAction: function () {
                    console.log("Primary clicked.");
                },
                classes: "",
                icon: {
                    iconSet: "material-icons",
                    classes: "",
                    text: "add"
                },
                tooltip: {
                    direction: "left",
                    text: "Cancel"
                }
            },
            secondary: [
                {
                    clickAction: function () {
                        console.log("Secondary 1 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "place"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New point"
                    }
                },
                {
                    clickAction: function () {
                        console.log("Secondary 2 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "timeline"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New line"
                    }
                },
                {
                    clickAction: function () {
                        console.log("Secondary 3 clicked.");
                    },
                    classes: "",
                    icon: {
                        iconSet: "material-icons",
                        classes: "",
                        text: "select_all"
                    },
                    tooltip: {
                        direction: "left",
                        text: "New polygon"
                    }
                }
            ],
            options: {
                isOpen: false,
                tooltipsVisible: false,
                direction: "up",
                location: "md-fab-bottom-right"
            }
        };

        $scope.fab = {
            update: function (primary, secondary, options) {
                this.primary = angular.copy(primary);
                if (secondary) {
                    this.secondary = angular.copy(secondary);
                } else if (this.secondary) {
                    delete this.secondary;
                }
                this.options = angular.copy(options);
            },
            unset: function () {
                this.primary = angular.copy($scope.defaultFab.primary);
                this.secondary = $scope.defaultFab.secondary ? angular.copy($scope.defaultFab.secondary) : undefined;
                this.options = angular.copy($scope.defaultFab.options);
            }
        };

        $scope.$watch('fab.options.isOpen', function (isOpen) {
            if (isOpen) {
                $scope.showTooltips = $timeout(function () {
                    $scope.fab.options.tooltipsVisible = $scope.fab.options.isOpen;
                    $scope.hideTooltips = $timeout(function () {
                        $scope.fab.options.tooltipsVisible = false;
                    }, 2500);
                }, 500);
            } else {
                $timeout.cancel($scope.showTooltips);
                $timeout.cancel($scope.hideTooltips);
                $scope.fab.options.tooltipsVisible = $scope.fab.options.isOpen;
            }
        });

        $scope.$on('scope_loaded', function () {
            $scope.fab.unset();
        });

        function removeLoadingLogo() {
            var el = document.getElementById('hs-loading-logo');
            if (el) {
                el.parentElement.removeChild(el);
            }
        }

        $rootScope.$on('$viewContentLoaded', removeLoadingLogo);
        $timeout(removeLoadingLogo, 100);

        $scope.leftSidenavOpen = false;

        $rootScope.$on('geolocation.started', function () {
            $scope.location.status.icon = "my_location";
            $scope.location.status.class = "searching";
        });

        $rootScope.$on('geolocation.updated', function () {
            $scope.location.status.icon = "my_location";
            $scope.location.status.class = "on";
        });

        $rootScope.$on('geolocation.stopped', function () {
            $scope.location.status.icon = "location_searching";
            $scope.location.status.class = "off";
        });

        $rootScope.$on('geolocation.failed', function () {
            $scope.location.status.icon = "location_disabled";
            $scope.location.status.class = "off";
        });

        var $mdMedia;
        try {
            $mdMedia = $injector.get('$mdMedia')
        } catch (ex) {

        }
        $scope.openPanel = function (panel) {
            Core.setMainPanel(panel.name);
            $scope.bottomSheetTitle = panel.title;
            if ($mdMedia && !$mdMedia('gt-sm') && !$scope.getBottomSheetState) {
                $scope.closeLeftSidenav();
                $scope.openBottomSheet(panel);
            }
        }

        $scope.switchBottomSheetState = function () {
            if ($scope.getBottomSheetState() === "minimized") {
                $scope.setHalfway();
            } else {
                $scope.setMinimized();
            }
        }

        // $scope.bottomSheetState = "halfway";
        // $scope.bottomSheetSwitchStateIcon = "expand_more";

        function resolveScrollPosition(raw) {
            if ($scope.getBottomSheetState === "expanded" && raw.scrollHeight > raw.clientHeight) {
                raw.style["touch-action"] = "pan-y";
                if (raw.scrollTop + raw.offsetHeight > raw.scrollHeight) {
                    raw.style["touch-action"] = "pan-up";
                }
                if (raw.scrollTop == 0) {
                    raw.style["touch-action"] = "pan-down";
                }
            } else {
                raw.style["touch-action"] = "none";
            }
        }

        $scope.openBottomSheet = function (panel) {
            try {
                var $mdBottomSheetCollapsible = $injector.get('$mdBottomSheetCollapsible');
                $mdBottomSheetCollapsible.show({
                    template: require('components/layout/partials/bottom-sheet.html'),
                    scope: $scope,
                    parent: "#layout",
                    preserveScope: true,
                    disableBackdrop: true,
                    // disableParentScroll: false,
                    clickOutsideToClose: true,
                    onLoad: function (e) {
                        $scope.setMinimized = e.setMinimized;
                        $scope.setHalfway = e.setHalfway;
                        $scope.setExpanded = e.setExpanded;
                        $scope.getBottomSheetState = e.getState;
                        $scope.bottomSheet = e.element;
                        // () => {
                        //     var raw = $scope.bottomSheet;
                        //     $scope.$watch(() => raw.scrollHeight,
                        //         resolveScrollPosition(raw));
                        //     $scope.$watch(() => $scope.getBottomSheetState(), resolveScrollPosition(raw));
                        //     element.bind('scroll', function () {
                        //         raw.style["touch-action"] = "pan-y";
                        //         if (raw.scrollTop + raw.offsetHeight > raw.scrollHeight) {
                        //             raw.style["touch-action"] = "pan-up";
                        //         }
                        //         if (raw.scrollTop == 0) {
                        //             raw.style["touch-action"] = "pan-down";
                        //         }
                        //     })
                        // }
                    }
                }).then(function (e) {
                    console.log("Bottom sheet closed", Date.now());
                    $scope.unsetBottomSheet();
                }).catch(function (e) {
                    console.log("Bottom sheet canceled", Date.now());
                    $scope.unsetBottomSheet();
                });
            } catch (e) {
                console.log('Injector does not have mdBottomSheetCollapsible service!');
            }

            // $scope.$watch(function() {
            //     return $scope.getBottomSheetState();
            // }, function() {
            //     $scope.bottomSheetSwitchStateIcon = $scope.getBottomSheetState === "minimized" ? "expand_less" : "expand_more";
            // });
        }

        $scope.closeBottomSheet = function () {
            $scope.bottomSheet.hide();
            $scope.unsetBottomSheet();
        }

        $scope.unsetBottomSheet = function () {
            $scope.setMinimized = undefined;
            $scope.setHalfway = undefined;
            $scope.setExpanded = undefined;
            $scope.getBottomSheetState = undefined;
            $scope.bottomSheet = undefined;
        }

        if (config.layout == "md") {
            try {
                var $mdSidenav = $injector.get('$mdSidenav');

                $scope.openLeftSidenav = function () {
                    $mdSidenav('sidenav-left').open()
                        .then(function () {
                            $scope.leftSidenavOpen = true;
                        });
                }

                $scope.closeLeftSidenav = function () {
                    $mdSidenav('sidenav-left').close();
                }

                $mdSidenav('sidenav-left', true).then(function () {
                    $mdSidenav('sidenav-left').onClose(function () {
                        $scope.leftSidenavOpen = false;
                    });

                    Hammer(document.getElementsByClassName("md-sidenav-left")[0]).on("swipeleft", () => {
                        $scope.closeLeftSidenav();
                    });

                    Hammer(document.getElementById("sidenav-swipe-overlay")).on("swiperight", () => {
                        $scope.openLeftSidenav();
                    });
                });

                $scope.openRightPanel = function () {
                    $mdSidenav('right-panel').open()
                        .then(function () {
                            $scope.rightPanelOpen = true;
                        });
                }

                $scope.closeRightPanel = function () {
                    $mdSidenav('right-panel').close();
                }

                $mdSidenav('right-panel', true).then(function () {
                    $mdSidenav('right-panel').onClose(function () {
                        $scope.rightPanelOpen = false;
                    });
                });
            } catch (ex) {
                console.error("$mdSidenav missing")
            }

            try {
                var $mdDialog = $injector.get('$mdDialog');

                $scope.showRemoveDialog = function (e, layer) {
                    var confirm = $mdDialog.confirm()
                        .title('Remove basemap ' + layer.title)
                        .textContent('Are you sure about layer removal?')
                        .ariaLabel('Confirm layer removal')
                        .targetEvent(e)
                        .ok('Remove')
                        .cancel('Cancel')
                        .hasBackdrop(false);

                    $mdDialog.show(confirm).then(function () {
                        $scope.removeLayer(layer);
                    }, function () {
                    });
                }
            } catch (ex) {

            }
        }

        $scope.defaultBaselayerThumbnail = require(`components/layout/osm.png`);
        $scope.defaultTerrainlayerThumbnail = require(`components/layout/osm.png`);

        let baselayersPanelRef;

        $scope.removeLayer = function (layer) {
            var active = layer.active;
            OlMap.map.removeLayer(layer.layer);
            if (active) {
                if (LM.data.baselayers.length > 0) LM.changeBaseLayerVisibility(true, LM.data.baselayers[0]);
            }
        }

        $scope.hasImage = function (layer) {
            return angular.isDefined(layer.layer.get('img')) ? true : false;
        }

        $scope.getImage = function (layer) {
            return layer.layer.get('img');
        }

        $scope.isRemovable = function (layer) {
            return layer.layer.get('removable');
        }

        try {
            var $mdPanel = $injector.get('$mdPanel');

            $scope.openBaselayersPanel = function ($event) {
                let panelPosition = $mdPanel.newPanelPosition()
                    // .relativeTo($event.srcElement)
                    .relativeTo($event.target)
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_END,
                        $mdPanel.yPosition.ALIGN_TOPS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_START,
                        $mdPanel.yPosition.ALIGN_TOPS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_END,
                        $mdPanel.yPosition.ALIGN_BOTTOMS
                    )
                    .addPanelPosition(
                        $mdPanel.xPosition.ALIGN_START,
                        $mdPanel.yPosition.ALIGN_BOTTOMS
                    );
                let panelAnimation = $mdPanel.newPanelAnimation()
                    .openFrom($event.target)
                    .closeTo($event.target)
                    // .targetEvent($event)
                    // .defaultAnimation('md-panel-animate-fly')
                    .withAnimation($mdPanel.animation.SCALE);
                let config = {
                    attachTo: angular.element("#gui"),
                    position: panelPosition,
                    animation: panelAnimation,
                    targetEvent: $event,
                    template: require(`components/layout/partials/baselayers.html`),
                    panelClass: 'baselayers-panel md-whiteframe-8dp',
                    scope: this,
                    trapFocus: true,
                    clickOutsideToClose: true,
                    clickEscapeToClose: true,
                    zIndex: 50
                }

                $mdPanel.open(config)
                    .then(function (result) {
                        baselayersPanelRef = result;
                    });

                $scope.closeBaselayersPanel = function (MdPanelRef) {
                    if (MdPanelRef) MdPanelRef.close();
                }
            }

        } catch (ex) {

        }

        $scope.panelSpaceWidth = layoutService.panelSpaceWidth;

        $scope.infoContainerStyle = function () {
            if (!Core.sidebarRight)
                return {
                    marginLeft: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
            else
                return {
                    marginRight: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
        }

        $scope.mapStyle = function () {
            if (!Core.sidebarRight)
                return {
                    marginLeft: layoutService.panelSpaceWidth() + 'px',
                    width: layoutService.widthWithoutPanelSpace()
                }
            else
                return {
                    marginLeft: '-px',
                    width: layoutService.widthWithoutPanelSpace()
                }
        }

        $scope.onlyEnabled = function (item) {
            return item.enabled;
        };

        $scope.$emit('scope_loaded', "Layout");
    }

]<|endoftext|>


<commit_before>const base = require('./base.config.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');
const path = require('path');

const proPlugins = [
    new webpack.HashedModuleIdsPlugin(),
    new ExtractTextPlugin('css/[name].css'),
];
base.module.rules.push({
    test: /\.css$/,
    exclude: ['node_modules'],
    use: ExtractTextPlugin.extract({
        use: ['css-loader', 'postcss-loader'],
        fallback: 'vue-style-loader',
        publicPath: '../assets/dist/css/',
    }),
});
module.exports = {
    mode: 'production',
    optimization: {
        splitChunks: {
            chunks: "initial", // 必须三选一： "initial" | "all"(默认就是all) | "async"
            minSize: 0, // 最小尺寸，默认0
            minChunks: 1, // 最小 chunk ，默认1
            maxAsyncRequests: 1, // 最大异步请求数， 默认1
            maxInitialRequests: 1, // 最大初始化请求书，默认1
            name: function () {}, // 名称，此选项可接收 function
            cacheGroups: { // 这里开始设置缓存的 chunks
                priority: false, // 缓存组优先级
                vendor: { // key 为entry中定义的 入口名称
                    chunks: "initial", // 必须三选一： "initial" | "all" | "async"(默认就是异步)
                    test: /react|lodash/, // 正则规则验证，如果符合就提取 chunk
                    name: "vendor", // 要缓存的 分隔出来的 chunk 名称
                    minSize: 0,
                    minChunks: 1,
                    enforce: true,
                    maxAsyncRequests: 1, // 最大异步请求数， 默认1
                    maxInitialRequests: 1, // 最大初始化请求书，默认1
                    reuseExistingChunk: true // 可设置是否重用该chunk（查看源码没有发现默认值）
                }
            }
        }
    },
    context: path.resolve(__dirname, '../assets/'),
    entry: base.entry,
    output: {
        filename: '[name].[chunkhash:8].js',
        chunkFilename: '[name].[chunkhash:8].js',
        publicPath: '/assets/dist/',
        path: path.resolve(__dirname, '../assets/dist'),
    },
    module: base.module,
    devtool: 'cheap-module-source-map',
    resolve: base.resolve,
    plugins: base.plugins.concat(proPlugins),
    performance: {
        hints: 'error',
    },
};
<commit_msg>update the build configuration<commit_after>const base = require('./base.config.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const webpack = require('webpack');
const path = require('path');

const proPlugins = [
    new webpack.HashedModuleIdsPlugin(),
    new ExtractTextPlugin('css/[name].css'),
];
base.module.rules.push({
    test: /\.css$/,
    exclude: ['node_modules'],
    use: ExtractTextPlugin.extract({
        use: [{
            loader: 'css-loader',
            options: {
                minimize: true,
            }
        }, 'postcss-loader'],
        fallback: 'vue-style-loader',
        publicPath: '../',
    }),
});
module.exports = {
    mode: 'production',
    optimization: {
        splitChunks: {
            chunks: "initial", // 必须三选一： "initial" | "all"(默认就是all) | "async"
            minSize: 0, // 最小尺寸，默认0
            minChunks: 1, // 最小 chunk ，默认1
            maxAsyncRequests: 1, // 最大异步请求数， 默认1
            maxInitialRequests: 1, // 最大初始化请求书，默认1
            name: function () {}, // 名称，此选项可接收 function
            cacheGroups: { // 这里开始设置缓存的 chunks
                priority: false, // 缓存组优先级
                vendor: { // key 为entry中定义的 入口名称
                    chunks: "initial", // 必须三选一： "initial" | "all" | "async"(默认就是异步)
                    test: /react|lodash/, // 正则规则验证，如果符合就提取 chunk
                    name: "vendor", // 要缓存的 分隔出来的 chunk 名称
                    minSize: 0,
                    minChunks: 1,
                    enforce: true,
                    maxAsyncRequests: 1, // 最大异步请求数， 默认1
                    maxInitialRequests: 1, // 最大初始化请求书，默认1
                    reuseExistingChunk: true // 可设置是否重用该chunk（查看源码没有发现默认值）
                }
            }
        }
    },
    context: path.resolve(__dirname, '../assets/'),
    entry: base.entry,
    output: {
        filename: '[name].[chunkhash:8].js',
        chunkFilename: '[name].[chunkhash:8].js',
        publicPath: './assets/dist/',
        path: path.resolve(__dirname, '../assets/dist'),
    },
    module: base.module,
    devtool: 'cheap-module-source-map',
    resolve: base.resolve,
    plugins: base.plugins.concat(proPlugins),
    performance: {
        hints: 'error',
    },
};
<|endoftext|>