<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Tabs extends Component {
  state = {
    selectedTab: null,
  };

  handleSelect = tab => {
    this.setState({
      selectedTab: tab,
    });
  };

  findDefault(children) {
    if (this.defaultTab) {
      return this.defaultTab;
    }

    let firstLink;
    let firstDefaultLink;

    const traverse = child => {
      if (!child || !child.props || firstDefaultLink) {
        return;
      }

      if (child.props.to) {
        firstLink = firstLink || child.props.to;
        firstDefaultLink =
          firstDefaultLink || (child.props.default && child.props.to);
      }

      React.Children.forEach(child.props.children, traverse);
    };

    React.Children.forEach(children, traverse);

    this.defaultTab = firstDefaultLink || firstLink;
    return this.defaultTab;
  }

  transformChildren(
    children,
    {
      handleSelect,
      selectedTab,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name,
    },
  ) {
    if (typeof children !== 'object') {
      return children;
    }

    return React.Children.map(children, child => {
      if (!child) {
        return child;
      }
      if (child.props && child.props.to) {
        return React.cloneElement(child, {
          handleSelect,
          isActive: child.props.to === selectedTab,
          activeStyle: activeLinkStyle,
          disableInlineStyles,
          namespace: name,
        });
      }

      if (child.props && child.props.for) {
        return React.cloneElement(child, {
          isVisible: child.props.for === selectedTab,
          visibleStyle: visibleTabStyle,
          disableInlineStyles,
          renderActiveTabContentOnly: this.props.renderActiveTabContentOnly,
        });
      }

      return React.cloneElement(
        child,
        {},
        this.transformChildren(child.props && child.props.children, {
          handleSelect,
          selectedTab,
          activeLinkStyle,
          visibleTabStyle,
          disableInlineStyles,
          name,
        }),
      );
    });
  }

  render() {
    const {
      handleSelect: handleSelectProp,
      selectedTab: selectedTabProp,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name,
      renderActiveTabContentOnly, // eslint-disable-line
      ...divProps
    } = this.props;
    const handleSelect = handleSelectProp || this.handleSelect;
    const selectedTab =
      selectedTabProp ||
      this.state.selectedTab ||
      this.findDefault(this.props.children);

    const children = this.transformChildren(this.props.children, {
      handleSelect,
      selectedTab,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name: name,
    });

    return
{children}
;
  }
}

Tabs.propTypes = {
  name: PropTypes.string,
  handleSelect: PropTypes.func,
  selectedTab: PropTypes.string,
  activeLinkStyle: PropTypes.object,
  visibleTabStyle: PropTypes.object,
  disableInlineStyles: PropTypes.bool,
  renderActiveTabContentOnly: PropTypes.bool,
};

export default Tabs;
<commit_msg>Change in TabLink/TabContent detection<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';

import TabLink from './TabLink';
import TabContent from './TabContent';

class Tabs extends Component {
  state = {
    selectedTab: null,
  };

  handleSelect = tab => {
    this.setState({
      selectedTab: tab,
    });
  };

  findDefault(children) {
    if (this.defaultTab) {
      return this.defaultTab;
    }

    let firstLink;
    let firstDefaultLink;

    const traverse = child => {
      if (!child || !child.props || firstDefaultLink) {
        return;
      }

      if (child.type === TabLink) {
        firstLink = firstLink || child.props.to;
        firstDefaultLink =
          firstDefaultLink || (child.props.default && child.props.to);
      }

      React.Children.forEach(child.props.children, traverse);
    };

    React.Children.forEach(children, traverse);

    this.defaultTab = firstDefaultLink || firstLink;
    return this.defaultTab;
  }

  transformChildren(
    children,
    {
      handleSelect,
      selectedTab,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name,
    },
  ) {
    if (typeof children !== 'object') {
      return children;
    }

    return React.Children.map(children, child => {
      if (!child) {
        return child;
      }

      if (child.type === TabLink) {
        return React.cloneElement(child, {
          handleSelect,
          isActive: child.props.to === selectedTab,
          activeStyle: activeLinkStyle,
          disableInlineStyles,
          namespace: name,
        });
      }

      if (child.type === TabContent) {
        return React.cloneElement(child, {
          isVisible: child.props.for === selectedTab,
          visibleStyle: visibleTabStyle,
          disableInlineStyles,
          renderActiveTabContentOnly: this.props.renderActiveTabContentOnly,
        });
      }

      return React.cloneElement(
        child,
        {},
        this.transformChildren(child.props && child.props.children, {
          handleSelect,
          selectedTab,
          activeLinkStyle,
          visibleTabStyle,
          disableInlineStyles,
          name,
        }),
      );
    });
  }

  render() {
    const {
      handleSelect: handleSelectProp,
      selectedTab: selectedTabProp,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name,
      renderActiveTabContentOnly, // eslint-disable-line
      ...divProps
    } = this.props;
    const handleSelect = handleSelectProp || this.handleSelect;
    const selectedTab =
      selectedTabProp ||
      this.state.selectedTab ||
      this.findDefault(this.props.children);

    const children = this.transformChildren(this.props.children, {
      handleSelect,
      selectedTab,
      activeLinkStyle,
      visibleTabStyle,
      disableInlineStyles,
      name: name,
    });

    return
{children}
;
  }
}

Tabs.propTypes = {
  name: PropTypes.string,
  handleSelect: PropTypes.func,
  selectedTab: PropTypes.string,
  activeLinkStyle: PropTypes.object,
  visibleTabStyle: PropTypes.object,
  disableInlineStyles: PropTypes.bool,
  renderActiveTabContentOnly: PropTypes.bool,
};

export default Tabs;
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import Category from './Category';
import More from '../components/More'
import _ from 'lodash';
import { imageComposer } from '../lib/image-composer.js'
if (process.env.BROWSER) {
  require("./Tags.css")
}

export default class Tags extends Component {
  constructor(props, context) {
    super(props, context)
  }

  render() {
    const { articles, device, hasMore, loadMore } = this.props
    let cat_display = "台灣"
    if (articles && articles.length > 0) {
      return (

            <Category>{cat_display}</Category>

            { _.map(articles, (a) => {
              let image = imageComposer(a).mobileImage
              var d = new Date()
              d.setTime(a.lastPublish*1000)
              var d_str = d.toISOString().substring(0,10)
              var re = /^[\w\d]/
              var url = "https://www.twreporter.org/a/" + a.slug
              if (image) {
                return (

{a.title}

{a.excerpt}

{d_str}

                  );}
            })
            }

          {hasMore ? <More loadMore={loadMore} device={device} /> : null}

      )
    } else {
      return (

)
    }
  }
}

export { Tags };
<commit_msg>indent<commit_after>import React, { Component, PropTypes } from 'react'
import Category from './Category';
import More from '../components/More'
import _ from 'lodash';
import { imageComposer } from '../lib/image-composer.js'
if (process.env.BROWSER) {
  require("./Tags.css")
}

export default class Tags extends Component {
  constructor(props, context) {
    super(props, context)
  }

  render() {
    const { articles, device, hasMore, loadMore } = this.props
    let cat_display = "台灣"
    if (articles && articles.length > 0) {
      return (

            <Category>{cat_display}</Category>

            { _.map(articles, (a) => {
              let image = imageComposer(a).mobileImage
              var d = new Date()
              d.setTime(a.lastPublish*1000)
              var d_str = d.toISOString().substring(0,10)
              var re = /^[\w\d]/
              var url = "https://www.twreporter.org/a/" + a.slug
              if (image) {
                return (

{a.title}

{a.excerpt}

{d_str}

                  )
                }
              })
            }

          {hasMore ? <More loadMore={loadMore} device={device} /> : null}

      )
    } else {
      return (

)
    }
  }
}

export { Tags };
<|endoftext|>


<commit_before>/*
 * MIT License

 * Copyright (c) 2016 Garrett Vargas

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

module.exports = {
    // Recommended actions follow Basic Strategy, based on the rules currently in play
    HandTotal: function(cards)
    {
        var retval = { total: 0, soft: false };
        var hasAces = false;

        for (var i = 0; i < cards.length; i++) {
            retval.total += cards[i];

            // Note if there's an ace
            if (cards[i] == 1) {
                hasAces = true;
            }
        }

        // If there are aces, add 10 to the total (unless it would go over 21)
        // Note that in this case the hand is soft
        if ((retval.total <= 11) && hasAces) {
            retval.total += 10;
            retval.soft = true;
        }

        return retval;
    }
};
<commit_msg>Remove redundant file<commit_after><|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import linkState from 'react-link-state';

export default class Form extends Component {
  constructor(props, context) {
    super(props, context);
    const { user, data } = this.props;
    this.state = this.mapUser(user, data);
  }

  componentWillReceiveProps(next) {
    const { user, data } = next;
    this.setState(this.mapUser(user, data));
  }

  mapUser(user, data) {
    return data.reduce((memo, section) => {
      section.data.forEach((field) => {
        memo[field.key] = (user && user[field.key]) ? user[field.key] : '';
      });
      return memo;
    }, {});
  }

  exists(value) {
    var { team } = this.props;
    return team.some((user) => {
      return user.github.toLowerCase() === value.toLowerCase();
    });
  }

  formData() {
    const { data } = this.props;
    let formData = [];

    for (const p in data) {
      formData = formData.concat(data[p].data);
    }
    return formData;
  }

  onDelete(e) {
    const { onDelete, user } = this.props;
    e.preventDefault();
    onDelete();
  }

  onSubmit(e) {
    e.preventDefault();
    const data = this.state;
    const { setError, onSubmit, user, validators, normalizers } = this.props;

    // - Check that GitHub username does not exist.
    if (this.exists(data.github) && !user) {
      return setError(`User ${data.github} already exists.`);
    }

    // - Check all the required fields.
    const missingRequired = this.formData().filter((d) => {
      return d.required;
    }).filter((d) => {
      let contains;

      for (let key in data) {
        let value;
        if (data[key] && data[d.key]) value = data[d.key];
        if (typeof value === 'string' && value ||
            typeof value === 'object' && value.length) contains = true;
      }

      return !contains;
    });

    if (missingRequired.length) {
      const requiredList = missingRequired.reduce((memo, req) => {
        memo.push('"' + req.label + '"');
        return memo;
      }, []).join(', ');

      return setError(`Missing required fields ${requiredList}`);
    }

    // Validate
    validators(data, (err) => {
      if (err) return setError(err);

      // Normalize
      for (const key in data) {

        // Remove unfilled values
        if (!data[key]) delete data[key];

        if (typeof data[key] === 'object') {

          // Object structures (provided by "Add" inputs).
          // Ensure a value was filled. Otherwise remove it.
          data[key] = data[key].filter((d) => {
            let hasValue;
            for (let prop in d) {
              if (d[prop]) hasValue = true;
            }
            return hasValue;
          });
        }
      }

      // Client normalization
      normalizers(data, (res) => {
        onSubmit(res); // Submit!
      });
    });
  }

  radioOnChange(e) {
    const obj = {};
    let val = e.target.id;
    if (val === 'true') val = true;
    if (val === 'false') val = false;
    obj[e.target.name] = val;
    this.setState(obj);
  }

  checkboxOnChange(e) {
    const group = ReactDOM.findDOMNode(this.refs[e.target.name]).getElementsByTagName('input');
    const checked = [];
    Array.prototype.forEach.call(group, (el) => {
      if (el.checked) checked.push(el.id);
    });

    const obj = {};
    obj[e.target.name] = checked;
    this.setState(obj);
  }

  addGroupOnChange(e) {
    const group = ReactDOM.findDOMNode(this.refs[e.target.name]).getElementsByTagName('div');
    const groupSet = [];

    Array.prototype.forEach.call(group, (el) => {
      const item = el.getElementsByTagName('input');
      const pairings = [];

      // Name/Value pairings
      Array.prototype.forEach.call(item, (itm) => {
        pairings.push(itm.value);
      });

      if (pairings[0] || pairings[1]) {
        groupSet.push({
          name: pairings[0],
          value: pairings[1]
        });
      }
    });

    const obj = {};
    obj[e.target.name] = groupSet;
    this.setState(obj);
  }

  addtoAddGroup(e) {
    e.preventDefault();
    const addGroup = this.state[e.target.name] ?
      this.state[e.target.name] : [];
    addGroup.push({name: '', value: ''});

    const obj = {};
    obj[e.target.name] = addGroup;
    this.setState(obj);
  }

  render() {
    const { data, actor, user, onDelete } = this.props;

    const colN = function(length) {
      if (length === 2) return 6;
      if (length === 3) return 4;
      if (length === 4) return 3;
      if (length > 4 || length === 1) return 12;
    };

    const renderRadioGroup = function(component, field, i) {
      const n = colN(this.fields.length);
      const labelClass = 'button icon check col' + n;
      const containerClass = 'react set' + n;
      return (

          <input
            type='radio'
            name={this.key}
            id={field.key}
            defaultChecked={component.state[this.key] === field.key}
            onChange={component.radioOnChange.bind(component)}
          />
          {field.label}

      );
    };

    const renderCheckGroup = function(component, field, i) {
      const n = colN(this.fields.length);
      const labelClass = 'button icon check col' + n;
      const containerClass = 'react set' + n;

      return (

          <input
            type='checkbox'
            name={this.key}
            id={field.key}
            defaultChecked={component.state[this.key].indexOf(field.key) > -1}
            onChange={component.checkboxOnChange.bind(component)}
          />
          {field.label}

      );
    };

    const renderAddGroup = function(component, field, i) {
      return (

          <input
            type='text'
            className='col6'
            name={this.key}
            placeholder='Name'
            defaultValue={field.name}
            onChange={component.addGroupOnChange.bind(component)}
          />
          <input
            type='text'
            name={this.key}
            className='col6'
            placeholder='Value'
            defaultValue={field.value}
            onChange={component.addGroupOnChange.bind(component)}
          />

      );
    };

    const addFields = function(d, i) {
      const type = (d.type) ? d.type : 'text';
      const hidden = (type === 'hidden') ? 'hidden' : false;
      const defaultRenderer = (type === 'text' ||
                               type ==='date' ||
                               type === 'number');

      if (d.admin && !actor.admin) return;
      return (
        <fieldset id={d.key} key={i} className={`col6 pad1x ${hidden}`}>
          {d.label}
            {d.required && *}
            {d.admin && admin
            }

          {type === 'textarea' && <textarea
            className='col12'
            placeholder={d.label}
            required={d.required}
            valueLink={linkState(this, d.key)}
          />}
          {type === 'hidden' && <input
            type={type}
            className='hidden'
            valueLink={linkState(this, d.key)}
          />}
          {type === 'radio' && <fieldset
            className='radio-pill pill clearfix col12'>
            {d.fields.map(renderRadioGroup.bind(d, this))}
          </fieldset>}
          {type === 'checkbox' && <fieldset
            ref={d.key}
            className='checkbox-pill pill clearfix col12'>
            {d.fields.map(renderCheckGroup.bind(d, this))}
          </fieldset>}
          {type === 'add' && <fieldset ref={d.key}>
            {linkState(this, d.key).value && linkState(this, d.key).value.map(renderAddGroup.bind(d, this))}
            <button
              name={d.key}
              onClick={this.addtoAddGroup.bind(this)}
              className='button icon plus col12'>
              Add
            </button>
          </fieldset>}
          {defaultRenderer && <input
            type={type}
            className='col12'
            placeholder={d.label}
            required={d.required}
            valueLink={linkState(this, d.key)}
          />}
        </fieldset>
      );
    }.bind(this);

    const renderSection = function(section, i) {
      return (
        <fieldset className='fill-grey keyline-top pad1x pad4y' key={i}>

{section.section}

          <fieldset className='col12 clearfix'>
            {section.data.map(addFields)}
          </fieldset>
        </fieldset>
      );
    };

    return (
      <form onSubmit={this.onSubmit.bind(this)}>
        {data.map(renderSection)}
        <fieldset className='fill-light pad2 round-bottom clearfix quiet dark'>

            {user && <button
              className='button fill-red pad2x'
              onClick={this.onDelete.bind(this)}>
              Delete user
            </button>}

            <input type='submit' className='button col12' />

        </fieldset>
      </form>
    );
  }
}

Form.propTypes = {
  data: PropTypes.array.isRequired,
  setError: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired,
  team: PropTypes.array.isRequired,
  actor: PropTypes.object.isRequired,
  onDelete: PropTypes.func,
  user: PropTypes.object,
  validators: PropTypes.func,
  normalizers: PropTypes.func
}
<commit_msg>Dismiss link<commit_after>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import linkState from 'react-link-state';

export default class Form extends Component {
  constructor(props, context) {
    super(props, context);
    const { user, data } = this.props;
    this.state = this.mapUser(user, data);
  }

  componentWillReceiveProps(next) {
    const { user, data } = next;
    this.setState(this.mapUser(user, data));
  }

  mapUser(user, data) {
    return data.reduce((memo, section) => {
      section.data.forEach((field) => {
        memo[field.key] = (user && user[field.key]) ? user[field.key] : '';
      });
      return memo;
    }, {});
  }

  exists(value) {
    var { team } = this.props;
    return team.some((user) => {
      return user.github.toLowerCase() === value.toLowerCase();
    });
  }

  formData() {
    const { data } = this.props;
    let formData = [];

    for (const p in data) {
      formData = formData.concat(data[p].data);
    }
    return formData;
  }

  onDelete(e) {
    const { onDelete, user } = this.props;
    e.preventDefault();
    onDelete();
  }

  onSubmit(e) {
    e.preventDefault();
    const data = this.state;
    const { setError, onSubmit, user, validators, normalizers } = this.props;

    // - Check that GitHub username does not exist.
    if (this.exists(data.github) && !user) {
      return setError(`User ${data.github} already exists.`);
    }

    // - Check all the required fields.
    const missingRequired = this.formData().filter((d) => {
      return d.required;
    }).filter((d) => {
      let contains;

      for (let key in data) {
        let value;
        if (data[key] && data[d.key]) value = data[d.key];
        if (typeof value === 'string' && value ||
            typeof value === 'object' && value.length) contains = true;
      }

      return !contains;
    });

    if (missingRequired.length) {
      const requiredList = missingRequired.reduce((memo, req) => {
        memo.push('"' + req.label + '"');
        return memo;
      }, []).join(', ');

      return setError(`Missing required fields ${requiredList}`);
    }

    // Validate
    validators(data, (err) => {
      if (err) return setError(err);

      // Normalize
      for (const key in data) {

        // Remove unfilled values
        if (!data[key]) delete data[key];

        if (typeof data[key] === 'object') {

          // Object structures (provided by "Add" inputs).
          // Ensure a value was filled. Otherwise remove it.
          data[key] = data[key].filter((d) => {
            let hasValue;
            for (let prop in d) {
              if (d[prop]) hasValue = true;
            }
            return hasValue;
          });
        }
      }

      // Client normalization
      normalizers(data, (res) => {
        onSubmit(res); // Submit!
      });
    });
  }

  radioOnChange(e) {
    const obj = {};
    let val = e.target.id;
    if (val === 'true') val = true;
    if (val === 'false') val = false;
    obj[e.target.name] = val;
    this.setState(obj);
  }

  checkboxOnChange(e) {
    const group = ReactDOM.findDOMNode(this.refs[e.target.name]).getElementsByTagName('input');
    const checked = [];
    Array.prototype.forEach.call(group, (el) => {
      if (el.checked) checked.push(el.id);
    });

    const obj = {};
    obj[e.target.name] = checked;
    this.setState(obj);
  }

  addGroupOnChange(e) {
    const group = ReactDOM.findDOMNode(this.refs[e.target.name]).getElementsByTagName('div');
    const groupSet = [];

    Array.prototype.forEach.call(group, (el) => {
      const item = el.getElementsByTagName('input');
      const pairings = [];

      // Name/Value pairings
      Array.prototype.forEach.call(item, (itm) => {
        pairings.push(itm.value);
      });

      if (pairings[0] || pairings[1]) {
        groupSet.push({
          name: pairings[0],
          value: pairings[1]
        });
      }
    });

    const obj = {};
    obj[e.target.name] = groupSet;
    this.setState(obj);
  }

  addtoAddGroup(e) {
    e.preventDefault();
    const addGroup = this.state[e.target.name] ?
      this.state[e.target.name] : [];
    addGroup.push({name: '', value: ''});

    const obj = {};
    obj[e.target.name] = addGroup;
    this.setState(obj);
  }

  removeFromAddGroup(e) {
    e.preventDefault();
    const index = parseInt(e.target.getAttribute('data-index'), 10);
    var obj = {};
    obj[e.target.name] = this.state[e.target.name].filter((_, i) => i !== index);
    this.setState(obj, this.render);
  }

  render() {
    const { data, actor, user, onDelete } = this.props;

    const colN = function(length) {
      if (length === 2) return 6;
      if (length === 3) return 4;
      if (length === 4) return 3;
      if (length > 4 || length === 1) return 12;
    };

    const renderRadioGroup = function(component, field, i) {
      const n = colN(this.fields.length);
      const labelClass = 'button icon check col' + n;
      const containerClass = 'react set' + n;
      return (

          <input
            type='radio'
            name={this.key}
            id={field.key}
            defaultChecked={component.state[this.key] === field.key}
            onChange={component.radioOnChange.bind(component)}
          />
          {field.label}

      );
    };

    const renderCheckGroup = function(component, field, i) {
      const n = colN(this.fields.length);
      const labelClass = 'button icon check col' + n;
      const containerClass = 'react set' + n;

      return (

          <input
            type='checkbox'
            name={this.key}
            id={field.key}
            defaultChecked={component.state[this.key].indexOf(field.key) > -1}
            onChange={component.checkboxOnChange.bind(component)}
          />
          {field.label}

      );
    };

    const renderAddGroup = function(component, field, i) {
      return (

          <button
            name={this.key}
            data-index={i}
            style={{width: '40px'}}
            onClick={component.removeFromAddGroup.bind(component)}
            className='icon close pin-right round-right'
          />
          <input
            type='text'
            className='col6'
            name={this.key}
            placeholder='Name'
            value={field.name}
            onChange={component.addGroupOnChange.bind(component)}
          />
          <input
            type='text'
            name={this.key}
            className='col6'
            placeholder='Value'
            value={field.value}
            onChange={component.addGroupOnChange.bind(component)}
          />

      );
    };

    const addFields = function(d, i) {
      const type = (d.type) ? d.type : 'text';
      const hidden = (type === 'hidden') ? 'hidden' : false;
      const defaultRenderer = (type === 'text' ||
                               type ==='date' ||
                               type === 'number');

      if (d.admin && !actor.admin) return;
      return (
        <fieldset id={d.key} key={i} className={`col6 pad1x ${hidden}`}>
          {d.label}
            {d.required && *}
            {d.admin && admin
            }

          {type === 'textarea' && <textarea
            className='col12'
            placeholder={d.label}
            required={d.required}
            valueLink={linkState(this, d.key)}
          />}
          {type === 'hidden' && <input
            type={type}
            className='hidden'
            valueLink={linkState(this, d.key)}
          />}
          {type === 'radio' && <fieldset
            className='radio-pill pill clearfix col12'>
            {d.fields.map(renderRadioGroup.bind(d, this))}
          </fieldset>}
          {type === 'checkbox' && <fieldset
            ref={d.key}
            className='checkbox-pill pill clearfix col12'>
            {d.fields.map(renderCheckGroup.bind(d, this))}
          </fieldset>}
          {type === 'add' && <fieldset ref={d.key}>
            {linkState(this, d.key).value && linkState(this, d.key).value.map(renderAddGroup.bind(d, this))}
            <button
              name={d.key}
              onClick={this.addtoAddGroup.bind(this)}
              className='button icon plus col12'>
              Add
            </button>
          </fieldset>}
          {defaultRenderer && <input
            type={type}
            className='col12'
            placeholder={d.label}
            required={d.required}
            valueLink={linkState(this, d.key)}
          />}
        </fieldset>
      );
    }.bind(this);

    const renderSection = function(section, i) {
      return (
        <fieldset className='fill-grey keyline-top pad1x pad4y' key={i}>

{section.section}

          <fieldset className='col12 clearfix'>
            {section.data.map(addFields)}
          </fieldset>
        </fieldset>
      );
    };

    return (
      <form onSubmit={this.onSubmit.bind(this)}>
        {data.map(renderSection)}
        <fieldset className='fill-light pad2 round-bottom clearfix quiet dark'>

            {user && <button
              className='button fill-red pad2x'
              onClick={this.onDelete.bind(this)}>
              Delete user
            </button>}

            <input type='submit' className='button col12' />

        </fieldset>
      </form>
    );
  }
}

Form.propTypes = {
  data: PropTypes.array.isRequired,
  setError: PropTypes.func.isRequired,
  onSubmit: PropTypes.func.isRequired,
  team: PropTypes.array.isRequired,
  actor: PropTypes.object.isRequired,
  onDelete: PropTypes.func,
  user: PropTypes.object,
  validators: PropTypes.func,
  normalizers: PropTypes.func
}
<|endoftext|>


<commit_before>import React from 'react';

import KataGroups from './katagroups';
import Katas from './katas.js';
import Kata from './kata.js';

export default class Page extends React.Component {
  render() {
    const {kataGroups} = this.props;
    const {selectedGroup=kataGroups[0]} = kataGroups;
    return

        <KataGroups kataGroups={kataGroups}/>
        <Katas kataGroup={selectedGroup} />
        <Kata />

;
  }
}

import {default as KataGroupsData} from '../katagroups.js';
Page.propTypes = {
  kataGroups: React.PropTypes.instanceOf(KataGroupsData).isRequired
};
<commit_msg>Move the propTypes as a static property.<commit_after>import React from 'react';

import KataGroups from './katagroups';
import Katas from './katas.js';
import Kata from './kata.js';
import {default as KataGroupsData} from '../katagroups.js';

export default class Page extends React.Component {
  static propTypes = {
    kataGroups: React.PropTypes.instanceOf(KataGroupsData).isRequired
  };

  render() {
    const {kataGroups} = this.props;
    const {selectedGroup=kataGroups[0]} = kataGroups;
    return

        <KataGroups kataGroups={kataGroups}/>
        <Katas kataGroup={selectedGroup} />
        <Kata />

;
  }
}
<|endoftext|>


<commit_before>;(function( win, $ ) {

	function featureTest( property, value, noPrefixes ) {
		// Thanks Modernizr! https://github.com/phistuck/Modernizr/commit/3fb7217f5f8274e2f11fe6cfeda7cfaf9948a1f5
		var prop = property + ':',
			el = document.createElement( 'test' ),
			mStyle = el.style;

		if( !noPrefixes ) {
			mStyle.cssText = prop + [ '-webkit-', '-moz-', '-ms-', '-o-', '' ].join( value + ';' + prop ) + value + ';';
		} else {
			mStyle.cssText = prop + value;
		}
		return mStyle[ property ].indexOf( value ) !== -1;
	}

	function getPx( unit ) {
		return parseInt( unit, 10 ) || 0;
	}

	var S = {
		classes: {
			plugin: 'fixedsticky',
			active: 'fixedsticky-on',
			inactive: 'fixedsticky-off',
			clone: 'fixedsticky-dummy',
			withoutFixedFixed: 'fixedsticky-withoutfixedfixed'
		},
		keys: {
			offset: 'fixedStickyOffset',
			position: 'fixedStickyPosition'
		},
		tests: {
			sticky: featureTest( 'position', 'sticky' ),
			fixed: featureTest( 'position', 'fixed', true )
		},
		// Thanks jQuery!
		getScrollTop: function() {
			var prop = 'pageYOffset',
				method = 'scrollTop';
			return win ? (prop in win) ? win[ prop ] :
				win.document.documentElement[ method ] :
				win.document.body[ method ];
		},
		bypass: function() {
			// Check native sticky, check fixed and if fixed-fixed is also included on the page and is supported
			return ( S.tests.sticky && !S.optOut ) ||
				!S.tests.fixed ||
				win.FixedFixed && !$( win.document.documentElement ).hasClass( 'fixed-supported' );
		},
		update: function( el ) {
			if( !el.offsetWidth ) { return; }

			var $el = $( el ),
				height = $el.outerHeight(),
				initialOffset = $el.data( S.keys.offset ),
				scroll = S.getScrollTop(),
				isAlreadyOn = $el.is( '.' + S.classes.active ),
				toggle = function( turnOn ) {
					$el[ turnOn ? 'addClass' : 'removeClass' ]( S.classes.active )
						[ !turnOn ? 'addClass' : 'removeClass' ]( S.classes.inactive );
				},
				viewportHeight = $( window ).height(),
				position = $el.data( S.keys.position ),
				skipSettingToFixed,
				elTop,
				elBottom,
				$parent = $el.parent(),
				parentOffset = $parent.offset().top,
				parentHeight = $parent.outerHeight();

			if( !initialOffset ) {
				initialOffset = $el.offset().top;
				$el.data( S.keys.offset, initialOffset );
				$el.after( $( '<div>' ).addClass( S.classes.clone ).height( height ) );
			}

			if( !position ) {
				// Some browsers require fixed/absolute to report accurate top/left values.
				skipSettingToFixed = $el.css( 'top' ) !== 'auto' || $el.css( 'bottom' ) !== 'auto';

				if( !skipSettingToFixed ) {
					$el.css( 'position', 'fixed' );
				}

				position = {
					top: $el.css( 'top' ) !== 'auto',
					bottom: $el.css( 'bottom' ) !== 'auto'
				};

				if( !skipSettingToFixed ) {
					$el.css( 'position', '' );
				}

				$el.data( S.keys.position, position );
			}

			function isFixedToTop() {
				var offsetTop = scroll + elTop;

				// Initial Offset Top
				return initialOffset < offsetTop &&
					// Container Bottom
					offsetTop + height <= parentOffset + parentHeight;
			}

			function isFixedToBottom() {
				// Initial Offset Top + Height
				return initialOffset + ( height || 0 ) > scroll + viewportHeight - elBottom &&
					// Container Top
					scroll + viewportHeight - elBottom >= parentOffset + ( height || 0 );
			}

			elTop = getPx( $el.css( 'top' ) );
			elBottom = getPx( $el.css( 'bottom' ) );

			if( position.top && isFixedToTop() || position.bottom && isFixedToBottom() ) {
				if( !isAlreadyOn ) {
					toggle( true );
				}
			} else {
				if( isAlreadyOn ) {
					toggle( false );
				}
			}
		},
		destroy: function( el ) {
			var $el = $( el );
			if (S.bypass()) {
				return;
			}

			$( win ).unbind( '.fixedsticky' );

			return $el.each(function() {
				$( this )
					.removeData( [ S.keys.offset, S.keys.position ] )
					.removeClass( S.classes.active )
					.removeClass( S.classes.inactive )
					.nextUntil( S.classes.clone ).remove();
			});
		},
		init: function( el ) {
			var $el = $( el );

			if( S.bypass() ) {
				return;
			}

			return $el.each(function() {
				var _this = this;
				$( win ).bind( 'scroll.fixedsticky', function() {
					S.update( _this );
				}).trigger( 'scroll.fixedsticky' );

				$( win ).bind( 'resize.fixedsticky', function() {
					if( $el.is( '.' + S.classes.active ) ) {
						S.update( _this );
					}
				});
			});
		}
	};

	win.FixedSticky = S;

	// Plugin
	$.fn.fixedsticky = function( method ) {
		if ( typeof S[ method ] === 'function') {
			return S[ method ].call( S, this);
		} else if ( typeof method === 'object' || ! method ) {
			return S.init.call( S, this );
		} else {
			throw new Error( 'Method `' +  method + '` does not exist on jQuery.fixedsticky' );
		}
	};

	// Add fallback when fixed-fixed is not available.
	if( !win.FixedFixed ) {
		$( win.document.documentElement ).addClass( S.classes.withoutFixedFixed );
	}

})( this, jQuery );<commit_msg>Fixes #11<commit_after>;(function( win, $ ) {

	function featureTest( property, value, noPrefixes ) {
		// Thanks Modernizr! https://github.com/phistuck/Modernizr/commit/3fb7217f5f8274e2f11fe6cfeda7cfaf9948a1f5
		var prop = property + ':',
			el = document.createElement( 'test' ),
			mStyle = el.style;

		if( !noPrefixes ) {
			mStyle.cssText = prop + [ '-webkit-', '-moz-', '-ms-', '-o-', '' ].join( value + ';' + prop ) + value + ';';
		} else {
			mStyle.cssText = prop + value;
		}
		return mStyle[ property ].indexOf( value ) !== -1;
	}

	function getPx( unit ) {
		return parseInt( unit, 10 ) || 0;
	}

	var S = {
		classes: {
			plugin: 'fixedsticky',
			active: 'fixedsticky-on',
			inactive: 'fixedsticky-off',
			clone: 'fixedsticky-dummy',
			withoutFixedFixed: 'fixedsticky-withoutfixedfixed'
		},
		keys: {
			offset: 'fixedStickyOffset',
			position: 'fixedStickyPosition'
		},
		tests: {
			sticky: featureTest( 'position', 'sticky' ),
			fixed: featureTest( 'position', 'fixed', true )
		},
		// Thanks jQuery!
		getScrollTop: function() {
			var prop = 'pageYOffset',
				method = 'scrollTop';
			return win ? (prop in win) ? win[ prop ] :
				win.document.documentElement[ method ] :
				win.document.body[ method ];
		},
		bypass: function() {
			// Check native sticky, check fixed and if fixed-fixed is also included on the page and is supported
			return ( S.tests.sticky && !S.optOut ) ||
				!S.tests.fixed ||
				win.FixedFixed && !$( win.document.documentElement ).hasClass( 'fixed-supported' );
		},
		update: function( el ) {
			if( !el.offsetWidth ) { return; }

			var $el = $( el ),
				height = $el.outerHeight(),
				initialOffset = $el.data( S.keys.offset ),
				scroll = S.getScrollTop(),
				isAlreadyOn = $el.is( '.' + S.classes.active ),
				toggle = function( turnOn ) {
					$el[ turnOn ? 'addClass' : 'removeClass' ]( S.classes.active )
						[ !turnOn ? 'addClass' : 'removeClass' ]( S.classes.inactive );
				},
				viewportHeight = $( window ).height(),
				position = $el.data( S.keys.position ),
				skipSettingToFixed,
				elTop,
				elBottom,
				$parent = $el.parent(),
				parentOffset = $parent.offset().top,
				parentHeight = $parent.outerHeight();

			if( !initialOffset ) {
				initialOffset = $el.offset().top;
				$el.data( S.keys.offset, initialOffset );
				$el.after( $( '<div>' ).addClass( S.classes.clone ).height( height ) );
			}

			if( !position ) {
				// Some browsers require fixed/absolute to report accurate top/left values.
				skipSettingToFixed = $el.css( 'top' ) !== 'auto' || $el.css( 'bottom' ) !== 'auto';

				if( !skipSettingToFixed ) {
					$el.css( 'position', 'fixed' );
				}

				position = {
					top: $el.css( 'top' ) !== 'auto',
					bottom: $el.css( 'bottom' ) !== 'auto'
				};

				if( !skipSettingToFixed ) {
					$el.css( 'position', '' );
				}

				$el.data( S.keys.position, position );
			}

			function isFixedToTop() {
				var offsetTop = scroll + elTop;

				// Initial Offset Top
				return initialOffset < offsetTop &&
					// Container Bottom
					offsetTop + height <= parentOffset + parentHeight;
			}

			function isFixedToBottom() {
				// Initial Offset Top + Height
				return initialOffset + ( height || 0 ) > scroll + viewportHeight - elBottom &&
					// Container Top
					scroll + viewportHeight - elBottom >= parentOffset + ( height || 0 );
			}

			elTop = getPx( $el.css( 'top' ) );
			elBottom = getPx( $el.css( 'bottom' ) );

			if( position.top && isFixedToTop() || position.bottom && isFixedToBottom() ) {
				if( !isAlreadyOn ) {
					toggle( true );
				}
			} else {
				if( isAlreadyOn ) {
					toggle( false );
				}
			}
		},
		destroy: function( el ) {
			var $el = $( el );
			if (S.bypass()) {
				return;
			}

			$( win ).unbind( '.fixedsticky' );

			return $el.each(function() {
				$( this )
					.removeData( [ S.keys.offset, S.keys.position ] )
					.removeClass( S.classes.active )
					.removeClass( S.classes.inactive )
					.next( '.' + S.classes.clone ).remove();
			});
		},
		init: function( el ) {
			var $el = $( el );

			if( S.bypass() ) {
				return;
			}

			return $el.each(function() {
				var _this = this;
				$( win ).bind( 'scroll.fixedsticky', function() {
					S.update( _this );
				}).trigger( 'scroll.fixedsticky' );

				$( win ).bind( 'resize.fixedsticky', function() {
					if( $el.is( '.' + S.classes.active ) ) {
						S.update( _this );
					}
				});
			});
		}
	};

	win.FixedSticky = S;

	// Plugin
	$.fn.fixedsticky = function( method ) {
		if ( typeof S[ method ] === 'function') {
			return S[ method ].call( S, this);
		} else if ( typeof method === 'object' || ! method ) {
			return S.init.call( S, this );
		} else {
			throw new Error( 'Method `' +  method + '` does not exist on jQuery.fixedsticky' );
		}
	};

	// Add fallback when fixed-fixed is not available.
	if( !win.FixedFixed ) {
		$( win.document.documentElement ).addClass( S.classes.withoutFixedFixed );
	}

})( this, jQuery );<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;

const dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);

// https://developer.mozilla.org/en/Using_JavaScript_code_modules
var EXPORTED_SYMBOLS = ["Storage", "StorageService", "TextService"];

Components.utils["import"]("resource://firebug/firebug-trace-service.js");
var FBTrace = traceConsoleService.getTracer("extensions.firebug");

// ********************************************************************************************* //
// Implementation

/**
 * http://dev.w3.org/html5/webstorage/#storage-0
 * interface Storage {
 *     readonly attribute unsigned long length;
 *     getter DOMString key(in unsigned long index);
 *     getter any getItem(in DOMString key);
 *     setter creator void setItem(in DOMString key, in any data);
 *     deleter void removeItem(in DOMString key);
 *     void clear();
 * };
 */
function Storage(leafName)
{
    this.leafName = leafName;
    this.objectTable = {};
}

Storage.prototype =
{
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Read

    get length()
    {
        return this.key(-1);
    },

    key: function(index)
    {
        var i = 0;
        for (var p in this.objectTable)
        {
            if (i === index)
                return p;
            i++;
        }
        return (index < 0 ? i : null);
    },

    getItem: function(key)
    {
        return this.objectTable[key];
    },

    getKeys: function()
    {
        var keys = [];

        for (var p in this.objectTable)
            if (this.objectTable.hasOwnProperty(p))
                keys.push(p);

        return keys;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Write

    setItem: function(key, data)
    {
        this.objectTable[key] = data;
        StorageService.setStorage(this);
    },

    removeItem: function(key)
    {
        delete this.objectTable[key];
        StorageService.setStorage(this);
    },

    clear: function(now)
    {
        this.objectTable = {};
        StorageService.setStorage(this, now);
    },
};

// ********************************************************************************************* //

/**
 * var store = StorageService.getStorage(leafName);
 * store.setItem("foo", bar);  // writes to disk
 */
var StorageService =
{
    getStorage: function(leafName)
    {
        var store = new Storage(leafName);

        try
        {
            var obj = ObjectPersister.readObject(leafName);
            if (obj)
                store.objectTable = obj;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
            {
                FBTrace.sysout("StorageService.getStorage; EXCEPTION for " + leafName +
                    ": " + err, err);
            }
        }

        return store;
    },

    setStorage: function(store, now)
    {
        if (!store || !store.leafName || !store.objectTable)
            throw new Error("StorageService.setStorage requires Storage Object argument");

        if (now)
            ObjectPersister.writeNow(store.leafName,  store.objectTable);
        else
            ObjectPersister.writeObject(store.leafName,  store.objectTable);
    },

    removeStorage: function(leafName)
    {
        ObjectPersister.deleteObject(leafname);
    },
};

// ********************************************************************************************* //
// Implementation

/**
 * @class Represents an internal Firebug persistence service.
 */
var ObjectPersister =
{
    getProfileDirectory: function()
    {
        var file = dirService.get("ProfD", Ci.nsIFile);
        return file;
    },

    getFileInDirectory: function(file, path)  // forward slash separated
    {
        var segs = path.split('/');
        for (var i = 0; i < segs.length; i++)
        {
            file.append(segs[i]);
        }
        return file;
    },

    getFileInProfileDirectory: function(path)
    {
        // Get persistence file stored within the profile directory.
        var file = ObjectPersister.getProfileDirectory();
        file = ObjectPersister.getFileInDirectory(file, path);
        if (FBTrace.DBG_STORAGE)
            FBTrace.sysout("ObjectPersister getFileInProfileDirectory("+path+")="+file.path);

        return file;
    },

    readObject: function(leafName)
    {
        if (FBTrace.DBG_STORAGE)
            FBTrace.sysout("ObjectPersister read from leafName "+leafName);

        var file = ObjectPersister.getFileInProfileDirectory("firebug/"+leafName);

        if (!file.exists())
        {
            file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
            if (FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.readTextFromFile file created " + file.path);
            return;
        }

        var obj = this.readObjectFromFile(file);
        return obj;
    },

    readObjectFromFile: function(file)
    {
        var text = ObjectPersister.readTextFromFile(file);
        if (!text)
            return null;

        var obj = JSON.parse(text);
        if (!obj)
            return;

        if (FBTrace.DBG_STORAGE)
            FBTrace.sysout("PersistedObject loaded from " + file.path+" got text "+text, obj);

        return obj;
    },

    readTextFromFile: function(file)
    {
        try
        {
            var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                .createInstance(Ci.nsIFileInputStream);
            var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
                .createInstance(Ci.nsIConverterInputStream);

            // Initialize input stream.
            inputStream.init(file, 0x01 | 0x08, 0666, 0); // read, create
            cstream.init(inputStream, "UTF-8", 0, 0);

            // Load  json.
            var json = "";
            var data = {};
            while (cstream.readString(-1, data) != 0)
                json += data.value;

            inputStream.close();

            return json;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.initialize; EXCEPTION", err);
        }
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    // Batch the writes for each event loop
    writeDelay: 250,

    writeObject: function(leafName, obj)
    {
        if (this.isPrivateBrowsing())
            throw new Error("No storage is written while in private browsing mode");

        if (ObjectPersister.flushTimeout)
            return;

        ObjectPersister.flushTimeout = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

        var writeOnTimeout = ObjectPersister.getWriteOnTimeout(leafName, obj);
        ObjectPersister.flushTimeout.init(writeOnTimeout, ObjectPersister.writeDelay,
            Ci.nsITimer.TYPE_ONE_SHOT);
    },

    getWriteOnTimeout: function(leafName, obj)
    {
        var writerClosure =
        {
            leafName: leafName,
            obj: obj,
            observe: function(timer, topic, data)
            {
                ObjectPersister.writeNow(writerClosure.leafName, writerClosure.obj);
                delete ObjectPersister.flushTimeout;
            }
        };
        return writerClosure;
    },

    writeNow: function(leafName, obj)
    {
        try
        {
            // Convert data to JSON.
            var jsonString = JSON.stringify(obj);
            var file = ObjectPersister.getFileInProfileDirectory("firebug/"+leafName);
            ObjectPersister.writeTextToFile(file, jsonString);
        }
        catch(exc)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeNow; EXCEPTION for " + leafName + ": " +
                    exc, {exception: exc, obj: obj});
        }
    },

    writeTextToFile: function(file, string)
    {
        try
        {
            // Initialize output stream.
            var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
                .createInstance(Ci.nsIFileOutputStream);
            outputStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate

            // Store JSON
            outputStream.write(string, string.length);
            outputStream.close();

            if (FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeNow to " + file.path, string);

            return file.path;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeTextToFile; EXCEPTION for " + file.path +
                    ": "+err, {exception: err, string: string});
        }
    },

    isPrivateBrowsing: function()
    {
        try
        {
            // Unfortunatelly the "firebug/chrome/privacy" module can't be used
            // since this scope is JavaScript code module.
            // xxxHonza: storageService should be converted into AMD (but it's used
            // in firebug-service, which is also JS code module).
            var pbs = Components.classes["@mozilla.org/privatebrowsing;1"]
                .getService(Components.interfaces.nsIPrivateBrowsingService);
            return pbs.privateBrowsingEnabled;
        }
        catch (e)
        {
        }

        return false;
    }
};

// ********************************************************************************************* //

var TextService =
{
    readText: ObjectPersister.readTextFromFile,
    writeText: ObjectPersister.writeTextToFile,
    getProfileDirectory: ObjectPersister.getProfileDirectory,
    getFileInDirectory: ObjectPersister.getFileInDirectory,
};

// ********************************************************************************************* //
<commit_msg>Cleaned up code by using FileUtils<commit_after>/* See license.txt for terms of usage */

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;

const dirService = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);

// https://developer.mozilla.org/en/Using_JavaScript_code_modules
var EXPORTED_SYMBOLS = ["Storage", "StorageService", "TextService"];

Components.utils.import("resource://firebug/firebug-trace-service.js");
Components.utils.import("resource://gre/modules/FileUtils.jsm");

var FBTrace = traceConsoleService.getTracer("extensions.firebug");

// ********************************************************************************************* //
// Implementation

/**
 * http://dev.w3.org/html5/webstorage/#storage-0
 * interface Storage {
 *     readonly attribute unsigned long length;
 *     getter DOMString key(in unsigned long index);
 *     getter any getItem(in DOMString key);
 *     setter creator void setItem(in DOMString key, in any data);
 *     deleter void removeItem(in DOMString key);
 *     void clear();
 * };
 */
function Storage(leafName)
{
    this.leafName = leafName;
    this.objectTable = {};
}

Storage.prototype =
{
    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Read

    get length()
    {
        return this.key(-1);
    },

    key: function(index)
    {
        var i = 0;
        for (var p in this.objectTable)
        {
            if (i === index)
                return p;
            i++;
        }
        return (index < 0 ? i : null);
    },

    getItem: function(key)
    {
        return this.objectTable[key];
    },

    getKeys: function()
    {
        var keys = [];

        for (var p in this.objectTable)
            if (this.objectTable.hasOwnProperty(p))
                keys.push(p);

        return keys;
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
    // Write

    setItem: function(key, data)
    {
        this.objectTable[key] = data;
        StorageService.setStorage(this);
    },

    removeItem: function(key)
    {
        delete this.objectTable[key];
        StorageService.setStorage(this);
    },

    clear: function(now)
    {
        this.objectTable = {};
        StorageService.setStorage(this, now);
    }
};

// ********************************************************************************************* //

/**
 * var store = StorageService.getStorage(leafName);
 * store.setItem("foo", bar);  // writes to disk
 */
var StorageService =
{
    getStorage: function(leafName)
    {
        var store = new Storage(leafName);

        try
        {
            var obj = ObjectPersister.readObject(leafName);
            if (obj)
                store.objectTable = obj;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
            {
                FBTrace.sysout("StorageService.getStorage; EXCEPTION for " + leafName +
                    ": " + err, err);
            }
        }

        return store;
    },

    setStorage: function(store, now)
    {
        if (!store || !store.leafName || !store.objectTable)
            throw new Error("StorageService.setStorage requires Storage Object argument");

        if (now)
            ObjectPersister.writeNow(store.leafName,  store.objectTable);
        else
            ObjectPersister.writeObject(store.leafName,  store.objectTable);
    },

    removeStorage: function(leafName)
    {
        ObjectPersister.deleteObject(leafname);
    }
};

// ********************************************************************************************* //
// Implementation

/**
 * @class Represents an internal Firebug persistence service.
 */
var ObjectPersister =
{
    readObject: function(leafName)
    {
        if (FBTrace.DBG_STORAGE)
            FBTrace.sysout("ObjectPersister read from leafName "+leafName);

        var file = FileUtils.getFile("ProfD", ["firebug", leafName]);

        if (!file.exists())
        {
            file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0666);
            if (FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.readTextFromFile file created " + file.path);
            return;
        }

        var obj = this.readObjectFromFile(file);
        return obj;
    },

    readObjectFromFile: function(file)
    {
        var text = ObjectPersister.readTextFromFile(file);
        if (!text)
            return null;

        var obj = JSON.parse(text);
        if (!obj)
            return;

        if (FBTrace.DBG_STORAGE)
            FBTrace.sysout("PersistedObject loaded from " + file.path+" got text "+text, obj);

        return obj;
    },

    readTextFromFile: function(file)
    {
        try
        {
            var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                .createInstance(Ci.nsIFileInputStream);
            var cstream = Cc["@mozilla.org/intl/converter-input-stream;1"]
                .createInstance(Ci.nsIConverterInputStream);

            // Initialize input stream.
            inputStream.init(file, 0x01 | 0x08, 0666, 0); // read, create
            cstream.init(inputStream, "UTF-8", 0, 0);

            // Load  json.
            var json = "";
            var data = {};
            while (cstream.readString(-1, data) != 0)
                json += data.value;

            inputStream.close();

            return json;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.initialize; EXCEPTION", err);
        }
    },

    // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

    // Batch the writes for each event loop
    writeDelay: 250,

    writeObject: function(leafName, obj)
    {
        if (this.isPrivateBrowsing())
            throw new Error("No storage is written while in private browsing mode");

        if (ObjectPersister.flushTimeout)
            return;

        ObjectPersister.flushTimeout = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);

        var writeOnTimeout = ObjectPersister.getWriteOnTimeout(leafName, obj);
        ObjectPersister.flushTimeout.init(writeOnTimeout, ObjectPersister.writeDelay,
            Ci.nsITimer.TYPE_ONE_SHOT);
    },

    getWriteOnTimeout: function(leafName, obj)
    {
        var writerClosure =
        {
            leafName: leafName,
            obj: obj,
            observe: function(timer, topic, data)
            {
                ObjectPersister.writeNow(writerClosure.leafName, writerClosure.obj);
                delete ObjectPersister.flushTimeout;
            }
        };
        return writerClosure;
    },

    writeNow: function(leafName, obj)
    {
        try
        {
            // Convert data to JSON.
            var jsonString = JSON.stringify(obj);
            var file = FileUtils.getFile("ProfD", ["firebug", leafName]);
            ObjectPersister.writeTextToFile(file, jsonString);
        }
        catch(exc)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeNow; EXCEPTION for " + leafName + ": " +
                    exc, {exception: exc, obj: obj});
        }
    },

    writeTextToFile: function(file, string)
    {
        try
        {
            // Initialize output stream.
            var outputStream = Cc["@mozilla.org/network/file-output-stream;1"]
                .createInstance(Ci.nsIFileOutputStream);
            outputStream.init(file, 0x02 | 0x08 | 0x20, 0666, 0); // write, create, truncate

            // Store JSON
            outputStream.write(string, string.length);
            outputStream.close();

            if (FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeNow to " + file.path, string);

            return file.path;
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS || FBTrace.DBG_STORAGE)
                FBTrace.sysout("ObjectPersister.writeTextToFile; EXCEPTION for " + file.path +
                    ": "+err, {exception: err, string: string});
        }
    },

    isPrivateBrowsing: function()
    {
        try
        {
            // Unfortunatelly the "firebug/chrome/privacy" module can't be used
            // since this scope is JavaScript code module.
            // xxxHonza: storageService should be converted into AMD (but it's used
            // in firebug-service, which is also JS code module).
            var pbs = Components.classes["@mozilla.org/privatebrowsing;1"]
                .getService(Components.interfaces.nsIPrivateBrowsingService);
            return pbs.privateBrowsingEnabled;
        }
        catch (e)
        {
        }

        return false;
    }
};

// ********************************************************************************************* //

var TextService =
{
    readText: ObjectPersister.readTextFromFile,
    writeText: ObjectPersister.writeTextToFile
};

// ********************************************************************************************* //
<|endoftext|>


<commit_before>var parse = function(tokens) {
  var root = {
    type: "ROOT",
    children: []
  }

  var state = {
    current: 0,
    tokens: tokens
  }

  while(state.current < tokens.length) {
    root.children.push(walk(state));
  }

  return root;
}

var createParseNode = function(type, props, children) {
  return {
    type: type,
    props: props,
    children: children
  }
}

var walk = function(state) {
  var token = state.tokens[state.current];
  var secondToken = state.tokens[state.current + 1];
  var thirdToken = state.tokens[state.current + 2];
  var fourthToken = state.tokens[state.current + 3]

  if(token.type === "text") {
    state.current++;
    return createParseNode("#text", {}, [token.value]);
  }
  if(token.type === "tagStart" && !token.close && !fourthToken.close) {

  }
}

console.log(parse([ { type: 'tagStart', close: false },
  { type: 'tag', value: 'h1' },
  { type: 'attribute', value: {} },
  { type: 'tagEnd', close: false },
  { type: 'text', value: '{{msg}}' },
  { type: 'tagStart', close: true },
  { type: 'tag', value: 'h1' },
  { type: 'attribute', value: {} },
  { type: 'tagEnd', close: false } ]));
<commit_msg>simple parser<commit_after>var parse = function(tokens) {
  var root = {
    type: "ROOT",
    children: []
  }

  var state = {
    current: 0,
    tokens: tokens
  }

  while(state.current < tokens.length) {
    root.children.push(walk(state));
  }

  return root;
}

var createParseNode = function(type, props, children) {
  return {
    type: type,
    props: props,
    children: children
  }
}

var walk = function(state) {
  var token = state.tokens[state.current];
  var secondToken = state.tokens[state.current + 1];
  var thirdToken = state.tokens[state.current + 2];
  var fourthToken = state.tokens[state.current + 3];

  var increment = function(num) {
    state.current += num || 1;
    token = state.tokens[state.current];
    secondToken = state.tokens[state.current + 1];
    thirdToken = state.tokens[state.current + 2];
    fourthToken = state.tokens[state.current + 3];
  }

  if(token.type === "text") {
    return createParseNode("#text", {}, [token.value]);
    increment();
  }

  // Start of new Tag
  if(token.type === "tagStart" && !token.close && !fourthToken.close) {
    increment();

    var node = createParseNode(token.value, secondToken.value, []);

    increment(3);
    while(token.type !== 'tagStart' && !token.close && !fourthToken.close) {
      node.children.push(walk(state));
      increment();
    }

    increment(4);
    return node;
  }

  return;
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Kari Karvonen, OH1KK

var ant_switch_ext_name = 'ant_switch';		// NB: must match ant_switch.c:ant_switch_ext.name
var ant_switch_first_time = true;
var ant_switch_poll_interval;
var ant_switch_exantennas=0; // to avoid console.log spam on timerupdates
var ant_switch_denymixing = ext_get_cfg_param_string('ant_switch.denymixing', '', EXT_NO_SAVE);
var ant_switch_denyswitching = ext_get_cfg_param_string('ant_switch.denyswitching', '', EXT_NO_SAVE);

function ant_switch_main()
{
	ext_switch_to_client(ant_switch_ext_name, ant_switch_first_time, ant_switch_recv);		// tell server to use us (again)
	if (!ant_switch_first_time)
		ant_switch_controls_setup();
	ant_switch_first_time = false;
}

var ant_switch_cmd_e = { CMD1:0 };

function ant_switch_recv(data)
{
	var firstChars = getFirstChars(data, 3);

	// process data sent from server/C by ext_send_data_msg()
	if (firstChars == "DAT") {
		var ba = new Uint8Array(data, 4);
		var cmd = ba[0];

		if (cmd == ant_switch_cmd_e.CMD1) {
			// do something ...
		} else {
			console.log('ant_switch_recv: DATA UNKNOWN cmd='+ cmd +' len='+ (ba.length-1));
		}
		return;
	}

	// process command sent from server/C by ext_send_msg() or ext_send_encoded_msg()
	var stringData = arrayBufferToString(data);
	var params = stringData.substring(4).split(" ");

	for (var i=0; i < params.length; i++) {
		var param = params[i].split("=");

		switch (param[0]) {

			case "ready":
				ant_switch_controls_setup();
				break;

			case "Antenna":
				var arg = param[1];
                                ant_switch_process_reply(arg);
				break;
                        case "AntennaDenySwitching":
                                var arg = param[1];
                                if (arg == 1) ant_switch_denyswitching=1; else ant_switch_denyswitching=0;
                                ant_switch_showpermissions();
                                break;
                        case "AntennaDenyMixing":
                                var arg = param[1];
                                if (arg == 1) ant_switch_denymixing=1; else ant_switch_denymixing=0;
                                ant_switch_showpermissions();
                                break;
			default:
				console.log('ant_switch_recv: UNKNOWN CMD '+ param[0]);
				break;
		}
	}
}

function ant_switch_controls_setup()
{

   var buttons_html = '';
   var antdesc = [ ];
   var tmp;
   for (tmp=1; tmp<8; tmp++) antdesc[tmp] = ext_get_cfg_param_string('ant_switch.ant'+tmp+'desc', '', EXT_NO_SAVE);
   console.log('ant_switch: Antenna configuration');
   for (tmp = 1; tmp<8; tmp++) {
           if (antdesc[tmp] == undefined || antdesc[tmp] == null || antdesc[tmp] == '') {
                antdesc[tmp] = '';
           }  else {
                buttons_html+=w3_divs('','', w3_inline('', '', w3_btn('Antenna '+tmp, 'ant_switch_select_'+tmp),antdesc[tmp]));
           }
           console.log('ant_switch: Antenna '+ tmp +': '+ antdesc[tmp]);
   }

   buttons_html+=w3_inline('', '', w3_btn('Ground all', 'ant_switch_select_groundall'), 'Ground all antennas');
   console.log('ant_switch: Antenna g: Ground all antennas');
   var data_html =
      '

';
	var controls_html =
		w3_divs('id-ant_display-controls w3-text-white', '',
			data_html,
        		w3_divs('w3-container', 'w3-tspace-8',
			        w3_divs('', 'w3-medium w3-text-aqua', 'Antenna switch'),
                                w3_divs('id-ant-display-selected', '','Selected antenna: unknown'),
                                w3_divs('id-ant-display-permissions', '','Permissions: unknown'),
                                w3_divs('', '',buttons_html),
                                w3_divs('', '','')
                        )
		);

	ext_panel_show(controls_html, null, null);
	ant_switch_visible(1);
        ant_switch_poll();
}

function ant_switch_blur()
{
        kiwi_clearInterval(ant_switch_poll_interval);
	console.log('### ant_switch_blur');
	ant_switch_visible(0);		// hook to be called when controls panel is closed
}

// called to display HTML for configuration parameters in admin interface
function ant_switch_config_html()
{
        var denyswitching = ext_get_cfg_param('ant_switch.denyswitching', '', EXT_NO_SAVE);
        var denymixing = ext_get_cfg_param('ant_switch.denymixing', '', EXT_NO_SAVE);
	ext_admin_config(ant_switch_ext_name, 'Antenna switch',
		w3_divs('id-ant_switch w3-text-teal w3-hide', '',
			'Antenna switch configuration' +
			'
' +
			w3_divs('', 'w3-container',
				w3_divs('', 'w3-margin-bottom',
                                        w3_divs('', '','If antenna switching is denied then users cannot switch antennas. Admin can always switch antennas from KiwiSDR ssh root console using /root/extensions/ant_switch/frontend/ant-switch-frontend script.'),
                                        w3_divs('', '', 'Deny antenna switching? ' +
                                                w3_radio_btn('No', 'ant_switch.denyswitching', denyswitching? 0:1, 'ant_switch_conf_denyswitching') +
                                                w3_radio_btn('Yes', 'ant_switch.denyswitching', denyswitching? 1:0, 'ant_switch_conf_denyswitching')
                                        ),
                                        w3_divs('', '','If antenna mixing is denied then users can select only one antenna at time.'),
                                        w3_divs('', '', 'Deny antenna mixing? ' +
                                                w3_radio_btn('No', 'ant_switch.denymixing', denymixing? 0:1, 'ant_switch_conf_denymixing') +
                                                w3_radio_btn('Yes', 'ant_switch.denymixing', denymixing? 1:0, 'ant_switch_conf_denymixing')
                                        ),
                                        w3_divs('', '','
Antenna buttons configuration
'),
                                        w3_divs('', '','Leave Antenna description field empty if you want to hide antenna button from users
'),
					w3_input_get_param('Antenna 1 description', 'ant_switch.ant1desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 2 description', 'ant_switch.ant2desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 3 description', 'ant_switch.ant3desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 4 description', 'ant_switch.ant4desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 5 description', 'ant_switch.ant5desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 6 description', 'ant_switch.ant6desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 7 description', 'ant_switch.ant7desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna switch failure or unknown status decription', 'ant_switch.ant0desc', 'w3_string_set_cfg_cb')
				), '', ''
			)
		)
	);
}

function ant_switch_visible(v)
{
	visible_block('id-ant_switch-data', v);
}

function ant_switch_select_1(path,val) {
        ant_switch_select_antenna('1');
}
function ant_switch_select_2(path,val) {
        ant_switch_select_antenna('2');
}
function ant_switch_select_3(path,val) {
        ant_switch_select_antenna('3');
}
function ant_switch_select_4(path,val) {
        ant_switch_select_antenna('4');
}
function ant_switch_select_5(path,val) {
        ant_switch_select_antenna('5');
}
function ant_switch_select_6(path,val) {
        ant_switch_select_antenna('6');
}
function ant_switch_select_7(path,val) {
        ant_switch_select_antenna('7');
}

function ant_switch_select_groundall(path,val) {
        setTimeout('w3_radio_unhighlight('+ q(path) +')', w3_highlight_time);
        ant_switch_select_antenna('g');
}

function ant_switch_select_antenna(ant) {
        console.log('ant_switch: switching antenna '+ant);
	ext_send('SET Antenna='+ant);
        ext_send('GET Antenna');
}

function ant_switch_poll() {
        kiwi_clearInterval(ant_switch_poll_interval);
        ant_switch_poll_interval = setInterval(ant_switch_poll, 10000);
        ext_send('GET Antenna');
}

function ant_switch_process_reply(ant) {
        ant_selected_antenna = ant;
        var need_to_inform = false;

        if (ant_switch_exantennas != ant) {
                // antenna changed.
                need_to_inform = true;
                ant_switch_exantennas = ant;
        }

        if (ant == 'g') {
                if (need_to_inform) console.log('ant_switch: all antennas grouded');
                ant_switch_display_update('Thunderstorm mode. All antennas are grounded.');
        } else {
                if (need_to_inform) console.log('ant_switch: antenna '+ ant_selected_antenna +' in use');
                ant_switch_display_update('Selected antennas are now: '+ant_selected_antenna);
        }

        // update highlight
	var selected_antennas_list = ant.match(/([0-9])/g);
        var inputs = document.getElementsByTagName("button");
        for (var i = 0; i < inputs.length; i++) {
                var re=/^Antenna ([1-7])/i;
                if (inputs[i].textContent.match(re)) {
                        w3_unhighlight(inputs[i]);
                        for (var tmp=1; tmp<8; tmp++) {
                                var chr = String.fromCharCode(48 + tmp);
        		        if (selected_antennas_list != null && selected_antennas_list.indexOf(chr) >= 0) {
			                 if (inputs[i].textContent == 'Antenna '+tmp) w3_highlight(inputs[i]);
                                }
                        }
                }
	}
}

function ant_switch_lock_buttons(lock) {
        var inputs = document.getElementsByTagName("button");
        for (var i = 0; i < inputs.length; i++) {
                // ant 1-7
                var re=/^Antenna ([1-7])/i;
                if (inputs[i].textContent.match(re)) {
                        if (lock == true) {
                                inputs[i].disabled = true;
                        } else {
                                inputs[i].disabled = false;
                        }
                }
                // Ground All
                var re=/^Ground all$/i;
                if (inputs[i].textContent.match(re)) {
                        if (lock == true) {
                                inputs[i].disabled = true;
                        } else {
                                inputs[i].disabled = false;
                        }
                }

        }
}

function ant_switch_showpermissions() {
        if (ant_switch_denyswitching == 1) {
                ant_switch_lock_buttons(true);
                html('id-ant-display-permissions').innerHTML = 'Antenna switching is disabled by admin';
        } else {
                ant_switch_lock_buttons(false);
                if (ant_switch_denymixing == 1) {
                        html('id-ant-display-permissions').innerHTML = 'Antenna switching is allowed.';
                } else {
                        html('id-ant-display-permissions').innerHTML = 'Antenna switching and mixing is allowed.';
                }
        }
}

function ant_switch_display_update(ant) {
        // FIXME: How to notify other users about antenna changes?
        html('id-ant-display-selected').innerHTML = ant;
}

function ant_switch_conf_denyswitching(id, idx) {
        var tmp = ext_set_cfg_param(id, idx, EXT_SAVE);
}
function ant_switch_conf_denymixing(id, idx) {
        var tmp = ext_set_cfg_param(id, idx, EXT_SAVE);
}

<commit_msg>arrayBufferToStringLen() change for KiwiSDR 1.92 compability<commit_after>// Copyright (c) 2017 Kari Karvonen, OH1KK

var ant_switch_ext_name = 'ant_switch';		// NB: must match ant_switch.c:ant_switch_ext.name
var ant_switch_first_time = true;
var ant_switch_poll_interval;
var ant_switch_exantennas=0; // to avoid console.log spam on timerupdates
var ant_switch_denymixing = ext_get_cfg_param_string('ant_switch.denymixing', '', EXT_NO_SAVE);
var ant_switch_denyswitching = ext_get_cfg_param_string('ant_switch.denyswitching', '', EXT_NO_SAVE);

function ant_switch_main()
{
	ext_switch_to_client(ant_switch_ext_name, ant_switch_first_time, ant_switch_recv);		// tell server to use us (again)
	if (!ant_switch_first_time)
		ant_switch_controls_setup();
	ant_switch_first_time = false;
}

var ant_switch_cmd_e = { CMD1:0 };

function ant_switch_recv(data)
{
	var firstChars = arrayBufferToStringLen(data, 3);

	// process data sent from server/C by ext_send_data_msg()
	if (firstChars == "DAT") {
		var ba = new Uint8Array(data, 4);
		var cmd = ba[0];

		if (cmd == ant_switch_cmd_e.CMD1) {
			// do something ...
		} else {
			console.log('ant_switch_recv: DATA UNKNOWN cmd='+ cmd +' len='+ (ba.length-1));
		}
		return;
	}

	// process command sent from server/C by ext_send_msg() or ext_send_encoded_msg()
	var stringData = arrayBufferToString(data);
	var params = stringData.substring(4).split(" ");

	for (var i=0; i < params.length; i++) {
		var param = params[i].split("=");

		switch (param[0]) {

			case "ready":
				ant_switch_controls_setup();
				break;

			case "Antenna":
				var arg = param[1];
                                ant_switch_process_reply(arg);
				break;
                        case "AntennaDenySwitching":
                                var arg = param[1];
                                if (arg == 1) ant_switch_denyswitching=1; else ant_switch_denyswitching=0;
                                ant_switch_showpermissions();
                                break;
                        case "AntennaDenyMixing":
                                var arg = param[1];
                                if (arg == 1) ant_switch_denymixing=1; else ant_switch_denymixing=0;
                                ant_switch_showpermissions();
                                break;
			default:
				console.log('ant_switch_recv: UNKNOWN CMD '+ param[0]);
				break;
		}
	}
}

function ant_switch_controls_setup()
{

   var buttons_html = '';
   var antdesc = [ ];
   var tmp;
   for (tmp=1; tmp<8; tmp++) antdesc[tmp] = ext_get_cfg_param_string('ant_switch.ant'+tmp+'desc', '', EXT_NO_SAVE);
   console.log('ant_switch: Antenna configuration');
   for (tmp = 1; tmp<8; tmp++) {
           if (antdesc[tmp] == undefined || antdesc[tmp] == null || antdesc[tmp] == '') {
                antdesc[tmp] = '';
           }  else {
                buttons_html+=w3_divs('','', w3_inline('', '', w3_btn('Antenna '+tmp, 'ant_switch_select_'+tmp),antdesc[tmp]));
           }
           console.log('ant_switch: Antenna '+ tmp +': '+ antdesc[tmp]);
   }

   buttons_html+=w3_inline('', '', w3_btn('Ground all', 'ant_switch_select_groundall'), 'Ground all antennas');
   console.log('ant_switch: Antenna g: Ground all antennas');
   var data_html =
      '

';
	var controls_html =
		w3_divs('id-ant_display-controls w3-text-white', '',
			data_html,
        		w3_divs('w3-container', 'w3-tspace-8',
			        w3_divs('', 'w3-medium w3-text-aqua', 'Antenna switch'),
                                w3_divs('id-ant-display-selected', '','Selected antenna: unknown'),
                                w3_divs('id-ant-display-permissions', '','Permissions: unknown'),
                                w3_divs('', '',buttons_html),
                                w3_divs('', '','')
                        )
		);

	ext_panel_show(controls_html, null, null);
	ant_switch_visible(1);
        ant_switch_poll();
}

function ant_switch_blur()
{
        kiwi_clearInterval(ant_switch_poll_interval);
	console.log('### ant_switch_blur');
	ant_switch_visible(0);		// hook to be called when controls panel is closed
}

// called to display HTML for configuration parameters in admin interface
function ant_switch_config_html()
{
        var denyswitching = ext_get_cfg_param('ant_switch.denyswitching', '', EXT_NO_SAVE);
        var denymixing = ext_get_cfg_param('ant_switch.denymixing', '', EXT_NO_SAVE);
	ext_admin_config(ant_switch_ext_name, 'Antenna switch',
		w3_divs('id-ant_switch w3-text-teal w3-hide', '',
			'Antenna switch configuration' +
			'
' +
			w3_divs('', 'w3-container',
				w3_divs('', 'w3-margin-bottom',
                                        w3_divs('', '','If antenna switching is denied then users cannot switch antennas. Admin can always switch antennas from KiwiSDR ssh root console using /root/extensions/ant_switch/frontend/ant-switch-frontend script.'),
                                        w3_divs('', '', 'Deny antenna switching? ' +
                                                w3_radio_btn('No', 'ant_switch.denyswitching', denyswitching? 0:1, 'ant_switch_conf_denyswitching') +
                                                w3_radio_btn('Yes', 'ant_switch.denyswitching', denyswitching? 1:0, 'ant_switch_conf_denyswitching')
                                        ),
                                        w3_divs('', '','If antenna mixing is denied then users can select only one antenna at time.'),
                                        w3_divs('', '', 'Deny antenna mixing? ' +
                                                w3_radio_btn('No', 'ant_switch.denymixing', denymixing? 0:1, 'ant_switch_conf_denymixing') +
                                                w3_radio_btn('Yes', 'ant_switch.denymixing', denymixing? 1:0, 'ant_switch_conf_denymixing')
                                        ),
                                        w3_divs('', '','
Antenna buttons configuration
'),
                                        w3_divs('', '','Leave Antenna description field empty if you want to hide antenna button from users
'),
					w3_input_get_param('Antenna 1 description', 'ant_switch.ant1desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 2 description', 'ant_switch.ant2desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 3 description', 'ant_switch.ant3desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 4 description', 'ant_switch.ant4desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 5 description', 'ant_switch.ant5desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 6 description', 'ant_switch.ant6desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna 7 description', 'ant_switch.ant7desc', 'w3_string_set_cfg_cb'),
					w3_input_get_param('Antenna switch failure or unknown status decription', 'ant_switch.ant0desc', 'w3_string_set_cfg_cb')
				), '', ''
			)
		)
	);
}

function ant_switch_visible(v)
{
	visible_block('id-ant_switch-data', v);
}

function ant_switch_select_1(path,val) {
        ant_switch_select_antenna('1');
}
function ant_switch_select_2(path,val) {
        ant_switch_select_antenna('2');
}
function ant_switch_select_3(path,val) {
        ant_switch_select_antenna('3');
}
function ant_switch_select_4(path,val) {
        ant_switch_select_antenna('4');
}
function ant_switch_select_5(path,val) {
        ant_switch_select_antenna('5');
}
function ant_switch_select_6(path,val) {
        ant_switch_select_antenna('6');
}
function ant_switch_select_7(path,val) {
        ant_switch_select_antenna('7');
}

function ant_switch_select_groundall(path,val) {
        setTimeout('w3_radio_unhighlight('+ q(path) +')', w3_highlight_time);
        ant_switch_select_antenna('g');
}

function ant_switch_select_antenna(ant) {
        console.log('ant_switch: switching antenna '+ant);
	ext_send('SET Antenna='+ant);
        ext_send('GET Antenna');
}

function ant_switch_poll() {
        kiwi_clearInterval(ant_switch_poll_interval);
        ant_switch_poll_interval = setInterval(ant_switch_poll, 10000);
        ext_send('GET Antenna');
}

function ant_switch_process_reply(ant) {
        ant_selected_antenna = ant;
        var need_to_inform = false;

        if (ant_switch_exantennas != ant) {
                // antenna changed.
                need_to_inform = true;
                ant_switch_exantennas = ant;
        }

        if (ant == 'g') {
                if (need_to_inform) console.log('ant_switch: all antennas grouded');
                ant_switch_display_update('Thunderstorm mode. All antennas are grounded.');
        } else {
                if (need_to_inform) console.log('ant_switch: antenna '+ ant_selected_antenna +' in use');
                ant_switch_display_update('Selected antennas are now: '+ant_selected_antenna);
        }

        // update highlight
	var selected_antennas_list = ant.match(/([0-9])/g);
        var inputs = document.getElementsByTagName("button");
        for (var i = 0; i < inputs.length; i++) {
                var re=/^Antenna ([1-7])/i;
                if (inputs[i].textContent.match(re)) {
                        w3_unhighlight(inputs[i]);
                        for (var tmp=1; tmp<8; tmp++) {
                                var chr = String.fromCharCode(48 + tmp);
        		        if (selected_antennas_list != null && selected_antennas_list.indexOf(chr) >= 0) {
			                 if (inputs[i].textContent == 'Antenna '+tmp) w3_highlight(inputs[i]);
                                }
                        }
                }
	}
}

function ant_switch_lock_buttons(lock) {
        var inputs = document.getElementsByTagName("button");
        for (var i = 0; i < inputs.length; i++) {
                // ant 1-7
                var re=/^Antenna ([1-7])/i;
                if (inputs[i].textContent.match(re)) {
                        if (lock == true) {
                                inputs[i].disabled = true;
                        } else {
                                inputs[i].disabled = false;
                        }
                }
                // Ground All
                var re=/^Ground all$/i;
                if (inputs[i].textContent.match(re)) {
                        if (lock == true) {
                                inputs[i].disabled = true;
                        } else {
                                inputs[i].disabled = false;
                        }
                }

        }
}

function ant_switch_showpermissions() {
        if (ant_switch_denyswitching == 1) {
                ant_switch_lock_buttons(true);
                html('id-ant-display-permissions').innerHTML = 'Antenna switching is disabled by admin';
        } else {
                ant_switch_lock_buttons(false);
                if (ant_switch_denymixing == 1) {
                        html('id-ant-display-permissions').innerHTML = 'Antenna switching is allowed.';
                } else {
                        html('id-ant-display-permissions').innerHTML = 'Antenna switching and mixing is allowed.';
                }
        }
}

function ant_switch_display_update(ant) {
        // FIXME: How to notify other users about antenna changes?
        html('id-ant-display-selected').innerHTML = ant;
}

function ant_switch_conf_denyswitching(id, idx) {
        var tmp = ext_set_cfg_param(id, idx, EXT_SAVE);
}
function ant_switch_conf_denymixing(id, idx) {
        var tmp = ext_set_cfg_param(id, idx, EXT_SAVE);
}

<|endoftext|>