<commit_before>/**
    Framework for building object relational database apps
    Copyright (C) 2019  John Rogelstad

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/
import catalog from "./catalog.js";
import model from "./model.js";

/*
  Role Membership model
*/
function roleMembership(data, feather) {
    feather = feather || catalog.getFeather("RoleMembership");
    let that = model(data, feather);

    function roleNames() {
        let roles = catalog.store().data().roles();
        let name;
        let result;

        result = roles.filter(function (role) {
            return role.data.objectType() === "Role";
        });
        result = result.map(function (role) {
            name = role.data.name();
            return {
                value: name,
                label: name
            };
        });
        result.unshift({
            value: "",
            label: ""
        });
        return result;
    }

    that.addCalculated({
        name: "roleNames",
        type: "array",
        function: roleNames
    });

    return that;
}

catalog.registerModel("RoleMembership", roleMembership);
<commit_msg>Update role-membership.js<commit_after>/**
    Framework for building object relational database apps
    Copyright (C) 2019  John Rogelstad

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
**/
import catalog from "./catalog.js";
import model from "./model.js";

/*
  Role Membership model
*/
function roleMembership(data, feather) {
    feather = feather || catalog.getFeather("RoleMembership");
    let that = model(data, feather);

    function roleNames() {
        let roles = catalog.store().data().roles().slice();
        let name;
        let result;

        result = roles.filter(function (role) {
            return role.data.objectType() === "Role";
        });
        result = result.map(function (role) {
            name = role.data.name();
            return {
                value: name,
                label: name
            };
        });
        result.unshift({
            value: "",
            label: ""
        });
        return result;
    }

    that.addCalculated({
        name: "roleNames",
        type: "array",
        function: roleNames
    });

    return that;
}

catalog.registerModel("RoleMembership", roleMembership);
<|endoftext|>


<commit_before>module.exports = {
    middleware: [
        function(){
            if (m.hasProfile("first_user") || m.hasProfile("logined")) return;
            var dfd = $.Deferred();
            var a = this.m.models["user.user"].collection();
            a.fetch().then(function(a){
                m.setProfile("first_user",a.length == 0);
                dfd.resolve();
            });
            return dfd.promise();
        },
        function(){
            if (m.hasProfile("first_user") || !m.hasProfile("logined")) return;
            if (this.m.getProjection("admin.config")) return;
            var dfd = $.Deferred();
            var _this = this;
            var config = new this.m.models["project.project"]("config");
            config.fetch().then(function(){
                if (config.get("server_mode") == "production") m.setProfile("admin_production");
                else {
                    if (config.get("wait_restart")) m.setProfile("admin_need_restart");
                    else
                        config.once("change:wait_restart",function(){
                            if (config.get("wait_restart")) m.setProfile("admin_need_restart");
                        })
                    _this.m.setProjection("admin.config",config);
                    _.defer(m.setProfile.bind(m),"admin_config");
                }
                dfd.resolve();
            },function(){
                dfd.resolve();
            })
            return dfd.promise();
        }
    ],
    routes: [
        {route: ""},
        {route: "server"},
        {
            route: "server/models",
            callback: function(){
                m.removeProfile("model_admin");
                var a = this.m.getProjection("admin.models");
                if (!a){
                    a = this.m.models["project.server.model"].collection();
                    this.m.setProjection("admin.models",a);
                }
//                console.log(a);
                a.fetch();
            },
            routes: { "server/models/:name": "model" }
        },
        {
            route: "server/plugins",
            callback: function(){m.removeProfile("server_plugin_admin");},
            routes: { "server/plugins/:name": "server_plugin" }
        },
        {route: "client"},
        {
            route: "client/packages",
            callback: function(){m.removeProfile("package_admin");},
            routes: { "client/packages/:name": "package" }
        },
        {
            route: "client/pages",
            callback: function(){m.removeProfile("page_admin");},
            routes: { "client/pages/:name": "page" }
        },
        {
            route: "client/plugins",
            callback: function(){m.removeProfile("client_plugin_admin");},
            routes: { "client/plugins/:name": "client_plugin" }
        },
        {
            route: "client/views",
            callback: function(){m.removeProfile("view_admin");},
            routes: { "client/views/:name": "view" }
        },
        {route: "data"},
        {route: "users"},
        {route: "market"},
        {route: "logs"},
        {route: "profile"},
        {
            route: "*a",
            page: "error"
        }
    ],
    surrogate: {
        "model": function(a){
            m.setProfile("model_admin");
            this.m.setProjection("model_admin",new this.m["project.server.model"](a));
        },
        "server_plugin": function(a){
            m.setProfile("server_plugin_admin");
            this.m.setProjection("server_plugin_admin",new this.m["project.server.plugin"](a));
        },
        "package": function(a){
            m.setProfile("package_admin");
            this.m.setProjection("package_admin",new this.m["project.client.package"](a));
        },
        "page": function(a){
            m.setProfile("page_admin");
            this.m.setProjection("page_admin",new this.m["project.client.page"](a));
        },
        "client_plugin": function(a){
            m.setProfile("client_plugin_admin");
            this.m.setProjection("client_plugin_admin",new this.m["project.client.plugin"](a));
        },
        "view": function(a){
            m.setProfile("view_admin");
            this.m.setProjection("view_admin",new this.m["project.client.view"](a));
        }
    },
    ready: function(next){
        var _this = this;
        var a = new this.m.model_user_user("me");
        a.fetch().then(function(a){
            _this.m.setProjection("admin.logined",a);
            m.setProfile("logined");
            next();
        },next);
    }
};<commit_msg>admin package.js ready -> .get("me")<commit_after>module.exports = {
    middleware: [
        function(){
            if (m.hasProfile("first_user") || m.hasProfile("logined")) return;
            var dfd = $.Deferred();
            var a = this.m.models["user.user"].collection();
            a.fetch().then(function(a){
                m.setProfile("first_user",a.length == 0);
                dfd.resolve();
            });
            return dfd.promise();
        },
        function(){
            if (m.hasProfile("first_user") || !m.hasProfile("logined")) return;
            if (this.m.getProjection("admin.config")) return;
            var dfd = $.Deferred();
            var _this = this;
            var config = new this.m.models["project.project"]("config");
            config.fetch().then(function(){
                if (config.get("server_mode") == "production") m.setProfile("admin_production");
                else {
                    if (config.get("wait_restart")) m.setProfile("admin_need_restart");
                    else
                        config.once("change:wait_restart",function(){
                            if (config.get("wait_restart")) m.setProfile("admin_need_restart");
                        })
                    _this.m.setProjection("admin.config",config);
                    _.defer(m.setProfile.bind(m),"admin_config");
                }
                dfd.resolve();
            },function(){
                dfd.resolve();
            })
            return dfd.promise();
        }
    ],
    routes: [
        {route: ""},
        {route: "server"},
        {
            route: "server/models",
            callback: function(){
                m.removeProfile("model_admin");
                var a = this.m.getProjection("admin.models");
                if (!a){
                    a = this.m.models["project.server.model"].collection();
                    this.m.setProjection("admin.models",a);
                }
//                console.log(a);
                a.fetch();
            },
            routes: { "server/models/:name": "model" }
        },
        {
            route: "server/plugins",
            callback: function(){m.removeProfile("server_plugin_admin");},
            routes: { "server/plugins/:name": "server_plugin" }
        },
        {route: "client"},
        {
            route: "client/packages",
            callback: function(){m.removeProfile("package_admin");},
            routes: { "client/packages/:name": "package" }
        },
        {
            route: "client/pages",
            callback: function(){m.removeProfile("page_admin");},
            routes: { "client/pages/:name": "page" }
        },
        {
            route: "client/plugins",
            callback: function(){m.removeProfile("client_plugin_admin");},
            routes: { "client/plugins/:name": "client_plugin" }
        },
        {
            route: "client/views",
            callback: function(){m.removeProfile("view_admin");},
            routes: { "client/views/:name": "view" }
        },
        {route: "data"},
        {route: "users"},
        {route: "market"},
        {route: "logs"},
        {route: "profile"},
        {
            route: "*a",
            page: "error"
        }
    ],
    surrogate: {
        "model": function(a){
            m.setProfile("model_admin");
            this.m.setProjection("model_admin",new this.m["project.server.model"](a));
        },
        "server_plugin": function(a){
            m.setProfile("server_plugin_admin");
            this.m.setProjection("server_plugin_admin",new this.m["project.server.plugin"](a));
        },
        "package": function(a){
            m.setProfile("package_admin");
            this.m.setProjection("package_admin",new this.m["project.client.package"](a));
        },
        "page": function(a){
            m.setProfile("page_admin");
            this.m.setProjection("page_admin",new this.m["project.client.page"](a));
        },
        "client_plugin": function(a){
            m.setProfile("client_plugin_admin");
            this.m.setProjection("client_plugin_admin",new this.m["project.client.plugin"](a));
        },
        "view": function(a){
            m.setProfile("view_admin");
            this.m.setProjection("view_admin",new this.m["project.client.view"](a));
        }
    },
    ready: function(next){
        var _this = this;
        this.m.model_user_user.get("me").then(function(a){
            _this.m.setProjection("admin.logined",a);
            m.setProfile("logined");
            next();
        },next);
    }
};<|endoftext|>


<commit_before>import React from 'react';

import { getCurrentPlaying } from '../lib/fetch';
import FA from 'react-fontawesome';

const styles = {
  container: {
    marginLeft: -15,
  },
  songName: {
    color: 'white',
    fontSize: '2em',
    textAlign: 'left',
  },
  musicIcon: {
    color: 'white',
    marginLeft: 0,
    fontSize: '0.9em',
    paddingRight: 15
  },
}

export default class Spotify extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentPlaying: null,

    };
    this.refreshPlaying = this.refreshPlaying.bind(this);
    this.handleSpotifyCurrentData = this.handleSpotifyCurrentData.bind(this);
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.refreshPlaying(),
      1000
    );
    this.refreshPlaying();
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  refreshPlaying() {
    getCurrentPlaying()
    .then(this.handleSpotifyCurrentData)
    .catch((err) => console.log(err));
  }

  handleSpotifyCurrentData(currentPlaying){
    this.setState({
      currentPlaying: currentPlaying
    })
  }

  render() {
    if (this.state.currentPlaying === null) return null;

    const iconName = this.state.currentPlaying.is_playing ? 'play' : 'pause';
    return (

          <FA
            name={iconName}
            style={styles.musicIcon}
          />
          {this.state.currentPlaying.item.name} av {this.state.currentPlaying.item.artists[0].name}

    );
  }
}
<commit_msg>Fixes null pointer if no song currently playing on Spotify.<commit_after>import React from 'react';

import { getCurrentPlaying } from '../lib/fetch';
import FA from 'react-fontawesome';

const styles = {
  container: {
    marginLeft: -15,
  },
  songName: {
    color: 'white',
    fontSize: '2em',
    textAlign: 'left',
  },
  musicIcon: {
    color: 'white',
    marginLeft: 0,
    fontSize: '0.9em',
    paddingRight: 15
  },
}

export default class Spotify extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentPlaying: null,

    };
    this.refreshPlaying = this.refreshPlaying.bind(this);
    this.handleSpotifyCurrentData = this.handleSpotifyCurrentData.bind(this);
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.refreshPlaying(),
      1000
    );
    this.refreshPlaying();
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  refreshPlaying() {
    getCurrentPlaying()
    .then(this.handleSpotifyCurrentData)
    .catch((err) => console.log(err));
  }

  handleSpotifyCurrentData(currentPlaying){
    if (currentPlaying === null || currentPlaying.item === null) return;
    this.setState({
      currentPlaying: currentPlaying
    })
  }

  render() {
    if (this.state.currentPlaying === null) return null;

    const iconName = this.state.currentPlaying.is_playing ? 'play' : 'pause';
    return (

          <FA
            name={iconName}
            style={styles.musicIcon}
          />
          {this.state.currentPlaying.item.name} av {this.state.currentPlaying.item.artists[0].name}

    );
  }
}
<|endoftext|>


<commit_before>import {Button} from 'widget/button'
import {ColorElement} from 'widget/colorElement'
import {Form, form} from 'widget/form/form'
import {Layout} from 'widget/layout'
import {NoData} from 'widget/noData'
import {PalettePreSets, pickColors} from './visParams/palettePreSets'
import {Widget} from 'widget/widget'
import {compose} from 'compose'
import {msg} from 'translate'
import Color from 'color'
import React from 'react'
import RemoveButton from 'widget/removeButton'
import _ from 'lodash'
import styles from './legendBuilder.module.css'

export class LegendBuilder extends React.Component {
    state = {
        invalidEntries: {},
        showHexColorCode: false
    }

    constructor(props) {
        super(props)
        this.applyPreset = this.applyPreset.bind(this)
        this.updateEntry = this.updateEntry.bind(this)
        this.updateValidation = this.updateValidation.bind(this)
    }

    render() {
        const {entries} = this.props
        return entries.length
            ? this.renderEntries()
            : this.renderNoEntries()
    }

    renderNoEntries() {
        return (
            <NoData message={msg('map.legendBuilder.noEntries')}/>
        )
    }

    renderEntries() {
        const {entries} = this.props
        return (
            <Layout type='vertical' fill>
                <Widget
                    layout='vertical'
                    spacing='compact'
                    label={msg('map.legendBuilder.label')}
                    labelButtons={this.renderLabelButtons()}
                    scrollable
                    framed>
                    {entries.map((entry, i) => this.renderEntry(entry, i === entries.length - 1))}
                </Widget>
                <PalettePreSets
                    onSelect={this.applyPreset}
                    count={entries.length}
                    className={styles.palettePreSets}
                    autoFocus={false}
                />
            </Layout>
        )
    }

    renderEntry(entry, last) {
        const {entries, locked} = this.props
        const {showHexColorCode} = this.state
        return (
            <Layout key={entry.id} type='horizontal-nowrap'>
                <Entry
                    showHexColorCode={showHexColorCode}
                    entry={entry}
                    entries={entries}
                    onChange={this.updateEntry}
                    onValidate={this.updateValidation}
                    onSwap={color => this.swap(color, entry.color)}
                    locked={locked}
                    autoFocus={last}
                />
                {locked ? null : this.renderRemoveButton(entry)}
            </Layout>
        )
    }

    renderRemoveButton(entry) {
        const {locked} = this.props
        return (
            <RemoveButton
                size='small'
                disabled={locked}
                tooltip={msg('map.legendBuilder.entry.remove.tooltip')}
                tooltipPlacement='left'
                onRemove={() => this.removeEntry(entry)}
            />
        )
    }

    renderLabelButtons() {
        const {showHexColorCode} = this.state
        return [
            <Button
                key={'showHexColorCode'}
                look={showHexColorCode ? 'selected' : 'default'}
                size='small'
                shape='pill'
                air='less'
                label={'HEX'}
                onClick={() => this.toggleshowHexColorCode()}
            />
        ]
    }

    toggleshowHexColorCode() {
        this.setState(({showHexColorCode}) => ({showHexColorCode: !showHexColorCode}))
    }

    applyPreset(colors) {
        const {entries, onChange} = this.props
        const mappedColors = pickColors(entries.length, colors)
        onChange(entries.map((entry, i) => ({
            ...entry, color: mappedColors[i]
        })), this.hasInvalidEntries())
    }

    swap(color1, color2) {
        const {entries, onChange} = this.props
        const entry1 = entries.find(({color}) => color === color1)
        const entry2 = entries.find(({color}) => color === color2)
        const updatedEntries = [
            {...entry1, color: color2},
            {...entry2, color: color1}
        ]

        onChange(entries.map(entry =>
            updatedEntries.find(({id}) => id === entry.id) || entry
        ), this.hasInvalidEntries())
    }

    updateEntry(updatedEntry) {
        const {entries, onChange} = this.props
        onChange(entries.map(entry =>
            entry.id === updatedEntry.id
                ? updatedEntry
                : entry
        ), this.hasInvalidEntries())
    }

    updateValidation(entry, invalid) {
        const {entries, onChange} = this.props
        this.setState(({invalidEntries}) => {
            return invalid
                ? {invalidEntries: {...invalidEntries, ...{[entry.id]: invalid}}}
                : {invalidEntries: _.omit(invalidEntries, entry.id)}
        }, () => onChange(entries, this.hasInvalidEntries()))

    }

    hasInvalidEntries() {
        const {invalidEntries} = this.state
        return !!Object.keys(invalidEntries).length
    }

    removeEntry(entry) {
        const {entries, onChange} = this.props
        onChange(entries.filter(({id}) => id !== entry.id), this.hasInvalidEntries())
    }
}

const isUnique = (key, value, entries) => !entries || entries.filter(entry => entry[key] === value).length <= 1

const entryFields = {
    entries: new Form.Field(),
    value: new Form.Field()
        .notBlank(),
    color: new Form.Field()
        .notBlank()
        .predicate(color => {
            try {
                Color(color).hex()
                return true
            } catch (e) {
                return false
            }
        }, 'map.legendBuilder.entry.error.invalidColor'),
    label: new Form.Field()
        .notBlank()
}

const entryConstraints = {
    valueUnique: new Form.Constraint(['value', 'entries'])
        .predicate(({
            value,
            entries
        }) => isUnique('value', value, entries), 'map.legendBuilder.entry.error.duplicateValue'),
    colorUnique: new Form.Constraint(['color', 'entries'])
        .predicate(({
            color,
            entries
        }) => isUnique('color', color, entries), 'map.legendBuilder.entry.error.duplicateColor'),
    labelUnique: new Form.Constraint(['label', 'entries'])
        .predicate(({
            label,
            entries
        }) => {
            return isUnique('label', label, entries)
        }, 'map.legendBuilder.entry.error.duplicateLabel')
}

class _Entry extends React.Component {
    state = {invalid: true}

    render() {
        const {showHexColorCode} = this.props
        return (
            <Layout type='horizontal-nowrap' fill className={styles.entry}>
                {this.renderColorPicker()}
                {showHexColorCode ? this.renderHexColor() : null}
                {this.renderValueInput()}
                {this.renderLabelInput()}
            </Layout>
        )
    }

    renderColorPicker() {
        const {form, entry, entries, locked, inputs: {color}, onSwap} = this.props
        const otherColors = entries
            .filter(({id}) => entry.id !== id)
            .map(({color}) => color)
        return (
            <ColorInput
                input={color}
                otherColors={otherColors}
                onChange={color => this.notifyChange({color})}
                onSwap={color => onSwap(color)}
                invalid={!!form.errors.colorUnique}
                disabled={locked}
            />
        )
    }

    renderHexColor() {
        const {inputs: {color}} = this.props
        return (
            <Form.Input
                className={styles.colorText}
                input={color}
                errorMessage={[color, 'colorUnique']}
                autoComplete={false}
                onChange={e => this.notifyChange({color: e.target.value})}
            />
        )
    }

    renderValueInput() {
        const {locked, inputs: {value}} = this.props
        return (
            <Form.Input
                className={styles.value}
                input={value}
                type='number'
                errorMessage={[value, 'valueUnique']}
                autoComplete={false}
                disabled={locked}
                onChange={e => this.notifyChange({value: e.target.value})}
            />
        )
    }

    renderLabelInput() {
        const {entry, inputs: {label}} = this.props
        return (
            <Form.Input
                className={styles.label}
                input={label}
                placeholder={msg('map.legendBuilder.entry.classLabel.placeholder')}
                autoFocus={!entry.label}
                errorMessage={[label, 'labelUnique']}
                autoComplete={false}
                onChange={e => {
                    this.notifyChange({label: e.target.value})
                }}
            />
        )
    }

    componentDidMount() {
        const {form, entry, onValidate} = this.props
        this.update()
        const invalid = form.isInvalid()
        onValidate(entry, invalid)
    }

    componentDidUpdate(prevProps) {
        const {entry: prevEntry, entries: prevEntries} = prevProps
        const {entry, entries, form, onValidate} = this.props
        const {invalid: prevInvalid} = this.state
        const invalid = form.isInvalid()
        if (invalid !== prevInvalid) {
            this.setState({invalid})
            onValidate(entry, invalid)
        }
        if (entry !== prevEntry || entries !== prevEntries) {
            this.update()
        }
    }

    update() {
        const {entry, entries, inputs} = this.props
        inputs.entries.set(entries)
        inputs.value.set(entry.value)
        inputs.color.set(entry.color)
        inputs.label.set(entry.label)
    }

    notifyChange({color, value, label}) {
        const {entry, onChange, inputs, form} = this.props
        const updatedEntry = {
            ...entry,
            id: entry.id,
            color: color === undefined ? inputs.color.value : color,
            value: parseInt(value === undefined ? inputs.value.value : value),
            label: label === undefined ? inputs.label.value : label
        }
        onChange(updatedEntry, form.isInvalid())
    }
}

const Entry = compose(
    _Entry,
    form({fields: entryFields, constraints: entryConstraints})
)

class ColorInput extends React.Component {
    state = {swap: false}
    colorInputRef = React.createRef()

    render() {
        const {input, invalid, onChange} = this.props
        const {swap} = this.state
        return (
            <ColorElement
                color={input.value}
                invalid={invalid}
                tooltip={this.renderTooltip()}
                tooltipPlacement='left'
                onTooltipVisibleChange={visible => swap && !visible && this.setState({swap: false})}
                onChange={value => {
                    input.set(value)
                    onChange(value)
                }}
            />
        )
    }

    renderTooltip() {
        const {swap} = this.state
        return swap
            ? this.renderSwap()
            : this.renderColorButtons()
    }

    renderColorButtons() {
        return (
            <Button
                icon='exchange-alt'
                chromeless
                shape='circle'
                size='small'
                tooltip={msg('map.legendBuilder.colors.swap.tooltip')}
                onClick={() => this.setState({swap: true})}
            />
        )
    }

    renderSwap() {
        const {otherColors, onSwap} = this.props
        return (
            <Layout type='horizontal' spacing='tight' alignment='left'>
                {otherColors.map((c, i) =>
                    <ColorElement
                        key={i}
                        color={c}
                        onClick={() => {
                            this.setState({swap: false})
                            onSwap(c)
                        }}
                    />
                )}
            </Layout>
        )
    }
}

const COLORS = [
    '#FFB300',  // Vivid Yellow
    '#803E75',  // Strong Purple
    '#FF6800',  // Vivid Orange
    '#A6BDD7',  // Very Light Blue
    '#C10020',  // Vivid Red
    '#CEA262',  // Grayish Yellow
    '#817066',  // Medium Gray
    '#007D34',  // Vivid Green
    '#F6768E',  // Strong Purplish Pink
    '#00538A',  // Strong Blue
    '#FF7A5C',  // Strong Yellowish Pink
    '#53377A',  // Strong Violet
    '#FF8E00',  // Vivid Orange Yellow
    '#B32851',  // Strong Purplish Red
    '#F4C800',  // Vivid Greenish Yellow
    '#7F180D',  // Strong Reddish Brown
    '#93AA00',  // Vivid Yellowish Green
    '#593315',  // Deep Yellowish Brown
    '#F13A13',  // Vivid Reddish Orange
    '#232C16'  // Dark Olive Green
]

export const defaultColor = i => i < COLORS.length ? COLORS[i] : COLORS[COLORS.length - 1]
<commit_msg>Added more Layout type/alignment/spacing options.<commit_after>import {Button} from 'widget/button'
import {ColorElement} from 'widget/colorElement'
import {Form, form} from 'widget/form/form'
import {Layout} from 'widget/layout'
import {NoData} from 'widget/noData'
import {PalettePreSets, pickColors} from './visParams/palettePreSets'
import {Widget} from 'widget/widget'
import {compose} from 'compose'
import {msg} from 'translate'
import Color from 'color'
import React from 'react'
import RemoveButton from 'widget/removeButton'
import _ from 'lodash'
import styles from './legendBuilder.module.css'

export class LegendBuilder extends React.Component {
    state = {
        invalidEntries: {},
        showHexColorCode: false
    }

    constructor(props) {
        super(props)
        this.applyPreset = this.applyPreset.bind(this)
        this.updateEntry = this.updateEntry.bind(this)
        this.updateValidation = this.updateValidation.bind(this)
    }

    render() {
        const {entries} = this.props
        return entries.length
            ? this.renderEntries()
            : this.renderNoEntries()
    }

    renderNoEntries() {
        return (
            <NoData message={msg('map.legendBuilder.noEntries')}/>
        )
    }

    renderEntries() {
        const {entries} = this.props
        return (
            <Layout type='vertical-fill'>
                <Widget
                    layout='vertical-scrollable'
                    spacing='compact'
                    label={msg('map.legendBuilder.label')}
                    labelButtons={this.renderLabelButtons()}
                    framed>
                    {entries.map((entry, i) => this.renderEntry(entry, i === entries.length - 1))}
                </Widget>
                <PalettePreSets
                    onSelect={this.applyPreset}
                    count={entries.length}
                    className={styles.palettePreSets}
                    autoFocus={false}
                />
            </Layout>
        )
    }

    renderEntry(entry, last) {
        const {entries, locked} = this.props
        const {showHexColorCode} = this.state
        return (
            <Layout key={entry.id} type='horizontal-nowrap'>
                <Entry
                    showHexColorCode={showHexColorCode}
                    entry={entry}
                    entries={entries}
                    onChange={this.updateEntry}
                    onValidate={this.updateValidation}
                    onSwap={color => this.swap(color, entry.color)}
                    locked={locked}
                    autoFocus={last}
                />
                {locked ? null : this.renderRemoveButton(entry)}
            </Layout>
        )
    }

    renderRemoveButton(entry) {
        const {locked} = this.props
        return (
            <RemoveButton
                size='small'
                disabled={locked}
                tooltip={msg('map.legendBuilder.entry.remove.tooltip')}
                tooltipPlacement='left'
                onRemove={() => this.removeEntry(entry)}
            />
        )
    }

    renderLabelButtons() {
        const {showHexColorCode} = this.state
        return [
            <Button
                key={'showHexColorCode'}
                look={showHexColorCode ? 'selected' : 'default'}
                size='small'
                shape='pill'
                air='less'
                label={'HEX'}
                onClick={() => this.toggleshowHexColorCode()}
            />
        ]
    }

    toggleshowHexColorCode() {
        this.setState(({showHexColorCode}) => ({showHexColorCode: !showHexColorCode}))
    }

    applyPreset(colors) {
        const {entries, onChange} = this.props
        const mappedColors = pickColors(entries.length, colors)
        onChange(entries.map((entry, i) => ({
            ...entry, color: mappedColors[i]
        })), this.hasInvalidEntries())
    }

    swap(color1, color2) {
        const {entries, onChange} = this.props
        const entry1 = entries.find(({color}) => color === color1)
        const entry2 = entries.find(({color}) => color === color2)
        const updatedEntries = [
            {...entry1, color: color2},
            {...entry2, color: color1}
        ]

        onChange(entries.map(entry =>
            updatedEntries.find(({id}) => id === entry.id) || entry
        ), this.hasInvalidEntries())
    }

    updateEntry(updatedEntry) {
        const {entries, onChange} = this.props
        onChange(entries.map(entry =>
            entry.id === updatedEntry.id
                ? updatedEntry
                : entry
        ), this.hasInvalidEntries())
    }

    updateValidation(entry, invalid) {
        const {entries, onChange} = this.props
        this.setState(({invalidEntries}) => {
            return invalid
                ? {invalidEntries: {...invalidEntries, ...{[entry.id]: invalid}}}
                : {invalidEntries: _.omit(invalidEntries, entry.id)}
        }, () => onChange(entries, this.hasInvalidEntries()))

    }

    hasInvalidEntries() {
        const {invalidEntries} = this.state
        return !!Object.keys(invalidEntries).length
    }

    removeEntry(entry) {
        const {entries, onChange} = this.props
        onChange(entries.filter(({id}) => id !== entry.id), this.hasInvalidEntries())
    }
}

const isUnique = (key, value, entries) => !entries || entries.filter(entry => entry[key] === value).length <= 1

const entryFields = {
    entries: new Form.Field(),
    value: new Form.Field()
        .notBlank(),
    color: new Form.Field()
        .notBlank()
        .predicate(color => {
            try {
                Color(color).hex()
                return true
            } catch (e) {
                return false
            }
        }, 'map.legendBuilder.entry.error.invalidColor'),
    label: new Form.Field()
        .notBlank()
}

const entryConstraints = {
    valueUnique: new Form.Constraint(['value', 'entries'])
        .predicate(({
            value,
            entries
        }) => isUnique('value', value, entries), 'map.legendBuilder.entry.error.duplicateValue'),
    colorUnique: new Form.Constraint(['color', 'entries'])
        .predicate(({
            color,
            entries
        }) => isUnique('color', color, entries), 'map.legendBuilder.entry.error.duplicateColor'),
    labelUnique: new Form.Constraint(['label', 'entries'])
        .predicate(({
            label,
            entries
        }) => {
            return isUnique('label', label, entries)
        }, 'map.legendBuilder.entry.error.duplicateLabel')
}

class _Entry extends React.Component {
    state = {invalid: true}

    render() {
        const {showHexColorCode} = this.props
        return (
            <Layout type='horizontal-nowrap' fill className={styles.entry}>
                {this.renderColorPicker()}
                {showHexColorCode ? this.renderHexColor() : null}
                {this.renderValueInput()}
                {this.renderLabelInput()}
            </Layout>
        )
    }

    renderColorPicker() {
        const {form, entry, entries, locked, inputs: {color}, onSwap} = this.props
        const otherColors = entries
            .filter(({id}) => entry.id !== id)
            .map(({color}) => color)
        return (
            <ColorInput
                input={color}
                otherColors={otherColors}
                onChange={color => this.notifyChange({color})}
                onSwap={color => onSwap(color)}
                invalid={!!form.errors.colorUnique}
                disabled={locked}
            />
        )
    }

    renderHexColor() {
        const {inputs: {color}} = this.props
        return (
            <Form.Input
                className={styles.colorText}
                input={color}
                errorMessage={[color, 'colorUnique']}
                autoComplete={false}
                onChange={e => this.notifyChange({color: e.target.value})}
            />
        )
    }

    renderValueInput() {
        const {locked, inputs: {value}} = this.props
        return (
            <Form.Input
                className={styles.value}
                input={value}
                type='number'
                errorMessage={[value, 'valueUnique']}
                autoComplete={false}
                disabled={locked}
                onChange={e => this.notifyChange({value: e.target.value})}
            />
        )
    }

    renderLabelInput() {
        const {entry, inputs: {label}} = this.props
        return (
            <Form.Input
                className={styles.label}
                input={label}
                placeholder={msg('map.legendBuilder.entry.classLabel.placeholder')}
                autoFocus={!entry.label}
                errorMessage={[label, 'labelUnique']}
                autoComplete={false}
                onChange={e => {
                    this.notifyChange({label: e.target.value})
                }}
            />
        )
    }

    componentDidMount() {
        const {form, entry, onValidate} = this.props
        this.update()
        const invalid = form.isInvalid()
        onValidate(entry, invalid)
    }

    componentDidUpdate(prevProps) {
        const {entry: prevEntry, entries: prevEntries} = prevProps
        const {entry, entries, form, onValidate} = this.props
        const {invalid: prevInvalid} = this.state
        const invalid = form.isInvalid()
        if (invalid !== prevInvalid) {
            this.setState({invalid})
            onValidate(entry, invalid)
        }
        if (entry !== prevEntry || entries !== prevEntries) {
            this.update()
        }
    }

    update() {
        const {entry, entries, inputs} = this.props
        inputs.entries.set(entries)
        inputs.value.set(entry.value)
        inputs.color.set(entry.color)
        inputs.label.set(entry.label)
    }

    notifyChange({color, value, label}) {
        const {entry, onChange, inputs, form} = this.props
        const updatedEntry = {
            ...entry,
            id: entry.id,
            color: color === undefined ? inputs.color.value : color,
            value: parseInt(value === undefined ? inputs.value.value : value),
            label: label === undefined ? inputs.label.value : label
        }
        onChange(updatedEntry, form.isInvalid())
    }
}

const Entry = compose(
    _Entry,
    form({fields: entryFields, constraints: entryConstraints})
)

class ColorInput extends React.Component {
    state = {swap: false}
    colorInputRef = React.createRef()

    render() {
        const {input, invalid, onChange} = this.props
        const {swap} = this.state
        return (
            <ColorElement
                color={input.value}
                invalid={invalid}
                tooltip={this.renderTooltip()}
                tooltipPlacement='left'
                onTooltipVisibleChange={visible => swap && !visible && this.setState({swap: false})}
                onChange={value => {
                    input.set(value)
                    onChange(value)
                }}
            />
        )
    }

    renderTooltip() {
        const {swap} = this.state
        return swap
            ? this.renderSwap()
            : this.renderColorButtons()
    }

    renderColorButtons() {
        return (
            <Button
                icon='exchange-alt'
                chromeless
                shape='circle'
                size='small'
                tooltip={msg('map.legendBuilder.colors.swap.tooltip')}
                onClick={() => this.setState({swap: true})}
            />
        )
    }

    renderSwap() {
        const {otherColors, onSwap} = this.props
        return (
            <Layout type='horizontal' spacing='tight' alignment='left'>
                {otherColors.map((c, i) =>
                    <ColorElement
                        key={i}
                        color={c}
                        onClick={() => {
                            this.setState({swap: false})
                            onSwap(c)
                        }}
                    />
                )}
            </Layout>
        )
    }
}

const COLORS = [
    '#FFB300',  // Vivid Yellow
    '#803E75',  // Strong Purple
    '#FF6800',  // Vivid Orange
    '#A6BDD7',  // Very Light Blue
    '#C10020',  // Vivid Red
    '#CEA262',  // Grayish Yellow
    '#817066',  // Medium Gray
    '#007D34',  // Vivid Green
    '#F6768E',  // Strong Purplish Pink
    '#00538A',  // Strong Blue
    '#FF7A5C',  // Strong Yellowish Pink
    '#53377A',  // Strong Violet
    '#FF8E00',  // Vivid Orange Yellow
    '#B32851',  // Strong Purplish Red
    '#F4C800',  // Vivid Greenish Yellow
    '#7F180D',  // Strong Reddish Brown
    '#93AA00',  // Vivid Yellowish Green
    '#593315',  // Deep Yellowish Brown
    '#F13A13',  // Vivid Reddish Orange
    '#232C16'  // Dark Olive Green
]

export const defaultColor = i => i < COLORS.length ? COLORS[i] : COLORS[COLORS.length - 1]
<|endoftext|>


<commit_before>angular.module('controllers', [])

.controller('MenuController', function($scope, $http, $rootScope, PlayerResource, localStorageService) {
  $scope.dci = localStorageService.get('dci') || '';
  $scope.name = localStorageService.get('name') || '';
  $scope.loggedIn = ($scope.dci !== '');
  $scope.menuCollapsed = true;

  $scope.login = function() {
    PlayerResource.get({dci: $scope.dci}, function(data) {
      localStorageService.add('dci', data.dci);
      localStorageService.add('name', data.name);
      $scope.loggedIn = true;
      $scope.name = data.name;
      $rootScope.$broadcast('loggedIn', $scope.dci);
      console.log("Logged in: " + data.dci + " " + data.name);
    }, function() {
      $scope.dci = '';
      $scope.loggedIn = false;
      console.log("Failed to log in");
    });
  };
  $scope.logout = function() {
    localStorageService.remove('dci');
    localStorageService.remove('name');
    $scope.dci = '';
    $scope.loggedIn = false;
    $rootScope.$broadcast('loggedOut');
  };
})

.controller('TournamentController', function($scope, $routeParams, TournamentResource) {
  TournamentResource.get({id: $routeParams.tournament}).$promise.then(function(tournament) {
    $scope.model = tournament;
    $scope.model.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
  });
})

.controller('TournamentsController', function($scope, TournamentResource) {
  TournamentResource.query().$promise.then(function(tournaments) {
    $scope.tournaments = _.map(tournaments, function(tournament) {
      tournament.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
      return tournament;
    });
  });
})

.controller('MainController', function($scope, localStorageService, TournamentResource, PlayerResource) {
  var loadTournaments = function() {
    if($scope.loggedIn) {
      PlayerResource.tournaments({dci: $scope.dci}).$promise.then(function(tournaments) {
        if(tournaments.length > 0) {
          var latestTournament = tournaments[0];
          if(latestTournament.pairings.length > 0) {
            $scope.latestPairing = latestTournament.pairings[0];
            $scope.latestPairing.tournament = latestTournament.name;
            $scope.latestPairing.day = latestTournament.day;
          } else {
            $scope.latestPairing = latestTournament.seating;
          }
        } else {
          $scope.latestPairing = null;
        }
        $scope.tournaments = tournaments
      });
    } else {
      TournamentResource.query().$promise.then(function(tournaments) {
        $scope.tournaments = _.map(tournaments, function(tournament) {
          tournament.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
          return tournament;
        });
      });
    }
  };
  $scope.dci = localStorageService.get('dci') || '';
  $scope.loggedIn = ($scope.dci !== '');
  $scope.$on('loggedIn', function(event, dci) {
    $scope.loggedIn = true;
    $scope.dci = dci;
    loadTournaments();
  });
  $scope.$on('loggedOut', function() {
    $scope.loggedIn = false;
    loadTournaments();
  });
  loadTournaments();
})

.controller('PairingsController', function($scope, $routeParams, TournamentResource, PairingService) {
  $scope.allPairings = TournamentResource.pairings({id: $routeParams.tournament,
                                                    round: $routeParams.round});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
  $scope.round = $routeParams.round;
  $scope.sort = 'table_number';
  $scope.$watch('sort', sortPairings);
  $scope.$watchCollection('allPairings', sortPairings);

  function sortPairings() {
    if($scope.sort == 'team1_name') {
      $scope.pairings = PairingService.duplicatePairings($scope.allPairings);
    } else {
      $scope.pairings = $scope.allPairings;
    }
  }
})

.controller('StandingsController', function($scope, $routeParams, TournamentResource) {
  $scope.standings = TournamentResource.standings({id: $routeParams.tournament,
                                                   round: $routeParams.round});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
  $scope.round = $routeParams.round;
})

.controller('SeatingsController', function($scope, $routeParams, TournamentResource) {
  $scope.seatings = TournamentResource.seatings({id: $routeParams.tournament});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
})

.controller('OrganizerController', function($scope, $routeParams, $window, TournamentResource, PairingService) {
  var tournamentId = $routeParams.tournament;
  var menuWindow;
  var menuScope;
  $scope.displayMenu = true;
  $scope.newStandings = false;
  $scope.newPairings = false;
  $scope.popupMenu = popupMenu;
  $scope.showPairings = showPairings;
  $scope.$on('showPairings', function(event, round) {
    $scope.pairings_round = round;
    showPairings();
  });
  $scope.showStandings = showStandings;
  $scope.$on('showStandings', function(event, round) {
    $scope.standings_round = round;
    showStandings();
  });
  $scope.showSeatings = showSeatings;
  $scope.$on('showSeatings', function() {
    showSeatings();
  });
  $scope.showClock = showClock;
  $scope.$on('showClock', showClock);
  $scope.setClock = setClock;
  $scope.$on('setClock', function(event, minutes) {
    $scope.minutes = minutes;
    setClock();
  });
  $scope.startClock = startClock;
  $scope.$on('startClock', startClock);
  $scope.stopClock = stopClock;
  $scope.$on('stopClock', stopClock);
  $scope.clock = {
    timeout: false,
    running: false,
    lastTime: new Date(),
    seconds: 0,
    text: "00:00"
  };
  $scope.$on('menuClosed', function() {
    $scope.displayMenu = true;
    setTimeout(function(){ $scope.$apply(); });
  });
  $scope.minutes = 50;
  loadTournament();
  var interval = setInterval(loadTournament, 5000);

  function arraysIdentical(a, b) {
    if(a === undefined || b === undefined) return false;
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  };

  function pad(num) {
    if(Math.abs(num) >= 10) return "" + num;
    else if(num < 0) return "-0" + Math.abs(num);
    else return "0" + num;
  }

  function loadTournament() {
    TournamentResource.get({id: tournamentId}).$promise.then(function(t) {
      $scope.tournament = t;
      if(!arraysIdentical($scope.pairing_rounds, t.round)) {
        $scope.newPairings = true;
        $scope.pairings_round = t.round[t.round.length - 1];
      }
      $scope.pairing_rounds = t.round;
      if(!arraysIdentical($scope.standings_rounds, t.standings)) {
        $scope.newStandings = true;
        $scope.standings_round = t.standings[t.standings.length - 1];
      }
      $scope.standings_rounds = t.standings;
      if(menuScope) {
        menuScope.$emit('tournament', {
          newPairings: $scope.newPairings,
          newStandings: $scope.newStandings,
          pairing_rounds: $scope.pairing_rounds,
          standings_rounds: $scope.standings_rounds
        });
      }
    });
  }

  function updateClock() {
    if($scope.clock.running) {
      var now = new Date();
      var diff = (now.getTime() - $scope.clock.lastTime.getTime()) / 1000;
      $scope.clock.lastTime = now;
      $scope.clock.seconds -= diff;
      setTimeout(updateClock, 200);
    }
    $scope.clock.timeout = ($scope.clock.seconds < 0);
    var minutes;
    if($scope.clock.timeout) minutes = Math.ceil($scope.clock.seconds / 60);
    else minutes = Math.floor($scope.clock.seconds / 60);
    var seconds = Math.floor($scope.clock.seconds % 60);
    var sign = "";
    if($scope.clock.timeout) sign = "-";
    $scope.clock.text = sign + pad(Math.abs(minutes)) + ":" + pad(Math.abs(seconds));
    setTimeout(function(){ $scope.$apply(); });
  }

  function showPairings() {
    var round = $scope.pairings_round;
    TournamentResource.pairings({id: tournamentId, round: round}).$promise.then(function(pairings) {
      var duplicated = _.sortBy(PairingService.duplicatePairings(pairings), "team1_name");
      var pairings = [];
      var perColumn = Math.ceil(duplicated.length / Math.ceil(duplicated.length / 40));
      while(!_.isEmpty(duplicated)) {
        pairings.push(_.take(duplicated, perColumn));
        duplicated = _.drop(duplicated, perColumn);
      }
      $scope.pairings = pairings;
      $scope.mode = "pairings";
      $scope.newPairings = false;
    });
  }

  function showStandings() {
    var round = $scope.standings_round;
    TournamentResource.standings({id: tournamentId, round: round, secret: "secret"}).$promise.then(function(data) {
      var standings = [];
      var perColumn = Math.ceil(data.length / Math.ceil(data.length / 40));
      while(!_.isEmpty(data)) {
        standings.push(_.take(data, perColumn));
        data = _.drop(data, perColumn);
      }
      $scope.standings = standings;
      $scope.mode = "standings";
      $scope.newStandings = false;
    });
  }

  function showSeatings() {
    TournamentResource.seatings({id: tournamentId}).$promise.then(function(data) {
      var seatings = [];
      var perColumn = Math.ceil(data.length / Math.ceil(data.length / 40));
      while(!_.isEmpty(data)) {
        seatings.push(_.take(data, perColumn));
        data = _.drop(data, perColumn);
      }
      $scope.seatings = seatings;
      $scope.mode = "seatings";
    });
  }

  function stopClock() {
    $scope.clock.running = false;
  }

  function startClock() {
    $scope.clock.running = true;
    $scope.clock.lastTime = new Date();
    setTimeout(updateClock, 100);
  }

  function setClock() {
    $scope.clock.seconds = $scope.minutes * 60;
    updateClock();
  }

  function showClock() {
    $scope.mode = "clock";
    setTimeout(function(){ $scope.$apply(); });
  }

  function popupMenu() {
    menuWindow = $window.open('menu.html', 'menu');
    setTimeout(function() { menuScope = menuWindow.angular.element('#menu').scope(); }, 1000);
    $scope.displayMenu = false;
  }
})

.controller('OrganizerMenuController', function($scope, TournamentResource) {
  var openerScope = window.opener.angular.element('#content').scope();
  var tournamentId;
  $scope.$on('tournament', function(event, tournament) {
    $scope.newPairings = tournament.newPairings;
    $scope.newStandings = tournament.newStandings;
    $scope.pairing_rounds = tournament.pairing_rounds;
    $scope.standings_rounds = tournament.standings_rounds;
    if(tournament.newPairings) $scope.pairings_round = tournament.pairing_rounds[tournament.pairing_rounds.length - 1];
    if(tournament.newStandings) $scope.standings_round = tournament.standings_rounds[tournament.standings_rounds.length - 1];
    setTimeout(function(){ $scope.$apply(); });
  });
  $scope.newPairings = false;
  $scope.newStandings = false;
  $scope.running = false;
  $scope.minutes = 50;
  $scope.showPairings = showPairings;
  $scope.showStandings = showStandings;
  $scope.showSeatings = showSeatings;
  $scope.showClock = showClock;
  $scope.setClock = setClock;
  $scope.startClock = startClock;
  $scope.stopClock = stopClock;
  $scope.close = close;

  function arraysIdentical(a, b) {
    if(a === undefined || b === undefined) return false;
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  };

  function loadTournament() {
    TournamentResource.get({id: tournamentId}).$promise.then(function(t) {
      $scope.tournament = t;
      if(!arraysIdentical($scope.pairing_rounds, t.round)) {
        $scope.newPairings = true;
        $scope.pairings_round = t.round[t.round.length - 1];
      }
      $scope.pairing_rounds = t.round;
      if(!arraysIdentical($scope.standings_rounds, t.standings)) {
        $scope.newStandings = true;
        $scope.standings_round = t.standings[t.standings.length - 1];
      }
      $scope.standings_rounds = t.standings;
    });
  }

  function send(event, data) {
    openerScope.$emit(event, data);
  }
  function close() {
    send('menuClosed');
    window.close();
  }

  function showPairings() {
    send('showPairings', $scope.pairings_round);
  }
  function showStandings() {
    send('showStandings', $scope.standings_round);
  }
  function showSeatings() {
    send('showSeatings');
  }
  function showClock() {
    send('showClock');
  }
  function setClock() {
    send('setClock', $scope.minutes);
  }
  function startClock() {
    $scope.running = true;
    send('startClock');
  }
  function stopClock() {
    $scope.running = false;
    send('stopClock');
  }
});
<commit_msg>Poistettu console.log<commit_after>angular.module('controllers', [])

.controller('MenuController', function($scope, $http, $rootScope, PlayerResource, localStorageService) {
  $scope.dci = localStorageService.get('dci') || '';
  $scope.name = localStorageService.get('name') || '';
  $scope.loggedIn = ($scope.dci !== '');
  $scope.menuCollapsed = true;

  $scope.login = function() {
    PlayerResource.get({dci: $scope.dci}, function(data) {
      localStorageService.add('dci', data.dci);
      localStorageService.add('name', data.name);
      $scope.loggedIn = true;
      $scope.name = data.name;
      $rootScope.$broadcast('loggedIn', $scope.dci);
    }, function() {
      $scope.dci = '';
      $scope.loggedIn = false;
    });
  };
  $scope.logout = function() {
    localStorageService.remove('dci');
    localStorageService.remove('name');
    $scope.dci = '';
    $scope.loggedIn = false;
    $rootScope.$broadcast('loggedOut');
  };
})

.controller('TournamentController', function($scope, $routeParams, TournamentResource) {
  TournamentResource.get({id: $routeParams.tournament}).$promise.then(function(tournament) {
    $scope.model = tournament;
    $scope.model.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
  });
})

.controller('TournamentsController', function($scope, TournamentResource) {
  TournamentResource.query().$promise.then(function(tournaments) {
    $scope.tournaments = _.map(tournaments, function(tournament) {
      tournament.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
      return tournament;
    });
  });
})

.controller('MainController', function($scope, localStorageService, TournamentResource, PlayerResource) {
  var loadTournaments = function() {
    if($scope.loggedIn) {
      PlayerResource.tournaments({dci: $scope.dci}).$promise.then(function(tournaments) {
        if(tournaments.length > 0) {
          var latestTournament = tournaments[0];
          if(latestTournament.pairings.length > 0) {
            $scope.latestPairing = latestTournament.pairings[0];
            $scope.latestPairing.tournament = latestTournament.name;
            $scope.latestPairing.day = latestTournament.day;
          } else {
            $scope.latestPairing = latestTournament.seating;
          }
        } else {
          $scope.latestPairing = null;
        }
        $scope.tournaments = tournaments
      });
    } else {
      TournamentResource.query().$promise.then(function(tournaments) {
        $scope.tournaments = _.map(tournaments, function(tournament) {
          tournament.round_nums = _(_.union(tournament.round, tournament.standings)).sortBy().reverse().value();
          return tournament;
        });
      });
    }
  };
  $scope.dci = localStorageService.get('dci') || '';
  $scope.loggedIn = ($scope.dci !== '');
  $scope.$on('loggedIn', function(event, dci) {
    $scope.loggedIn = true;
    $scope.dci = dci;
    loadTournaments();
  });
  $scope.$on('loggedOut', function() {
    $scope.loggedIn = false;
    loadTournaments();
  });
  loadTournaments();
})

.controller('PairingsController', function($scope, $routeParams, TournamentResource, PairingService) {
  $scope.allPairings = TournamentResource.pairings({id: $routeParams.tournament,
                                                    round: $routeParams.round});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
  $scope.round = $routeParams.round;
  $scope.sort = 'table_number';
  $scope.$watch('sort', sortPairings);
  $scope.$watchCollection('allPairings', sortPairings);

  function sortPairings() {
    if($scope.sort == 'team1_name') {
      $scope.pairings = PairingService.duplicatePairings($scope.allPairings);
    } else {
      $scope.pairings = $scope.allPairings;
    }
  }
})

.controller('StandingsController', function($scope, $routeParams, TournamentResource) {
  $scope.standings = TournamentResource.standings({id: $routeParams.tournament,
                                                   round: $routeParams.round});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
  $scope.round = $routeParams.round;
})

.controller('SeatingsController', function($scope, $routeParams, TournamentResource) {
  $scope.seatings = TournamentResource.seatings({id: $routeParams.tournament});
  $scope.tournament = TournamentResource.get({id: $routeParams.tournament});
})

.controller('OrganizerController', function($scope, $routeParams, $window, TournamentResource, PairingService) {
  var tournamentId = $routeParams.tournament;
  var menuWindow;
  var menuScope;
  $scope.displayMenu = true;
  $scope.newStandings = false;
  $scope.newPairings = false;
  $scope.popupMenu = popupMenu;
  $scope.showPairings = showPairings;
  $scope.$on('showPairings', function(event, round) {
    $scope.pairings_round = round;
    showPairings();
  });
  $scope.showStandings = showStandings;
  $scope.$on('showStandings', function(event, round) {
    $scope.standings_round = round;
    showStandings();
  });
  $scope.showSeatings = showSeatings;
  $scope.$on('showSeatings', function() {
    showSeatings();
  });
  $scope.showClock = showClock;
  $scope.$on('showClock', showClock);
  $scope.setClock = setClock;
  $scope.$on('setClock', function(event, minutes) {
    $scope.minutes = minutes;
    setClock();
  });
  $scope.startClock = startClock;
  $scope.$on('startClock', startClock);
  $scope.stopClock = stopClock;
  $scope.$on('stopClock', stopClock);
  $scope.clock = {
    timeout: false,
    running: false,
    lastTime: new Date(),
    seconds: 0,
    text: "00:00"
  };
  $scope.$on('menuClosed', function() {
    $scope.displayMenu = true;
    setTimeout(function(){ $scope.$apply(); });
  });
  $scope.minutes = 50;
  loadTournament();
  var interval = setInterval(loadTournament, 5000);

  function arraysIdentical(a, b) {
    if(a === undefined || b === undefined) return false;
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  };

  function pad(num) {
    if(Math.abs(num) >= 10) return "" + num;
    else if(num < 0) return "-0" + Math.abs(num);
    else return "0" + num;
  }

  function loadTournament() {
    TournamentResource.get({id: tournamentId}).$promise.then(function(t) {
      $scope.tournament = t;
      if(!arraysIdentical($scope.pairing_rounds, t.round)) {
        $scope.newPairings = true;
        $scope.pairings_round = t.round[t.round.length - 1];
      }
      $scope.pairing_rounds = t.round;
      if(!arraysIdentical($scope.standings_rounds, t.standings)) {
        $scope.newStandings = true;
        $scope.standings_round = t.standings[t.standings.length - 1];
      }
      $scope.standings_rounds = t.standings;
      if(menuScope) {
        menuScope.$emit('tournament', {
          newPairings: $scope.newPairings,
          newStandings: $scope.newStandings,
          pairing_rounds: $scope.pairing_rounds,
          standings_rounds: $scope.standings_rounds
        });
      }
    });
  }

  function updateClock() {
    if($scope.clock.running) {
      var now = new Date();
      var diff = (now.getTime() - $scope.clock.lastTime.getTime()) / 1000;
      $scope.clock.lastTime = now;
      $scope.clock.seconds -= diff;
      setTimeout(updateClock, 200);
    }
    $scope.clock.timeout = ($scope.clock.seconds < 0);
    var minutes;
    if($scope.clock.timeout) minutes = Math.ceil($scope.clock.seconds / 60);
    else minutes = Math.floor($scope.clock.seconds / 60);
    var seconds = Math.floor($scope.clock.seconds % 60);
    var sign = "";
    if($scope.clock.timeout) sign = "-";
    $scope.clock.text = sign + pad(Math.abs(minutes)) + ":" + pad(Math.abs(seconds));
    setTimeout(function(){ $scope.$apply(); });
  }

  function showPairings() {
    var round = $scope.pairings_round;
    TournamentResource.pairings({id: tournamentId, round: round}).$promise.then(function(pairings) {
      var duplicated = _.sortBy(PairingService.duplicatePairings(pairings), "team1_name");
      var pairings = [];
      var perColumn = Math.ceil(duplicated.length / Math.ceil(duplicated.length / 40));
      while(!_.isEmpty(duplicated)) {
        pairings.push(_.take(duplicated, perColumn));
        duplicated = _.drop(duplicated, perColumn);
      }
      $scope.pairings = pairings;
      $scope.mode = "pairings";
      $scope.newPairings = false;
    });
  }

  function showStandings() {
    var round = $scope.standings_round;
    TournamentResource.standings({id: tournamentId, round: round, secret: "secret"}).$promise.then(function(data) {
      var standings = [];
      var perColumn = Math.ceil(data.length / Math.ceil(data.length / 40));
      while(!_.isEmpty(data)) {
        standings.push(_.take(data, perColumn));
        data = _.drop(data, perColumn);
      }
      $scope.standings = standings;
      $scope.mode = "standings";
      $scope.newStandings = false;
    });
  }

  function showSeatings() {
    TournamentResource.seatings({id: tournamentId}).$promise.then(function(data) {
      var seatings = [];
      var perColumn = Math.ceil(data.length / Math.ceil(data.length / 40));
      while(!_.isEmpty(data)) {
        seatings.push(_.take(data, perColumn));
        data = _.drop(data, perColumn);
      }
      $scope.seatings = seatings;
      $scope.mode = "seatings";
    });
  }

  function stopClock() {
    $scope.clock.running = false;
  }

  function startClock() {
    $scope.clock.running = true;
    $scope.clock.lastTime = new Date();
    setTimeout(updateClock, 100);
  }

  function setClock() {
    $scope.clock.seconds = $scope.minutes * 60;
    updateClock();
  }

  function showClock() {
    $scope.mode = "clock";
    setTimeout(function(){ $scope.$apply(); });
  }

  function popupMenu() {
    menuWindow = $window.open('menu.html', 'menu');
    setTimeout(function() { menuScope = menuWindow.angular.element('#menu').scope(); }, 1000);
    $scope.displayMenu = false;
  }
})

.controller('OrganizerMenuController', function($scope, TournamentResource) {
  var openerScope = window.opener.angular.element('#content').scope();
  var tournamentId;
  $scope.$on('tournament', function(event, tournament) {
    $scope.newPairings = tournament.newPairings;
    $scope.newStandings = tournament.newStandings;
    $scope.pairing_rounds = tournament.pairing_rounds;
    $scope.standings_rounds = tournament.standings_rounds;
    if(tournament.newPairings) $scope.pairings_round = tournament.pairing_rounds[tournament.pairing_rounds.length - 1];
    if(tournament.newStandings) $scope.standings_round = tournament.standings_rounds[tournament.standings_rounds.length - 1];
    setTimeout(function(){ $scope.$apply(); });
  });
  $scope.newPairings = false;
  $scope.newStandings = false;
  $scope.running = false;
  $scope.minutes = 50;
  $scope.showPairings = showPairings;
  $scope.showStandings = showStandings;
  $scope.showSeatings = showSeatings;
  $scope.showClock = showClock;
  $scope.setClock = setClock;
  $scope.startClock = startClock;
  $scope.stopClock = stopClock;
  $scope.close = close;

  function arraysIdentical(a, b) {
    if(a === undefined || b === undefined) return false;
    var i = a.length;
    if (i != b.length) return false;
    while (i--) {
      if (a[i] !== b[i]) return false;
    }
    return true;
  };

  function loadTournament() {
    TournamentResource.get({id: tournamentId}).$promise.then(function(t) {
      $scope.tournament = t;
      if(!arraysIdentical($scope.pairing_rounds, t.round)) {
        $scope.newPairings = true;
        $scope.pairings_round = t.round[t.round.length - 1];
      }
      $scope.pairing_rounds = t.round;
      if(!arraysIdentical($scope.standings_rounds, t.standings)) {
        $scope.newStandings = true;
        $scope.standings_round = t.standings[t.standings.length - 1];
      }
      $scope.standings_rounds = t.standings;
    });
  }

  function send(event, data) {
    openerScope.$emit(event, data);
  }
  function close() {
    send('menuClosed');
    window.close();
  }

  function showPairings() {
    send('showPairings', $scope.pairings_round);
  }
  function showStandings() {
    send('showStandings', $scope.standings_round);
  }
  function showSeatings() {
    send('showSeatings');
  }
  function showClock() {
    send('showClock');
  }
  function setClock() {
    send('setClock', $scope.minutes);
  }
  function startClock() {
    $scope.running = true;
    send('startClock');
  }
  function stopClock() {
    $scope.running = false;
    send('stopClock');
  }
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name               Refuse mobile page!
// @name:zh-CN         拒绝手机版页面！
// @description        Redirect mobile-only URL to its corresponding PC version automatically
// @description:zh-CN  访问移动设备专用 URL 时跳转到对应的 PC 版页面（目前已支持微博、淘宝、天猫、京东）
// @namespace          https://github.com/xdqi/refuse-mobile-page
// @version            0.1
// @author             xdqi
// @grant              none
// @run-at             document-start
// @include            *
// ==/UserScript==

(function() {
    'use strict';

    var replaceTable = [
        // weibo.com 用户页面
        [/^http:\/\/m\.weibo\.cn\/[d|u]\/(.+)/g, 'http://weibo.com/$1'],
        // weibo.com 用户页面
        [/^http:\/\/m\.weibo\.cn\/n\/(.+)/g, 'http://weibo.com/n/$1'],
        // weibo.com 话题
        [/^http:\/\/m\.weibo\.cn\/[k]\/(.+)/g, 'http://huati.weibo.com/$1'],
        // weibo.com 微博
        [/^http:\/\/m\.weibo\.cn\/(\d+)\/([A-Za-z0-9]+)(?:\?.*)?/g, 'http://weibo.com/$1/$2'],
        // weibo.com 文章
        [/^http:\/\/media\.weibo\.cn\/article(.+)/g, 'http://weibo.com/ttarticle/p/show$1'],
        // 哔哩哔哩 视频
        [/^http:\/\/www\.bilibili\.com\/mobile\/video\/(av\d+)\.html/g, 'http://www.bilibili.com/video/$1/'],
        // 京东 产品页面
        [/^http[s]?:\/\/item\.m\.jd\.com\/product\/(\d+).html/g, 'http://item.jd.com/$1.html'],
        // 天猫 产品页面
        [/^http[s]?:\/\/detail\.m\.tmall\.com\/(.*)/g, 'http://detail.tmall.com/$1'],
        // 淘宝 产品页面
        [/^http[s]?:\/\/h5\.m\.taobao\.com\/awp\/core\/detail\.htm\?(.*)/g, 'https://item.taobao.com/item.htm?$1'],
        // 北邮人
        [/^http[s]?:\/\/m\.byr\.cn\/(.*)/g, 'https://bbs.byr.cn/#!$1'],
    ];

    var pageURL = window.location.href;
    replaceTable.forEach(function(replaceRule) {
        var newURL = pageURL.replace(replaceRule[0], replaceRule[1]);
        if (pageURL !== newURL) {
            window.location = newURL;
        }
    });

})();

<commit_msg>Bump 0.2<commit_after>// ==UserScript==
// @name               Refuse mobile page!
// @name:zh-CN         拒绝手机版页面！
// @description        Redirect mobile-only URL to its corresponding PC version automatically
// @description:zh-CN  访问移动设备专用 URL 时跳转到对应的 PC 版页面（目前已支持微博、淘宝、天猫、京东）
// @namespace          https://github.com/xdqi/refuse-mobile-page
// @version            0.2
// @author             xdqi
// @grant              none
// @run-at             document-start
// @include            *
// ==/UserScript==

(function() {
    'use strict';

    var replaceTable = [
        // weibo.com 用户页面
        [/^http:\/\/m\.weibo\.cn\/[d|u]\/(.+)/g, 'http://weibo.com/$1'],
        // weibo.com 用户页面
        [/^http:\/\/m\.weibo\.cn\/n\/(.+)/g, 'http://weibo.com/n/$1'],
        // weibo.com 话题
        [/^http:\/\/m\.weibo\.cn\/[k]\/(.+)/g, 'http://huati.weibo.com/$1'],
        // weibo.com 微博
        [/^http:\/\/m\.weibo\.cn\/(\d+)\/([A-Za-z0-9]+)(?:\?.*)?/g, 'http://weibo.com/$1/$2'],
        // weibo.com 文章
        [/^http:\/\/media\.weibo\.cn\/article(.+)/g, 'http://weibo.com/ttarticle/p/show$1'],
        // 哔哩哔哩 视频
        [/^http:\/\/www\.bilibili\.com\/mobile\/video\/(av\d+)\.html/g, 'http://www.bilibili.com/video/$1/'],
        // 京东 产品页面
        [/^http[s]?:\/\/item\.m\.jd\.com\/product\/(\d+).html/g, 'http://item.jd.com/$1.html'],
        // 天猫 产品页面
        [/^http[s]?:\/\/detail\.m\.tmall\.com\/(.*)/g, 'http://detail.tmall.com/$1'],
        // 淘宝 产品页面
        [/^http[s]?:\/\/h5\.m\.taobao\.com\/awp\/core\/detail\.htm\?(.*)/g, 'https://item.taobao.com/item.htm?$1'],
        // 北邮人
        [/^http[s]?:\/\/m\.byr\.cn\/(.*)/g, 'https://bbs.byr.cn/#!$1'],
    ];

    var pageURL = window.location.href;
    replaceTable.forEach(function(replaceRule) {
        var newURL = pageURL.replace(replaceRule[0], replaceRule[1]);
        if (pageURL !== newURL) {
            window.location = newURL;
        }
    });

})();

<|endoftext|>


<commit_before>// @datatype_void
/*
 * When testing with webpack and ES6, we have to do some extra
 * things get testing to work right. Because we are gonna write test
 * in ES6 to, we have to compile those as well. That's handled in
 * karma.conf.js with the karma-webpack plugin. This is the entry
 * file for webpack test. Just like webpack will create a bundle.js
 * file for our client, when we run test, it well compile and bundle them
 * all here! Crazy huh. So we need to do some setup
*/
Error.stackTraceLimit = Infinity;
//require('phantomjs-polyfill');
//require('es6-promise');
//require('es6-shim');
//require('es7-reflect-metadata');

// Prefer `CoreJS` over the polyfills commented out above
require('core-js');

// Zone.js
require('zone.js/dist/zone.js');
require('zone.js/dist/long-stack-trace-zone.js');
require('zone.js/dist/jasmine-patch.js');

// RxJS
require('rxjs/Rx');

var testing = require('angular2/testing');
var browser = require('angular2/platform/testing/browser');

testing.setBaseTestProviders(
  browser.TEST_BROWSER_PLATFORM_PROVIDERS,
  browser.TEST_BROWSER_APPLICATION_PROVIDERS
);

Object.assign(global, testing);

/*
  Ok, this is kinda crazy. We can use the the context method on
  require that webpack created in order to tell webpack
  what files we actually want to require or import.
  Below, context will be an function/object with file names as keys.
  using that regex we are saying look in ./src/app and ./test then find
  any file that ends with spec.js and get its path. By passing in true
  we say do this recursively
*/
var testContext = require.context('../src', true, /\.spec\.ts/);

// get all the files, for each file, call the context function
// that will require the file and load it up here. Context will
// loop and require those spec files here
function requireAll(requireContext) {
  return requireContext.keys().map(requireContext);
}

// require and return all modules that match
var modules = requireAll(testContext);
<commit_msg>chore(spec-bundle.js): remove file extensions<commit_after>// @datatype_void
/*
 * When testing with webpack and ES6, we have to do some extra
 * things get testing to work right. Because we are gonna write test
 * in ES6 to, we have to compile those as well. That's handled in
 * karma.conf.js with the karma-webpack plugin. This is the entry
 * file for webpack test. Just like webpack will create a bundle.js
 * file for our client, when we run test, it well compile and bundle them
 * all here! Crazy huh. So we need to do some setup
*/
Error.stackTraceLimit = Infinity;
//require('phantomjs-polyfill');
//require('es6-promise');
//require('es6-shim');
//require('es7-reflect-metadata');

// Prefer `CoreJS` over the polyfills commented out above
require('core-js');

// Zone.js
require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/jasmine-patch');

// RxJS
require('rxjs/Rx');

var testing = require('angular2/testing');
var browser = require('angular2/platform/testing/browser');

testing.setBaseTestProviders(
  browser.TEST_BROWSER_PLATFORM_PROVIDERS,
  browser.TEST_BROWSER_APPLICATION_PROVIDERS
);

Object.assign(global, testing);

/*
  Ok, this is kinda crazy. We can use the the context method on
  require that webpack created in order to tell webpack
  what files we actually want to require or import.
  Below, context will be an function/object with file names as keys.
  using that regex we are saying look in ./src/app and ./test then find
  any file that ends with spec.js and get its path. By passing in true
  we say do this recursively
*/
var testContext = require.context('../src', true, /\.spec\.ts/);

// get all the files, for each file, call the context function
// that will require the file and load it up here. Context will
// loop and require those spec files here
function requireAll(requireContext) {
  return requireContext.keys().map(requireContext);
}

// require and return all modules that match
var modules = requireAll(testContext);
<|endoftext|>


<commit_before>// -----------------------------------------------------------------------------
// CONSTANTS
// -----------------------------------------------------------------------------

// Default modifier keys for binds
const MODIFIERS = ["ctrl", "shift"];

// Grid width and height for centering windows with different sizes
const GRID_WIDTH = 12;
const GRID_HEIGHT = 10;

// Margin to have around each window edge
const MARGIN = 10;

// -----------------------------------------------------------------------------
// FUNCTIONS
// -----------------------------------------------------------------------------

Bind.on("s", MODIFIERS, () => Stark.run("/usr/local/bin/lock", []));

Bind.on("return", MODIFIERS, () => {
  if (App.find("iTerm2")) {
    Stark.run("/usr/bin/osascript", [
      "-e",
      `tell application "iTerm2" to create window with default profile`
    ]);
    return;
  }

  Stark.run("/usr/bin/open", ["-a", "iTerm"]);
});

// -----------------------------------------------------------------------------
// WINDOW CENTERING
// -----------------------------------------------------------------------------

Bind.on("z", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = (r.width / GRID_WIDTH) * 2;
  const y = r.y + (r.height / GRID_HEIGHT) * 2;
  const width = (r.width / GRID_WIDTH) * 8;
  const height = (r.height / GRID_HEIGHT) * 6;

  win.setFrame({ x, y, width, height });
});

Bind.on("x", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.width / GRID_WIDTH;
  const y = r.y + r.height / GRID_HEIGHT;
  const width = (r.width / GRID_WIDTH) * 10;
  const height = (r.height / GRID_HEIGHT) * 8;

  win.setFrame({ x, y, width, height });
});

Bind.on("c", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width - MARGIN * 2;
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

// -----------------------------------------------------------------------------
// WINDOW POSITIONING
// -----------------------------------------------------------------------------

Bind.on("h", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

Bind.on("l", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + r.width / 2 + MARGIN / 2;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

Bind.on("j", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height / 2 - (MARGIN + MARGIN / 2);

  win.setFrame({ x, y, width, height });
});

Bind.on("k", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + r.width / 2 + MARGIN / 2;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height / 2 - (MARGIN + MARGIN / 2);

  win.setFrame({ x, y, width, height });
});

// -----------------------------------------------------------------------------
// SPACES MANAGEMENT
// -----------------------------------------------------------------------------

const numOfSpaces = Space.all().length;
const spaces = [...Array(numOfSpaces).keys()].map(i => i + 1);

spaces.forEach(space => {
  Bind.on(`${space}`, MODIFIERS, () => {
    const win = Window.focused();

    if (!win) {
      return;
    }

    Space.active().removeWindows([win]);
    Space.all()[space - 1].addWindows([win]);
  });
});
<commit_msg>Increase MARGIN in stark config<commit_after>// -----------------------------------------------------------------------------
// CONSTANTS
// -----------------------------------------------------------------------------

// Default modifier keys for binds
const MODIFIERS = ["ctrl", "shift"];

// Grid width and height for centering windows with different sizes
const GRID_WIDTH = 12;
const GRID_HEIGHT = 10;

// Margin to have around each window edge
const MARGIN = 14;

// -----------------------------------------------------------------------------
// FUNCTIONS
// -----------------------------------------------------------------------------

Bind.on("s", MODIFIERS, () => Stark.run("/usr/local/bin/lock", []));

Bind.on("return", MODIFIERS, () => {
  if (App.find("iTerm2")) {
    Stark.run("/usr/bin/osascript", [
      "-e",
      `tell application "iTerm2" to create window with default profile`
    ]);
    return;
  }

  Stark.run("/usr/bin/open", ["-a", "iTerm"]);
});

// -----------------------------------------------------------------------------
// WINDOW CENTERING
// -----------------------------------------------------------------------------

Bind.on("z", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = (r.width / GRID_WIDTH) * 2;
  const y = r.y + (r.height / GRID_HEIGHT) * 2;
  const width = (r.width / GRID_WIDTH) * 8;
  const height = (r.height / GRID_HEIGHT) * 6;

  win.setFrame({ x, y, width, height });
});

Bind.on("x", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.width / GRID_WIDTH;
  const y = r.y + r.height / GRID_HEIGHT;
  const width = (r.width / GRID_WIDTH) * 10;
  const height = (r.height / GRID_HEIGHT) * 8;

  win.setFrame({ x, y, width, height });
});

Bind.on("c", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width - MARGIN * 2;
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

// -----------------------------------------------------------------------------
// WINDOW POSITIONING
// -----------------------------------------------------------------------------

Bind.on("h", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

Bind.on("l", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + r.width / 2 + MARGIN / 2;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height - MARGIN * 2;

  win.setFrame({ x, y, width, height });
});

Bind.on("j", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + MARGIN;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height / 2 - (MARGIN + MARGIN / 2);

  win.setFrame({ x, y, width, height });
});

Bind.on("k", MODIFIERS, () => {
  const win = Window.focused();

  if (!win) {
    return;
  }

  const r = win.screen.frameWithoutDockOrMenu;
  const x = r.x + r.width / 2 + MARGIN / 2;
  const y = r.y + MARGIN;
  const width = r.width / 2 - (MARGIN + MARGIN / 2);
  const height = r.height / 2 - (MARGIN + MARGIN / 2);

  win.setFrame({ x, y, width, height });
});

// -----------------------------------------------------------------------------
// SPACES MANAGEMENT
// -----------------------------------------------------------------------------

const numOfSpaces = Space.all().length;
const spaces = [...Array(numOfSpaces).keys()].map(i => i + 1);

spaces.forEach(space => {
  Bind.on(`${space}`, MODIFIERS, () => {
    const win = Window.focused();

    if (!win) {
      return;
    }

    Space.active().removeWindows([win]);
    Space.all()[space - 1].addWindows([win]);
  });
});
<|endoftext|>


<commit_before>const express = require('express');
const expressLogging = require('express-logging');
const logger = require('logops');
const bodyParser = require('body-parser');
logger.setLevel('DEBUG');
const fs = require('fs');
const redis = require('redis');
const Promise = require('bluebird');
const REDIS_PORT = process.env.REDIS_PORT || 6379;

Promise.promisifyAll(redis.RedisClient.prototype);

const app = express();
const redisClient = redis.createClient(REDIS_PORT);

redisClient.on("error", function (err) {
    console.log("Redis error " + err);
});

app.use(expressLogging(logger));
app.use(express.static('public'));
app.use(bodyParser.json());

function saveData(type, req, res) {
    var timestamp = 'Timestamp' in req.body ? parseInt(req.body.Timestamp) : Date.now();
    redisClient.zadd(type, timestamp, JSON.stringify(req.body), function(err, reply) {
        if (err) {
            logger.error(err);
            res.status(500).send(err);
        } else {
            res.sendStatus(200);
        }
    });
}

function sendLatestData(type, req, res) {
    if (req.query.since) {
        redisClient.zrangebyscore(type, req.query.since, '+inf', 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var items = [];
                for (var i = 0; i < reply.length/2; i++) {
                    var data = JSON.parse(reply[i*2]);
                    data.Timestamp = parseInt(reply[i*2+1]);
                    items.push(data)
                }
                res.status(200).send(items);
            }
        });
    } else {
        redisClient.zrevrange(type, 0, 1, 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var data = JSON.parse(reply[0]);
                data.Timestamp = parseInt(reply[1]);
                res.status(200).send(data);
            }
        });
    }
}

function dewpoint(humidity, temperature) {
    var a, b;
    if (temperature >= 0) {
        a = 7.5;
        b = 237.3;
    } else {
        a = 7.6;
        b = 240.7;
    }

    var sdd = 6.1078 * Math.pow(10, (a * temperature) / (b + temperature));
    var dd = sdd * (humidity / 100);
    v = Math.log(dd / 6.1078) / Math.log(10);
    return (b * v) / (a - v);
}

app.post('/api', function(req, res) {
    if (req.body.Type == 'PTU') {
        saveData('ptu', req, res);
    } else if (req.body.Type == 'Wind') {
        saveData('wind', req, res);
    } else if (req.body.Type == 'Rain') {
        saveData('rain', req, res);
    } else if (req.body.Type == 'Status') {
        saveData('status', req, res);
    } else if (req.body.Type == 'Radar') {
        saveData('radar', req, res);
    } else {
        res.sendStatus(400);
        return;
    }
});

app.get('/api/ptu', function(req, res) {
    sendLatestData('ptu', req, res);
});

app.get('/api/wind', function(req, res) {
    sendLatestData('wind', req, res);
});

app.get('/api/rain', function(req, res) {
    sendLatestData('rain', req, res);
});

app.get('/api/status', function(req, res) {
    sendLatestData('status', req, res);
});

app.get('/api/radar', function(req, res) {
    sendLatestData('radar', req, res);
});

app.get('/api/weather', function(req, res) {
    var a = redisClient.zrevrangeAsync('ptu', 0, 0);
    var b = redisClient.zrevrangeAsync('wind', 0, 0);
    var c = redisClient.zrevrangeAsync('rain', 0, 0);

    Promise.join(a, b, c, function(ptureply, windreply, rainreply) {
        if (ptureply.length == 0 || windreply.length == 0 || rainreply.length == 0) {
            logger.error('Reading Redis failed');
            res.status(500).send('{"error":"Reading redis failed"}');
            return;
        }
        var ptu = JSON.parse(ptureply[0]);
        var wind = JSON.parse(windreply[0]);
        var rain = JSON.parse(rainreply[0]);

        var data = {
            temperature: ptu.Data.Temperature.Ambient[0],
            humidity: ptu.Data.Humidity[0],
            dewpoint: parseInt(dewpoint(ptu.Data.Humidity[0], ptu.Data.Temperature.Ambient[0])*100)/100,
            pressure: ptu.Data.Pressure[0],
            windspeed: wind.Data.Speed.average[0],
            windgust: wind.Data.Speed.limits[1][0],
            winddir: wind.Data.Direction.average[0],
            rainrate: rain.Data.Rain.Intensity[0],
            cloudcover: 0,
            skytemperature: 0,
            skyquality: 0
        };
        res.json(data);
    }).catch(function(err) {
        logger.error('Reading Redis failed', err);
        res.status(500).send('{"error":"Reading redis failed: ' + err+  '"}');
    });
});

var server = app.listen(9001, function() {
    var host = server.address().address;
    var port = server.address().port;

    logger.info('koma-weather-server listening at http://%s:%s', host, port);
}).on('error', function(err) {
    logger.error(err);
});
<commit_msg>add raintrigger data type<commit_after>const express = require('express');
const expressLogging = require('express-logging');
const logger = require('logops');
const bodyParser = require('body-parser');
logger.setLevel('DEBUG');
const fs = require('fs');
const redis = require('redis');
const Promise = require('bluebird');
const REDIS_PORT = process.env.REDIS_PORT || 6379;

Promise.promisifyAll(redis.RedisClient.prototype);

const app = express();
const redisClient = redis.createClient(REDIS_PORT);

redisClient.on("error", function (err) {
    console.log("Redis error " + err);
});

app.use(expressLogging(logger));
app.use(express.static('public'));
app.use(bodyParser.json());

function saveData(type, req, res) {
    var timestamp = 'Timestamp' in req.body ? parseInt(req.body.Timestamp) : Date.now();
    redisClient.zadd(type, timestamp, JSON.stringify(req.body), function(err, reply) {
        if (err) {
            logger.error(err);
            res.status(500).send(err);
        } else {
            res.sendStatus(200);
        }
    });
}

function sendLatestData(type, req, res) {
    if (req.query.since) {
        redisClient.zrangebyscore(type, req.query.since, '+inf', 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var items = [];
                for (var i = 0; i < reply.length/2; i++) {
                    var data = JSON.parse(reply[i*2]);
                    data.Timestamp = parseInt(reply[i*2+1]);
                    items.push(data)
                }
                res.status(200).send(items);
            }
        });
    } else {
        redisClient.zrevrange(type, 0, 1, 'WITHSCORES', function(err, reply) {
            if (err) {
                logger.error(err);
                res.status(500).send(err);
            } else {
                var data = JSON.parse(reply[0]);
                data.Timestamp = parseInt(reply[1]);
                res.status(200).send(data);
            }
        });
    }
}

function dewpoint(humidity, temperature) {
    var a, b;
    if (temperature >= 0) {
        a = 7.5;
        b = 237.3;
    } else {
        a = 7.6;
        b = 240.7;
    }

    var sdd = 6.1078 * Math.pow(10, (a * temperature) / (b + temperature));
    var dd = sdd * (humidity / 100);
    v = Math.log(dd / 6.1078) / Math.log(10);
    return (b * v) / (a - v);
}

app.post('/api', function(req, res) {
    if (req.body.Type == 'PTU') {
        saveData('ptu', req, res);
    } else if (req.body.Type == 'Wind') {
        saveData('wind', req, res);
    } else if (req.body.Type == 'Rain') {
        saveData('rain', req, res);
    } else if (req.body.Type == 'RainTrigger') {
        saveData('raintrigger', req, res);
    } else if (req.body.Type == 'Status') {
        saveData('status', req, res);
    } else if (req.body.Type == 'Radar') {
        saveData('radar', req, res);
    } else {
        res.sendStatus(400);
        return;
    }
});

app.get('/api/ptu', function(req, res) {
    sendLatestData('ptu', req, res);
});

app.get('/api/wind', function(req, res) {
    sendLatestData('wind', req, res);
});

app.get('/api/rain', function(req, res) {
    sendLatestData('rain', req, res);
});

app.get('/api/raintrigger', function(req, res) {
    sendLatestData('raintrigger', req, res);
});

app.get('/api/status', function(req, res) {
    sendLatestData('status', req, res);
});

app.get('/api/radar', function(req, res) {
    sendLatestData('radar', req, res);
});

app.get('/api/weather', function(req, res) {
    var a = redisClient.zrevrangeAsync('ptu', 0, 0);
    var b = redisClient.zrevrangeAsync('wind', 0, 0);
    var c = redisClient.zrevrangeAsync('rain', 0, 0);

    Promise.join(a, b, c, function(ptureply, windreply, rainreply) {
        if (ptureply.length == 0 || windreply.length == 0 || rainreply.length == 0) {
            logger.error('Reading Redis failed');
            res.status(500).send('{"error":"Reading redis failed"}');
            return;
        }
        var ptu = JSON.parse(ptureply[0]);
        var wind = JSON.parse(windreply[0]);
        var rain = JSON.parse(rainreply[0]);

        var data = {
            temperature: ptu.Data.Temperature.Ambient[0],
            humidity: ptu.Data.Humidity[0],
            dewpoint: parseInt(dewpoint(ptu.Data.Humidity[0], ptu.Data.Temperature.Ambient[0])*100)/100,
            pressure: ptu.Data.Pressure[0],
            windspeed: wind.Data.Speed.average[0],
            windgust: wind.Data.Speed.limits[1][0],
            winddir: wind.Data.Direction.average[0],
            rainrate: rain.Data.Rain.Intensity[0],
            cloudcover: 0,
            skytemperature: 0,
            skyquality: 0
        };
        res.json(data);
    }).catch(function(err) {
        logger.error('Reading Redis failed', err);
        res.status(500).send('{"error":"Reading redis failed: ' + err+  '"}');
    });
});

var server = app.listen(9001, function() {
    var host = server.address().address;
    var port = server.address().port;

    logger.info('koma-weather-server listening at http://%s:%s', host, port);
}).on('error', function(err) {
    logger.error(err);
});
<|endoftext|>


<commit_before>// GRNsight web client

var express = require("express");
var http = require("http");
var path = require("path");
var stylus = require("stylus");
var morgan = require("morgan");
var methodOverride = require("method-override");
var serveStatic = require("serve-static");
var bodyParser = require("body-parser");
var errorHandler = require("errorhandler");

var env = process.env.NODE_ENV || "development";
var config = require("./config/config")[env];
var app = express();

app.set("port", process.env.PORT || config.port || 3001);
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(morgan("dev"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(methodOverride());
// app.use(express.cookieSession());
app.use(stylus.middleware(path.join(__dirname, "public")));
app.use(serveStatic(path.join(__dirname, "public")));

if (app.get("env") === "development") {
    app.use(errorHandler());
}

app.set("serviceRoot", config.serviceRoot);
console.log("Web service root: " + app.get("serviceRoot"));

require("./controllers/main")(app);

// Don't start the server if this app is run as a child process.
if (!module.parent) {
    http.createServer(app).listen(app.get("port"), function () {
        console.log("GRNsight web client running on port %s, environment=%s", app.get("port"), env);
    });
} else {
    module.exports = app;
}
<commit_msg>make web-client https<commit_after>// GRNsight web client

var express = require("express");
var https = require("https");
var fs = require("fs");
var path = require("path");
var stylus = require("stylus");
var morgan = require("morgan");
var methodOverride = require("method-override");
var serveStatic = require("serve-static");
var bodyParser = require("body-parser");
var errorHandler = require("errorhandler");

var env = process.env.NODE_ENV || "development";
var config = require("./config/config")[env];
var app = express();

app.set("port", process.env.PORT || config.port || 3001);
app.set("views", path.join(__dirname, "views"));
app.set("view engine", "jade");

app.use(morgan("dev"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(methodOverride());
// app.use(express.cookieSession());
app.use(stylus.middleware(path.join(__dirname, "public")));
app.use(serveStatic(path.join(__dirname, "public")));

if (app.get("env") === "development") {
    app.use(errorHandler());
}

var options = {
    key: fs.readFileSync(path.resolve("./server.key")),
    cert: fs.readFileSync(path.resolve("./server.cert")),
};

app.set("serviceRoot", config.serviceRoot);
console.log("Web service root: " + app.get("serviceRoot"));

require("./controllers/main")(app);

// Don't start the server if this app is run as a child process.
if (!module.parent) {
    https.createServer(options, app).listen(app.get("port"), function () {
        console.log("GRNsight web client running on port %s, environment=%s", app.get("port"), env);
    });
} else {
    module.exports = app;
}
<|endoftext|>