<commit_before>/*
 * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import React, {Component} from 'react'
import {Link} from 'react-router-dom'
import {Col, Popconfirm, Row, Form, Dropdown, Tag, Menu, Badge, message} from 'antd';

const FormItem = Form.Item;
import Loading from '../../Base/Loading/Loading'
import ResourceNotFound from "../../Base/Errors/ResourceNotFound";
import Api from '../../../data/api'

import {withStyles} from 'material-ui/styles';
import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import Card, {CardActions, CardContent, CardMedia} from 'material-ui/Card';
import {MenuItem} from 'material-ui/Menu';
import {Delete, Edit, CreateNewFolder, Description}from 'material-ui-icons';
import Table, {TableBody, TableCell, TableRow} from 'material-ui/Table';
import Select from 'material-ui/Select';
import 'react-select/dist/react-select.css';
import Subscriptions  from 'material-ui-icons/Subscriptions';
import {FormControl} from 'material-ui/Form';
import NotificationSystem from 'react-notification-system';

const styles = theme => ({
    fullWidth: {
        width: "100%",
        "margin-top": "1%"
    }
});

class BasicInfo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            api: null,
            applications: null,
            policies: null,
            dropDownApplications: null,
            dropDownPolicies: null,
            notFound: false,
            matDropVisible: false,
            matDropValue: 'one',
            subscribedApplicationIds: [],
            options: [],
            tiers: [],
            applicationId: null,
            policyName: null
        };
        this.api_uuid = this.props.uuid;
        this.logChange = this.logChange.bind(this);
    }

    componentDidMount() {
        const api = new Api();
        let promised_api = api.getAPIById(this.api_uuid);
        promised_api.then(
            response => {
                console.info(response.obj);
                this.setState({api: response.obj});
                let apiTiers = response.obj.policies;
                let tiers = [];
                for (let i = 0; i < apiTiers.length; i++) {
                    let tierName = apiTiers[i];
                    tiers.push({value: tierName, label: tierName});
                }
                this.setState({tiers: tiers});
                if (tiers.length > 0) {
                    this.setState({policyName: tiers[0].value});
                }
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let existing_subscriptions = api.getSubscriptions(this.api_uuid, null);
        existing_subscriptions.then((response) => {
            let subscribedApplications = [];
            //get the application IDs of existing subscriptions
            response.body.list.map(element => subscribedApplications.push(element.applicationId));
            this.setState({subscribedApplicationIds: subscribedApplications});
        }).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let promised_applications = api.getAllApplications();
        promised_applications.then(
            (response) => {
                let applicationResponseObj = response.body;
                let applications = [];
                for (let i = 0; i < applicationResponseObj.list.length; i++) {
                    let applicationId = applicationResponseObj.list[i].applicationId;
                    let applicationName = applicationResponseObj.list[i].name;
                    //include the application only if it does not has an existing subscriptions
                    if (this.state.subscribedApplicationIds.includes(applicationId)) {
                        continue;
                    } else {
                        applications.push({value: applicationId, label: applicationName});
                    }
                }
                this.setState({options: applications});
                if (options.length > 0) {
                    this.setState({applicationId: options[0].value});
                }
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let promised_subscriptions = api.getSubscriptions(this.api_uuid, null);
        promised_subscriptions.then(
            response => {
                this.dropDownApplications = [<Option key="custom" onClick={this.handleClick}>New Application</Option>];

                for (var i = 0; i < this.api.policies.length; i++) {
                    this.dropDownPolicies.push(<Option key={this.api.policies[i]}>{this.api.policies[i]}</Option>);
                }
                var subscription = {};
                var subscriptions = response.obj.list;
                var application = {};
                var subscribedApp = false;
                for (var i = 0; i < this.applications.length; i++) {
                    subscribedApp = false;
                    application = applications[i];
                    if (application.lifeCycleStatus != "APPROVED") {
                        continue;
                    }
                    for (var j = 0; j < subscriptions.length; j++) {
                        subscription = subscriptions[j];
                        if (subscription.applicationId === application.applicationId) {
                            subscribedApp = true;
                            continue;
                        }
                    }
                    if (!subscribedApp) {
                        this.dropDownApplications.push(<Option key={application.id}>{application.name}</Option>);
                    }
                }
                this.policies = this.api.policies;
                console.info(this.api.policies)
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );
    }

    handleChange = name => event => {
        this.setState({[name]: event.target.value});
    };

    addNotifications() {
        this.refs.notificationSystem.addNotification( {
            message: 'Subscribe to API successfully',
            position: 'tc',
            level: 'success'
        });
    };

    createSubscription = (e) => {
        e.preventDefault();
        let apiId = this.api_uuid;
        let applicationId = this.state.applicationId;
        let policy = this.state.policyName;
        let api = new Api();
        let promised_subscribe = api.subscribe(apiId, applicationId, policy);
        promised_subscribe.then(response => {
            console.log("Subscription created successfully with ID : " + response.body.subscriptionId);
            this.addNotifications();
            let applications = [];
            for (let i = 0; i < this.state.options.length; i++) {
                let subscribeApplicationId = this.state.options[i].value;
                let applicationName = this.state.options[i].label;
                if (subscribeApplicationId !== applicationId) {
                    applications.push({value: applicationId, label: applicationName});
                }
            }
            this.setState({options: applications});
        }).catch(error => {
                console.log("Error while creating the subscription.");
                console.error(error);
            }
        )
    };

    populateApplicationDropdown() {
        return this.dropDownApplications;
    }

    populatePolicyDropdown() {
        return this.dropDownPolicies;
    }

    handleClick() {
        this.setState({redirect: true});
    }

    selectChange() {
        this.setState({matDropVisible: !this.state.matDropVisible})
    }

    onBlur(e) {
        console.info(document.activeElement);
        if (!e.currentTarget.contains(document.activeElement)) {
            this.setState({matDropVisible: false});
        }
    }

    selectOption(option) {
        console.info(option);
        this.setState({selectOption: option});
    }

    logChange(val) {
        this.setState({matDropValue: val.value});
        console.log("Selected: " + JSON.stringify(val));
    }

    render() {
        const formItemLayout = {
            labelCol: {span: 6},
            wrapperCol: {span: 18}
        };
        if (this.state.notFound) {
            return <ResourceNotFound />
        }
        if (this.state.redirect) {
            return <Redirect push to="/application-create"/>;
        }
        const {classes} = this.props;
        const api = this.state.api;

        return (
            this.state.api ?

                    <Grid className={classes.fullWidth} container justify="center" alignItems="center">
                        <Grid item xs={12}>
                            <Paper style={{display: "flex"}}>
                                <Typography type="display2" gutterBottom className="page-title">
                                    {api.name} - Overview
                                </Typography>
                                {/*<Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <Edit /> Edit
                                 </Button>
                                 <Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <CreateNewFolder /> Create New Version
                                 </Button>
                                 <Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <Description /> View Swagger
                                 </Button>*/}
                            </Paper>
                        </Grid>
                        <Grid item xs={12} sm={12} md={3} lg={3} xl={2} style={{paddingLeft: "40px"}}>

                            <Card>
                                <CardMedia image="/store/public/app/images/api/api-default.png">
                                </CardMedia>
                                <CardContent>

                                        <Badge status="processing" text={api.lifeCycleStatus}/>

11 Apps

                                        View in
                                            store

                                </CardContent>
                                <CardActions>
                                    {api.lifeCycleStatus}

                                    <Button dense color="primary">
                                        View in
                                            store
                                    </Button>
                                </CardActions>
                            </Card>
                        </Grid>
                        <Grid item xs={12} sm={6} md={3} lg={3} xl={2} style={{paddingLeft: "40px"}}>

                                <TableBody>

                                    <TableRow>
                                        <TableCell>Version</TableCell><TableCell>{api.version}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Context</TableCell><TableCell>{api.context}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Context</TableCell><TableCell>{api.provider}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Date Created</TableCell><TableCell>{api.createdTime}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Date Last
                                            Updated</TableCell><TableCell>{api.lastUpdatedTime}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Default API
                                            Version</TableCell><TableCell>{api.isDefaultVersion}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Default API
                                            Version</TableCell><TableCell>{api.lifeCycleStatus}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Rating</TableCell>
                                        <TableCell>
                                            <StarRatingBar apiIdProp={this.api_uuid}></StarRatingBar>
                                        </TableCell>
                                    </TableRow>

                                </TableBody>

                        </Grid>
                        <Grid item xs={12} sm={6} md={6} lg={6} xl={8} style={{paddingLeft: "40px"}}>
                            <Typography type="subheading" gutterBottom>
                                Applications
                            </Typography>
                            {this.state.options &&
                            <FormControl style={{width: "100%", marginBottom: "20px"}}>
                                <Select
                                    style={{width: "100%"}}
                                    value={this.state.applicationId}
                                    onChange={this.handleChange('applicationId')}
                                >
                                    {this.state.options.map((option) => <MenuItem key={option.value}
                                                                                value={option.value}>{option.label}</MenuItem>)}
                                </Select>
                            </FormControl>
                            }
                            <Typography type="subheading" gutterBottom>
                                Tiers
                            </Typography>
                            {this.state.tiers &&
                            <FormControl style={{width: "100%"}}>`
                                <Select
                                    style={{width: "100%"}}
                                    value={this.state.policyName}
                                    onChange={this.handleChange('policyName')}
                                >
                                    {this.state.tiers.map((tier) => <MenuItem key={tier.value}
                                                                              value={tier.value}>{tier.label}</MenuItem>)}
                                </Select>
                            </FormControl>
                            }

                            <Button onClick={this.createSubscription} raised color="primary"
                                    style={{paddingTop: '20px'}}>
                                <NotificationSystem ref="notificationSystem"/>
                                <Subscriptions style={{paddingRight: '10px'}}/> Subscribe
                            </Button>

                            {/*<Select>
                             {this.populateApplicationDropdown()}
                             </Select>
                             <Select>
                             {this.populatePolicyDropdown()}
                             </Select>*/}

                        </Grid>
                    </Grid>

                : <Loading/>
        );
    }
}

class Star extends React.Component {
    constructor(props) {
        super(props);

        this.handleHoveringOver = this.handleHoveringOver.bind(this);
    }

    handleHoveringOver(event) {
        this.props.hoverOver(this.props.name);
    }

    render() {
        return this.props.isRated ?

                ★
             :

                ☆
            ;
    }
}

class StarRatingBar extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            previousRating: 0,
            rating: 0
        };

        this.handleMouseOver = this.handleMouseOver.bind(this);
        this.handleRatingUpdate = this.handleRatingUpdate.bind(this);
        this.handleMouseOut = this.handleMouseOut.bind(this);
    }

    componentDidMount() {
        var api = new Api();
        let promised_api = api.getAPIById(this.props.apiIdProp);
        promised_api.then(
            response => {
            }
        );

        //get user rating
        let promised_rating = api.getRatingFromUser(this.props.apiIdProp, null);
        promised_rating.then(
            response => {
                this.setState({rating: response.obj.userRating});
                this.setState({previousRating: response.obj.userRating});
            }
        );
    }

    handleMouseOver(index) {
        this.setState({rating: index});
    }

    handleMouseOut() {
        this.setState({rating: this.state.previousRating});
    }

    handleRatingUpdate() {
        this.setState({previousRating: this.state.rating});
        this.setState({rating: this.state.rating});

        var api = new Api();
        let ratingInfo = {"rating": this.state.rating};
        let promise = api.addRating(this.props.apiIdProp, ratingInfo);
        promise.then(
            response => {
                message.success("Rating updated successfully");
            }).catch(
            error => {
                message.error("Error occurred while adding ratings!");
            }
        );
    }

    render() {
        return (

            <Star name={1} isRated={this.state.rating >= 1} hoverOver={this.handleMouseOver}> </Star>
            <Star name={2} isRated={this.state.rating >= 2} hoverOver={this.handleMouseOver}> </Star>
            <Star name={3} isRated={this.state.rating >= 3} hoverOver={this.handleMouseOver}> </Star>
            <Star name={4} isRated={this.state.rating >= 4} hoverOver={this.handleMouseOver}> </Star>
            <Star name={5} isRated={this.state.rating >= 5} hoverOver={this.handleMouseOver}> </Star>

);
    }
}

export default withStyles(styles)(BasicInfo);
<commit_msg>comment changes<commit_after>/*
 * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import React, {Component} from 'react'
import {Link} from 'react-router-dom'
import {Col, Popconfirm, Row, Form, Dropdown, Tag, Menu, Badge, message} from 'antd';

const FormItem = Form.Item;
import Loading from '../../Base/Loading/Loading'
import ResourceNotFound from "../../Base/Errors/ResourceNotFound";
import Api from '../../../data/api'

import {withStyles} from 'material-ui/styles';
import Paper from 'material-ui/Paper';
import Grid from 'material-ui/Grid';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import Card, {CardActions, CardContent, CardMedia} from 'material-ui/Card';
import {MenuItem} from 'material-ui/Menu';
import {Delete, Edit, CreateNewFolder, Description}from 'material-ui-icons';
import Table, {TableBody, TableCell, TableRow} from 'material-ui/Table';
import Select from 'material-ui/Select';
import 'react-select/dist/react-select.css';
import Subscriptions  from 'material-ui-icons/Subscriptions';
import {FormControl} from 'material-ui/Form';
import NotificationSystem from 'react-notification-system';

const styles = theme => ({
    fullWidth: {
        width: "100%",
        "margin-top": "1%"
    }
});

class BasicInfo extends Component {
    constructor(props) {
        super(props);
        this.state = {
            api: null,
            applications: null,
            policies: null,
            dropDownApplications: null,
            dropDownPolicies: null,
            notFound: false,
            matDropVisible: false,
            matDropValue: 'one',
            subscribedApplicationIds: [],
            options: [],
            tiers: [],
            applicationId: null,
            policyName: null
        };
        this.api_uuid = this.props.uuid;
        this.logChange = this.logChange.bind(this);
    }

    componentDidMount() {
        const api = new Api();
        let promised_api = api.getAPIById(this.api_uuid);
        promised_api.then(
            response => {
                console.info(response.obj);
                this.setState({api: response.obj});
                let apiTiers = response.obj.policies;
                let tiers = [];
                for (let i = 0; i < apiTiers.length; i++) {
                    let tierName = apiTiers[i];
                    tiers.push({value: tierName, label: tierName});
                }
                this.setState({tiers: tiers});
                if (tiers.length > 0) {
                    this.setState({policyName: tiers[0].value});
                }
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let existing_subscriptions = api.getSubscriptions(this.api_uuid, null);
        existing_subscriptions.then((response) => {
            let subscribedApplications = [];
            //get the application IDs of existing subscriptions
            response.body.list.map(element => subscribedApplications.push(element.applicationId));
            this.setState({subscribedApplicationIds: subscribedApplications});
        }).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let promised_applications = api.getAllApplications();
        promised_applications.then(
            (response) => {
                let applicationResponseObj = response.body;
                let applications = [];
                for (let i = 0; i < applicationResponseObj.list.length; i++) {
                    let applicationId = applicationResponseObj.list[i].applicationId;
                    let applicationName = applicationResponseObj.list[i].name;
                    //include the application only if it does not has an existing subscriptions
                    if (this.state.subscribedApplicationIds.includes(applicationId)) {
                        continue;
                    } else {
                        applications.push({value: applicationId, label: applicationName});
                    }
                }
                this.setState({options: applications});
                if (options.length > 0) {
                    this.setState({applicationId: options[0].value});
                }
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );

        let promised_subscriptions = api.getSubscriptions(this.api_uuid, null);
        promised_subscriptions.then(
            response => {
                this.dropDownApplications = [<Option key="custom" onClick={this.handleClick}>New Application</Option>];

                for (var i = 0; i < this.api.policies.length; i++) {
                    this.dropDownPolicies.push(<Option key={this.api.policies[i]}>{this.api.policies[i]}</Option>);
                }
                var subscription = {};
                var subscriptions = response.obj.list;
                var application = {};
                var subscribedApp = false;
                for (var i = 0; i < this.applications.length; i++) {
                    subscribedApp = false;
                    application = applications[i];
                    if (application.lifeCycleStatus != "APPROVED") {
                        continue;
                    }
                    for (var j = 0; j < subscriptions.length; j++) {
                        subscription = subscriptions[j];
                        if (subscription.applicationId === application.applicationId) {
                            subscribedApp = true;
                            continue;
                        }
                    }
                    if (!subscribedApp) {
                        this.dropDownApplications.push(<Option key={application.id}>{application.name}</Option>);
                    }
                }
                this.policies = this.api.policies;
                console.info(this.api.policies)
            }
        ).catch(
            error => {
                if (process.env.NODE_ENV !== "production") {
                    console.log(error);
                }
                let status = error.status;
                if (status === 404) {
                    this.setState({notFound: true});
                }
            }
        );
    }

    handleChange = name => event => {
        this.setState({[name]: event.target.value});
    };

    addNotifications() {
        this.refs.notificationSystem.addNotification( {
            message: 'Subscribe to API successfully',
            position: 'tc',
            level: 'success'
        });
    };

    createSubscription = (e) => {
        e.preventDefault();
        let apiId = this.api_uuid;
        let applicationId = this.state.applicationId;
        let policy = this.state.policyName;
        let api = new Api();
        let promised_subscribe = api.subscribe(apiId, applicationId, policy);
        promised_subscribe.then(response => {
            console.log("Subscription created successfully with ID : " + response.body.subscriptionId);
            this.addNotifications();
            let applications = this.state.options.filter(application => applicationId !== application.value );
            this.setState({options: applications});
        }).catch(error => {
                console.log("Error while creating the subscription.");
                console.error(error);
            }
        )
    };

    populateApplicationDropdown() {
        return this.dropDownApplications;
    }

    populatePolicyDropdown() {
        return this.dropDownPolicies;
    }

    handleClick() {
        this.setState({redirect: true});
    }

    selectChange() {
        this.setState({matDropVisible: !this.state.matDropVisible})
    }

    onBlur(e) {
        console.info(document.activeElement);
        if (!e.currentTarget.contains(document.activeElement)) {
            this.setState({matDropVisible: false});
        }
    }

    selectOption(option) {
        console.info(option);
        this.setState({selectOption: option});
    }

    logChange(val) {
        this.setState({matDropValue: val.value});
        console.log("Selected: " + JSON.stringify(val));
    }

    render() {
        const formItemLayout = {
            labelCol: {span: 6},
            wrapperCol: {span: 18}
        };
        if (this.state.notFound) {
            return <ResourceNotFound />
        }
        if (this.state.redirect) {
            return <Redirect push to="/application-create"/>;
        }
        const {classes} = this.props;
        const api = this.state.api;

        return (
            this.state.api ?

                    <Grid className={classes.fullWidth} container justify="center" alignItems="center">
                        <Grid item xs={12}>
                            <Paper style={{display: "flex"}}>
                                <Typography type="display2" gutterBottom className="page-title">
                                    {api.name} - Overview
                                </Typography>
                                {/*<Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <Edit /> Edit
                                 </Button>
                                 <Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <CreateNewFolder /> Create New Version
                                 </Button>
                                 <Button aria-owns="simple-menu" aria-haspopup="true" >
                                 <Description /> View Swagger
                                 </Button>*/}
                            </Paper>
                        </Grid>
                        <Grid item xs={12} sm={12} md={3} lg={3} xl={2} style={{paddingLeft: "40px"}}>

                            <Card>
                                <CardMedia image="/store/public/app/images/api/api-default.png">
                                </CardMedia>
                                <CardContent>

                                        <Badge status="processing" text={api.lifeCycleStatus}/>

11 Apps

                                        View in
                                            store

                                </CardContent>
                                <CardActions>
                                    {api.lifeCycleStatus}

                                    <Button dense color="primary">
                                        View in
                                            store
                                    </Button>
                                </CardActions>
                            </Card>
                        </Grid>
                        <Grid item xs={12} sm={6} md={3} lg={3} xl={2} style={{paddingLeft: "40px"}}>

                                <TableBody>

                                    <TableRow>
                                        <TableCell>Version</TableCell><TableCell>{api.version}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Context</TableCell><TableCell>{api.context}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Context</TableCell><TableCell>{api.provider}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Date Created</TableCell><TableCell>{api.createdTime}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Date Last
                                            Updated</TableCell><TableCell>{api.lastUpdatedTime}</TableCell>
                                    </TableRow>

                                    <TableRow>
                                        <TableCell>Default API
                                            Version</TableCell><TableCell>{api.isDefaultVersion}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Default API
                                            Version</TableCell><TableCell>{api.lifeCycleStatus}</TableCell>
                                    </TableRow>
                                    <TableRow>
                                        <TableCell>Rating</TableCell>
                                        <TableCell>
                                            <StarRatingBar apiIdProp={this.api_uuid}></StarRatingBar>
                                        </TableCell>
                                    </TableRow>

                                </TableBody>

                        </Grid>
                        <Grid item xs={12} sm={6} md={6} lg={6} xl={8} style={{paddingLeft: "40px"}}>
                            <Typography type="subheading" gutterBottom>
                                Applications
                            </Typography>
                            {this.state.options &&
                            <FormControl style={{width: "100%", marginBottom: "20px"}}>
                                <Select
                                    style={{width: "100%"}}
                                    value={this.state.applicationId}
                                    onChange={this.handleChange('applicationId')}
                                >
                                    {this.state.options.map((option) => <MenuItem key={option.value}
                                                                                value={option.value}>{option.label}</MenuItem>)}
                                </Select>
                            </FormControl>
                            }
                            <Typography type="subheading" gutterBottom>
                                Tiers
                            </Typography>
                            {this.state.tiers &&
                            <FormControl style={{width: "100%"}}>`
                                <Select
                                    style={{width: "100%"}}
                                    value={this.state.policyName}
                                    onChange={this.handleChange('policyName')}
                                >
                                    {this.state.tiers.map((tier) => <MenuItem key={tier.value}
                                                                              value={tier.value}>{tier.label}</MenuItem>)}
                                </Select>
                            </FormControl>
                            }

                            <Button onClick={this.createSubscription} raised color="primary"
                                    style={{paddingTop: '20px'}}>
                                <NotificationSystem ref="notificationSystem"/>
                                <Subscriptions style={{paddingRight: '10px'}}/> Subscribe
                            </Button>

                            {/*<Select>
                             {this.populateApplicationDropdown()}
                             </Select>
                             <Select>
                             {this.populatePolicyDropdown()}
                             </Select>*/}

                        </Grid>
                    </Grid>

                : <Loading/>
        );
    }
}

class Star extends React.Component {
    constructor(props) {
        super(props);

        this.handleHoveringOver = this.handleHoveringOver.bind(this);
    }

    handleHoveringOver(event) {
        this.props.hoverOver(this.props.name);
    }

    render() {
        return this.props.isRated ?

                ★
             :

                ☆
            ;
    }
}

class StarRatingBar extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            previousRating: 0,
            rating: 0
        };

        this.handleMouseOver = this.handleMouseOver.bind(this);
        this.handleRatingUpdate = this.handleRatingUpdate.bind(this);
        this.handleMouseOut = this.handleMouseOut.bind(this);
    }

    componentDidMount() {
        var api = new Api();
        let promised_api = api.getAPIById(this.props.apiIdProp);
        promised_api.then(
            response => {
            }
        );

        //get user rating
        let promised_rating = api.getRatingFromUser(this.props.apiIdProp, null);
        promised_rating.then(
            response => {
                this.setState({rating: response.obj.userRating});
                this.setState({previousRating: response.obj.userRating});
            }
        );
    }

    handleMouseOver(index) {
        this.setState({rating: index});
    }

    handleMouseOut() {
        this.setState({rating: this.state.previousRating});
    }

    handleRatingUpdate() {
        this.setState({previousRating: this.state.rating});
        this.setState({rating: this.state.rating});

        var api = new Api();
        let ratingInfo = {"rating": this.state.rating};
        let promise = api.addRating(this.props.apiIdProp, ratingInfo);
        promise.then(
            response => {
                message.success("Rating updated successfully");
            }).catch(
            error => {
                message.error("Error occurred while adding ratings!");
            }
        );
    }

    render() {
        return (

            <Star name={1} isRated={this.state.rating >= 1} hoverOver={this.handleMouseOver}> </Star>
            <Star name={2} isRated={this.state.rating >= 2} hoverOver={this.handleMouseOver}> </Star>
            <Star name={3} isRated={this.state.rating >= 3} hoverOver={this.handleMouseOver}> </Star>
            <Star name={4} isRated={this.state.rating >= 4} hoverOver={this.handleMouseOver}> </Star>
            <Star name={5} isRated={this.state.rating >= 5} hoverOver={this.handleMouseOver}> </Star>

);
    }
}

export default withStyles(styles)(BasicInfo);
<|endoftext|>


<commit_before>// @flow

import { NativeModules } from 'react-native';

import { getAppProp } from '../../app';
import {
    CONFERENCE_FAILED,
    CONFERENCE_JOINED,
    CONFERENCE_LEFT,
    CONFERENCE_WILL_JOIN,
    CONFERENCE_WILL_LEAVE,
    JITSI_CONFERENCE_URL_KEY,
    SET_ROOM,
    forEachConference,
    isRoomValid
} from '../../base/conference';
import { LOAD_CONFIG_ERROR } from '../../base/config';
import { CONNECTION_FAILED } from '../../base/connection';
import { MiddlewareRegistry } from '../../base/redux';
import { toURLString } from '../../base/util';
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';

/**
 * Middleware that captures Redux actions and uses the ExternalAPI module to
 * turn them into native events so the application knows about them.
 *
 * @param {Store} store - Redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    const result = next(action);
    const { type } = action;

    switch (type) {
    case CONFERENCE_FAILED: {
        const { error, ...data } = action;

        // XXX Certain CONFERENCE_FAILED errors are recoverable i.e. they have
        // prevented the user from joining a specific conference but the app may
        // be able to eventually join the conference. For example, the app will
        // ask the user for a password upon
        // JitsiConferenceErrors.PASSWORD_REQUIRED and will retry joining the
        // conference afterwards. Such errors are to not reach the native
        // counterpart of the External API (or at least not in the
        // fatality/finality semantics attributed to
        // conferenceFailed:/onConferenceFailed).
        if (!error.recoverable) {
            _sendConferenceEvent(store, /* action */ {
                error: _toErrorString(error),
                ...data
            });
        }
        break;
    }

    case CONFERENCE_JOINED:
    case CONFERENCE_LEFT:
    case CONFERENCE_WILL_JOIN:
    case CONFERENCE_WILL_LEAVE:
        _sendConferenceEvent(store, action);
        break;

    case CONNECTION_FAILED:
        !action.error.recoverable
            && _sendConferenceFailedOnConnectionError(store, action);
        break;

    case ENTER_PICTURE_IN_PICTURE:
        _sendEvent(store, _getSymbolDescription(type), /* data */ {});
        break;

    case LOAD_CONFIG_ERROR: {
        const { error, locationURL } = action;

        _sendEvent(
            store,
            _getSymbolDescription(type),
            /* data */ {
                error: _toErrorString(error),
                url: toURLString(locationURL)
            });
        break;
    }

    case SET_ROOM:
        _maybeTriggerEarlyConferenceWillJoin(store, action);
        break;
    }

    return result;
});

/**
 * Returns a {@code String} representation of a specific error {@code Object}.
 *
 * @param {Error|Object|string} error - The error {@code Object} to return a
 * {@code String} representation of.
 * @returns {string} A {@code String} representation of the specified
 * {@code error}.
 */
function _toErrorString(
        error: Error | { message: ?string, name: ?string } | string) {
    // XXX In lib-jitsi-meet and jitsi-meet we utilize errors in the form of
    // strings, Error instances, and plain objects which resemble Error.
    return (
        error
            ? typeof error === 'string'
                ? error
                : Error.prototype.toString.apply(error)
            : '');
}

/**
 * Gets the description of a specific {@code Symbol}.
 *
 * @param {Symbol} symbol - The {@code Symbol} to retrieve the description of.
 * @private
 * @returns {string} The description of {@code symbol}.
 */
function _getSymbolDescription(symbol: Symbol) {
    let description = symbol.toString();

    if (description.startsWith('Symbol(') && description.endsWith(')')) {
        description = description.slice(7, -1);
    }

    // The polyfill es6-symbol that we use does not appear to comply with the
    // Symbol standard and, merely, adds @@ at the beginning of the description.
    if (description.startsWith('@@')) {
        description = description.slice(2);
    }

    return description;
}

/**
 * If {@link SET_ROOM} action happens for a valid conference room this method
 * will emit an early {@link CONFERENCE_WILL_JOIN} event to let the external API
 * know that a conference is being joined. Before that happens a connection must
 * be created and only then base/conference feature would emit
 * {@link CONFERENCE_WILL_JOIN}. That is fine for the Jitsi Meet app, because
 * that's the a conference instance gets created, but it's too late for
 * the external API to learn that. The latter {@link CONFERENCE_WILL_JOIN} is
 * swallowed in {@link _swallowEvent}.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _maybeTriggerEarlyConferenceWillJoin(store, action) {
    const { locationURL } = store.getState()['features/base/connection'];
    const { room } = action;

    isRoomValid(room) && locationURL && _sendEvent(
        store,
        _getSymbolDescription(CONFERENCE_WILL_JOIN),
        /* data */ {
            url: toURLString(locationURL)
        });
}

/**
 * Sends an event to the native counterpart of the External API for a specific
 * conference-related redux action.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _sendConferenceEvent(
        store: Object,
        action: {
            conference: Object,
            type: Symbol,
            url: ?string
        }) {
    const { conference, type, ...data } = action;

    // For these (redux) actions, conference identifies a JitsiConference
    // instance. The external API cannot transport such an object so we have to
    // transport an "equivalent".
    if (conference) {
        data.url = toURLString(conference[JITSI_CONFERENCE_URL_KEY]);
    }

    _swallowEvent(store, action, data)
        || _sendEvent(store, _getSymbolDescription(type), data);
}

/**
 * Sends {@link CONFERENCE_FAILED} event when the {@link CONNECTION_FAILED}
 * occurs. Otherwise the external API will not emit such event, because at this
 * point conference has not been created yet and the base/conference feature
 * will not emit it.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _sendConferenceFailedOnConnectionError(store, action) {
    const { locationURL } = store.getState()['features/base/connection'];

    locationURL && _sendEvent(
        store,
        _getSymbolDescription(CONFERENCE_FAILED),
        /* data */ {
            url: toURLString(locationURL),
            error: action.error.name
        });
}

/**
 * Sends a specific event to the native counterpart of the External API. Native
 * apps may listen to such events via the mechanisms provided by the (native)
 * mobile Jitsi Meet SDK.
 *
 * @param {Object} store - The redux store.
 * @param {string} name - The name of the event to send.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code name}.
 * @private
 * @returns {void}
 */
function _sendEvent(store: Object, name: string, data: Object) {
    // The JavaScript App needs to provide uniquely identifying information to
    // the native ExternalAPI module so that the latter may match the former to
    // the native JitsiMeetView which hosts it.
    const externalAPIScope = getAppProp(store, 'externalAPIScope');

    externalAPIScope
        && NativeModules.ExternalAPI.sendEvent(name, data, externalAPIScope);
}

/**
 * Determines whether to not send a {@code CONFERENCE_LEFT} event to the native
 * counterpart of the External API.
 *
 * @param {Object} store - The redux store.
 * @param {Action} action - The redux action which is causing the sending of the
 * event.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code action}.
 * @returns {boolean} If the specified event is to not be sent, {@code true};
 * otherwise, {@code false}.
 */
function _swallowConferenceLeft({ getState }, action, { url }) {
    // XXX Internally, we work with JitsiConference instances. Externally
    // though, we deal with URL strings. The relation between the two is many to
    // one so it's technically and practically possible (by externally loading
    // the same URL string multiple times) to try to send CONFERENCE_LEFT
    // externally for a URL string which identifies a JitsiConference that the
    // app is internally legitimately working with.
    let swallowConferenceLeft = false;

    url
        && forEachConference(getState, (conference, conferenceURL) => {
            if (conferenceURL && conferenceURL.toString() === url) {
                swallowConferenceLeft = true;
            }

            return !swallowConferenceLeft;
        });

    return swallowConferenceLeft;
}

/**
 * Determines whether to not send a specific event to the native counterpart of
 * the External API.
 *
 * @param {Object} store - The redux store.
 * @param {Action} action - The redux action which is causing the sending of the
 * event.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code action}.
 * @returns {boolean} If the specified event is to not be sent, {@code true};
 * otherwise, {@code false}.
 */
function _swallowEvent(store, action, data) {
    switch (action.type) {
    case CONFERENCE_LEFT:
        return _swallowConferenceLeft(store, action, data);
    case CONFERENCE_WILL_JOIN:
        // CONFERENCE_WILL_JOIN is dispatched to the external API on SET_ROOM,
        // before the connection is created, so we need to swallow the original
        // one emitted by base/conference.
        return true;

    default:
        return false;
    }
}
<commit_msg>ref(mobile/external-api): skip event if conference exists<commit_after>// @flow

import { NativeModules } from 'react-native';

import { getAppProp } from '../../app';
import {
    CONFERENCE_FAILED,
    CONFERENCE_JOINED,
    CONFERENCE_LEFT,
    CONFERENCE_WILL_JOIN,
    CONFERENCE_WILL_LEAVE,
    JITSI_CONFERENCE_URL_KEY,
    SET_ROOM,
    forEachConference,
    isRoomValid
} from '../../base/conference';
import { LOAD_CONFIG_ERROR } from '../../base/config';
import { CONNECTION_FAILED } from '../../base/connection';
import { MiddlewareRegistry } from '../../base/redux';
import { toURLString } from '../../base/util';
import { ENTER_PICTURE_IN_PICTURE } from '../picture-in-picture';

/**
 * Middleware that captures Redux actions and uses the ExternalAPI module to
 * turn them into native events so the application knows about them.
 *
 * @param {Store} store - Redux store.
 * @returns {Function}
 */
MiddlewareRegistry.register(store => next => action => {
    const result = next(action);
    const { type } = action;

    switch (type) {
    case CONFERENCE_FAILED: {
        const { error, ...data } = action;

        // XXX Certain CONFERENCE_FAILED errors are recoverable i.e. they have
        // prevented the user from joining a specific conference but the app may
        // be able to eventually join the conference. For example, the app will
        // ask the user for a password upon
        // JitsiConferenceErrors.PASSWORD_REQUIRED and will retry joining the
        // conference afterwards. Such errors are to not reach the native
        // counterpart of the External API (or at least not in the
        // fatality/finality semantics attributed to
        // conferenceFailed:/onConferenceFailed).
        if (!error.recoverable) {
            _sendConferenceEvent(store, /* action */ {
                error: _toErrorString(error),
                ...data
            });
        }
        break;
    }

    case CONFERENCE_JOINED:
    case CONFERENCE_LEFT:
    case CONFERENCE_WILL_JOIN:
    case CONFERENCE_WILL_LEAVE:
        _sendConferenceEvent(store, action);
        break;

    case CONNECTION_FAILED:
        !action.error.recoverable
            && _sendConferenceFailedOnConnectionError(store, action);
        break;

    case ENTER_PICTURE_IN_PICTURE:
        _sendEvent(store, _getSymbolDescription(type), /* data */ {});
        break;

    case LOAD_CONFIG_ERROR: {
        const { error, locationURL } = action;

        _sendEvent(
            store,
            _getSymbolDescription(type),
            /* data */ {
                error: _toErrorString(error),
                url: toURLString(locationURL)
            });
        break;
    }

    case SET_ROOM:
        _maybeTriggerEarlyConferenceWillJoin(store, action);
        break;
    }

    return result;
});

/**
 * Returns a {@code String} representation of a specific error {@code Object}.
 *
 * @param {Error|Object|string} error - The error {@code Object} to return a
 * {@code String} representation of.
 * @returns {string} A {@code String} representation of the specified
 * {@code error}.
 */
function _toErrorString(
        error: Error | { message: ?string, name: ?string } | string) {
    // XXX In lib-jitsi-meet and jitsi-meet we utilize errors in the form of
    // strings, Error instances, and plain objects which resemble Error.
    return (
        error
            ? typeof error === 'string'
                ? error
                : Error.prototype.toString.apply(error)
            : '');
}

/**
 * Gets the description of a specific {@code Symbol}.
 *
 * @param {Symbol} symbol - The {@code Symbol} to retrieve the description of.
 * @private
 * @returns {string} The description of {@code symbol}.
 */
function _getSymbolDescription(symbol: Symbol) {
    let description = symbol.toString();

    if (description.startsWith('Symbol(') && description.endsWith(')')) {
        description = description.slice(7, -1);
    }

    // The polyfill es6-symbol that we use does not appear to comply with the
    // Symbol standard and, merely, adds @@ at the beginning of the description.
    if (description.startsWith('@@')) {
        description = description.slice(2);
    }

    return description;
}

/**
 * If {@link SET_ROOM} action happens for a valid conference room this method
 * will emit an early {@link CONFERENCE_WILL_JOIN} event to let the external API
 * know that a conference is being joined. Before that happens a connection must
 * be created and only then base/conference feature would emit
 * {@link CONFERENCE_WILL_JOIN}. That is fine for the Jitsi Meet app, because
 * that's the a conference instance gets created, but it's too late for
 * the external API to learn that. The latter {@link CONFERENCE_WILL_JOIN} is
 * swallowed in {@link _swallowEvent}.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _maybeTriggerEarlyConferenceWillJoin(store, action) {
    const { locationURL } = store.getState()['features/base/connection'];
    const { room } = action;

    isRoomValid(room) && locationURL && _sendEvent(
        store,
        _getSymbolDescription(CONFERENCE_WILL_JOIN),
        /* data */ {
            url: toURLString(locationURL)
        });
}

/**
 * Sends an event to the native counterpart of the External API for a specific
 * conference-related redux action.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _sendConferenceEvent(
        store: Object,
        action: {
            conference: Object,
            type: Symbol,
            url: ?string
        }) {
    const { conference, type, ...data } = action;

    // For these (redux) actions, conference identifies a JitsiConference
    // instance. The external API cannot transport such an object so we have to
    // transport an "equivalent".
    if (conference) {
        data.url = toURLString(conference[JITSI_CONFERENCE_URL_KEY]);
    }

    _swallowEvent(store, action, data)
        || _sendEvent(store, _getSymbolDescription(type), data);
}

/**
 * Sends {@link CONFERENCE_FAILED} event when the {@link CONNECTION_FAILED}
 * occurs. It should be done only if the connection fails before the conference
 * instance is created. Otherwise the eventual failure event is supposed to be
 * emitted by the base/conference feature.
 *
 * @param {Store} store - The redux store.
 * @param {Action} action - The redux action.
 * @returns {void}
 */
function _sendConferenceFailedOnConnectionError(store, action) {
    const { locationURL } = store.getState()['features/base/connection'];
    const { connection } = action;

    locationURL
        && forEachConference(
            store,

            // If there's any conference in the  base/conference state then the
            // base/conference feature is supposed to emit a failure.
            conference => conference.getConnection() !== connection)
        && _sendEvent(
        store,
        _getSymbolDescription(CONFERENCE_FAILED),
        /* data */ {
            url: toURLString(locationURL),
            error: action.error.name
        });
}

/**
 * Sends a specific event to the native counterpart of the External API. Native
 * apps may listen to such events via the mechanisms provided by the (native)
 * mobile Jitsi Meet SDK.
 *
 * @param {Object} store - The redux store.
 * @param {string} name - The name of the event to send.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code name}.
 * @private
 * @returns {void}
 */
function _sendEvent(store: Object, name: string, data: Object) {
    // The JavaScript App needs to provide uniquely identifying information to
    // the native ExternalAPI module so that the latter may match the former to
    // the native JitsiMeetView which hosts it.
    const externalAPIScope = getAppProp(store, 'externalAPIScope');

    externalAPIScope
        && NativeModules.ExternalAPI.sendEvent(name, data, externalAPIScope);
}

/**
 * Determines whether to not send a {@code CONFERENCE_LEFT} event to the native
 * counterpart of the External API.
 *
 * @param {Object} store - The redux store.
 * @param {Action} action - The redux action which is causing the sending of the
 * event.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code action}.
 * @returns {boolean} If the specified event is to not be sent, {@code true};
 * otherwise, {@code false}.
 */
function _swallowConferenceLeft({ getState }, action, { url }) {
    // XXX Internally, we work with JitsiConference instances. Externally
    // though, we deal with URL strings. The relation between the two is many to
    // one so it's technically and practically possible (by externally loading
    // the same URL string multiple times) to try to send CONFERENCE_LEFT
    // externally for a URL string which identifies a JitsiConference that the
    // app is internally legitimately working with.
    let swallowConferenceLeft = false;

    url
        && forEachConference(getState, (conference, conferenceURL) => {
            if (conferenceURL && conferenceURL.toString() === url) {
                swallowConferenceLeft = true;
            }

            return !swallowConferenceLeft;
        });

    return swallowConferenceLeft;
}

/**
 * Determines whether to not send a specific event to the native counterpart of
 * the External API.
 *
 * @param {Object} store - The redux store.
 * @param {Action} action - The redux action which is causing the sending of the
 * event.
 * @param {Object} data - The details/specifics of the event to send determined
 * by/associated with the specified {@code action}.
 * @returns {boolean} If the specified event is to not be sent, {@code true};
 * otherwise, {@code false}.
 */
function _swallowEvent(store, action, data) {
    switch (action.type) {
    case CONFERENCE_LEFT:
        return _swallowConferenceLeft(store, action, data);
    case CONFERENCE_WILL_JOIN:
        // CONFERENCE_WILL_JOIN is dispatched to the external API on SET_ROOM,
        // before the connection is created, so we need to swallow the original
        // one emitted by base/conference.
        return true;

    default:
        return false;
    }
}
<|endoftext|>


<commit_before>var UI = require('./ui.js'),
	OpenFisca = require('./openfisca.js'),
	debounce = require('../lib/debounce.js'),
	Tests = require('./tests.js');

/** Handle events from the given form to update data.
*/
function bindToForm(form) {
	form.addEventListener('change', UI.reflectParameterChange);

	var update = OpenFisca.update.bind(form);

	update();

	update = debounce(update, 300);

	form.addEventListener('change', update);
	form.addEventListener('keyup', update);
}

bindToForm(document.querySelector('.embauche form'));

var jsNodes = document.querySelectorAll('.embauche .js-only');

for (var i = 0; i < jsNodes.length; i++) {
	jsNodes[i].className = jsNodes[i].className.replace('js-only', '');
}

document.getElementById('createTest').addEventListener('click', Tests.create);

module.exports.OpenFisca = OpenFisca;
<commit_msg>Avoid race condition<commit_after>var UI = require('./ui.js'),
	OpenFisca = require('./openfisca.js'),
	debounce = require('../lib/debounce.js'),
	Tests = require('./tests.js');

/** Handle events from the given form to update data.
*/
function bindToForm(form) {
	form.addEventListener('change', UI.reflectParameterChange);

	var update = debounce(OpenFisca.update.bind(form), 300);

	update();

	form.addEventListener('change', update);
	form.addEventListener('keyup', update);
}

bindToForm(document.querySelector('.embauche form'));

var jsNodes = document.querySelectorAll('.embauche .js-only');

for (var i = 0; i < jsNodes.length; i++) {
	jsNodes[i].className = jsNodes[i].className.replace('js-only', '');
}

document.getElementById('createTest').addEventListener('click', Tests.create);

module.exports.OpenFisca = OpenFisca;
<|endoftext|>


<commit_before>import execPromise from 'child-process-promise'
import mkdirp from 'mkdirp'
import fse from 'fs-extra'
import limax from 'limax'
import Jimp from 'jimp'
import path from 'path'
import {Promise} from 'bluebird'

import {
  abeExtend,
  coreUtils,
  cmsData,
  config
} from '../../'

export function cropAndSaveFile(imageSize, file, newFile) {
  var p = new Promise((resolve) => {
    Jimp.read(file).then(function (lenna) {
      lenna.crop(0, 0, parseInt(imageSize[0]), parseInt(imageSize[1])).write(newFile)
    }).catch(function (err) {
      console.error(err)
    })
    resolve()
  })
  return p
}

export function smartCropAndSaveFile(imageSize, file, newFile) {
  var cmd = `node node_modules/smartcrop-cli/smartcrop-cli.js --width ${parseInt(imageSize[0])} --height ${parseInt(imageSize[1])} ${file} ${newFile}`
  var p = execPromise.exec(cmd)
  return p
}

export function cropAndSaveFiles(images, file, resp) {
  var length = images.length
  var cropedImage = 0
  resp.thumbs = []
  var p = new Promise((resolve) => {
    for (var i = 0; i < length; i++) {
      var image = images[i]
      var ext = path.extname(file)
      var newFile = file.replace(ext, `_${images[i]}${ext}`)
      resp.thumbs.push({
        name: newFile.replace(path.join(config.root, config.publish.url), ''),
        size: image
      })
      smartCropAndSaveFile(image.split('x'), file, newFile)
        .then(function (result) {
          if(result.stderr) {
            cropAndSaveFile(image.split('x'), file, newFile).then(function () {
              if(++cropedImage === length) {
                resolve(resp)
              }
            })
          }
          else if(++cropedImage === length) {
            resolve(resp)
          }
        })
        .catch(function (err) {
          console.log(err)
        })
    }
  })

  return p
}

export function generateThumbnail(file) {
  var ext = path.extname(file).toLowerCase()
  var thumbFileName = file.replace(ext, `_thumb${ext}`)
  var thumbFileNameRelative = thumbFileName.replace(path.join(config.root, config.publish.url), '')
  var p = new Promise((resolve) => {
    var cropThumb = smartCropAndSaveFile([250, 250], file, thumbFileName)
    cropThumb.then(function (result) {
      var stderr = result.stderr
      if(thumbsList != null) {
        thumbsList.push({
          originalFile: file.replace(path.join(config.root, config.publish.url), ''),
          thumbFile: thumbFileNameRelative
        })
      }
      if(stderr) {
        cropAndSaveFile([250, 250], file, thumbFileName).then(function () {
          resolve({thumb: thumbFileNameRelative})
        })
      }
      else resolve({thumb: thumbFileNameRelative})
    })
  })

  return p
}

export function saveFile(req) {
  var p = new Promise((resolve) => {
    var folderFilePath = createMediaFolder(req)
    var resp = {success: 1}
    var filePath
    req.pipe(req.busboy)
    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
      var ext = path.extname(filename).toLowerCase()
      var slug = createMediaSlug(filename, ext)
      var hasSentHeader = false

      filePath = path.join(folderFilePath, slug)
      resp['filePath'] = path.join('/' + config.upload.image, slug)

      var returnErr = function (msg) {
        hasSentHeader = true
        file.resume()
        resolve({error: 1, response: msg})
      }

      file.on('limit', function() {
        returnErr('file is too big')
      })

      var isValid = isValidMedia(mimetype, ext)
      if(isValid.error) returnErr(isValid.error)

      var fstream = fse.createWriteStream(filePath)
      fstream.on('finish', function() {
        if(hasSentHeader) return
        if(/\.(jpg|png|gif|svg)/.test(filePath)) resp = abeExtend.hooks.instance.trigger('afterSaveImage', resp, req)

        var thumbPromise = generateThumbnail(filePath)
        thumbPromise.then(function (thumbResp) {
          resp.thumbnail = thumbResp.thumb
          if(req.query.input.indexOf('data-size') > -1){
            var thumbsSizes = cmsData.regex.getAttr(req.query.input, 'data-size').split(',')
            cropAndSaveFiles(thumbsSizes, filePath, resp).then(function (resp) {
              resolve(resp)
            })
          }
          else resolve(resp)
        })
      })
      file.pipe(fstream)
    })
  })

  return p
}

export function isValidMedia(mimetype, ext) {
  var allowedExtensions = ['.gif', '.jpg', '.jpeg', '.png', '.svg', '.mp4']
  var allowedMimetype = ['image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'video/mp4']
  var error = false
  if (allowedMimetype.indexOf(mimetype) < 0) error = 'unauthorized file'
  else if (allowedExtensions.indexOf(ext) < 0) error = 'not a valid asset'

  return {error: error}
}

export function createMediaSlug(filename, ext) {
  var filenameNoExt = path.basename(filename, ext)
  return limax(filenameNoExt, {separateNumbers: false}) + '-' + coreUtils.random.generateUniqueIdentifier(2) + ext
}

export function createMediaFolder(req) {
  var folderWebPath = '/' + config.upload.image
  folderWebPath = abeExtend.hooks.instance.trigger('beforeSaveImage', folderWebPath, req)
  var folderFilePath = path.join(config.root, config.publish.url, folderWebPath)
  mkdirp.sync(folderFilePath)

  return folderFilePath
}

var thumbsList

export function getThumbsList() {
  if(thumbsList != null) return thumbsList
  thumbsList = []
  var pathToThumbs = path.join(config.root, config.publish.url, config.upload.image)
  var files = coreUtils.file.getFilesSync(pathToThumbs, true)
  Array.prototype.forEach.call(files, (pathFile) => {
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
    if(pathFile.indexOf('_thumb.') > -1){
      thumbsList.push({
        originalFile: pathFile.replace('_thumb.', '.'),
        thumbFile: pathFile
      })
    }
  })

  return thumbsList
}

export function getAssociatedImageFileFromThumb(name) {
  var rexMatchImageName = /_(thumb|\d+x\d+)/
  var originalName = name.replace(rexMatchImageName, '')
  var imageList = {
    thumbFile: name,
    originalFile: originalName,
    thumbs: []
  }
  var pathThumb = name.split('/')
  pathThumb.pop()
  pathThumb = path.join(config.root, config.publish.url, pathThumb.join('/'))

  var files = coreUtils.file.getFilesSync(pathThumb, true)
  Array.prototype.forEach.call(files, (pathFile) => {
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
    if(pathFile !== originalName && pathFile !== name && pathFile.replace(rexMatchImageName, '') === originalName){
      imageList.thumbs.push(pathFile)
    }
  })

  return imageList
}
<commit_msg>fix: path with separator for windows<commit_after>import execPromise from 'child-process-promise'
import mkdirp from 'mkdirp'
import fse from 'fs-extra'
import limax from 'limax'
import Jimp from 'jimp'
import path from 'path'
import {Promise} from 'bluebird'

import {
  abeExtend,
  coreUtils,
  cmsData,
  config
} from '../../'

export function cropAndSaveFile(imageSize, file, newFile) {
  var p = new Promise((resolve) => {
    Jimp.read(file).then(function (lenna) {
      lenna.crop(0, 0, parseInt(imageSize[0]), parseInt(imageSize[1])).write(newFile)
    }).catch(function (err) {
      console.error(err)
    })
    resolve()
  })
  return p
}

export function smartCropAndSaveFile(imageSize, file, newFile) {
  var cmd = `node node_modules/smartcrop-cli/smartcrop-cli.js --width ${parseInt(imageSize[0])} --height ${parseInt(imageSize[1])} ${file} ${newFile}`
  var p = execPromise.exec(cmd)
  return p
}

export function cropAndSaveFiles(images, file, resp) {
  var length = images.length
  var cropedImage = 0
  resp.thumbs = []
  var p = new Promise((resolve) => {
    for (var i = 0; i < length; i++) {
      var image = images[i]
      var ext = path.extname(file)
      var newFile = file.replace(ext, `_${images[i]}${ext}`)
      resp.thumbs.push({
        name: newFile.replace(path.join(config.root, config.publish.url), ''),
        size: image
      })
      smartCropAndSaveFile(image.split('x'), file, newFile)
        .then(function (result) {
          if(result.stderr) {
            cropAndSaveFile(image.split('x'), file, newFile).then(function () {
              if(++cropedImage === length) {
                resolve(resp)
              }
            })
          }
          else if(++cropedImage === length) {
            resolve(resp)
          }
        })
        .catch(function (err) {
          console.log(err)
        })
    }
  })

  return p
}

export function generateThumbnail(file) {
  var ext = path.extname(file).toLowerCase()
  var thumbFileName = file.replace(ext, `_thumb${ext}`)
  var thumbFileNameRelative = thumbFileName.replace(path.join(config.root, config.publish.url), '')
  var p = new Promise((resolve) => {
    var cropThumb = smartCropAndSaveFile([250, 250], file, thumbFileName)
    cropThumb.then(function (result) {
      var stderr = result.stderr
      if(thumbsList != null) {
        thumbsList.push({
          originalFile: file.replace(path.join(config.root, config.publish.url), ''),
          thumbFile: thumbFileNameRelative
        })
      }
      if(stderr) {
        cropAndSaveFile([250, 250], file, thumbFileName).then(function () {
          resolve({thumb: thumbFileNameRelative})
        })
      }
      else resolve({thumb: thumbFileNameRelative})
    })
  })

  return p
}

export function saveFile(req) {
  var p = new Promise((resolve) => {
    var folderFilePath = createMediaFolder(req)
    var resp = {success: 1}
    var filePath
    req.pipe(req.busboy)
    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
      var ext = path.extname(filename).toLowerCase()
      var slug = createMediaSlug(filename, ext)
      var hasSentHeader = false

      filePath = path.join(folderFilePath, slug)
      resp['filePath'] = path.join('/' + config.upload.image, slug)

      var returnErr = function (msg) {
        hasSentHeader = true
        file.resume()
        resolve({error: 1, response: msg})
      }

      file.on('limit', function() {
        returnErr('file is too big')
      })

      var isValid = isValidMedia(mimetype, ext)
      if(isValid.error) returnErr(isValid.error)

      var fstream = fse.createWriteStream(filePath)
      fstream.on('finish', function() {
        if(hasSentHeader) return
        if(/\.(jpg|png|gif|svg)/.test(filePath)) resp = abeExtend.hooks.instance.trigger('afterSaveImage', resp, req)

        var thumbPromise = generateThumbnail(filePath)
        thumbPromise.then(function (thumbResp) {
          resp.thumbnail = thumbResp.thumb
          if(req.query.input.indexOf('data-size') > -1){
            var thumbsSizes = cmsData.regex.getAttr(req.query.input, 'data-size').split(',')
            cropAndSaveFiles(thumbsSizes, filePath, resp).then(function (resp) {
              resolve(resp)
            })
          }
          else resolve(resp)
        })
      })
      file.pipe(fstream)
    })
  })

  return p
}

export function isValidMedia(mimetype, ext) {
  var allowedExtensions = ['.gif', '.jpg', '.jpeg', '.png', '.svg', '.mp4']
  var allowedMimetype = ['image/gif', 'image/jpeg', 'image/png', 'image/svg+xml', 'video/mp4']
  var error = false
  if (allowedMimetype.indexOf(mimetype) < 0) error = 'unauthorized file'
  else if (allowedExtensions.indexOf(ext) < 0) error = 'not a valid asset'

  return {error: error}
}

export function createMediaSlug(filename, ext) {
  var filenameNoExt = path.basename(filename, ext)
  return limax(filenameNoExt, {separateNumbers: false}) + '-' + coreUtils.random.generateUniqueIdentifier(2) + ext
}

export function createMediaFolder(req) {
  var folderWebPath = '/' + config.upload.image
  folderWebPath = abeExtend.hooks.instance.trigger('beforeSaveImage', folderWebPath, req)
  var folderFilePath = path.join(config.root, config.publish.url, folderWebPath)
  mkdirp.sync(folderFilePath)

  return folderFilePath
}

var thumbsList

export function getThumbsList() {
  if(thumbsList != null) return thumbsList
  thumbsList = []
  var pathToThumbs = path.join(config.root, config.publish.url, config.upload.image)
  var files = coreUtils.file.getFilesSync(pathToThumbs, true)
  Array.prototype.forEach.call(files, (pathFile) => {
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
    if(pathFile.indexOf('_thumb.') > -1){
      thumbsList.push({
        originalFile: pathFile.replace('_thumb.', '.'),
        thumbFile: pathFile
      })
    }
  })

  return thumbsList
}

export function getAssociatedImageFileFromThumb(name) {
  var rexMatchImageName = /_(thumb|\d+x\d+)/
  name = path.join(path.sep, name)
  var originalName = path.join(path.sep, name.replace(rexMatchImageName, ''))
  var imageList = {
    thumbFile: name,
    originalFile: originalName,
    thumbs: []
  }
  var pathThumb = name.split('/')
  pathThumb.pop()
  pathThumb = path.join(config.root, config.publish.url, pathThumb.join('/'))

  var files = coreUtils.file.getFilesSync(pathThumb, true)
  Array.prototype.forEach.call(files, (pathFile) => {
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
    if(pathFile !== originalName && pathFile !== name && pathFile.replace(rexMatchImageName, '') === originalName){
      imageList.thumbs.push(pathFile)
    }
  })

  return imageList
}
<|endoftext|>


<commit_before>function selectCardInRoom(element, selected) {
    var amount = selected ? '12px' : '0px';

    $(element).stop().animate({
            top: amount
        }, 'fast'
    );
}

function selectMap(selected) {
    var amount = selected ? '8px' : '0px';

    $('#map-bottom').stop().animate({
            left: amount
        }, 'fast'
    );
}

//////////////////
// event hookups

$("#room .card").mouseenter(
    function() {
        selectCardInRoom(this, true);
    }
);

$("#room .card").mouseleave(
    function() {
        selectCardInRoom(this, false);
    }
);

$("#map").mouseenter(
    function() {
        selectMap(true);
    }
);

$("#map").mouseleave(
    function() {
        selectMap(false);
    }
);<commit_msg>Unused.<commit_after><|endoftext|>