<commit_before>import React from 'react';
import Alert from 'react-bootstrap/lib/Alert.js';
import metadataProvider from '../../lib/metadataProvider.js';
import MetaFormGroup from '../groupComponents/MetaFormGroup.js';
import GlyphButton from '../GlyphButton.js';
import Glyphicon from 'react-bootstrap/lib/Glyphicon.js';
import DropdownButton from 'react-bootstrap/lib/DropdownButton.js';
import MenuItem from 'react-bootstrap/lib/MenuItem.js';
import Dropdown from 'react-bootstrap/lib/Dropdown.js';
import arrayHelper from'../../lib/helpers/arrayHelper.js';
import Modal from 'react-bootstrap/lib/Modal.js';
import Button from 'react-bootstrap/lib/Button.js';
import MetaForm from '../../MetaForm.js';
import Table from 'react-bootstrap/lib/Table.js';
import FormGroup from './FormGroup.js';
import functionHelper from '../../lib/helpers/functionHelper.js';
import _ from 'underscore';

const MetaFormModal = React.createClass({

    propTypes: {
        schema: React.PropTypes.object.isRequired,
        componentFactory: React.PropTypes.object.isRequired,
        model: React.PropTypes.object.isRequired,
        entityType: React.PropTypes.string.isRequired,
        layoutName: React.PropTypes.string.isRequired,
        onSave: React.PropTypes.func,
        onCancel: React.PropTypes.func
    },

    handleSave: function (model) {
        if (this.props.onSave) {
            this.props.onSave(model);
        }
    },

    handleCancel: function () {
        if (this.props.onCancel) {
            this.props.onCancel();
        }
    },

    getDefaultProps: function () {
        return {
            show: false
        }
    },

    render: function () {
        return <Modal show={this.props.show} bsSize="large">
            <Modal.Header>
                <Modal.Title>Modal heading</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <MetaForm
                    schema={this.props.schema}
                    ref="mf"
                    entityName={this.props.entityType}
                    layoutName={this.props.layoutName}
                    componentFactory={this.props.componentFactory}
                    model={this.props.model}
                    onSave={this.handleSave}
                    onCancel={this.handleCancel}
                />
            </Modal.Body>
        </Modal>
    }
});

const ArrayGridRow = React.createClass({

    propTypes: {
        index: React.PropTypes.number.isRequired,
        onAction: React.PropTypes.func
    },

    handleAction: function (e, eventKey) {
        if (this.props.onAction) {
            this.props.onAction(this.props.index, eventKey)
        }
    },

    render: function () {
        return

                {this.props.children}

                <Dropdown pullRight onSelect={this.handleAction}>
                    <Dropdown.Toggle noCaret>
                        <Glyphicon glyph="cog"/>
                    </Dropdown.Toggle>
                    <Dropdown.Menu >
                        <MenuItem eventKey="edit">
                            <Glyphicon glyph="pencil"/>
                            Edit
                        </MenuItem>
                        <MenuItem eventKey="remove">
                            <Glyphicon glyph="remove" className="text-danger"/>
                            Remove

                        </MenuItem>
                        <MenuItem divider/>
                        <MenuItem eventKey="moveUp">
                            <Glyphicon glyph="chevron-up"/>
                            Move up
                        </MenuItem>
                        <MenuItem eventKey="moveDown">
                            <Glyphicon glyph="chevron-down"/>
                            Move down

                        </MenuItem>
                        <MenuItem divider/>
                        <MenuItem eventKey="moveFirst">
                            <Glyphicon glyph="chevron-up"/>
                            Move first

                        </MenuItem>
                        <MenuItem eventKey="moveLast">
                            <Glyphicon glyph="chevron-down"/>
                            Move last

                        </MenuItem>
                    </Dropdown.Menu>
                </Dropdown>

;
    }
});

const ArrayGrid = React.createClass({

    handleItemAction: function (index, eventKey) {
        if (this.props.onItemAction) {
            this.props.onItemAction(index, eventKey);
        }
    },

    render: function () {

        let rows = this.props.items.map((item, index) => {
            return <ArrayGridRow index={index} onAction={this.handleItemAction}>
                {item.text}
            </ArrayGridRow>;
        });

        return

                {rows}

;
    }
});

const ArrayGridContainer = React.createClass({

    propTypes: {
        name: React.PropTypes.string.isRequired,
        componentFactory: React.PropTypes.object.isRequired,
        entityType: React.PropTypes.string.isRequired,
        layoutName: React.PropTypes.string,
        _itemDisplayName: React.PropTypes.oneOfType([
            React.PropTypes.string,
            React.PropTypes.func
        ])
    },

    getInitialState: () => {
        return {
            editingItemIndex: null
        }
    },

    handleAdd: function () {
        let newState = _.extend({}, this.state);
        newState.editingItemIndex = -1;
        this.setState(newState);

        // This "return false" is so a "#" doesn't get added to the URL when this method is triggered from a "a" element.
        return false;
    },

    handleSaveItem: function (model) {
        if (this.props.onChange) {
            let value = this.props.value;

            if (!this.state.editingItemIndex === null || this.state.editingItemIndex === undefined) {
                // I'm not sure if an exception should be thrown here. It should probably be. However, there could
                // be a situation that could change it. Needs further analysis.
                return;
            }
            else if (this.state.editingItemIndex == -1) {
                value.push(model);
            }
            else {
                value[this.state.editingItemIndex] = model;
            }

            this.props.onChange({id: this.props.id, value: value});
            this.handleModalClose();
        }
    },

    handleModalClose: function () {
        let newState = _.extend({}, this.state);
        newState.editingItemIndex = null;
        this.setState(newState);
    },

    handleItemAction: function (index, eventKey) {
        let value = this.props.value;

        if (eventKey == "edit") {
            let newState = _.extend({}, this.state);
            newState.editingItemIndex = index;
            this.setState(newState);
            return;
        }

        switch (eventKey) {
            case "remove":
                value.splice(index, 1);
                break;
            case 'moveUp':
                arrayHelper.move(value, index, index - 1);
                break;
            case 'moveDown':
                arrayHelper.move(value, index, index + 1);
                break;
            case 'moveFirst':
                arrayHelper.move(value, index, 0);
                break;
            case 'moveLast':
                arrayHelper.move(value, index, value.length - 1);
                break;
        }
        if (this.props.onChange) {
            this.props.onChange({id: this.props.id, value: value});
        }
    },

    render: function () {

        let items;
        if (this.props._itemDisplayName) {
            let itemDisplayNameFunction = functionHelper.getFunction(this.props._itemDisplayName);
            items = this.props.value.map(i => {
                return {
                    text: itemDisplayNameFunction(i)
                };
            });
        }
        else {
            items = this.props.value.map(i => {
                return {
                    text: i.toString()
                };
            });
        }

        let showModal = this.state.editingItemIndex != null && this.state.editingItemIndex != undefined;
        let modalModel;
        if (this.state.editingItemIndex == -1) {
            modalModel = {}
        }
        else if (this.state.editingItemIndex >= 0) {
            modalModel = this.props.value[this.state.editingItemIndex];
        }
        else {
            modalModel = null;
        }

        return (

                <FormGroup displayName={this.props.displayName}>
                    {items.length
                        ? <ArrayGrid items={items} onItemAction={this.handleItemAction}/>
                        : <Alert bsStyle="warning">
                        This array is empty. Consideradding a new item.
                    </Alert>}

                </FormGroup>

                        <GlyphButton glyph="plus" text={this.props.addText ? this.props.addText : "Add" }
                                     onClick={this.handleAdd}/>

                <MetaFormModal
                    show={showModal}
                    schema={this.props.schema}
                    componentFactory={this.props.componentFactory}
                    model={modalModel}
                    entityType={this.props.entityType}
                    layoutName={this.props.layoutName}
                    onSave={this.handleSaveItem}
                    onCancel={this.handleModalClose}
                />

        );
    }
});

export default ArrayGridContainer;<commit_msg>MetaFormModal: Fixing PropTypes.. Some other warnings are gone<commit_after>import React from 'react';
import Alert from 'react-bootstrap/lib/Alert.js';
import metadataProvider from '../../lib/metadataProvider.js';
import MetaFormGroup from '../groupComponents/MetaFormGroup.js';
import GlyphButton from '../GlyphButton.js';
import Glyphicon from 'react-bootstrap/lib/Glyphicon.js';
import DropdownButton from 'react-bootstrap/lib/DropdownButton.js';
import MenuItem from 'react-bootstrap/lib/MenuItem.js';
import Dropdown from 'react-bootstrap/lib/Dropdown.js';
import arrayHelper from'../../lib/helpers/arrayHelper.js';
import Modal from 'react-bootstrap/lib/Modal.js';
import Button from 'react-bootstrap/lib/Button.js';
import MetaForm from '../../MetaForm.js';
import Table from 'react-bootstrap/lib/Table.js';
import FormGroup from './FormGroup.js';
import functionHelper from '../../lib/helpers/functionHelper.js';
import _ from 'underscore';

const MetaFormModal = React.createClass({

    propTypes: {
        schema: React.PropTypes.object.isRequired,
        componentFactory: React.PropTypes.object.isRequired,
        model: React.PropTypes.object,
        entityType: React.PropTypes.string.isRequired,
        layoutName: React.PropTypes.string.isRequired,
        onSave: React.PropTypes.func,
        onCancel: React.PropTypes.func
    },

    handleSave: function (model) {
        if (this.props.onSave) {
            this.props.onSave(model);
        }
    },

    handleCancel: function () {
        if (this.props.onCancel) {
            this.props.onCancel();
        }
    },

    getDefaultProps: function () {
        return {
            show: false
        }
    },

    render: function () {
        return <Modal show={this.props.show} bsSize="large">
            <Modal.Header>
                <Modal.Title>Modal heading</Modal.Title>
            </Modal.Header>
            <Modal.Body>
                <MetaForm
                    schema={this.props.schema}
                    ref="mf"
                    entityName={this.props.entityType}
                    layoutName={this.props.layoutName}
                    componentFactory={this.props.componentFactory}
                    model={this.props.model}
                    onSave={this.handleSave}
                    onCancel={this.handleCancel}
                />
            </Modal.Body>
        </Modal>
    }
});

const ArrayGridRow = React.createClass({

    propTypes: {
        index: React.PropTypes.number.isRequired,
        onAction: React.PropTypes.func
    },

    handleAction: function (e, eventKey) {
        if (this.props.onAction) {
            this.props.onAction(this.props.index, eventKey)
        }
    },

    render: function () {
        return

                {this.props.children}

                <Dropdown pullRight onSelect={this.handleAction}>
                    <Dropdown.Toggle noCaret>
                        <Glyphicon glyph="cog"/>
                    </Dropdown.Toggle>
                    <Dropdown.Menu >
                        <MenuItem eventKey="edit">
                            <Glyphicon glyph="pencil"/>
                            Edit
                        </MenuItem>
                        <MenuItem eventKey="remove">
                            <Glyphicon glyph="remove" className="text-danger"/>
                            Remove

                        </MenuItem>
                        <MenuItem divider/>
                        <MenuItem eventKey="moveUp">
                            <Glyphicon glyph="chevron-up"/>
                            Move up
                        </MenuItem>
                        <MenuItem eventKey="moveDown">
                            <Glyphicon glyph="chevron-down"/>
                            Move down

                        </MenuItem>
                        <MenuItem divider/>
                        <MenuItem eventKey="moveFirst">
                            <Glyphicon glyph="chevron-up"/>
                            Move first

                        </MenuItem>
                        <MenuItem eventKey="moveLast">
                            <Glyphicon glyph="chevron-down"/>
                            Move last

                        </MenuItem>
                    </Dropdown.Menu>
                </Dropdown>

;
    }
});

const ArrayGrid = React.createClass({

    handleItemAction: function (index, eventKey) {
        if (this.props.onItemAction) {
            this.props.onItemAction(index, eventKey);
        }
    },

    render: function () {

        let rows = this.props.items.map((item, index) => {
            return <ArrayGridRow index={index} onAction={this.handleItemAction}>
                {item.text}
            </ArrayGridRow>;
        });

        return

                {rows}

;
    }
});

const ArrayGridContainer = React.createClass({

    propTypes: {
        name: React.PropTypes.string.isRequired,
        componentFactory: React.PropTypes.object.isRequired,
        entityType: React.PropTypes.string.isRequired,
        layoutName: React.PropTypes.string,
        _itemDisplayName: React.PropTypes.oneOfType([
            React.PropTypes.string,
            React.PropTypes.func
        ])
    },

    getInitialState: () => {
        return {
            editingItemIndex: null
        }
    },

    handleAdd: function () {
        let newState = _.extend({}, this.state);
        newState.editingItemIndex = -1;
        this.setState(newState);

        // This "return false" is so a "#" doesn't get added to the URL when this method is triggered from a "a" element.
        return false;
    },

    handleSaveItem: function (model) {
        if (this.props.onChange) {
            let value = this.props.value;

            if (!this.state.editingItemIndex === null || this.state.editingItemIndex === undefined) {
                // I'm not sure if an exception should be thrown here. It should probably be. However, there could
                // be a situation that could change it. Needs further analysis.
                return;
            }
            else if (this.state.editingItemIndex == -1) {
                value.push(model);
            }
            else {
                value[this.state.editingItemIndex] = model;
            }

            this.props.onChange({id: this.props.id, value: value});
            this.handleModalClose();
        }
    },

    handleModalClose: function () {
        let newState = _.extend({}, this.state);
        newState.editingItemIndex = null;
        this.setState(newState);
    },

    handleItemAction: function (index, eventKey) {
        let value = this.props.value;

        if (eventKey == "edit") {
            let newState = _.extend({}, this.state);
            newState.editingItemIndex = index;
            this.setState(newState);
            return;
        }

        switch (eventKey) {
            case "remove":
                value.splice(index, 1);
                break;
            case 'moveUp':
                arrayHelper.move(value, index, index - 1);
                break;
            case 'moveDown':
                arrayHelper.move(value, index, index + 1);
                break;
            case 'moveFirst':
                arrayHelper.move(value, index, 0);
                break;
            case 'moveLast':
                arrayHelper.move(value, index, value.length - 1);
                break;
        }
        if (this.props.onChange) {
            this.props.onChange({id: this.props.id, value: value});
        }
    },

    render: function () {

        let items;
        if (this.props._itemDisplayName) {
            let itemDisplayNameFunction = functionHelper.getFunction(this.props._itemDisplayName);
            items = this.props.value.map(i => {
                return {
                    text: itemDisplayNameFunction(i)
                };
            });
        }
        else {
            items = this.props.value.map(i => {
                return {
                    text: i.toString()
                };
            });
        }

        let showModal = this.state.editingItemIndex != null && this.state.editingItemIndex != undefined;
        let modalModel;
        if (this.state.editingItemIndex == -1) {
            modalModel = {}
        }
        else if (this.state.editingItemIndex >= 0) {
            modalModel = this.props.value[this.state.editingItemIndex];
        }
        else {
            modalModel = null;
        }

        return (

                <FormGroup displayName={this.props.displayName}>
                    {items.length
                        ? <ArrayGrid items={items} onItemAction={this.handleItemAction}/>
                        : <Alert bsStyle="warning">
                        This array is empty. Consideradding a new item.
                    </Alert>}

                </FormGroup>

                        <GlyphButton glyph="plus" text={this.props.addText ? this.props.addText : "Add" }
                                     onClick={this.handleAdd}/>

                <MetaFormModal
                    show={showModal}
                    schema={this.props.schema}
                    componentFactory={this.props.componentFactory}
                    model={modalModel}
                    entityType={this.props.entityType}
                    layoutName={this.props.layoutName}
                    onSave={this.handleSaveItem}
                    onCancel={this.handleModalClose}
                />

        );
    }
});

export default ArrayGridContainer;<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { withStyles } from 'material-ui/styles';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import AddIcon from 'material-ui-icons/Add';
import Snackbar from 'material-ui/Snackbar';

const styles = theme => ({
  root: {
    position: 'relative',
    overflow: 'hidden',
  },
  appFrame: {
    width: 360,
    height: 360,
    backgroundColor: theme.palette.background.paper,
  },
  menuButton: {
    marginLeft: -12,
    marginRight: 20,
  },
  button: {
    marginBottom: theme.spacing.unit,
  },
  fab: {
    position: 'absolute',
    bottom: theme.spacing.unit * 2,
    right: theme.spacing.unit * 2,
  },
  fabMoveUp: {
    transform: 'translate3d(0, -46px, 0)',
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.enteringScreen,
      easing: theme.transitions.easing.easeOut,
    }),
  },
  fabMoveDown: {
    transform: 'translate3d(0, 0, 0)',
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.leavingScreen,
      easing: theme.transitions.easing.sharp,
    }),
  },
  snackbar: {
    position: 'absolute',
  },
  snackbarContent: {
    width: 360,
  },
});

class FabIntegrationSnackbar extends React.Component {
  state = {
    open: false,
  };

  handleClick = () => {
    this.setState({ open: true });
  };

  handleClose = () => {
    this.setState({ open: false });
  };

  render() {
    const { classes } = this.props;
    const { open } = this.state;
    const fabClassName = classNames(classes.fab, open ? classes.fabMoveUp : classes.fabMoveDown);

    return (

        <Button className={classes.button} onClick={this.handleClick}>
          Open snackbar
        </Button>

          <AppBar position="static" color="primary">
            <Toolbar>
              <IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
                <MenuIcon />
              </IconButton>
              <Typography variant="title" color="inherit">
                Out of my way!
              </Typography>
            </Toolbar>
          </AppBar>
          <Button variant="fab" color="secondary" className={fabClassName}>
            <AddIcon />
          </Button>
          <Snackbar
            open={open}
            autoHideDuration={4000}
            onClose={this.handleClose}
            SnackbarContentProps={{
              'aria-describedby': 'snackbar-fab-message-id',
              className: classes.snackbarContent,
            }}
            message={Archived}
            action={
              <Button color="secondary" size="small" onClick={this.handleClose}>
                Undo
              </Button>
            }
            className={classes.snackbar}
          />

    );
  }
}

FabIntegrationSnackbar.propTypes = {
  classes: PropTypes.object.isRequired,
};

export default withStyles(styles)(FabIntegrationSnackbar);
<commit_msg>[docs] Fix Snackbar FAB example undo button text color<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { withStyles } from 'material-ui/styles';
import AppBar from 'material-ui/AppBar';
import Toolbar from 'material-ui/Toolbar';
import IconButton from 'material-ui/IconButton';
import MenuIcon from 'material-ui-icons/Menu';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import AddIcon from 'material-ui-icons/Add';
import Snackbar from 'material-ui/Snackbar';

const styles = theme => ({
  root: {
    position: 'relative',
    overflow: 'hidden',
  },
  appFrame: {
    width: 360,
    height: 360,
    backgroundColor: theme.palette.background.paper,
  },
  menuButton: {
    marginLeft: -12,
    marginRight: 20,
  },
  button: {
    marginBottom: theme.spacing.unit,
  },
  fab: {
    position: 'absolute',
    bottom: theme.spacing.unit * 2,
    right: theme.spacing.unit * 2,
  },
  fabMoveUp: {
    transform: 'translate3d(0, -46px, 0)',
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.enteringScreen,
      easing: theme.transitions.easing.easeOut,
    }),
  },
  fabMoveDown: {
    transform: 'translate3d(0, 0, 0)',
    transition: theme.transitions.create('transform', {
      duration: theme.transitions.duration.leavingScreen,
      easing: theme.transitions.easing.sharp,
    }),
  },
  snackbar: {
    position: 'absolute',
  },
  snackbarContent: {
    width: 360,
  },
});

class FabIntegrationSnackbar extends React.Component {
  state = {
    open: false,
  };

  handleClick = () => {
    this.setState({ open: true });
  };

  handleClose = () => {
    this.setState({ open: false });
  };

  render() {
    const { classes } = this.props;
    const { open } = this.state;
    const fabClassName = classNames(classes.fab, open ? classes.fabMoveUp : classes.fabMoveDown);

    return (

        <Button className={classes.button} onClick={this.handleClick}>
          Open snackbar
        </Button>

          <AppBar position="static" color="primary">
            <Toolbar>
              <IconButton className={classes.menuButton} color="inherit" aria-label="Menu">
                <MenuIcon />
              </IconButton>
              <Typography variant="title" color="inherit">
                Out of my way!
              </Typography>
            </Toolbar>
          </AppBar>
          <Button variant="fab" color="secondary" className={fabClassName}>
            <AddIcon />
          </Button>
          <Snackbar
            open={open}
            autoHideDuration={4000}
            onClose={this.handleClose}
            SnackbarContentProps={{
              'aria-describedby': 'snackbar-fab-message-id',
              className: classes.snackbarContent,
            }}
            message={Archived}
            action={
              <Button color="inherit" size="small" onClick={this.handleClose}>
                Undo
              </Button>
            }
            className={classes.snackbar}
          />

    );
  }
}

FabIntegrationSnackbar.propTypes = {
  classes: PropTypes.object.isRequired,
};

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


<commit_before>(function($){

  function computeBoundsAndJustify(ctx, nodes, width, height) {
    var borderx = 20, bordery = 20, r = 7, h = 13;
    ctx.font = '13px Arial';
    var bws = [], xs = [], ys = [];
    $.each(nodes, function (index, node) {
      var w = ctx.measureText(node.label).width;
      node.labelWidth = w;
      node.bw = w + r + r;
      node.bh = h + r + r;
      node.br = r;
      bws.push(node.bw);
      xs.push(node.x);
      ys.push(node.y);
    });
    var maxbw = Math.max.apply(Math, bws);
    var maxbh = h + r + r;
    var minx = Math.min.apply(Math, xs);
    var maxx = Math.max.apply(Math, xs);
    var miny = Math.min.apply(Math, ys);
    var maxy = Math.max.apply(Math, ys);
    var bx = borderx + maxbw / 2;
    var by = bordery + maxbh / 2;
    if(minx == maxx) {
      minx -= 1;
      maxx += 1;
    }
    if(miny == maxy) {
      miny -= 1;
      maxy += 1;
    }
    $.each(nodes, function (index, node) {
      node.x = bx + (node.x - minx) * (width - 2 * bx) / (maxx - minx);
      node.y = by + (node.y - miny) * (height - 2 * by) / (maxy - miny);
      node.bx = node.x - node.bw / 2;
      node.by = node.y - node.bh / 2;
    });
  }

  function drawNode(ctx, node) {
    ctx.font = '13px Arial';
    ctx.textBaseline = 'middle';
    var x = node.bx, y = node.by, r = node.br, w = node.bw, h = node.bh;
    ctx.fillText(node.label, x + r, y + h / 2);
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h - r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
    ctx.stroke();
  }

  function lerp(a, b, t) {
      return a + t * (b - a);
  }

  function evalBezierCurve(p1, p2, p3, p4, t) {
    var p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t), p34 = lerp(p3, p4, t);
    var p123 = lerp(p12, p23, t), p234 = lerp(p23, p34, t);
    return {pos: lerp(p123, p234, t), dir: p234 - p123};
  }

  function drawEdge(ctx, edge) {
    var p = [{x: edge.source.bx + edge.source.bw * 2 / 3,
              y: edge.source.by - 1,
              dx: 0, dy: -1},
             {x: edge.source.bx + edge.source.bw / 3,
              y: edge.source.by + edge.source.bh + 1,
              dx: 0, dy: +1},
             {x: edge.source.bx - 1,
              y: edge.source.by + edge.source.bh * 2 / 3,
              dx: -1, dy: 0},
             {x: edge.source.bx + edge.source.bw + 1,
              y: edge.source.by + edge.source.bh / 3,
              dx: +1, dy: 0},
             {x: edge.target.bx + edge.target.bw / 3,
              y: edge.target.by - 1,
              dx: 0, dy: -1},
             {x: edge.target.bx + edge.target.bw * 2 / 3,
              y: edge.target.by + edge.target.bh + 1,
              dx: 0, dy: +1},
             {x: edge.target.bx - 1,
              y: edge.target.by + edge.target.bh / 3,
              dx: -1, dy: 0},
             {x: edge.target.bx + edge.target.bw + 1,
              y: edge.target.by + edge.target.bh * 2 / 3,
              dx: +1, dy: 0}];
    var pIndexByDist = {}, dist = [];
    for (var i = 0; i < 4; i++) {
      for (var j = 4; j < 8; j++) {
        var distx = Math.abs(p[i].x - p[j].x),
            disty = Math.abs(p[i].y - p[j].y);
        dist.push(distx + disty);
        pIndexByDist[dist[dist.length - 1]] = [i, j];
      }
    }
    var pIndex = (edge.source === edge.target || dist.length === 0) ?
                 [0, 7] : pIndexByDist[Math.min.apply(Math, dist)];
    var p1 = p[pIndex[0]], p4 = p[pIndex[1]];
    var hs = 10, hw = 4;
    var x1 = p1.x, y1 = p1.y, x4 = p4.x + hs * p4.dx, y4 = p4.y + hs * p4.dy;
    var adx = Math.max(Math.abs(x1 - x4) / 3, 20),
        ady = Math.max(Math.abs(y1 - y4) / 3, 20);
    var x2 = x1 + p1.dx * adx, y2 = y1 + p1.dy * ady,
        x3 = x4 + p4.dx * adx, y3 = y4 + p4.dy * ady;

    ctx.strokeStyle = edge.color;
    ctx.fillStyle = edge.color;
    ctx.lineWidth = 2;

    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.bezierCurveTo(x2, y2, x3, y3, x4, y4);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(p4.x, p4.y)
    ctx.lineTo(x4 + hw * p4.dy, y4 - hw * p4.dx);
    ctx.lineTo(x4 - hw * p4.dy, y4 + hw * p4.dx);
    ctx.fill();

    var labelLayout = {};
    edge.labelLayout = labelLayout;
    if (x1 < x4) {
      labelLayout.x = evalBezierCurve(x1, x2, x3, x4, 3 / 5);
      labelLayout.y = evalBezierCurve(y1, y2, y3, y4, 3 / 5);
      labelLayout.textBaseline = 'bottom';
      labelLayout.margin = -2;
    } else {
      labelLayout.x = evalBezierCurve(x4, x3, x2, x1, 2 / 5);
      labelLayout.y = evalBezierCurve(y4, y3, y2, y1, 2 / 5);
      labelLayout.textBaseline = 'top';
      labelLayout.margin = 2;
    }
  }

  function drawEdgeLabel(ctx, edge) {
    ctx.save();

    var l = edge.labelLayout;
    var dx = l.x.dir, dy = l.y.dir;
    var d = Math.sqrt(dx * dx + dy * dy)
    dx /= d;
    dy /= d;
    ctx.textBaseline = l.textBaseline;
    ctx.font = '12px Arial';
    var measured = ctx.measureText(edge.label);

    ctx.setTransform(dx, dy, -dy, dx, l.x.pos, l.y.pos);

    // Halo effect
    ctx.save();
    ctx.shadowBlur = 5;
    ctx.shadowColor = 'White';
    ctx.fillStyle = 'White';
    for (var i = 0; i < 5; i++)
      ctx.fillText(edge.label, -measured.width / 2, l.margin);
    ctx.restore();

    ctx.fillStyle = edge.color;
    ctx.fillText(edge.label, -measured.width / 2, l.margin);

    ctx.restore();
  };

  function hexColor(r, g, b) {
    r = (r < 16 ? '0' : '') + Math.floor(r).toString(16);
    g = (g < 16 ? '0' : '') + Math.floor(g).toString(16);
    b = (b < 16 ? '0' : '') + Math.floor(b).toString(16);
    return String.concat('#', r, g, b);
  }

  function saturatedColor(r, g, b) {
    var avg = (r + g + b) / 3;
    if (r < avg)
      r /= 2;
    if (g < avg)
      g /= 2;
    if (b < avg)
      b /= 2;
    return hexColor(r, g, b);
  }

  var colors = ['Black', 'Blue', 'Red', 'Green', 'Fuchsia'];

  function getActionColor(i) {
    if (i < colors.length)
      return colors[i];
    return saturatedColor(((i+70) * 30269) % 253, ((i+71) * 30307) % 253,
                          ((i+72) * 30323) % 253);
  }

  function layoutCircular(graph) {
    $.each(graph.nodes, function (index, node) {
      var radian = index * 2 * Math.PI / graph.nodes.length;
      node.x = Math.cos(radian);
      node.y = Math.sin(radian);
    });
  }

  $(document).ready(function() {
    $('.trac-workflow-graph').each(function (index) {
      var data = window['graph_' + this.id.slice(-12)];
      var width = data.width, height = data.height;
      var nodes = [], actions = [], edges = [];
      for (var i = 0; i < data.nodes.length; ++i)
        nodes.push({label: data.nodes[i]});
      for (var i = 0; i < data.actions.length; ++i)
        actions.push({label: data.actions[i], color: getActionColor(i)});
      for (var i = 0; i < data.edges.length; ++i) {
        var edge = data.edges[i];
        var sourceindex = edge[0], targetindex = edge[1], action = edge[2];
        action = actions[action];
        edges.push({label: action.label, color: action.color,
                    sourceindex: sourceindex, targetindex: targetindex,
                    source: nodes[sourceindex], target: nodes[targetindex]});
      }

      var canvas = $('<canvas>').css({width: width, height: height})[0];
      canvas.width = $(canvas).width();
      canvas.height = $(canvas).height();
      if (typeof(G_vmlCanvasManager) != 'undefined')
        canvas = G_vmlCanvasManager.initElement(canvas);
      $(this).replaceWith(canvas);
      var ctx = canvas.getContext('2d');

      if (nodes.length == 0)
        return;

      var graph = {nodes: nodes, edges: edges};
      layoutCircular(graph);
      computeBoundsAndJustify(ctx, nodes, width, height);

      $.each(nodes, function (index, node) {
        drawNode(ctx, node);
      });
      $.each(edges, function (index, edge) {
        drawEdge(ctx, edge);
      });
      $.each(edges, function (index, edge) {
        drawEdgeLabel(ctx, edge);
      });
    });
  });

})(jQuery);
<commit_msg>1.0dev: Fix `Workflow` macro to work with browsers other than Firefox. (Followup to [11196].) (`String.concat()` is not portable Javascript.)<commit_after>(function($){

  function computeBoundsAndJustify(ctx, nodes, width, height) {
    var borderx = 20, bordery = 20, r = 7, h = 13;
    ctx.font = '13px Arial';
    var bws = [], xs = [], ys = [];
    $.each(nodes, function (index, node) {
      var w = ctx.measureText(node.label).width;
      node.labelWidth = w;
      node.bw = w + r + r;
      node.bh = h + r + r;
      node.br = r;
      bws.push(node.bw);
      xs.push(node.x);
      ys.push(node.y);
    });
    var maxbw = Math.max.apply(Math, bws);
    var maxbh = h + r + r;
    var minx = Math.min.apply(Math, xs);
    var maxx = Math.max.apply(Math, xs);
    var miny = Math.min.apply(Math, ys);
    var maxy = Math.max.apply(Math, ys);
    var bx = borderx + maxbw / 2;
    var by = bordery + maxbh / 2;
    if(minx == maxx) {
      minx -= 1;
      maxx += 1;
    }
    if(miny == maxy) {
      miny -= 1;
      maxy += 1;
    }
    $.each(nodes, function (index, node) {
      node.x = bx + (node.x - minx) * (width - 2 * bx) / (maxx - minx);
      node.y = by + (node.y - miny) * (height - 2 * by) / (maxy - miny);
      node.bx = node.x - node.bw / 2;
      node.by = node.y - node.bh / 2;
    });
  }

  function drawNode(ctx, node) {
    ctx.font = '13px Arial';
    ctx.textBaseline = 'middle';
    var x = node.bx, y = node.by, r = node.br, w = node.bw, h = node.bh;
    ctx.fillText(node.label, x + r, y + h / 2);
    ctx.lineWidth = 2;
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
    ctx.lineTo(x + w, y + h - r);
    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
    ctx.lineTo(x + r, y + h);
    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
    ctx.lineTo(x, y + r);
    ctx.quadraticCurveTo(x, y, x + r, y);
    ctx.closePath();
    ctx.stroke();
  }

  function lerp(a, b, t) {
      return a + t * (b - a);
  }

  function evalBezierCurve(p1, p2, p3, p4, t) {
    var p12 = lerp(p1, p2, t), p23 = lerp(p2, p3, t), p34 = lerp(p3, p4, t);
    var p123 = lerp(p12, p23, t), p234 = lerp(p23, p34, t);
    return {pos: lerp(p123, p234, t), dir: p234 - p123};
  }

  function drawEdge(ctx, edge) {
    var p = [{x: edge.source.bx + edge.source.bw * 2 / 3,
              y: edge.source.by - 1,
              dx: 0, dy: -1},
             {x: edge.source.bx + edge.source.bw / 3,
              y: edge.source.by + edge.source.bh + 1,
              dx: 0, dy: +1},
             {x: edge.source.bx - 1,
              y: edge.source.by + edge.source.bh * 2 / 3,
              dx: -1, dy: 0},
             {x: edge.source.bx + edge.source.bw + 1,
              y: edge.source.by + edge.source.bh / 3,
              dx: +1, dy: 0},
             {x: edge.target.bx + edge.target.bw / 3,
              y: edge.target.by - 1,
              dx: 0, dy: -1},
             {x: edge.target.bx + edge.target.bw * 2 / 3,
              y: edge.target.by + edge.target.bh + 1,
              dx: 0, dy: +1},
             {x: edge.target.bx - 1,
              y: edge.target.by + edge.target.bh / 3,
              dx: -1, dy: 0},
             {x: edge.target.bx + edge.target.bw + 1,
              y: edge.target.by + edge.target.bh * 2 / 3,
              dx: +1, dy: 0}];
    var pIndexByDist = {}, dist = [];
    for (var i = 0; i < 4; i++) {
      for (var j = 4; j < 8; j++) {
        var distx = Math.abs(p[i].x - p[j].x),
            disty = Math.abs(p[i].y - p[j].y);
        dist.push(distx + disty);
        pIndexByDist[dist[dist.length - 1]] = [i, j];
      }
    }
    var pIndex = (edge.source === edge.target || dist.length === 0) ?
                 [0, 7] : pIndexByDist[Math.min.apply(Math, dist)];
    var p1 = p[pIndex[0]], p4 = p[pIndex[1]];
    var hs = 10, hw = 4;
    var x1 = p1.x, y1 = p1.y, x4 = p4.x + hs * p4.dx, y4 = p4.y + hs * p4.dy;
    var adx = Math.max(Math.abs(x1 - x4) / 3, 20),
        ady = Math.max(Math.abs(y1 - y4) / 3, 20);
    var x2 = x1 + p1.dx * adx, y2 = y1 + p1.dy * ady,
        x3 = x4 + p4.dx * adx, y3 = y4 + p4.dy * ady;

    ctx.strokeStyle = edge.color;
    ctx.fillStyle = edge.color;
    ctx.lineWidth = 2;

    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.bezierCurveTo(x2, y2, x3, y3, x4, y4);
    ctx.stroke();

    ctx.beginPath();
    ctx.moveTo(p4.x, p4.y)
    ctx.lineTo(x4 + hw * p4.dy, y4 - hw * p4.dx);
    ctx.lineTo(x4 - hw * p4.dy, y4 + hw * p4.dx);
    ctx.fill();

    var labelLayout = {};
    edge.labelLayout = labelLayout;
    if (x1 < x4) {
      labelLayout.x = evalBezierCurve(x1, x2, x3, x4, 3 / 5);
      labelLayout.y = evalBezierCurve(y1, y2, y3, y4, 3 / 5);
      labelLayout.textBaseline = 'bottom';
      labelLayout.margin = -2;
    } else {
      labelLayout.x = evalBezierCurve(x4, x3, x2, x1, 2 / 5);
      labelLayout.y = evalBezierCurve(y4, y3, y2, y1, 2 / 5);
      labelLayout.textBaseline = 'top';
      labelLayout.margin = 2;
    }
  }

  function drawEdgeLabel(ctx, edge) {
    ctx.save();

    var l = edge.labelLayout;
    var dx = l.x.dir, dy = l.y.dir;
    var d = Math.sqrt(dx * dx + dy * dy)
    dx /= d;
    dy /= d;
    ctx.textBaseline = l.textBaseline;
    ctx.font = '12px Arial';
    var measured = ctx.measureText(edge.label);

    ctx.setTransform(dx, dy, -dy, dx, l.x.pos, l.y.pos);

    // Halo effect
    ctx.save();
    ctx.shadowBlur = 5;
    ctx.shadowColor = 'White';
    ctx.fillStyle = 'White';
    for (var i = 0; i < 5; i++)
      ctx.fillText(edge.label, -measured.width / 2, l.margin);
    ctx.restore();

    ctx.fillStyle = edge.color;
    ctx.fillText(edge.label, -measured.width / 2, l.margin);

    ctx.restore();
  };

  function hexColor(r, g, b) {
    return '#' + (r < 16 ? '0' : '') + Math.floor(r).toString(16) +
                 (g < 16 ? '0' : '') + Math.floor(g).toString(16) +
                 (b < 16 ? '0' : '') + Math.floor(b).toString(16);
  }

  function saturatedColor(r, g, b) {
    var avg = (r + g + b) / 3;
    if (r < avg)
      r /= 2;
    if (g < avg)
      g /= 2;
    if (b < avg)
      b /= 2;
    return hexColor(r, g, b);
  }

  var colors = ['Black', 'Blue', 'Red', 'Green', 'Fuchsia'];

  function getActionColor(i) {
    if (i < colors.length)
      return colors[i];
    return saturatedColor(((i+70) * 30269) % 253, ((i+71) * 30307) % 253,
                          ((i+72) * 30323) % 253);
  }

  function layoutCircular(graph) {
    $.each(graph.nodes, function (index, node) {
      var radian = index * 2 * Math.PI / graph.nodes.length;
      node.x = Math.cos(radian);
      node.y = Math.sin(radian);
    });
  }

  $(document).ready(function() {
    $('.trac-workflow-graph').each(function (index) {
      var data = window['graph_' + this.id.slice(-12)];
      var width = data.width, height = data.height;
      var nodes = [], actions = [], edges = [];
      for (var i = 0; i < data.nodes.length; ++i)
        nodes.push({label: data.nodes[i]});
      for (var i = 0; i < data.actions.length; ++i)
        actions.push({label: data.actions[i], color: getActionColor(i)});
      for (var i = 0; i < data.edges.length; ++i) {
        var edge = data.edges[i];
        var sourceindex = edge[0], targetindex = edge[1], action = edge[2];
        action = actions[action];
        edges.push({label: action.label, color: action.color,
                    sourceindex: sourceindex, targetindex: targetindex,
                    source: nodes[sourceindex], target: nodes[targetindex]});
      }

      var canvas = $('<canvas>').css({width: width, height: height})[0];
      canvas.width = $(canvas).width();
      canvas.height = $(canvas).height();
      if (typeof(G_vmlCanvasManager) != 'undefined')
        canvas = G_vmlCanvasManager.initElement(canvas);
      $(this).replaceWith(canvas);
      var ctx = canvas.getContext('2d');

      if (nodes.length == 0)
        return;

      var graph = {nodes: nodes, edges: edges};
      layoutCircular(graph);
      computeBoundsAndJustify(ctx, nodes, width, height);

      $.each(nodes, function (index, node) {
        drawNode(ctx, node);
      });
      $.each(edges, function (index, edge) {
        drawEdge(ctx, edge);
      });
      $.each(edges, function (index, edge) {
        drawEdgeLabel(ctx, edge);
      });
    });
  });

})(jQuery);
<|endoftext|>


<commit_before>nb.define('checkbox', {
    events: {
        'change input': 'onchange'
    },

    _onCheckboxChecked: function(evtName, params) {
        if (params.name == this.getName() && params.value != this.getValue()) {
            this.uncheck();
        }
    },

    onchange: function() {
        if (this.$control.prop('checked')) {
            this.check();
        } else {
            this.uncheck();
        }
    },

    /**
     * Init a checkbox
     * @fires 'nb-inited'
     */

    oninit: function() {
        this.$control = this.$node.find('input[type]');
        this._isChecked = this.$control.prop('checked');

        this.$control.on('click', function(evt) {
            evt.stopPropagation();
        });

        if (!this._isChecked) {
            this.$control.prop('indeterminate', true);
        }

        nb.on('checkbox:checked', $.proxy(this._onCheckboxChecked, this));

        this.trigger('nb-inited', this);
    },

    /**
     * Override base getType()
     * @returns {String} — type of control
     */

    getType: function() {
        return this.$control.attr('type');
    },

    /**
     * Return check state of the checkbox or radio
     * @returns {Boolean}
     */
    isChecked: function() {
        return this._isChecked;
    },

    /**
     * Checking checkbox or radio
     * @fires 'nb-checked', 'nb-changed'
     * @returns {Object} nb.block
     */
    check: function() {
        if (!this.isEnabled()) {
            return this;
        }

        var isChecked = this.isChecked();

        this.$control.prop({
            'indeterminate': false,
            'checked': true
        });

        this._isChecked = true;
        this.trigger('nb-checked', this);

        if (!isChecked) {
            this.trigger('nb-changed', this);
        }

        nb.trigger('checkbox:checked', {
            name: this.getName(),
            value: this.getValue()
        });

        return this;
    },

    /**
     * Unchecking checkbox or radio
     * @fires 'nb-unchecked', 'nb-changed'
     * @returns {Object} nb.block
     */
    uncheck: function() {
        if (!this.isEnabled()) {
            return this;
        }

        var isChecked = this.isChecked();

        this.$control.prop({
            'indeterminate': false,
            'checked': false
        });

        this._isChecked = false;
        this.trigger('nb-unchecked', this);

        if (isChecked) {
            this.trigger('nb-changed', this);
        }

        return this;
    },

    /**
     * Toggle to the opposite state checkbox or radio
     * @fires 'change'
     * @return {Object} nb.block
     */
    toggle: function() {
        if (!this.isEnabled()) {
            return this;
        }

        this.trigger('nb-change', this);

        if (this.isChecked()) {
            this.uncheck();
        } else {
            this.check();
        }
        return this;
    },

    /**
     * Return indeterminate state of the checkbox or radio
     * @returns {Boolean}
     */
    isIndeterminate: function() {
        return this.$control.prop('indeterminate');
    },

    /**
     * Set indeterminate state of the checkbox or radio
     * @fires 'nb-indeterminated'
     * @returns {Object} nb.block
     */
    setIndeterminate: function() {
        if (this.isEnabled()) {
            this.$control.prop('indeterminate', true);
            this.trigger('nb-indeterminated', this);
        }
        return this;
    },

    /**
     * Set determinate state of the checkbox or radio
     * @fires 'nb-determinated'
     * @returns {Object} nb.block
     */
    setDeterminate: function() {
        if (this.isEnabled()) {
            this.$control.prop('indeterminate', false);
            this.trigger('nb-determinated', this);
        }
        return this;
    },

    /**
     * Return enable state of the checkbox or radio
     * @returns {Boolean}
     */
    isEnabled: function() {
        return !this.$control.prop('disabled');
    },

    /**
     * Enable the checkbox or radio
     * @fires 'nb-enabled'
     * @returns {Object} nb.block
     */
    enable: function() {
        if (!this.isEnabled()) {
            this.$node.removeClass('is-disabled');
            this.$control.removeAttr('disabled');
            this.trigger('nb-enabled', this);
        }
        return this;
    },

    /**
     * Disable the checkbox or radio
     * @fires 'nb-disabled'
     * @returns {Object} nb.block
     */
    disable: function() {
        if (this.isEnabled()) {
            this.blur();
            this.$node.addClass('is-disabled');
            this.$control.attr('disabled', 'disabled');
            this.trigger('nb-disabled', this);
        }
        return this;
    },

    /**
     * Focus the checkbox or radio
     * @fires 'nb-focused'
     * @returns {Object} nb.block
     */
    focus: function() {
        this.$control.focus();
        this.trigger('nb-focused', this);
        return this;
    },

    /**
     * Blur the checkbox or radio
     * @fires 'nb-blured'
     * @returns {Object} nb.block
     */
    blur: function() {
        this.$control.blur();
        this.trigger('nb-blured', this);
        return this;
    },

    /**
     * Sets label of the checkbox or radio
     * @param {String|Number} label
     * @fires 'nb-label-set'
     * @returns {Object} nb.block
     */
    setLabel: function(label) {
        this.$node.find('.nb-checkbox__label').html(label);
        this.trigger('nb-label-set', this);
        return this;
    },

    /**
     * Gets label of the checkbox or radio
     * @returns {String | Number}
     */
    getLabel: function() {
        return this.$node.find('.nb-checkbox__label').html();
    },

    /**
     * Get name of the checkbox or radio
     * @returns {String|Object} name
     */
    getName: function() {
        return this.$control.prop('name');
    },

    /**
     * Set checkbox value
     * @param {String|Number} value
     * @fires 'nb-name-set'
     * @returns {Object} nb.block
     */
    setName: function(value) {
        this.$control.attr('name', value);
        this.trigger('nb-name-set', this);
        return this;
    },

    /**
     * Returns checkbox value
     * @returns {String}
     */
    getValue: function() {
        return this.$control.attr('value') || '';
    },

    /**
     * Set checkbox value
     * @param {String|Number} value
     * @fires 'nb-value-set'
     * @returns {Object} nb.block
     */
    setValue: function(value) {
        this.$control.attr('value', value);
        this.trigger('nb-value-set', this);
        return this;
    },

    /**
     * Destroy checkbox
     * @fires 'nb-destroyed'
     */
    destroy: function() {
        this.$control.off('click');
        nb.off('checkbox:checked', $.proxy(this._onCheckboxChecked, this));
        this.trigger('nb-destroyed', this);
        this.nbdestroy();
    }

}, 'base');
<commit_msg>add checkbox group<commit_after>nb.define('checkbox', {
    events: {
        'change input': 'onchange'
    },

    _onCheckboxChecked: function(evtName, params) {
        if (params.name == this.getName() && params.value != this.getValue()) {
            this.uncheck();
        }
    },

    onchange: function() {
        if (this.$control.prop('checked')) {
            this.check();
        } else {
            this.uncheck();
        }
    },

    /**
     * Init a checkbox
     * @fires 'nb-inited'
     */

    oninit: function() {
        this.$control = this.$node.find('input[type]');
        this._isChecked = this.$control.prop('checked');

        this.$control.on('click', function(evt) {
            evt.stopPropagation();
        });

        if (!this._isChecked) {
            this.$control.prop('indeterminate', true);
        }

        nb.on('checkbox:checked', $.proxy(this._onCheckboxChecked, this));

        this.trigger('nb-inited', this);
    },

    /**
     * Override base getType()
     * @returns {String} — type of control
     */

    getType: function() {
        return this.$control.attr('type');
    },

    /**
     * Return check state of the checkbox or radio
     * @returns {Boolean}
     */
    isChecked: function() {
        return this._isChecked;
    },

    /**
     * Checking checkbox or radio
     * @fires 'nb-checked', 'nb-changed'
     * @returns {Object} nb.block
     */
    check: function() {
        if (!this.isEnabled()) {
            return this;
        }

        nb.trigger('checkbox:checked', {
            name: this.getName(),
            value: this.getValue()
        });

        var isChecked = this.isChecked();

        this.$control.prop({
            'indeterminate': false,
            'checked': true
        });

        this._isChecked = true;
        this.trigger('nb-checked', this);

        if (!isChecked) {
            this.trigger('nb-changed', this);
        }

        return this;
    },

    /**
     * Unchecking checkbox or radio
     * @fires 'nb-unchecked', 'nb-changed'
     * @returns {Object} nb.block
     */
    uncheck: function() {
        if (!this.isEnabled()) {
            return this;
        }

        var isChecked = this.isChecked();

        this.$control.prop({
            'indeterminate': false,
            'checked': false
        });

        this._isChecked = false;
        this.trigger('nb-unchecked', this);

        if (isChecked) {
            this.trigger('nb-changed', this);
        }

        return this;
    },

    /**
     * Toggle to the opposite state checkbox or radio
     * @fires 'change'
     * @return {Object} nb.block
     */
    toggle: function() {
        if (!this.isEnabled()) {
            return this;
        }

        this.trigger('nb-change', this);

        if (this.isChecked()) {
            this.uncheck();
        } else {
            this.check();
        }
        return this;
    },

    /**
     * Return indeterminate state of the checkbox or radio
     * @returns {Boolean}
     */
    isIndeterminate: function() {
        return this.$control.prop('indeterminate');
    },

    /**
     * Set indeterminate state of the checkbox or radio
     * @fires 'nb-indeterminated'
     * @returns {Object} nb.block
     */
    setIndeterminate: function() {
        if (this.isEnabled()) {
            this.$control.prop('indeterminate', true);
            this.trigger('nb-indeterminated', this);
        }
        return this;
    },

    /**
     * Set determinate state of the checkbox or radio
     * @fires 'nb-determinated'
     * @returns {Object} nb.block
     */
    setDeterminate: function() {
        if (this.isEnabled()) {
            this.$control.prop('indeterminate', false);
            this.trigger('nb-determinated', this);
        }
        return this;
    },

    /**
     * Return enable state of the checkbox or radio
     * @returns {Boolean}
     */
    isEnabled: function() {
        return !this.$control.prop('disabled');
    },

    /**
     * Enable the checkbox or radio
     * @fires 'nb-enabled'
     * @returns {Object} nb.block
     */
    enable: function() {
        if (!this.isEnabled()) {
            this.$node.removeClass('is-disabled');
            this.$control.removeAttr('disabled');
            this.trigger('nb-enabled', this);
        }
        return this;
    },

    /**
     * Disable the checkbox or radio
     * @fires 'nb-disabled'
     * @returns {Object} nb.block
     */
    disable: function() {
        if (this.isEnabled()) {
            this.blur();
            this.$node.addClass('is-disabled');
            this.$control.attr('disabled', 'disabled');
            this.trigger('nb-disabled', this);
        }
        return this;
    },

    /**
     * Focus the checkbox or radio
     * @fires 'nb-focused'
     * @returns {Object} nb.block
     */
    focus: function() {
        this.$control.focus();
        this.trigger('nb-focused', this);
        return this;
    },

    /**
     * Blur the checkbox or radio
     * @fires 'nb-blured'
     * @returns {Object} nb.block
     */
    blur: function() {
        this.$control.blur();
        this.trigger('nb-blured', this);
        return this;
    },

    /**
     * Sets label of the checkbox or radio
     * @param {String|Number} label
     * @fires 'nb-label-set'
     * @returns {Object} nb.block
     */
    setLabel: function(label) {
        this.$node.find('.nb-checkbox__label').html(label);
        this.trigger('nb-label-set', this);
        return this;
    },

    /**
     * Gets label of the checkbox or radio
     * @returns {String | Number}
     */
    getLabel: function() {
        return this.$node.find('.nb-checkbox__label').html();
    },

    /**
     * Get name of the checkbox or radio
     * @returns {String|Object} name
     */
    getName: function() {
        return this.$control.prop('name');
    },

    /**
     * Set checkbox value
     * @param {String|Number} value
     * @fires 'nb-name-set'
     * @returns {Object} nb.block
     */
    setName: function(value) {
        this.$control.attr('name', value);
        this.trigger('nb-name-set', this);
        return this;
    },

    /**
     * Returns checkbox value
     * @returns {String}
     */
    getValue: function() {
        return this.$control.attr('value') || '';
    },

    /**
     * Set checkbox value
     * @param {String|Number} value
     * @fires 'nb-value-set'
     * @returns {Object} nb.block
     */
    setValue: function(value) {
        this.$control.attr('value', value);
        this.trigger('nb-value-set', this);
        return this;
    },

    /**
     * Destroy checkbox
     * @fires 'nb-destroyed'
     */
    destroy: function() {
        this.$control.off('click');
        nb.off('checkbox:checked', $.proxy(this._onCheckboxChecked, this));
        this.trigger('nb-destroyed', this);
        this.nbdestroy();
    }

}, 'base');
<|endoftext|>


<commit_before>/**
 * $Id: functions.js,v 1.8 2007-09-02 12:26:39 thorstenr Exp $
 *
 * Some JavaScript functions used in the admin backend
 *
 * @author      Thorsten Rinne <thorsten@phpmyfaq.de>
 * @author      Periklis Tsirakidis <tsirakidis@phpdevel.de>
 * @author      Matteo Scaramuccia <matteo@scaramuccia.com>
 * @author      Minoru TODA <todam@netjapan.co.jp>
 * @author      Lars Tiedemann, <php@larstiedemann.de>
 * @since       2003-11-13
 * @copyright   (c) 2003-2007 phpMyFAQ Team
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 */

function Picture(pic,title,width,height)
{
    popup = window.open(pic, title, 'width='+width+', height='+height+', toolbar=no, directories=no, status=no, scrollbars=no, resizable=yes, menubar=no');
    popup.focus();
}

function checkAll(checkBox)
{
    var v = checkBox.checked;
    var f = checkBox.form;
    for (var i = 0; i < f.elements.length; i++) {
        if (f.elements[i].type == "checkbox") {
            f.elements[i].checked = v;
            }
        }
}

function addEngine(uri, name, ext, cat)
{
    if ((typeof window.sidebar == "object") && (typeof window.sidebar.addSearchEngine == "function")) {
        window.sidebar.addSearchEngine(uri+"/"+name+".src", uri+"/images/"+name+"."+ext, name, cat);
    } else {
        alert('Mozilla Firefox, Mozilla or Netscape 6 or later is needed to install the search plugin!');
    }
}

function focusOnUsernameField()
{
    if (document.getElementById('faqusername')) {
        document.getElementById('faqusername').focus();
    }
}

function focusOnSearchField()
{
    if (document.getElementById('searchfield')) {
        document.getElementById('searchfield').focus();
    }
    if (document.getElementById('suchbegriff')) {
        document.getElementById('suchbegriff').focus();
    }
}

/**
 * showhideCategory()
 *
 * Displays or hides a div block
 *
 * @param    string
 * @return   void
 * @access   public
 * @since    2006-03-04
 * @author   Thorsten Rinne <thorsten@phpmyfaq.de>
 */
function showhideCategory(id)
{
    if (document.getElementById(id).style.display == 'none') {
        document.getElementById(id).style.display = 'block';
    } else {
        document.getElementById(id).style.display = 'none';
    }
}

/**
* deletes all options from given select-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param select
* @return void
*/
function select_clear(select)
{
    while (select.length > 0) {
        select.remove(0);
    }
}

/**
* adds an option to the given select-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param select node
* @param string
* @param text node
* @param string
* @return void
*/
function select_addOption(select, value, content, classValue)
{
    var opt;
    opt = document.createElement("option");
    opt.value = value;
    if (classValue) {
        opt.className = classValue;
    }
    opt.appendChild(content);
    select.appendChild(opt);
}

/**
* selects all list options in the select with the given ID.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param string
* @return void
*/
function select_selectAll(select_id)
{
    var select_options = document.getElementById(select_id).options;
    for (var i = 0; i < select_options.length; i++) {
        select_options[i].selected = true;
    }
}

/**
* unselects all list options in the select with the given ID.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param string
* @return void
*/
function select_unselectAll(select_id)
{
    var select_options = document.getElementById(select_id).options;
    for (var i = 0; i < select_options.length; i++) {
        select_options[i].selected = false;
    }
}

/**
 * checks all checkboxes in form with the given ID.
 *
 * @access  public
 * @author  Lars Tiedemann, <php@larstiedemann.de>
 * @param   string
 * @return  void
 */
function form_checkAll(form_id)
{
    var inputElements = document.getElementById(form_id).getElementsByTagName('input');
    for (var i = 0, ele; ele = inputElements[i]; i++) {
        if (ele.type == "checkbox") {
            ele.checked = true;
        }
    }
}

/**
 * unchecks all checkboxes in form with the given ID.
 *
 * @access  public
 * @author  Lars Tiedemann, <php@larstiedemann.de>
 * @param   string
 * @return  void
 */
function form_uncheckAll(form_id)
{
    var inputElements = document.getElementById(form_id).getElementsByTagName('input');
    for (var i = 0, ele; ele = inputElements[i]; i++) {
        if (ele.type == "checkbox") {
            ele.checked = false;
        }
    }
}

/**
* returns the text content of a child element.
*
* When having a dom structure like this:
* <item id="1">
*   <name>Item Name</name>
*   <value>Text Value</value>
* </item>
* text_getFromParent(document.getElementById(1), "name")
* would return "Item Name".
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param Object select
* @return void
*/
function text_getFromParent(parentObject, childElement)
{
    var result = "";
    result = parentObject.getElementsByTagName(childElement)[0];
    if (result) {
        if (result.childNodes.length > 1) {
            return result.childNodes[1].nodeValue;
        } else {
            if (result.firstChild) {
                return result.firstChild.nodeValue;
            } else {
                return "";
            }
        }
    } else {
        return "n/a";
    }
}

/**
* deletes all rows from given table-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param table
* @return void
*/
function table_clear(table)
{
    while (table.rows.length > 0) {
        table.deleteRow(0);
    }
}

/**
* inserts a new row into the given table at the given position.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param table
* @param int
* @param node
* @param node
* @return void
*/
function table_addRow(table, rowNumber, col1, col2)
{
    var td1;
    var td2;
    var tr;
    td1 = document.createElement("td");
    td1.appendChild(col1);
    td2 = document.createElement("td");
    td2.appendChild(col2);
    tr = table.insertRow(rowNumber);
    tr.appendChild(td1);
    tr.appendChild(td2);
}

/**
* Function to get a pretty formatted output of a variable
*
* NOTE: Just for debugging!
*
* @param    string
* @return   void
* @access   public
* @since    2005-12-26
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function pmf_dump(data)
{
    var s = '';
    for(idx in data){
        s += idx + '('+typeof data[idx]+'): ' + data[idx]+'\n';
    }
    document.write('
' + s + '
');
}

/**
* Hide a
 container
*
* @param    string
* @return   void
* @access   public
* @since    2006-01-07
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function hide(id)
{
    document.getElementById(id).style.display = 'none';
}

/**
* Show a
 container
*
* @param    string
* @return   void
* @access   public
* @since    2006-01-07
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function show(id)
{
    document.getElementById(id).style.display = 'block';
}
<commit_msg>Fixed missing focus in instant response<commit_after>/**
 * Some JavaScript functions used in the admin backend
 *
 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
 * @author    Periklis Tsirakidis <tsirakidis@phpdevel.de>
 * @author    Matteo Scaramuccia <matteo@scaramuccia.com>
 * @author    Minoru TODA <todam@netjapan.co.jp>
 * @author    Lars Tiedemann <php@larstiedemann.de>
 * @since     2003-11-13
 * @copyright 2003-2008 phpMyFAQ Team
 * @version   CVS: $Id: functions.js,v 1.9 2008-01-20 16:21:43 thorstenr Exp $
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 */

function Picture(pic,title,width,height)
{
    popup = window.open(pic, title, 'width='+width+', height='+height+', toolbar=no, directories=no, status=no, scrollbars=no, resizable=yes, menubar=no');
    popup.focus();
}

function checkAll(checkBox)
{
    var v = checkBox.checked;
    var f = checkBox.form;
    for (var i = 0; i < f.elements.length; i++) {
        if (f.elements[i].type == "checkbox") {
            f.elements[i].checked = v;
            }
        }
}

function addEngine(uri, name, ext, cat)
{
    if ((typeof window.sidebar == "object") && (typeof window.sidebar.addSearchEngine == "function")) {
        window.sidebar.addSearchEngine(uri+"/"+name+".src", uri+"/images/"+name+"."+ext, name, cat);
    } else {
        alert('Mozilla Firefox, Mozilla or Netscape 6 or later is needed to install the search plugin!');
    }
}

function focusOnUsernameField()
{
    if (document.getElementById('faqusername')) {
        document.getElementById('faqusername').focus();
    }
}

function focusOnSearchField()
{
    if (document.getElementById('searchfield')) {
        document.getElementById('searchfield').focus();
    }
    if (document.getElementById('suchbegriff')) {
        document.getElementById('suchbegriff').focus();
    }
    if (document.getElementById('instantfield')) {
        document.getElementById('instantfield').focus();
    }
}

/**
 * showhideCategory()
 *
 * Displays or hides a div block
 *
 * @param    string
 * @return   void
 * @access   public
 * @since    2006-03-04
 * @author   Thorsten Rinne <thorsten@phpmyfaq.de>
 */
function showhideCategory(id)
{
    if (document.getElementById(id).style.display == 'none') {
        document.getElementById(id).style.display = 'block';
    } else {
        document.getElementById(id).style.display = 'none';
    }
}

/**
* deletes all options from given select-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param select
* @return void
*/
function select_clear(select)
{
    while (select.length > 0) {
        select.remove(0);
    }
}

/**
* adds an option to the given select-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param select node
* @param string
* @param text node
* @param string
* @return void
*/
function select_addOption(select, value, content, classValue)
{
    var opt;
    opt = document.createElement("option");
    opt.value = value;
    if (classValue) {
        opt.className = classValue;
    }
    opt.appendChild(content);
    select.appendChild(opt);
}

/**
* selects all list options in the select with the given ID.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param string
* @return void
*/
function select_selectAll(select_id)
{
    var select_options = document.getElementById(select_id).options;
    for (var i = 0; i < select_options.length; i++) {
        select_options[i].selected = true;
    }
}

/**
* unselects all list options in the select with the given ID.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param string
* @return void
*/
function select_unselectAll(select_id)
{
    var select_options = document.getElementById(select_id).options;
    for (var i = 0; i < select_options.length; i++) {
        select_options[i].selected = false;
    }
}

/**
 * checks all checkboxes in form with the given ID.
 *
 * @access  public
 * @author  Lars Tiedemann, <php@larstiedemann.de>
 * @param   string
 * @return  void
 */
function form_checkAll(form_id)
{
    var inputElements = document.getElementById(form_id).getElementsByTagName('input');
    for (var i = 0, ele; ele = inputElements[i]; i++) {
        if (ele.type == "checkbox") {
            ele.checked = true;
        }
    }
}

/**
 * unchecks all checkboxes in form with the given ID.
 *
 * @access  public
 * @author  Lars Tiedemann, <php@larstiedemann.de>
 * @param   string
 * @return  void
 */
function form_uncheckAll(form_id)
{
    var inputElements = document.getElementById(form_id).getElementsByTagName('input');
    for (var i = 0, ele; ele = inputElements[i]; i++) {
        if (ele.type == "checkbox") {
            ele.checked = false;
        }
    }
}

/**
* returns the text content of a child element.
*
* When having a dom structure like this:
* <item id="1">
*   <name>Item Name</name>
*   <value>Text Value</value>
* </item>
* text_getFromParent(document.getElementById(1), "name")
* would return "Item Name".
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param Object select
* @return void
*/
function text_getFromParent(parentObject, childElement)
{
    var result = "";
    result = parentObject.getElementsByTagName(childElement)[0];
    if (result) {
        if (result.childNodes.length > 1) {
            return result.childNodes[1].nodeValue;
        } else {
            if (result.firstChild) {
                return result.firstChild.nodeValue;
            } else {
                return "";
            }
        }
    } else {
        return "n/a";
    }
}

/**
* deletes all rows from given table-object.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param table
* @return void
*/
function table_clear(table)
{
    while (table.rows.length > 0) {
        table.deleteRow(0);
    }
}

/**
* inserts a new row into the given table at the given position.
*
* @access public
* @author Lars Tiedemann, <php@larstiedemann.de>
* @param table
* @param int
* @param node
* @param node
* @return void
*/
function table_addRow(table, rowNumber, col1, col2)
{
    var td1;
    var td2;
    var tr;
    td1 = document.createElement("td");
    td1.appendChild(col1);
    td2 = document.createElement("td");
    td2.appendChild(col2);
    tr = table.insertRow(rowNumber);
    tr.appendChild(td1);
    tr.appendChild(td2);
}

/**
* Function to get a pretty formatted output of a variable
*
* NOTE: Just for debugging!
*
* @param    string
* @return   void
* @access   public
* @since    2005-12-26
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function pmf_dump(data)
{
    var s = '';
    for(idx in data){
        s += idx + '('+typeof data[idx]+'): ' + data[idx]+'\n';
    }
    document.write('
' + s + '
');
}

/**
* Hide a
 container
*
* @param    string
* @return   void
* @access   public
* @since    2006-01-07
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function hide(id)
{
    document.getElementById(id).style.display = 'none';
}

/**
* Show a
 container
*
* @param    string
* @return   void
* @access   public
* @since    2006-01-07
* @author   Thorsten Rinne <thorsten@phpmyfaq.de>
*/
function show(id)
{
    document.getElementById(id).style.display = 'block';
}
<|endoftext|>


<commit_before>import $ from "jquery";

import render_message_view_header from "../templates/message_view_header.hbs";

import {$t} from "./i18n";
import * as narrow_state from "./narrow_state";
import {page_params} from "./page_params";
import * as peer_data from "./peer_data";
import * as recent_topics_util from "./recent_topics_util";
import * as rendered_markdown from "./rendered_markdown";
import * as search from "./search";

function get_formatted_sub_count(sub_count) {
    if (sub_count >= 1000) {
        // parseInt() is used to floor the value of division to an integer
        sub_count = Number.parseInt(sub_count / 1000, 10) + "k";
    }
    return sub_count;
}

function make_message_view_header(filter) {
    const message_view_header = {};
    if (recent_topics_util.is_visible()) {
        return {
            title: $t({defaultMessage: "Recent topics"}),
            icon: "clock-o",
        };
    }
    if (filter === undefined) {
        return {
            title: $t({defaultMessage: "All messages"}),
            icon: "align-left",
        };
    }
    message_view_header.title = filter.get_title();
    message_view_header.icon = filter.get_icon();
    if (filter.has_operator("stream") && !filter._sub) {
        message_view_header.sub_count = "0";
        message_view_header.formatted_sub_count = "0";
        message_view_header.rendered_narrow_description = $t({
            defaultMessage: "This stream does not exist or is private.",
        });
        return message_view_header;
    }
    if (filter._sub) {
        // We can now be certain that the narrow
        // involves a stream which exists and
        // the current user can access.
        const current_stream = filter._sub;
        message_view_header.rendered_narrow_description = current_stream.rendered_description;
        const sub_count = peer_data.get_subscriber_count(current_stream.stream_id);
        message_view_header.sub_count = sub_count;
        message_view_header.formatted_sub_count = get_formatted_sub_count(sub_count);
        // the "title" is passed as a variable and doesn't get translated (nor should it)
        message_view_header.sub_count_tooltip_text = $t(
            {defaultMessage: "{count} users are subscribed to #{title}"},
            {count: message_view_header.sub_count, title: message_view_header.title},
        );
        message_view_header.stream_settings_link =
            "#streams/" + current_stream.stream_id + "/" + current_stream.name;
    }
    return message_view_header;
}

export function colorize_message_view_header() {
    const filter = narrow_state.filter();
    if (filter === undefined || !filter._sub) {
        return;
    }
    $("#message_view_header .stream > .fa").css("color", filter._sub.color);
}

function append_and_display_title_area(message_view_header_data) {
    const message_view_header_elem = $("#message_view_header");
    message_view_header_elem.empty();
    const rendered = render_message_view_header(message_view_header_data);
    message_view_header_elem.append(rendered);
    if (message_view_header_data.stream_settings_link) {
        colorize_message_view_header();
    }
    message_view_header_elem.removeClass("notdisplayed");
    const content = message_view_header_elem.find("span.rendered_markdown");
    if (content) {
        // Update syntax like stream names, emojis, mentions, timestamps.
        rendered_markdown.update_elements(content);
    }
}

function bind_title_area_handlers() {
    $(".search_closed").on("click", (e) => {
        search.initiate_search();
        e.preventDefault();
        e.stopPropagation();
    });

    $("#message_view_header span:nth-last-child(2)").on("click", (e) => {
        if (document.getSelection().type === "Range") {
            // Allow copy/paste to work normally without interference.
            return;
        }

        // Let links behave normally, ie, do nothing if
        if ($(e.target).closest("a").length === 0) {
            search.initiate_search();
            e.preventDefault();
            e.stopPropagation();
        }
    });

    // handler that makes sure that hover plays nicely
    // with whether search is being opened or not.
    $("#message_view_header .narrow_description > a")
        .on("mouseenter", () => {
            $("#message_view_header .search_closed").addClass("search_icon_hover_highlight");
        })
        .on("mouseleave", () => {
            $("#message_view_header .search_closed").removeClass("search_icon_hover_highlight");
        });
}

function build_message_view_header(filter) {
    // This makes sure we don't waste time appending
    // message_view_header on a template where it's never used
    if (filter && !filter.is_common_narrow()) {
        open_search_bar_and_close_narrow_description();
    } else {
        const message_view_header_data = make_message_view_header(filter);
        append_and_display_title_area(message_view_header_data);
        bind_title_area_handlers();
        if (page_params.search_pills_enabled && $("#search_query").is(":focus")) {
            open_search_bar_and_close_narrow_description();
        } else {
            close_search_bar_and_open_narrow_description();
        }
    }
}

// we rely entirely on this function to ensure
// the searchbar has the right text.
export function reset_searchbox_text() {
    let search_string = narrow_state.search_string();
    if (search_string !== "") {
        if (!page_params.search_pills_enabled && !narrow_state.filter().is_search()) {
            // saves the user a keystroke for quick searches
            search_string = search_string + " ";
        }
        $("#search_query").val(search_string);
    }
}

export function exit_search() {
    const filter = narrow_state.filter();
    if (!filter || filter.is_common_narrow()) {
        // for common narrows, we change the UI (and don't redirect)
        close_search_bar_and_open_narrow_description();
    } else {
        // for "searching narrows", we redirect
        window.location.href = filter.generate_redirect_url();
    }
    $(".app").trigger("focus");
}

export function initialize() {
    render_title_area();

    // register searchbar click handler
    $("#search_exit").on("click", (e) => {
        exit_search();
        e.preventDefault();
        e.stopPropagation();
    });
}

export function render_title_area() {
    const filter = narrow_state.filter();
    build_message_view_header(filter);
}

// This function checks if "modified_sub" which is the stream whose values
// have been updated is the same as the stream which is currently
// narrowed (filter._sub) and rerenders if necessary
export function maybe_rerender_title_area_for_stream(modified_sub) {
    const filter = narrow_state.filter();
    if (filter && filter._sub && filter._sub.stream_id === modified_sub.stream_id) {
        render_title_area();
    }
}

export function open_search_bar_and_close_narrow_description() {
    reset_searchbox_text();
    $(".navbar-search").addClass("expanded");
    $("#message_view_header").addClass("hidden");
}

export function close_search_bar_and_open_narrow_description() {
    const filter = narrow_state.filter();
    if (!(filter && !filter.is_common_narrow())) {
        $(".navbar-search").removeClass("expanded");
        $("#message_view_header").removeClass("hidden");
    }
}
<commit_msg>message_view_header: Don't include stream name is tooltip.<commit_after>import $ from "jquery";

import render_message_view_header from "../templates/message_view_header.hbs";

import {$t} from "./i18n";
import * as narrow_state from "./narrow_state";
import {page_params} from "./page_params";
import * as peer_data from "./peer_data";
import * as recent_topics_util from "./recent_topics_util";
import * as rendered_markdown from "./rendered_markdown";
import * as search from "./search";

function get_formatted_sub_count(sub_count) {
    if (sub_count >= 1000) {
        // parseInt() is used to floor the value of division to an integer
        sub_count = Number.parseInt(sub_count / 1000, 10) + "k";
    }
    return sub_count;
}

function make_message_view_header(filter) {
    const message_view_header = {};
    if (recent_topics_util.is_visible()) {
        return {
            title: $t({defaultMessage: "Recent topics"}),
            icon: "clock-o",
        };
    }
    if (filter === undefined) {
        return {
            title: $t({defaultMessage: "All messages"}),
            icon: "align-left",
        };
    }
    message_view_header.title = filter.get_title();
    message_view_header.icon = filter.get_icon();
    if (filter.has_operator("stream") && !filter._sub) {
        message_view_header.sub_count = "0";
        message_view_header.formatted_sub_count = "0";
        message_view_header.rendered_narrow_description = $t({
            defaultMessage: "This stream does not exist or is private.",
        });
        return message_view_header;
    }
    if (filter._sub) {
        // We can now be certain that the narrow
        // involves a stream which exists and
        // the current user can access.
        const current_stream = filter._sub;
        message_view_header.rendered_narrow_description = current_stream.rendered_description;
        const sub_count = peer_data.get_subscriber_count(current_stream.stream_id);
        message_view_header.sub_count = sub_count;
        message_view_header.formatted_sub_count = get_formatted_sub_count(sub_count);
        // the "title" is passed as a variable and doesn't get translated (nor should it)
        message_view_header.sub_count_tooltip_text = $t(
            {defaultMessage: "This stream has {count} subscribers."},
            {count: message_view_header.sub_count, title: message_view_header.title},
        );
        message_view_header.stream_settings_link =
            "#streams/" + current_stream.stream_id + "/" + current_stream.name;
    }
    return message_view_header;
}

export function colorize_message_view_header() {
    const filter = narrow_state.filter();
    if (filter === undefined || !filter._sub) {
        return;
    }
    $("#message_view_header .stream > .fa").css("color", filter._sub.color);
}

function append_and_display_title_area(message_view_header_data) {
    const message_view_header_elem = $("#message_view_header");
    message_view_header_elem.empty();
    const rendered = render_message_view_header(message_view_header_data);
    message_view_header_elem.append(rendered);
    if (message_view_header_data.stream_settings_link) {
        colorize_message_view_header();
    }
    message_view_header_elem.removeClass("notdisplayed");
    const content = message_view_header_elem.find("span.rendered_markdown");
    if (content) {
        // Update syntax like stream names, emojis, mentions, timestamps.
        rendered_markdown.update_elements(content);
    }
}

function bind_title_area_handlers() {
    $(".search_closed").on("click", (e) => {
        search.initiate_search();
        e.preventDefault();
        e.stopPropagation();
    });

    $("#message_view_header span:nth-last-child(2)").on("click", (e) => {
        if (document.getSelection().type === "Range") {
            // Allow copy/paste to work normally without interference.
            return;
        }

        // Let links behave normally, ie, do nothing if
        if ($(e.target).closest("a").length === 0) {
            search.initiate_search();
            e.preventDefault();
            e.stopPropagation();
        }
    });

    // handler that makes sure that hover plays nicely
    // with whether search is being opened or not.
    $("#message_view_header .narrow_description > a")
        .on("mouseenter", () => {
            $("#message_view_header .search_closed").addClass("search_icon_hover_highlight");
        })
        .on("mouseleave", () => {
            $("#message_view_header .search_closed").removeClass("search_icon_hover_highlight");
        });
}

function build_message_view_header(filter) {
    // This makes sure we don't waste time appending
    // message_view_header on a template where it's never used
    if (filter && !filter.is_common_narrow()) {
        open_search_bar_and_close_narrow_description();
    } else {
        const message_view_header_data = make_message_view_header(filter);
        append_and_display_title_area(message_view_header_data);
        bind_title_area_handlers();
        if (page_params.search_pills_enabled && $("#search_query").is(":focus")) {
            open_search_bar_and_close_narrow_description();
        } else {
            close_search_bar_and_open_narrow_description();
        }
    }
}

// we rely entirely on this function to ensure
// the searchbar has the right text.
export function reset_searchbox_text() {
    let search_string = narrow_state.search_string();
    if (search_string !== "") {
        if (!page_params.search_pills_enabled && !narrow_state.filter().is_search()) {
            // saves the user a keystroke for quick searches
            search_string = search_string + " ";
        }
        $("#search_query").val(search_string);
    }
}

export function exit_search() {
    const filter = narrow_state.filter();
    if (!filter || filter.is_common_narrow()) {
        // for common narrows, we change the UI (and don't redirect)
        close_search_bar_and_open_narrow_description();
    } else {
        // for "searching narrows", we redirect
        window.location.href = filter.generate_redirect_url();
    }
    $(".app").trigger("focus");
}

export function initialize() {
    render_title_area();

    // register searchbar click handler
    $("#search_exit").on("click", (e) => {
        exit_search();
        e.preventDefault();
        e.stopPropagation();
    });
}

export function render_title_area() {
    const filter = narrow_state.filter();
    build_message_view_header(filter);
}

// This function checks if "modified_sub" which is the stream whose values
// have been updated is the same as the stream which is currently
// narrowed (filter._sub) and rerenders if necessary
export function maybe_rerender_title_area_for_stream(modified_sub) {
    const filter = narrow_state.filter();
    if (filter && filter._sub && filter._sub.stream_id === modified_sub.stream_id) {
        render_title_area();
    }
}

export function open_search_bar_and_close_narrow_description() {
    reset_searchbox_text();
    $(".navbar-search").addClass("expanded");
    $("#message_view_header").addClass("hidden");
}

export function close_search_bar_and_open_narrow_description() {
    const filter = narrow_state.filter();
    if (!(filter && !filter.is_common_narrow())) {
        $(".navbar-search").removeClass("expanded");
        $("#message_view_header").removeClass("hidden");
    }
}
<|endoftext|>


<commit_before>/*
 Copyright 2017 Travis Ralston

 Licensed 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.
 */

'use strict';

import React from "react";
import * as sdk from "../../../index";
import MatrixClientPeg from "../../../MatrixClientPeg";
import AccessibleButton from '../elements/AccessibleButton';
import Presence from "../../../Presence";
import dispatcher from "../../../dispatcher";
import UserSettingsStore from "../../../UserSettingsStore";
import * as ContextualMenu from "../../structures/ContextualMenu";

module.exports = React.createClass({
    displayName: 'MemberPresenceAvatar',

    propTypes: {
        member: React.PropTypes.object.isRequired,
        width: React.PropTypes.number,
        height: React.PropTypes.number,
        resizeMethod: React.PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            width: 40,
            height: 40,
            resizeMethod: 'crop',
        };
    },

    getInitialState: function() {
        const presenceState = this.props.member.user.presence;
        const presenceMessage = this.props.member.user.presenceStatusMsg;
        return {
            status: presenceState,
            message: presenceMessage,
        };
    },

    componentWillMount: function() {
        MatrixClientPeg.get().on("User.presence", this.onUserPresence);
        this.dispatcherRef = dispatcher.register(this.onAction);
    },

    componentWillUnmount: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("User.presence", this.onUserPresence);
        }
        dispatcher.unregister(this.dispatcherRef);
    },

    onAction: function(payload) {
        if (payload.action !== "self_presence_updated") return;
        if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) return;
        this.setState({
            status: payload.statusInfo.presence,
            message: payload.statusInfo.status_msg,
        });
    },

    onUserPresence: function(event, user) {
        if (user.userId !== MatrixClientPeg.get().getUserId()) return;
        this.setState({
            status: user.presence,
            message: user.presenceStatusMsg,
        });
    },

    onStatusChange: function(newStatus) {
        console.log(this.state);
        console.log(newStatus);
    },

    onClick: function(e) {
        const PresenceContextMenu = sdk.getComponent('context_menus.PresenceContextMenu');
        const elementRect = e.target.getBoundingClientRect();

        // The window X and Y offsets are to adjust position when zoomed in to page
        const x = (elementRect.left + window.pageXOffset) - (elementRect.width / 2) + 3;
        const chevronOffset = 12;
        let y = elementRect.top + (elementRect.height / 2) + window.pageYOffset;
        y = y - (chevronOffset + 4); // where 4 is 1/4 the height of the chevron

        const self = this;
        ContextualMenu.createMenu(PresenceContextMenu, {
            chevronOffset: chevronOffset,
            chevronFace: 'bottom',
            left: x,
            top: y,
            menuWidth: 300,
            currentStatus: this.state.status,
            onChange: this.onStatusChange,
        });

        e.stopPropagation();
        // const presenceState = this.props.member.user.presence;
        // const presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
        // const presenceLastTs = this.props.member.user.lastPresenceTs;
        // const presenceCurrentlyActive = this.props.member.user.currentlyActive;
        // const presenceMessage = this.props.member.user.presenceStatusMsg;
    },

    render: function() {
        const MemberAvatar = sdk.getComponent("avatars.MemberAvatar");

        let onClickFn = null;
        if (this.props.member.userId === MatrixClientPeg.get().getUserId()) {
            onClickFn = this.onClick;
        }

        const avatarNode = (
            <MemberAvatar member={this.props.member} width={this.props.width} height={this.props.height}
                          resizeMethod={this.props.resizeMethod} />
        );
        let statusNode = (

        );

        // LABS: Disable presence management functions for now
        if (!UserSettingsStore.isFeatureEnabled("feature_presence_management")) {
            statusNode = null;
            onClickFn = null;
        }

        let avatar = (

                { avatarNode }
                { statusNode }

        );
        if (onClickFn) {
            avatar = (
                <AccessibleButton onClick={onClickFn} className="mx_MemberPresenceAvatar" element="div">
                    { avatarNode }
                    { statusNode }
                </AccessibleButton>
            );
        }
        return avatar;
    },
});
<commit_msg>Respond to updates from presence context menu<commit_after>/*
 Copyright 2017 Travis Ralston

 Licensed 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.
 */

'use strict';

import React from "react";
import * as sdk from "../../../index";
import MatrixClientPeg from "../../../MatrixClientPeg";
import AccessibleButton from '../elements/AccessibleButton';
import Presence from "../../../Presence";
import dispatcher from "../../../dispatcher";
import UserSettingsStore from "../../../UserSettingsStore";
import * as ContextualMenu from "../../structures/ContextualMenu";

module.exports = React.createClass({
    displayName: 'MemberPresenceAvatar',

    propTypes: {
        member: React.PropTypes.object.isRequired,
        width: React.PropTypes.number,
        height: React.PropTypes.number,
        resizeMethod: React.PropTypes.string,
    },

    getDefaultProps: function() {
        return {
            width: 40,
            height: 40,
            resizeMethod: 'crop',
        };
    },

    getInitialState: function() {
        const presenceState = this.props.member.user.presence;
        const presenceMessage = this.props.member.user.presenceStatusMsg;
        return {
            status: presenceState,
            message: presenceMessage,
        };
    },

    componentWillMount: function() {
        MatrixClientPeg.get().on("User.presence", this.onUserPresence);
        this.dispatcherRef = dispatcher.register(this.onAction);
    },

    componentWillUnmount: function() {
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("User.presence", this.onUserPresence);
        }
        dispatcher.unregister(this.dispatcherRef);
    },

    onAction: function(payload) {
        if (payload.action !== "self_presence_updated") return;
        if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) return;
        this.setState({
            status: payload.statusInfo.presence,
            message: payload.statusInfo.status_msg,
        });
    },

    onUserPresence: function(event, user) {
        if (user.userId !== MatrixClientPeg.get().getUserId()) return;
        this.setState({
            status: user.presence,
            message: user.presenceStatusMsg,
        });
    },

    onStatusChange: function(newStatus) {
        Presence.stopMaintainingStatus();
        if (newStatus === "online") {
            Presence.setState(newStatus);
        } else Presence.setState(newStatus, null, true);
    },

    onClick: function(e) {
        const PresenceContextMenu = sdk.getComponent('context_menus.PresenceContextMenu');
        const elementRect = e.target.getBoundingClientRect();

        // The window X and Y offsets are to adjust position when zoomed in to page
        const x = (elementRect.left + window.pageXOffset) - (elementRect.width / 2) + 3;
        const chevronOffset = 12;
        let y = elementRect.top + (elementRect.height / 2) + window.pageYOffset;
        y = y - (chevronOffset + 4); // where 4 is 1/4 the height of the chevron

        ContextualMenu.createMenu(PresenceContextMenu, {
            chevronOffset: chevronOffset,
            chevronFace: 'bottom',
            left: x,
            top: y,
            menuWidth: 125,
            currentStatus: this.state.status,
            onChange: this.onStatusChange,
        });

        e.stopPropagation();
        // const presenceState = this.props.member.user.presence;
        // const presenceLastActiveAgo = this.props.member.user.lastActiveAgo;
        // const presenceLastTs = this.props.member.user.lastPresenceTs;
        // const presenceCurrentlyActive = this.props.member.user.currentlyActive;
        // const presenceMessage = this.props.member.user.presenceStatusMsg;
    },

    render: function() {
        const MemberAvatar = sdk.getComponent("avatars.MemberAvatar");

        let onClickFn = null;
        if (this.props.member.userId === MatrixClientPeg.get().getUserId()) {
            onClickFn = this.onClick;
        }

        const avatarNode = (
            <MemberAvatar member={this.props.member} width={this.props.width} height={this.props.height}
                          resizeMethod={this.props.resizeMethod} />
        );
        let statusNode = (

        );

        // LABS: Disable presence management functions for now
        if (!UserSettingsStore.isFeatureEnabled("feature_presence_management")) {
            statusNode = null;
            onClickFn = null;
        }

        let avatar = (

                { avatarNode }
                { statusNode }

        );
        if (onClickFn) {
            avatar = (
                <AccessibleButton onClick={onClickFn} className="mx_MemberPresenceAvatar" element="div">
                    { avatarNode }
                    { statusNode }
                </AccessibleButton>
            );
        }
        return avatar;
    },
});
<|endoftext|>