<commit_before>import React from 'react'
import echarts from 'echarts'
import _ from 'lodash'
import fetch from 'isomorphic-fetch'

import styles from './Main.css'
import Loading from '../Loading'

import { autobind } from 'react-decoration'
import { Checkbox } from 'antd'
import { checkStatus, serverAddress } from '../../util'
const CheckboxGroup = Checkbox.Group

const plainOptions = ['Apple', 'Pear', 'Orange']
const defaultCheckedList = ['Apple', 'Orange']

const getMaCalculator = (values) => (dayCount) => {
  const result = []
  for (let i = 0, len = values.length; i < len; i++) {
    if (i < dayCount) {
      result.push('-')
      continue
    }
    let sum = 0
    for (let j = 0; j < dayCount; j++) {
      sum += values[i - j]
    }
    result.push(sum / dayCount)
  }
  return result
}

const getOption = (trend) => {
  const calculateMA = getMaCalculator(trend.values)
  return {
    title: {
      text: '绝对趋势',
      left: 100
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross'
      }
    },
    legend: {
      data: ['MA5', 'MA10', 'MA20', 'MA30']
    },
    grid: {
      left: '10%',
      right: '10%',
      bottom: '15%'
    },
    xAxis: {
      type: 'category',
      data: trend.categoryData,
      scale: true,
      boundaryGap: false,
      axisLine: {onZero: false},
      splitLine: {show: false},
      splitNumber: 20,
      min: 'dataMin',
      max: 'dataMax'
    },
    yAxis: {
      scale: true,
      splitArea: {
        show: true
      }
    },
    dataZoom: [
      {
        type: 'inside',
        start: 50,
        end: 100
      },
      {
        show: true,
        type: 'slider',
        y: '90%',
        start: 50,
        end: 100
      }
    ],
    series: [
      {
        name: 'MA5',
        type: 'line',
        data: calculateMA(5),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA10',
        type: 'line',
        data: calculateMA(10),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA20',
        type: 'line',
        data: calculateMA(20),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA30',
        type: 'line',
        data: calculateMA(30),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      }

    ]
  }
}

const transformData = (rawData) => {
  const trend = {}
  rawData.forEach(data => {
    trend[data.date] = _.get(trend, data.date, 0) + 1
  })
  const categoryData = []
  const values = []
  _.forIn(trend, (value, key) => {
    categoryData.push(key)
    values.push(value)
  })
  return {categoryData, values}
}

export default class AbsoluteTrend extends React.Component {
  constructor () {
    super()
    this.state = {
      trend: [],
      checkedList: defaultCheckedList,
      indeterminate: true,
      checkAll: false
    }
    fetch(`${serverAddress}/lda-doc`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((doc) => this.setState({...this.state, trend: transformData(doc)}))
  }

  componentDidMount () {
    this.myChart = echarts.init(this.refs.AbsoluteTrend)
  }

  @autobind
  onChange (checkedList) {
    this.setState({
      checkedList,
      indeterminate: !!checkedList.length && (checkedList.length < plainOptions.length),
      checkAll: checkedList.length === plainOptions.length
    })
  }

  @autobind
  onCheckAllChange (e) {
    this.setState({
      checkedList: e.target.checked ? plainOptions : [],
      indeterminate: false,
      checkAll: e.target.checked
    })
  }

  @autobind
  drawAbsoluteTrend () {
    const option = getOption(this.state.trend)
    this.myChart.setOption(option)
    return

        <Checkbox
          indeterminate={this.state.indeterminate}
          onChange={this.onCheckAllChange}
          checked={this.state.checkAll}>
          全选
        </Checkbox>

      <CheckboxGroup options={plainOptions} value={this.state.checkedList} onChange={this.onChange} />

  }

  render () {
    return

        {_.isEmpty(this.state.trend)
        ? <Loading />
        : this.drawAbsoluteTrend()}

  }
}
<commit_msg>refactor ref name<commit_after>import React from 'react'
import echarts from 'echarts'
import _ from 'lodash'
import fetch from 'isomorphic-fetch'

import styles from './Main.css'
import Loading from '../Loading'

import { autobind } from 'react-decoration'
import { Checkbox } from 'antd'
import { checkStatus, serverAddress } from '../../util'

const refName = 'AbsoluteTrend'

const CheckboxGroup = Checkbox.Group
const plainOptions = ['Apple', 'Pear', 'Orange']
const defaultCheckedList = ['Apple', 'Orange']

const getMaCalculator = (values) => (dayCount) => {
  const result = []
  for (let i = 0, len = values.length; i < len; i++) {
    if (i < dayCount) {
      result.push('-')
      continue
    }
    let sum = 0
    for (let j = 0; j < dayCount; j++) {
      sum += values[i - j]
    }
    result.push(sum / dayCount)
  }
  return result
}

const getOption = (trend) => {
  const calculateMA = getMaCalculator(trend.values)
  return {
    title: {
      text: '绝对趋势',
      left: 100
    },
    tooltip: {
      trigger: 'axis',
      axisPointer: {
        type: 'cross'
      }
    },
    legend: {
      data: ['MA5', 'MA10', 'MA20', 'MA30']
    },
    grid: {
      left: '10%',
      right: '10%',
      bottom: '15%'
    },
    xAxis: {
      type: 'category',
      data: trend.categoryData,
      scale: true,
      boundaryGap: false,
      axisLine: {onZero: false},
      splitLine: {show: false},
      splitNumber: 20,
      min: 'dataMin',
      max: 'dataMax'
    },
    yAxis: {
      scale: true,
      splitArea: {
        show: true
      }
    },
    dataZoom: [
      {
        type: 'inside',
        start: 50,
        end: 100
      },
      {
        show: true,
        type: 'slider',
        y: '90%',
        start: 50,
        end: 100
      }
    ],
    series: [
      {
        name: 'MA5',
        type: 'line',
        data: calculateMA(5),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA10',
        type: 'line',
        data: calculateMA(10),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA20',
        type: 'line',
        data: calculateMA(20),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      },
      {
        name: 'MA30',
        type: 'line',
        data: calculateMA(30),
        smooth: true,
        lineStyle: {
          normal: {opacity: 0.5}
        }
      }

    ]
  }
}

const transformData = (rawData) => {
  const trend = {}
  rawData.forEach(data => {
    trend[data.date] = _.get(trend, data.date, 0) + 1
  })
  const categoryData = []
  const values = []
  _.forIn(trend, (value, key) => {
    categoryData.push(key)
    values.push(value)
  })
  return {categoryData, values}
}

export default class AbsoluteTrend extends React.Component {
  constructor () {
    super()
    this.state = {
      trend: [],
      checkedList: defaultCheckedList,
      indeterminate: true,
      checkAll: false
    }
    fetch(`${serverAddress}/lda-doc`, { method: 'GET' })
      .then(checkStatus)
      .then((res) => (res.json()))
      .then((doc) => this.setState({...this.state, trend: transformData(doc)}))
  }

  componentDidMount () {
    this.myChart = echarts.init(this.refs[refName])
  }

  @autobind
  onChange (checkedList) {
    this.setState({
      checkedList,
      indeterminate: !!checkedList.length && (checkedList.length < plainOptions.length),
      checkAll: checkedList.length === plainOptions.length
    })
  }

  @autobind
  onCheckAllChange (e) {
    this.setState({
      checkedList: e.target.checked ? plainOptions : [],
      indeterminate: false,
      checkAll: e.target.checked
    })
  }

  @autobind
  drawAbsoluteTrend () {
    const option = getOption(this.state.trend)
    this.myChart.setOption(option)
    return

        <Checkbox
          indeterminate={this.state.indeterminate}
          onChange={this.onCheckAllChange}
          checked={this.state.checkAll}>
          全选
        </Checkbox>

      <CheckboxGroup options={plainOptions} value={this.state.checkedList} onChange={this.onChange} />

  }

  render () {
    return

        {_.isEmpty(this.state.trend)
        ? <Loading />
        : this.drawAbsoluteTrend()}

  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2018 Google LLC
 *
 * 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
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from "react";
import "./index.css";
import { compose } from "redux";
import { connect } from "react-redux";
import { withHandlers } from "recompose";
import {
  getAuth,
  getActiveProjectId,
  getActiveProject,
  getLocalizedText,
  generateId,
  updateProject
} from "../../datastore.js";
import {
  Button,
  Dialog,
  DialogTitle,
  DialogActions,
  DialogContent,
  Typography
} from "@material-ui/core";
import GndInlineEdit from "../gnd-inline-edit";
import { withStyles } from "@material-ui/core/styles";
import { withFirebase, withFirestore } from "react-redux-firebase";
import GndMarkerImage from "../gnd-marker-image";
import GndFormEditor from "./gnd-form-editor";
import update from "immutability-helper";
import SwipeableViews from "react-swipeable-views";
import GndFormTabs from "./gnd-form-tabs";
import history from "../../history.js";

// TODO: Refactor.
update.extend("$auto", function(value, object) {
  return object ? update(object, value) : update({}, value);
});
update.extend("$autoArray", function(value, object) {
  return object ? update(object, value) : update([], value);
});

const styles = theme => ({
  dialog: {
    padding: 40
  }
});

class GndFeatureTypeEditor extends React.Component {
  state = {
    formIndex: 0,
    featureType: null
  };

  componentDidUpdate(prevProps) {
    if (prevProps.editState === this.props.editState) {
      return;
    }
    this.setState({ formIndex: 0, featureType: this.props.editState });
  }

  handleClose = () => {
    this.props.close();
  };

  handleTabChange = (event, value) => {
    this.setState({ formIndex: value });
  };

  handleFeatureTypeLabelChange(newLabel) {
    this.updateState({
      // TODO: i18n.
      defn: {
        itemLabel: prevLabels =>
          update(prevLabels || {}, { _: { $set: newLabel } })
      }
    });
    return Promise.resolve();
  }

  updateState(featureTypeUpdate, opt_newStates) {
    const { featureType } = this.state;
    this.setState({
      featureType: update(featureType, featureTypeUpdate),
      ...opt_newStates
    });
  }

  updateFormDefn(newForm) {
    this.updateState({
      defn: { forms: { [newForm.id]: { $set: newForm.defn } } }
    });
  }

  deleteForm(id, formIndex) {
    this.updateState(
      {
        defn: { forms: { $unset: [id] } }
      },
      { formIndex: Math.max(formIndex - 1, 0) }
    );
  }

  handleFormChange(newForm) {
    if (newForm.defn) {
      this.updateFormDefn(newForm);
    } else {
      this.deleteForm(newForm.id, this.state.formIndex);
    }
  }

  handleFormTitleChange(form, newTitle) {
    this.updateState({
      // TODO: i18n.
      defn: {
        forms: {
          [form.id]: {
            titles: prevTitles =>
              update(prevTitles || {}, { _: { $set: newTitle } })
          }
        }
      }
    });
  }

  createElement() {
    const { generateId } = this.props;
    return {
      id: generateId(),
      labels: {},
      type: "text_field",
      required: false
    };
  }

  createForm() {
    return { titles: {}, elements: [this.createElement()] };
  }

  handleAddFormClick() {
    const { generateId } = this.props;
    const { featureType } = this.state;
    const forms = featureType.defn.forms || {};
    this.updateState(
      {
        defn: {
          forms: prevForms =>
            update(prevForms || {}, {
              [generateId()]: {
                $set: this.createForm()
              }
            })
        }
      },
      { formIndex: Object.keys(forms).length }
    );
  }

  handleSave(event) {
    try {
      const { projectId, project, updateProject, auth } = this.props;
      const { featureType } = this.state;
      const newProject = update(project, {
        featureTypes: {
          $auto: { [featureType.id]: { $set: featureType.defn } }
        }
      });
      updateProject(projectId, newProject, auth).then(id => this.onSaved(id));
    } catch (e) {
      alert("Save failed");
      console.error(e);
    }
  }

  onSaved(id) {
    this.props.close();
    if (this.props.projectId !== id) {
      // TODO: Refactor into custom action.
      history.push(`/p/${id}`);
    }
  }

  render() {
    const { classes, generateId } = this.props;
    const { featureType, formIndex } = this.state;
    const defn = featureType && featureType.defn;
    const featureTypeLabel = getLocalizedText(defn && defn.itemLabel);
    const forms = (defn && defn.forms) || {};
    const formsArray = Object.keys(forms).map(id => ({
      id: id,
      title: getLocalizedText(forms[id].titles),
      defn: forms[id]
    }));

    // TODO: Adjust height of swipeable area so that forms don't scroll more
    // than necessary.
    return (
      <form noValidate autoComplete="off" onSubmit={ev => ev.preventDefault()}>
        <Dialog
          open={Boolean(featureType)}
          onClose={this.handleClose}
          aria-labelledby="form-dialog-title"
          scroll="paper"
          maxWidth={false}
          classes={{ paper: "ft-dialog" }}
          disableEscapeKeyDown
        >
          <DialogTitle>

                <GndMarkerImage className="marker" featureType={featureType} />

              <GndInlineEdit
                className="ft-label"
                inputClassName="ft-label-input"
                onCommitChanges={this.handleFeatureTypeLabelChange.bind(this)}
                value={featureTypeLabel}
                placeholder="Unnamed place type"
              />

                <Typography variant="caption">Place type definition</Typography>

            <GndFormTabs
              formIndex={formIndex}
              onTabChange={this.handleTabChange.bind(this)}
              onFormTitleChange={this.handleFormTitleChange.bind(this)}
              onAddFormClick={this.handleAddFormClick.bind(this)}
              formsArray={formsArray}
            />
          </DialogTitle>
          <DialogContent>
            <SwipeableViews
              index={formIndex}
              onChangeIndex={this.handleChangeIndex}
            >
              {formsArray.map((form, idx) => (
                <GndFormEditor
                  key={"form" + idx}
                  form={form}
                  onChange={this.handleFormChange.bind(this)}
                  generateId={generateId}
                />
              ))}
            </SwipeableViews>
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleClose} color="primary">
              Cancel
            </Button>
            <Button
              variant="contained"
              color="primary"
              className={classes.button}
              onClick={() => this.handleSave()}
            >
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </form>
    );
  }
}

const mapStateToProps = (store, props) => ({
  auth: getAuth(store),
  projectId: getActiveProjectId(store),
  project: getActiveProject(store),
  editState: store.featureTypeEditState
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  close: () => dispatch({ type: "CLOSE_FEATURE_TYPE_EDITOR" })
});

const enhance = compose(
  connect(
    mapStateToProps,
    mapDispatchToProps
  ),
  withFirebase,
  withFirestore,
  withHandlers({
    updateProject,
    generateId
  }),
  withStyles(styles, { withTheme: true })
);

export default enhance(GndFeatureTypeEditor);
<commit_msg>Rename Place(Type) to Feature(Type) throughout #21<commit_after>/**
 * @license
 * Copyright 2018 Google LLC
 *
 * 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
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from "react";
import "./index.css";
import { compose } from "redux";
import { connect } from "react-redux";
import { withHandlers } from "recompose";
import {
  getAuth,
  getActiveProjectId,
  getActiveProject,
  getLocalizedText,
  generateId,
  updateProject
} from "../../datastore.js";
import {
  Button,
  Dialog,
  DialogTitle,
  DialogActions,
  DialogContent,
  Typography
} from "@material-ui/core";
import GndInlineEdit from "../gnd-inline-edit";
import { withStyles } from "@material-ui/core/styles";
import { withFirebase, withFirestore } from "react-redux-firebase";
import GndMarkerImage from "../gnd-marker-image";
import GndFormEditor from "./gnd-form-editor";
import update from "immutability-helper";
import SwipeableViews from "react-swipeable-views";
import GndFormTabs from "./gnd-form-tabs";
import history from "../../history.js";

// TODO: Refactor.
update.extend("$auto", function(value, object) {
  return object ? update(object, value) : update({}, value);
});
update.extend("$autoArray", function(value, object) {
  return object ? update(object, value) : update([], value);
});

const styles = theme => ({
  dialog: {
    padding: 40
  }
});

class GndFeatureTypeEditor extends React.Component {
  state = {
    formIndex: 0,
    featureType: null
  };

  componentDidUpdate(prevProps) {
    if (prevProps.editState === this.props.editState) {
      return;
    }
    this.setState({ formIndex: 0, featureType: this.props.editState });
  }

  handleClose = () => {
    this.props.close();
  };

  handleTabChange = (event, value) => {
    this.setState({ formIndex: value });
  };

  handleFeatureTypeLabelChange(newLabel) {
    this.updateState({
      // TODO: i18n.
      defn: {
        itemLabel: prevLabels =>
          update(prevLabels || {}, { _: { $set: newLabel } })
      }
    });
    return Promise.resolve();
  }

  updateState(featureTypeUpdate, opt_newStates) {
    const { featureType } = this.state;
    this.setState({
      featureType: update(featureType, featureTypeUpdate),
      ...opt_newStates
    });
  }

  updateFormDefn(newForm) {
    this.updateState({
      defn: { forms: { [newForm.id]: { $set: newForm.defn } } }
    });
  }

  deleteForm(id, formIndex) {
    this.updateState(
      {
        defn: { forms: { $unset: [id] } }
      },
      { formIndex: Math.max(formIndex - 1, 0) }
    );
  }

  handleFormChange(newForm) {
    if (newForm.defn) {
      this.updateFormDefn(newForm);
    } else {
      this.deleteForm(newForm.id, this.state.formIndex);
    }
  }

  handleFormTitleChange(form, newTitle) {
    this.updateState({
      // TODO: i18n.
      defn: {
        forms: {
          [form.id]: {
            titles: prevTitles =>
              update(prevTitles || {}, { _: { $set: newTitle } })
          }
        }
      }
    });
  }

  createElement() {
    const { generateId } = this.props;
    return {
      id: generateId(),
      labels: {},
      type: "text_field",
      required: false
    };
  }

  createForm() {
    return { titles: {}, elements: [this.createElement()] };
  }

  handleAddFormClick() {
    const { generateId } = this.props;
    const { featureType } = this.state;
    const forms = featureType.defn.forms || {};
    this.updateState(
      {
        defn: {
          forms: prevForms =>
            update(prevForms || {}, {
              [generateId()]: {
                $set: this.createForm()
              }
            })
        }
      },
      { formIndex: Object.keys(forms).length }
    );
  }

  handleSave(event) {
    try {
      const { projectId, project, updateProject, auth } = this.props;
      const { featureType } = this.state;
      const newProject = update(project, {
        featureTypes: {
          $auto: { [featureType.id]: { $set: featureType.defn } }
        }
      });
      updateProject(projectId, newProject, auth).then(id => this.onSaved(id));
    } catch (e) {
      alert("Save failed");
      console.error(e);
    }
  }

  onSaved(id) {
    this.props.close();
    if (this.props.projectId !== id) {
      // TODO: Refactor into custom action.
      history.push(`/p/${id}`);
    }
  }

  render() {
    const { classes, generateId } = this.props;
    const { featureType, formIndex } = this.state;
    const defn = featureType && featureType.defn;
    const featureTypeLabel = getLocalizedText(defn && defn.itemLabel);
    const forms = (defn && defn.forms) || {};
    const formsArray = Object.keys(forms).map(id => ({
      id: id,
      title: getLocalizedText(forms[id].titles),
      defn: forms[id]
    }));

    // TODO: Adjust height of swipeable area so that forms don't scroll more
    // than necessary.
    return (
      <form noValidate autoComplete="off" onSubmit={ev => ev.preventDefault()}>
        <Dialog
          open={Boolean(featureType)}
          onClose={this.handleClose}
          aria-labelledby="form-dialog-title"
          scroll="paper"
          maxWidth={false}
          classes={{ paper: "ft-dialog" }}
          disableEscapeKeyDown
        >
          <DialogTitle>

                <GndMarkerImage className="marker" featureType={featureType} />

              <GndInlineEdit
                className="ft-label"
                inputClassName="ft-label-input"
                onCommitChanges={this.handleFeatureTypeLabelChange.bind(this)}
                value={featureTypeLabel}
                placeholder="Unnamed feature type"
              />

                <Typography variant="caption">Feature type</Typography>

            <GndFormTabs
              formIndex={formIndex}
              onTabChange={this.handleTabChange.bind(this)}
              onFormTitleChange={this.handleFormTitleChange.bind(this)}
              onAddFormClick={this.handleAddFormClick.bind(this)}
              formsArray={formsArray}
            />
          </DialogTitle>
          <DialogContent>
            <SwipeableViews
              index={formIndex}
              onChangeIndex={this.handleChangeIndex}
            >
              {formsArray.map((form, idx) => (
                <GndFormEditor
                  key={"form" + idx}
                  form={form}
                  onChange={this.handleFormChange.bind(this)}
                  generateId={generateId}
                />
              ))}
            </SwipeableViews>
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleClose} color="primary">
              Cancel
            </Button>
            <Button
              variant="contained"
              color="primary"
              className={classes.button}
              onClick={() => this.handleSave()}
            >
              Save
            </Button>
          </DialogActions>
        </Dialog>
      </form>
    );
  }
}

const mapStateToProps = (store, props) => ({
  auth: getAuth(store),
  projectId: getActiveProjectId(store),
  project: getActiveProject(store),
  editState: store.featureTypeEditState
});

const mapDispatchToProps = (dispatch, ownProps) => ({
  close: () => dispatch({ type: "CLOSE_FEATURE_TYPE_EDITOR" })
});

const enhance = compose(
  connect(
    mapStateToProps,
    mapDispatchToProps
  ),
  withFirebase,
  withFirestore,
  withHandlers({
    updateProject,
    generateId
  }),
  withStyles(styles, { withTheme: true })
);

export default enhance(GndFeatureTypeEditor);
<|endoftext|>


<commit_before>import React, { Suspense, lazy } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Input from '@material-ui/core/Input'
import IconButton from '@material-ui/core/IconButton'
import InputAdornment from '@material-ui/core/InputAdornment'
import SearchIcon from '@material-ui/icons/Search'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Paper from '@material-ui/core/Paper'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { isSearchPageEnabled } from 'js/utils/feature-flags'
import {
  adblockerWhitelistingForSearchURL,
  dashboardURL,
  modifyURLParams,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import LogoWithText from 'js/components/Logo/LogoWithText'
import { parseUrlSearchString } from 'js/utils/utils'
import SearchResults from 'js/components/Search/SearchResults'
import { isReactSnapClient } from 'js/utils/search-utils'
import SearchMenuQuery from 'js/components/Search/SearchMenuQuery'
import detectAdblocker from 'js/utils/detectAdblocker'
import Link from 'js/components/General/Link'

const Footer = lazy(() => import('js/components/General/Footer'))

const searchBoxBorderColor = '#ced4da'
const searchBoxBorderColorFocused = '#bdbdbd'

const styles = theme => ({
  inputRootStyle: {
    padding: 0,
    borderRadius: 28,
    backgroundColor: theme.palette.common.white,
    border: `1px solid ${searchBoxBorderColor}`,
    fontSize: 16,
    boxShadow: '0rem 0rem 0.02rem 0.02rem rgba(0, 0, 0, 0.1)',
    transition: theme.transitions.create(['border-color', 'box-shadow']),
    '&:hover': {
      borderColor: searchBoxBorderColorFocused,
      boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
    },
  },
  inputRootFocused: {
    borderColor: searchBoxBorderColorFocused,
    boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
  },
  inputStyle: {
    padding: '12px 16px',
  },
  tabsContainerRootStyle: {
    minHeight: 0,
  },
  tabRootStyle: {
    color: '#505050', // Same as search result description
    minHeight: 34,
    minWidth: 10,
  },
})

class SearchPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isAdBlockerEnabled: false,
      query: '',
      searchFeatureEnabled: isSearchPageEnabled(),
      searchSource: null,
      searchText: '',
      showPlaceholderText: false,
    }
  }

  componentDidMount() {
    if (!this.state.searchFeatureEnabled) {
      // Cannot use pushState now that the apps are separate.
      externalRedirect(dashboardURL)
    }
    const { location } = this.props

    // Wait until after mount to update prerendered state.
    const query = parseUrlSearchString(location.search).q || ''
    this.setState({
      // We always derive the query and page values URL parameter
      // values. We keep tehse in state so that we update the
      // prerendered components after mount, because at prerender
      // time we do not know the query string. We can remove this
      // from state if we switch to server-side rendering.
      query: query,
      page: this.getPageNumberFromSearchString(location.search),
      showPlaceholderText: !isReactSnapClient(),
      searchSource: parseUrlSearchString(location.search).src || null,
      searchText: query,
    })

    // AdBlockerDetection
    detectAdblocker()
      .then(isEnabled => {
        this.setState({
          isAdBlockerEnabled: isEnabled,
        })
      })
      .catch(e => {
        console.error(e)
      })
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props
    const currentQuery = parseUrlSearchString(location.search).q
    const prevQuery = parseUrlSearchString(prevProps.location.search).q
    if (currentQuery !== prevQuery) {
      this.setState({
        query: currentQuery,
        searchText: currentQuery,
      })
    }

    // Check if the page number has changed.
    const currentPage = this.getPageNumberFromSearchString(location.search)
    const prevPage = this.getPageNumberFromSearchString(
      prevProps.location.search
    )
    if (currentPage !== prevPage) {
      this.setState({
        page: currentPage,
      })
    }
  }

  /**
   * Take a search string, such as ?abc=hi&p=12, and return the
   * integer value of the "page" URL parameter. If the parameter is
   * not set or is not an integer, return 1.
   * @param {String} searchStr - The URL parameter string,
   *   such as '?myParam=foo&another=bar'
   * @return {Number} The search results page inded
   */
  getPageNumberFromSearchString(searchStr) {
    return parseInt(parseUrlSearchString(searchStr).page, 10) || 1
  }

  search() {
    const newQuery = this.state.searchText
    if (newQuery) {
      modifyURLParams({
        page: 1,
        q: newQuery,
        src: 'self',
      })
      this.setState({
        searchSource: 'self',
      })
    }
  }

  onSearchTextChange(e) {
    this.setState({
      searchText: e.target.value,
    })
  }

  render() {
    const { classes } = this.props
    const {
      isAdBlockerEnabled,
      page,
      query,
      searchSource,
      searchText,
    } = this.state
    const queryEncoded = query ? encodeURI(query) : ''
    const searchResultsPaddingLeft = 170
    if (!this.state.searchFeatureEnabled) {
      return null
    }
    return (
      <div
        data-test-id={'search-page'}
        style={{
          backgroundColor: '#fff',
          // For demonstration only
          minWidth: 1100,
          // minWidth: '100vw',
          minHeight: '100vh',
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        <div
          style={{
            backgroundColor: '#fff',
            borderBottom: '1px solid #e4e4e4',
          }}
        >
          <div
            style={{
              padding: '20px 18px 6px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'flex-start',
            }}
          >
            <LogoWithText
              style={{
                width: 100,
                height: 36,
              }}
            />
            <div
              style={{
                maxWidth: 600,
                marginLeft: 30,
                flex: 1,
              }}
            >
              <Input
                id="search-input"
                type={'text'}
                value={searchText}
                onChange={this.onSearchTextChange.bind(this)}
                onKeyPress={e => {
                  if (e.key === 'Enter') {
                    this.search()
                  }
                }}
                placeholder={
                  // Don't immediately render the placeholder text because
                  // we may rapidly replace it with the query on first render.
                  this.state.showPlaceholderText
                    ? 'Search to raise money for charity...'
                    : ''
                }
                disableUnderline
                fullWidth
                classes={{
                  root: classes.inputRootStyle,
                  input: classes.inputStyle,
                  focused: classes.inputRootFocused,
                }}
                endAdornment={
                  <InputAdornment position="end">
                    <IconButton
                      aria-label="Search button"
                      onClick={this.search.bind(this)}
                    >
                      <SearchIcon
                        style={{ color: searchBoxBorderColorFocused }}
                      />
                    </IconButton>
                  </InputAdornment>
                }
              />
            </div>
            <SearchMenuQuery
              style={{
                marginLeft: 'auto',
              }}
            />
          </div>
          <Tabs
            value={0}
            indicatorColor={'secondary'}
            style={{
              marginTop: 8,
              marginLeft: 149,
            }}
            classes={{
              root: classes.tabsContainerRootStyle,
            }}
          >
            <Tab
              label="Web"
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Images"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=isch`
                  : 'https://images.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="News"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=nws`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Videos"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=vid`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Maps"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/maps/?q=${queryEncoded}`
                  : 'https://www.google.com/maps'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
          </Tabs>
        </div>
        <div
          style={{
            display: 'flex',
          }}
        >
          <div
            style={{
              marginLeft: searchResultsPaddingLeft,
              marginTop: 20,
              width: 600,
            }}
          >
            {isAdBlockerEnabled ? (
              <div
                data-test-id={'search-prevented-warning'}
                style={{
                  marginBottom: 20,
                  width: '100%',
                }}
              >
                <Paper
                  style={{
                    padding: '10px 18px',
                    backgroundColor: 'rgb(242, 222, 222)',
                  }}
                >
                  <span
                    style={{
                      display: 'flex',
                      flexDirection: 'column',
                      alignItems: 'flex-start',
                    }}
                  >
                    <Typography
                      style={{
                        color: 'rgb(169, 68, 66)',
                        fontWeight: 'bold',
                        marginBottom: 8,
                        marginTop: 8,
                      }}
                      variant={'h6'}
                    >
                      Please disable your ad blocker
                    </Typography>
                    <Typography variant={'body2'}>
                      We use search ads to raise money for charity. You'll
                      likely need to whitelist Search for a Cause for search
                      results to show.
                    </Typography>
                    <div
                      style={{
                        display: 'flex',
                        alignSelf: 'flex-end',
                        marginTop: 10,
                      }}
                    >
                      <Link
                        to={adblockerWhitelistingForSearchURL}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        <Button color={'default'}>Show me how</Button>
                      </Link>
                    </div>
                  </span>
                </Paper>
              </div>
            ) : null}
            <SearchResults
              query={query}
              page={page}
              onPageChange={newPageIndex => {
                modifyURLParams({
                  page: newPageIndex,
                })
              }}
              isAdBlockerEnabled={isAdBlockerEnabled}
              searchSource={searchSource}
              style={{
                maxWidth: 600,
                marginBottom: 40,
              }}
            />
          </div>
          <div
            data-test-id={'search-sidebar'}
            style={{
              display: 'flex',
              flexDirection: 'column',
              margin: '20px 40px',
              width: '100%',
              boxSizing: 'border-box',
              maxWidth: 410,
              minWidth: 300,
            }}
          >
            <Paper
              style={{
                width: '100%',
                boxSizing: 'border-box',
                padding: '10px 18px',
                marginBottom: 20,
              }}
            >
              <span
                style={{
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'flex-start',
                }}
              >
                <Typography variant={'h6'}>Your searches do good :)</Typography>
                <Typography variant={'body2'}>
                  When you search, you raise money for charity! The money comes
                  from the ads in search results, and you decide where the money
                  goes by donating your hearts to your favorite nonprofit.
                </Typography>
                <div
                  style={{
                    display: 'flex',
                    alignSelf: 'flex-end',
                    marginTop: 10,
                  }}
                >
                  <Button color={'primary'}>Got it!</Button>
                </div>
              </span>
            </Paper>
          </div>
        </div>
        <Suspense fallback={null}>
          <Footer
            style={{
              marginTop: 'auto',
            }}
          />
        </Suspense>
      </div>
    )
  }
}

SearchPage.propTypes = {
  classes: PropTypes.object.isRequired,
  location: PropTypes.shape({
    search: PropTypes.string.isRequired,
  }),
  theme: PropTypes.object.isRequired,
}

SearchPage.defaultProps = {}

export default withStyles(styles, { withTheme: true })(SearchPage)
<commit_msg>Styling<commit_after>import React, { Suspense, lazy } from 'react'
import PropTypes from 'prop-types'
import { withStyles } from '@material-ui/core/styles'
import Input from '@material-ui/core/Input'
import IconButton from '@material-ui/core/IconButton'
import InputAdornment from '@material-ui/core/InputAdornment'
import SearchIcon from '@material-ui/icons/Search'
import Tabs from '@material-ui/core/Tabs'
import Tab from '@material-ui/core/Tab'
import Paper from '@material-ui/core/Paper'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import { isSearchPageEnabled } from 'js/utils/feature-flags'
import {
  adblockerWhitelistingForSearchURL,
  dashboardURL,
  modifyURLParams,
} from 'js/navigation/navigation'
import { externalRedirect } from 'js/navigation/utils'
import LogoWithText from 'js/components/Logo/LogoWithText'
import { parseUrlSearchString } from 'js/utils/utils'
import SearchResults from 'js/components/Search/SearchResults'
import { isReactSnapClient } from 'js/utils/search-utils'
import SearchMenuQuery from 'js/components/Search/SearchMenuQuery'
import detectAdblocker from 'js/utils/detectAdblocker'
import Link from 'js/components/General/Link'

const Footer = lazy(() => import('js/components/General/Footer'))

const searchBoxBorderColor = '#ced4da'
const searchBoxBorderColorFocused = '#bdbdbd'

const styles = theme => ({
  inputRootStyle: {
    padding: 0,
    borderRadius: 28,
    backgroundColor: theme.palette.common.white,
    border: `1px solid ${searchBoxBorderColor}`,
    fontSize: 16,
    boxShadow: '0rem 0rem 0.02rem 0.02rem rgba(0, 0, 0, 0.1)',
    transition: theme.transitions.create(['border-color', 'box-shadow']),
    '&:hover': {
      borderColor: searchBoxBorderColorFocused,
      boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
    },
  },
  inputRootFocused: {
    borderColor: searchBoxBorderColorFocused,
    boxShadow: '0rem 0.05rem 0.2rem 0.05rem rgba(0, 0, 0, 0.1)',
  },
  inputStyle: {
    padding: '12px 16px',
  },
  tabsContainerRootStyle: {
    minHeight: 0,
  },
  tabRootStyle: {
    color: '#505050', // Same as search result description
    minHeight: 34,
    minWidth: 10,
  },
})

class SearchPage extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      isAdBlockerEnabled: false,
      query: '',
      searchFeatureEnabled: isSearchPageEnabled(),
      searchSource: null,
      searchText: '',
      showPlaceholderText: false,
    }
  }

  componentDidMount() {
    if (!this.state.searchFeatureEnabled) {
      // Cannot use pushState now that the apps are separate.
      externalRedirect(dashboardURL)
    }
    const { location } = this.props

    // Wait until after mount to update prerendered state.
    const query = parseUrlSearchString(location.search).q || ''
    this.setState({
      // We always derive the query and page values URL parameter
      // values. We keep tehse in state so that we update the
      // prerendered components after mount, because at prerender
      // time we do not know the query string. We can remove this
      // from state if we switch to server-side rendering.
      query: query,
      page: this.getPageNumberFromSearchString(location.search),
      showPlaceholderText: !isReactSnapClient(),
      searchSource: parseUrlSearchString(location.search).src || null,
      searchText: query,
    })

    // AdBlockerDetection
    detectAdblocker()
      .then(isEnabled => {
        this.setState({
          isAdBlockerEnabled: isEnabled,
        })
      })
      .catch(e => {
        console.error(e)
      })
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props
    const currentQuery = parseUrlSearchString(location.search).q
    const prevQuery = parseUrlSearchString(prevProps.location.search).q
    if (currentQuery !== prevQuery) {
      this.setState({
        query: currentQuery,
        searchText: currentQuery,
      })
    }

    // Check if the page number has changed.
    const currentPage = this.getPageNumberFromSearchString(location.search)
    const prevPage = this.getPageNumberFromSearchString(
      prevProps.location.search
    )
    if (currentPage !== prevPage) {
      this.setState({
        page: currentPage,
      })
    }
  }

  /**
   * Take a search string, such as ?abc=hi&p=12, and return the
   * integer value of the "page" URL parameter. If the parameter is
   * not set or is not an integer, return 1.
   * @param {String} searchStr - The URL parameter string,
   *   such as '?myParam=foo&another=bar'
   * @return {Number} The search results page inded
   */
  getPageNumberFromSearchString(searchStr) {
    return parseInt(parseUrlSearchString(searchStr).page, 10) || 1
  }

  search() {
    const newQuery = this.state.searchText
    if (newQuery) {
      modifyURLParams({
        page: 1,
        q: newQuery,
        src: 'self',
      })
      this.setState({
        searchSource: 'self',
      })
    }
  }

  onSearchTextChange(e) {
    this.setState({
      searchText: e.target.value,
    })
  }

  render() {
    const { classes } = this.props
    const {
      isAdBlockerEnabled,
      page,
      query,
      searchSource,
      searchText,
    } = this.state
    const queryEncoded = query ? encodeURI(query) : ''
    const searchResultsPaddingLeft = 170
    if (!this.state.searchFeatureEnabled) {
      return null
    }
    return (
      <div
        data-test-id={'search-page'}
        style={{
          backgroundColor: '#fff',
          // For demonstration only
          minWidth: 1100,
          // minWidth: '100vw',
          minHeight: '100vh',
          display: 'flex',
          flexDirection: 'column',
        }}
      >
        <div
          style={{
            backgroundColor: '#fff',
            borderBottom: '1px solid #e4e4e4',
          }}
        >
          <div
            style={{
              padding: '20px 18px 6px',
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'flex-start',
            }}
          >
            <LogoWithText
              style={{
                width: 100,
                height: 36,
              }}
            />
            <div
              style={{
                maxWidth: 600,
                marginLeft: 30,
                flex: 1,
              }}
            >
              <Input
                id="search-input"
                type={'text'}
                value={searchText}
                onChange={this.onSearchTextChange.bind(this)}
                onKeyPress={e => {
                  if (e.key === 'Enter') {
                    this.search()
                  }
                }}
                placeholder={
                  // Don't immediately render the placeholder text because
                  // we may rapidly replace it with the query on first render.
                  this.state.showPlaceholderText
                    ? 'Search to raise money for charity...'
                    : ''
                }
                disableUnderline
                fullWidth
                classes={{
                  root: classes.inputRootStyle,
                  input: classes.inputStyle,
                  focused: classes.inputRootFocused,
                }}
                endAdornment={
                  <InputAdornment position="end">
                    <IconButton
                      aria-label="Search button"
                      onClick={this.search.bind(this)}
                    >
                      <SearchIcon
                        style={{ color: searchBoxBorderColorFocused }}
                      />
                    </IconButton>
                  </InputAdornment>
                }
              />
            </div>
            <SearchMenuQuery
              style={{
                marginLeft: 'auto',
              }}
            />
          </div>
          <Tabs
            value={0}
            indicatorColor={'secondary'}
            style={{
              marginTop: 8,
              marginLeft: 149,
            }}
            classes={{
              root: classes.tabsContainerRootStyle,
            }}
          >
            <Tab
              label="Web"
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Images"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=isch`
                  : 'https://images.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="News"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=nws`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Videos"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/search?q=${queryEncoded}&tbm=vid`
                  : 'https://www.google.com'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
            <Tab
              label="Maps"
              target="_top"
              href={
                queryEncoded
                  ? `https://www.google.com/maps/?q=${queryEncoded}`
                  : 'https://www.google.com/maps'
              }
              classes={{
                root: classes.tabRootStyle,
              }}
            />
          </Tabs>
        </div>
        <div
          style={{
            display: 'flex',
          }}
        >
          <div
            style={{
              marginLeft: searchResultsPaddingLeft,
              marginTop: 20,
              width: 600,
            }}
          >
            {isAdBlockerEnabled ? (
              <div
                data-test-id={'search-prevented-warning'}
                style={{
                  marginBottom: 20,
                  width: '100%',
                }}
              >
                <Paper
                  elevation={1}
                  style={{
                    padding: '10px 18px',
                    backgroundColor: 'rgb(242, 222, 222)',
                  }}
                >
                  <span
                    style={{
                      display: 'flex',
                      flexDirection: 'column',
                      alignItems: 'flex-start',
                    }}
                  >
                    <Typography
                      style={{
                        color: 'rgb(169, 68, 66)',
                        fontWeight: 'bold',
                        marginBottom: 8,
                        marginTop: 8,
                      }}
                      variant={'h6'}
                    >
                      Please disable your ad blocker
                    </Typography>
                    <Typography variant={'body2'}>
                      We use search ads to raise money for charity. You'll
                      likely need to whitelist Search for a Cause for search
                      results to show.
                    </Typography>
                    <div
                      style={{
                        display: 'flex',
                        alignSelf: 'flex-end',
                        marginTop: 10,
                      }}
                    >
                      <Link
                        to={adblockerWhitelistingForSearchURL}
                        target="_blank"
                        rel="noopener noreferrer"
                      >
                        <Button color={'default'}>Show me how</Button>
                      </Link>
                    </div>
                  </span>
                </Paper>
              </div>
            ) : null}
            <SearchResults
              query={query}
              page={page}
              onPageChange={newPageIndex => {
                modifyURLParams({
                  page: newPageIndex,
                })
              }}
              isAdBlockerEnabled={isAdBlockerEnabled}
              searchSource={searchSource}
              style={{
                maxWidth: 600,
                marginBottom: 40,
              }}
            />
          </div>
          <div
            data-test-id={'search-sidebar'}
            style={{
              display: 'flex',
              flexDirection: 'column',
              margin: '20px 40px',
              width: '100%',
              boxSizing: 'border-box',
              maxWidth: 410,
              minWidth: 300,
            }}
          >
            <Paper
              elevation={1}
              style={{
                width: '100%',
                boxSizing: 'border-box',
                padding: '10px 18px',
                marginBottom: 20,
              }}
            >
              <span
                style={{
                  display: 'flex',
                  flexDirection: 'column',
                  alignItems: 'flex-start',
                }}
              >
                <Typography
                  variant={'h6'}
                  style={{ marginTop: 8, marginBottom: 8 }}
                >
                  Your searches do good :)
                </Typography>
                <Typography variant={'body2'}>
                  When you search, you raise money for charity! The money comes
                  from the ads in search results, and you decide where the money
                  goes by donating your hearts to your favorite nonprofit.
                </Typography>
                <div
                  style={{
                    display: 'flex',
                    alignSelf: 'flex-end',
                    marginTop: 10,
                  }}
                >
                  <Button color={'primary'}>Got it!</Button>
                </div>
              </span>
            </Paper>
          </div>
        </div>
        <Suspense fallback={null}>
          <Footer
            style={{
              marginTop: 'auto',
            }}
          />
        </Suspense>
      </div>
    )
  }
}

SearchPage.propTypes = {
  classes: PropTypes.object.isRequired,
  location: PropTypes.shape({
    search: PropTypes.string.isRequired,
  }),
  theme: PropTypes.object.isRequired,
}

SearchPage.defaultProps = {}

export default withStyles(styles, { withTheme: true })(SearchPage)
<|endoftext|>


<commit_before>/*
 * Copyright 2005-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.
 */
// global vars
var $dialog = null;
var jq = jQuery.noConflict();

//clear out blockUI css, using css class overrides
jQuery.blockUI.defaults.css = {};
jQuery.blockUI.defaults.overlayCSS = {};

// validation init
var pageValidatorReady = false;

//sets up the validator with the necessary default settings and methods
//note the use of onClick and onFocusout for on the fly validation client side
function setupValidator(){
	jq('#kualiForm').dirty_form({changedClass: 'dirty'});

	//Make sure form doesn't have any unsaved data when user clicks on any other portal links, closes browser or presses fwd/back browser button
	jq(window).bind('beforeunload', function(evt){
		var validateDirty = jq("[name='validateDirty']").val();
		if (validateDirty == "true")
		{
			var dirty = jq(".kr-field-attribute").find("input.dirty");
			//methodToCall check is needed to skip from normal way of unloading (cancel,save,close)
			var methodToCall = jq("[name='methodToCall']").val();
			if (dirty.length > 0 && methodToCall == null)
			{
				return "Form has unsaved data. Do you want to leave anyway?";
			}
		}
	});

	jq('#kualiForm').validate(
	{
		onsubmit: false,
		ignore: ".ignoreValid",
		onclick: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
		},
		onfocusout: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
			if((element.type == "select-one" || element.type == "select-multiple") && jq(element).hasClass("valid")){
				applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
				showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
			}
		},
		wrapper: "li",
		highlight: function(element, errorClass, validClass) {
			jq(element).addClass(errorClass).removeClass(validClass);
            jq(element).attr("aria-invalid", "true");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 1, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 1);
		},
		unhighlight: function(element, errorClass, validClass) {
			jq(element).removeClass(errorClass).addClass(validClass);
            jq(element).attr("aria-invalid", "false");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
		},
		errorPlacement: function(error, element) {
			var id = getAttributeId(element.attr('id'), element[0].type);
			//check to see if the option to use labels is on
			if (!jq("#" + id + "_errors_div").hasClass("noLabels")) {
				var label = getLabel(id);
				label = jq.trim(label);
				if (label) {
					if (label.charAt(label.length - 1) == ":") {
						label = label.slice(0, -1);
					}
					error.find("label").before(label + " - ");
				}
			}
			jq("#" + id + "_errors_div").show();
			jq("#" + id + "_errors_errorMessages").show();
			var errorList = jq("#" + id + "_errors_errorMessages ul");
			error.appendTo(errorList);
		}
	});

    jq(".required").each(function(){
        jq(this).attr("aria-required", "true");
    });

	jq(document).trigger('validationSetup');
	pageValidatorReady = true;

	jq.watermark.showAll();
}

jQuery.validator.addMethod("minExclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value > param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxInclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value <= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("minLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) >= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) <= param[0];
	}
	else{
		return true;
	}
});

// data table initialize default sorting
jQuery.fn.dataTableExt.oSort['kuali_date-asc']  = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_date-desc'] = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_percent-asc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num1 - num2;
};

jQuery.fn.dataTableExt.oSort['kuali_percent-desc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num2 - num1;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-asc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x*1;
	y = isNaN(y) ? 0 : y*1;

	return x - y;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-desc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x;
	y = isNaN(y) ? 0 : y;

	return y - x;
};

jQuery.fn.dataTableExt.afnSortData['dom-text'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var input = jq(this).find('input:text');
		if(input.length != 0){
			aData.push( input.val() );
		}else{
            //match DataField or InputField CSS classes - respectively
			var input1 = jq(this).find('.kr-data-fld, .kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.find("span:first").text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the select options in a column */
jQuery.fn.dataTableExt.afnSortData['dom-select'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var selected = jq(this).find('select option:selected:first');
		if(selected.length != 0){
			aData.push( selected.text() );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the checkboxes in a column */
jQuery.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var checkboxes = jq(this).find('input:checkbox');
		if(checkboxes.length != 0){
			var str = "";
			for(i=0; i < checkboxes.length; i++){
				var check = checkboxes[i];
				if (check.checked == true && check.value.length > 0){
					str += check.value + " ";
				}
			}
			aData.push( str );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

jQuery.fn.dataTableExt.afnSortData['dom-radio'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var radioButtons = jq(this).find('input:radio');
		if(radioButtons.length != 0){
			var value = "";
			for(i=0; i < radioButtons.length; i++){
				var radio = radioButtons[i];
				if (radio.checked == true){
					value = radio.value;
					break;
				}
			}
			aData.push( value );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

// setup window javascript error handler
window.onerror = errorHandler;

function errorHandler(msg,url,lno)
{
  jq("#view_div").show();
  jq("#viewpage_div").show();
  var context = getContext();
  context.unblockUI();
  showGrowl(msg + '
' + url + '
' + lno, 'Javascript Error', 'errorGrowl');
  return false;
}

// common event registering done here through JQuery ready event
jq(document).ready(function() {
	setPageBreadcrumb();

	// buttons
	jq("input:submit").button();
	jq("input:button").button();
    jq("a.button").button();

    // common ajax setup
	jq.ajaxSetup({
		  beforeSend: function() {
		     createLoading(true);
		  },
		  complete: function(){
			 createLoading(false);
		  },
		  error: function(jqXHR, textStatus, errorThrown){
			 createLoading(false);
			 showGrowl('Status: ' + textStatus + '
' + errorThrown, 'Server Response Error', 'errorGrowl');
		  }
	});

	runHiddenScripts("");
    jq("#view_div").show();
    createLoading(false);
});

<commit_msg>KULRICE-5772 - preventing onchange validation on select boxes so that Firefox and Chrome don't kick the user out of the control immediately.<commit_after>/*
 * Copyright 2005-2012 The Kuali Foundation
 *
 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
 *
 * 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.
 */
// global vars
var $dialog = null;
var jq = jQuery.noConflict();

//clear out blockUI css, using css class overrides
jQuery.blockUI.defaults.css = {};
jQuery.blockUI.defaults.overlayCSS = {};

// validation init
var pageValidatorReady = false;

//sets up the validator with the necessary default settings and methods
//note the use of onClick and onFocusout for on the fly validation client side
function setupValidator(){
	jq('#kualiForm').dirty_form({changedClass: 'dirty'});

	//Make sure form doesn't have any unsaved data when user clicks on any other portal links, closes browser or presses fwd/back browser button
	jq(window).bind('beforeunload', function(evt){
		var validateDirty = jq("[name='validateDirty']").val();
		if (validateDirty == "true")
		{
			var dirty = jq(".kr-field-attribute").find("input.dirty");
			//methodToCall check is needed to skip from normal way of unloading (cancel,save,close)
			var methodToCall = jq("[name='methodToCall']").val();
			if (dirty.length > 0 && methodToCall == null)
			{
				return "Form has unsaved data. Do you want to leave anyway?";
			}
		}
	});

	jq('#kualiForm').validate(
	{
		onsubmit: false,
		ignore: ".ignoreValid",
		onclick: function(element) {
            if(element.type != "select-one") {
			    var valid = jq(element).valid();
			    dependsOnCheck(element, new Array());
            }
		},
		onfocusout: function(element) {
			var valid = jq(element).valid();
			dependsOnCheck(element, new Array());
			if((element.type == "select-one" || element.type == "select-multiple") && jq(element).hasClass("valid")){
				applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
				showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
			}
		},
		wrapper: "li",
		highlight: function(element, errorClass, validClass) {
			jq(element).addClass(errorClass).removeClass(validClass);
            jq(element).attr("aria-invalid", "true");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 1, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 1);
		},
		unhighlight: function(element, errorClass, validClass) {
			jq(element).removeClass(errorClass).addClass(validClass);
            jq(element).attr("aria-invalid", "false");
			applyErrorColors(getAttributeId(element.id, element.type) + "_errors_div", 0, 0, 0, true);
			showFieldIcon(getAttributeId(element.id, element.type) + "_errors_div", 0);
		},
		errorPlacement: function(error, element) {
			var id = getAttributeId(element.attr('id'), element[0].type);
			//check to see if the option to use labels is on
			if (!jq("#" + id + "_errors_div").hasClass("noLabels")) {
				var label = getLabel(id);
				label = jq.trim(label);
				if (label) {
					if (label.charAt(label.length - 1) == ":") {
						label = label.slice(0, -1);
					}
					error.find("label").before(label + " - ");
				}
			}
			jq("#" + id + "_errors_div").show();
			jq("#" + id + "_errors_errorMessages").show();
			var errorList = jq("#" + id + "_errors_errorMessages ul");
			error.appendTo(errorList);
		}
	});

    jq(".required").each(function(){
        jq(this).attr("aria-required", "true");
    });

	jq(document).trigger('validationSetup');
	pageValidatorReady = true;

	jq.watermark.showAll();
}

jQuery.validator.addMethod("minExclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value > param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxInclusive", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || value <= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("minLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) >= param[0];
	}
	else{
		return true;
	}
});
jQuery.validator.addMethod("maxLengthConditional", function(value, element, param){
	if (param.length == 1 || param[1]()) {
		return this.optional(element) || this.getLength(jq.trim(value), element) <= param[0];
	}
	else{
		return true;
	}
});

// data table initialize default sorting
jQuery.fn.dataTableExt.oSort['kuali_date-asc']  = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? -1 : ((x > y) ?  1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_date-desc'] = function(a,b) {
	var date1 = a.split('/');
	var date2 = b.split('/');
	var x = (date1[2] + date1[0] + date1[1]) * 1;
	var y = (date2[2] + date2[0] + date2[1]) * 1;
	return ((x < y) ? 1 : ((x > y) ?  -1 : 0));
};

jQuery.fn.dataTableExt.oSort['kuali_percent-asc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num1 - num2;
};

jQuery.fn.dataTableExt.oSort['kuali_percent-desc'] = function(a,b) {
	var num1 = a.replace(/[^0-9]/g, '');
	var num2 = b.replace(/[^0-9]/g, '');
	num1 = (num1 == "-" || num1 === "" || isNaN(num1)) ? 0 : num1*1;
	num2 = (num2 == "-" || num2 === "" || isNaN(num2)) ? 0 : num2*1;
	return num2 - num1;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-asc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x*1;
	y = isNaN(y) ? 0 : y*1;

	return x - y;
};

jQuery.fn.dataTableExt.oSort['kuali_currency-desc'] = function(a,b) {
	/* Remove any commas (assumes that if present all strings will have a fixed number of d.p) */
	var x = a == "-" ? 0 : a.replace( /,/g, "" );
	var y = b == "-" ? 0 : b.replace( /,/g, "" );
	/* Remove the currency sign */
	x = x.substring( 1 );
	y = y.substring( 1 );
	/* Parse and return */
	x = parseFloat( x );
	y = parseFloat( y );

	x = isNaN(x) ? 0 : x;
	y = isNaN(y) ? 0 : y;

	return y - x;
};

jQuery.fn.dataTableExt.afnSortData['dom-text'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var input = jq(this).find('input:text');
		if(input.length != 0){
			aData.push( input.val() );
		}else{
            //match DataField or InputField CSS classes - respectively
			var input1 = jq(this).find('.kr-data-fld, .kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.find("span:first").text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the select options in a column */
jQuery.fn.dataTableExt.afnSortData['dom-select'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var selected = jq(this).find('select option:selected:first');
		if(selected.length != 0){
			aData.push( selected.text() );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;
}

/* Create an array with the values of all the checkboxes in a column */
jQuery.fn.dataTableExt.afnSortData['dom-checkbox'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var checkboxes = jq(this).find('input:checkbox');
		if(checkboxes.length != 0){
			var str = "";
			for(i=0; i < checkboxes.length; i++){
				var check = checkboxes[i];
				if (check.checked == true && check.value.length > 0){
					str += check.value + " ";
				}
			}
			aData.push( str );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

jQuery.fn.dataTableExt.afnSortData['dom-radio'] = function  ( oSettings, iColumn )
{
	var aData = [];
	jq( 'td:eq('+iColumn+')', oSettings.oApi._fnGetTrNodes(oSettings) ).each( function () {
		var radioButtons = jq(this).find('input:radio');
		if(radioButtons.length != 0){
			var value = "";
			for(i=0; i < radioButtons.length; i++){
				var radio = radioButtons[i];
				if (radio.checked == true){
					value = radio.value;
					break;
				}
			}
			aData.push( value );
		}else{
			var input1 = jq(this).find('.kr-field-attribute');
			if(input1.length != 0){
				aData.push(jq.trim(input1.text()));
			}else{
				aData.push( "");
			}
		}

	} );
	return aData;

}

// setup window javascript error handler
window.onerror = errorHandler;

function errorHandler(msg,url,lno)
{
  jq("#view_div").show();
  jq("#viewpage_div").show();
  var context = getContext();
  context.unblockUI();
  showGrowl(msg + '
' + url + '
' + lno, 'Javascript Error', 'errorGrowl');
  return false;
}

// common event registering done here through JQuery ready event
jq(document).ready(function() {
	setPageBreadcrumb();

	// buttons
	jq("input:submit").button();
	jq("input:button").button();
    jq("a.button").button();

    // common ajax setup
	jq.ajaxSetup({
		  beforeSend: function() {
		     createLoading(true);
		  },
		  complete: function(){
			 createLoading(false);
		  },
		  error: function(jqXHR, textStatus, errorThrown){
			 createLoading(false);
			 showGrowl('Status: ' + textStatus + '
' + errorThrown, 'Server Response Error', 'errorGrowl');
		  }
	});

	runHiddenScripts("");
    jq("#view_div").show();
    createLoading(false);
});

<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function renderAdmin( data ) {
    $(document).ready( function() {
        renderView();
        renderData( data );
    } );
}

function renderView() {
    renderStatusLine();
    var txt = "<form method='post'>

" +
    "
<input type='hidden' name='action' value='create'/>" +
    "Name: <input id='name' type='text' name='name' style='width:70%' colspan='2'/>	" +
    "SSH Port: <input id='sshPort' type='text' name='sshPort' style='width:70%' colspan='2'/>	" +
    "RMI Port: <input id='rmiPort' type='text' name='rmiPort' style='width:70%' colspan='2'/>	" +
    "Location: <input id='location' type='text' name='location' style='width:70%' colspan='2'/>	" +
    "JavaOpts: <input id='javaopts' type='text' name='javaopts' style='width:70%' colspan='2'/>	" +
    "" +
    "

Features: <input id='features' type='text' name='features' style='width:70%' colspan='2'" +
    " title='Specify initial features separated by commas.'/>	" +
    "Feature URLs: <input id='featureURLs' type='text' name='featureURLs' style='width:80%' colspan='2'" +
    " title='Specify additional feature URLs separate by commas.'/>	" +
    "<input type='button' value='Create' onclick='createInstance()'/>	" +
    "

</form>
";
    $("#plugin_content").append( txt );
    renderTable( "Karaf Instances", "instances_table", ["Pid", "Name", "SSH Port", "RMI Port", "State", "JavaOpts", "Location", "Actions"] );
    renderStatusLine();
}

function createInstance() {
    var name = document.getElementById( "name" ).value;
    var sshPort = document.getElementById( "sshPort" ).value;
    var rmiPort = document.getElementById("rmiPort").value;
    var location = document.getElementById( "location" ).value;
    var javaopts = document.getElementById( "javaopts" ).value;
    var features = document.getElementById( "features" ).value;
    var featureURLs = document.getElementById( "featureURLs" ).value;
    postCreateInstance( name, sshPort, rmiPort, location, javaopts, features, featureURLs );
}

function postCreateInstance( /* String */ name, /* String */ sshPort, /* String */ rmiPort, /* String */ location,
		/* String */ javaopts, /* String */ features, /* String */ featureURLs ) {
    $.post( pluginRoot, {"action": "create", "name": name, "sshPort": sshPort, "rmiPort": rmiPort, location": location,
                             "javaopts": javaopts, "features": features, "featureURLs": featureURLs }, function( data ) {
        renderData( data );
    }, "json" );
}

function renderStatusLine() {
    $("#plugin_content").append( "

" );
}

function renderTable( /* String */ title, /* String */ id, /* array of Strings */ columns ) {
    var txt = "

" +
    "" +
    title + "

" +
    "

";
    for ( var name in columns ) {
        txt = txt + "" + columns[name] + "	";
    }
    txt = txt + "

";
    $("#plugin_content").append( txt );
}

function renderData( /* Object */ data ) {
    renderStatusData( data.status );
    renderInstancesTableData( data.instances );
    $("#instances_table").tablesorter( {
        headers: {
            5: {
                sorter: false
            }
        },
        sortList: [[0,0]],
    } );
}

function renderStatusData( /* String */ status )  {
    $(".statusline").empty().append( status );
}

function renderInstancesTableData( /* array of Objects */ instances ) {
    $("#instances_table > tbody > tr").remove();
    for ( var idx in instances ) {
        var trElement = tr( null, {
            id: instances[idx].pid
        } );
        renderInstanceData( trElement, instances[idx] );
        $("#instances_table > tbody").append( trElement );
    }
    $("#instances_table").trigger( "update" );
}

function renderInstanceData( /* Element */ parent, /* Object */ instance ) {
    parent.appendChild( td( null, null, [ text( instance.pid ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.name ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.sshPort ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.rmiPort ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.state ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.location ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.javaopts ) ] ) );
    var actionsTd = td( null, null );
    var div = createElement( "div", null, {
        style: {
            "text-align": "left"
        }
    } );
    actionsTd.appendChild( div );

    for ( var a in instance.actions ) {
        instanceButton( div, instance.name, instance.actions[a] );
    }
    parent.appendChild( actionsTd );
}

function instanceButton( /* Element */ parent, /* String */ name, /* Obj */ action ) {
    var input = createElement( "input", null, {
        type: 'image',
        style: {
            "margin-left": "10px"
        },
        title: action.title,
        alt: action.title,
        src: imgRoot + '/bundle_' + action.image + '.png'
    } );
    $(input).click( function() {
        changeInstanceState( action.op, name )
    } );
    parent.appendChild( input );
}

function changeInstanceState( /* String */ action, /* String */ name) {
    $.post( pluginRoot, {
        "action": action,
        "name": name
    }, function( data ) {
        renderData( data );
    }, "json" );
}

<commit_msg>[KARAF-289] The look and feel of the admin web console should match the other webconsole plugins, and use one CSS for rendering. <commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function renderAdmin( data ) {
    $(document).ready( function() {
        renderView();
        renderData( data );
    } );
}

function renderView() {
    renderStatusLine();
    var txt = "<form method='post'>

" +
    "
<input type='hidden' name='action' value='create'/>" +
    "Name: <input id='name' type='text' name='name' style='width:70%' colspan='2'/>	" +
    "SSH Port: <input id='sshPort' type='text' name='sshPort' style='width:70%' colspan='2'/>	" +
    "RMI Port: <input id='rmiPort' type='text' name='rmiPort' style='width:70%' colspan='2'/>	" +
    "Location: <input id='location' type='text' name='location' style='width:70%' colspan='2'/>	" +
    "JavaOpts: <input id='javaopts' type='text' name='javaopts' style='width:70%' colspan='2'/>	" +
    "" +
    "

Features: <input id='features' type='text' name='features' style='width:70%' colspan='2'" +
    " title='Specify initial features separated by commas.'/>	" +
    "Feature URLs: <input id='featureURLs' type='text' name='featureURLs' style='width:80%' colspan='2'" +
    " title='Specify additional feature URLs separate by commas.'/>	" +
    "<input type='button' value='Create' onclick='createInstance()'/>	" +
    "

</form>";
    $("#plugin_content").append( txt );
    renderTable( "Karaf Instances", "instances_table", ["Pid", "Name", "SSH Port", "RMI Port", "State", "JavaOpts", "Location", "Actions"] );
    renderStatusLine();
}

function createInstance() {
    var name = document.getElementById( "name" ).value;
    var sshPort = document.getElementById( "sshPort" ).value;
    var rmiPort = document.getElementById("rmiPort").value;
    var location = document.getElementById( "location" ).value;
    var javaopts = document.getElementById( "javaopts" ).value;
    var features = document.getElementById( "features" ).value;
    var featureURLs = document.getElementById( "featureURLs" ).value;
    postCreateInstance( name, sshPort, rmiPort, location, javaopts, features, featureURLs );
}

function postCreateInstance( /* String */ name, /* String */ sshPort, /* String */ rmiPort, /* String */ location,
		/* String */ javaopts, /* String */ features, /* String */ featureURLs ) {
    $.post( pluginRoot, {"action": "create", "name": name, "sshPort": sshPort, "rmiPort": rmiPort, "location": location,
                             "javaopts": javaopts, "features": features, "featureURLs": featureURLs }, function( data ) {
        renderData( data );
    }, "json" );
}

function renderStatusLine() {
    $("#plugin_content").append( "

" );
}

function renderTable( /* String */ title, /* String */ id, /* array of Strings */ columns ) {
    var txt = "

" +
    "" +
    title + "

" +
    "

";
    for ( var name in columns ) {
        txt = txt + "" + columns[name] + "	";
    }
    txt = txt + "

";
    $("#plugin_content").append( txt );
}

function renderData( /* Object */ data ) {
    renderStatusData( data.status );
    renderInstancesTableData( data.instances );
    $("#instances_table").tablesorter( {
        headers: {
            5: {
                sorter: false
            }
        },
        sortList: [[0,0]],
    } );
}

function renderStatusData( /* String */ status )  {
    $(".statusline").empty().append( status );
}

function renderInstancesTableData( /* array of Objects */ instances ) {
    $("#instances_table > tbody > tr").remove();
    for ( var idx in instances ) {
        var trElement = tr( null, {
            id: instances[idx].pid
        } );
        renderInstanceData( trElement, instances[idx] );
        $("#instances_table > tbody").append( trElement );
    }
    $("#instances_table").trigger( "update" );
}

function renderInstanceData( /* Element */ parent, /* Object */ instance ) {
    parent.appendChild( td( null, null, [ text( instance.pid ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.name ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.sshPort ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.rmiPort ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.state ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.location ) ] ) );
    parent.appendChild( td( null, null, [ text( instance.javaopts ) ] ) );
    var actionsTd = td( null, null );
    var div = createElement( "div", null, {
        style: {
            "text-align": "left"
        }
    } );
    actionsTd.appendChild( div );

    for ( var a in instance.actions ) {
        instanceButton( div, instance.name, instance.actions[a] );
    }
    parent.appendChild( actionsTd );
}

function instanceButton( /* Element */ parent, /* String */ name, /* Obj */ action ) {
    var input = createElement( "input", null, {
        type: 'image',
        style: {
            "margin-left": "10px"
        },
        title: action.title,
        alt: action.title,
        src: imgRoot + '/bundle_' + action.image + '.png'
    } );
    $(input).click( function() {
        changeInstanceState( action.op, name )
    } );
    parent.appendChild( input );
}

function changeInstanceState( /* String */ action, /* String */ name) {
    $.post( pluginRoot, {
        "action": action,
        "name": name
    }, function( data ) {
        renderData( data );
    }, "json" );
}

<|endoftext|>