<commit_before>"use strict";
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import { Link } from 'react-router';
import { getSite, getItem, deleteSession } from '../../actions';
import PageContents from '../page/PageContents';
import Section from '../../components/Section';
import FacebookIcon from '../../icons/Facebook';
import InstagramIcon from '../../icons/Instagram';
import TwitterIcon from '../../icons/Twitter';
import VimeoIcon from '../../icons/Vimeo';
import YouTubeIcon from '../../icons/YouTube';
import Button from '../../components/Button';
import Stored from '../../components/Stored';
import Loading from '../../components/Loading';
import Logo from '../../components/Logo';
import { isDarkBackground } from '../../utils/Color';

class Home extends Component {

  constructor () {
    super();
    this._onResize = this._onResize.bind(this);
    this._layout = this._layout.bind(this);
    this._signOut = this._signOut.bind(this);
    this._siteReady = this._siteReady.bind(this);
    this._showMenu = this._showMenu.bind(this);
    this._hideMenu = this._hideMenu.bind(this);
    this.state = { menuHeight: 0, showMenu: false };
  }

  componentDidMount () {
    const { site } = this.props;
    if (! site) {
      getSite()
      .then(this._siteReady)
      .catch(error => console.log('!!! Home catch', error));
    } else {
      this._siteReady(site)
      .catch(error => console.log('!!! Home re-catch', error));
    }
    window.addEventListener('resize', this._onResize);
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.page && ! this.props.page) {
      this.setState({ layoutNeeded: true });
    }
  }

  componentDidUpdate () {
    const { layoutNeeded } = this.state;
    if (layoutNeeded) {
      this.setState({ layoutNeeded: false });
      this._layout();
    }
  }

  componentWillUnmount () {
    window.removeEventListener('resize', this._onResize);
    clearTimeout(this._resizeTimer);
  }

  _siteReady (site) {
    const { page } = this.props;
    document.title = site.name;
    this._layout();
    if (page) {
      return Promise.resolve();
    } else if (! page && site.homePageId) {
      return getItem('pages', site.homePageId._id,
        { cache: true, populate: true });
    } else {
      return Promise.reject('No home page');
    }
  }

  _signOut () {
    deleteSession()
    .then(() => this.context.router.go('/'))
    .catch(error => console.log('!!! Home _signOut catch', error));
  }

  _onResize () {
    clearTimeout(this._resizeTimer);
    this._resizeTimer = setTimeout(this._layout, 20); // debounce
  }

  _layout () {
    if (window.innerWidth < 700 && this.refs.menuRef) {
      const rect = findDOMNode(this.refs.menuRef).getBoundingClientRect();
      this.setState({ menuHeight: rect.height, showMenu: false });
    } else {
      this.setState({ menuHeight: 0, showMenu: false });
    }
  }

  _showMenu (event) {
    this.setState({ showMenu: true });
  }

  _hideMenu (event) {
    this.setState({ showMenu: false });
  }

  _renderSession () {
    const { session } = this.props;
    let contents;
    if (session && session.token) {
      contents = [
        <Link key="name" to={`/users/${session.userId}/edit`}>
          {session.name}
        </Link>,

          Sign Out

      ];
    } else {
      contents = <Button path="/sign-in">Sign In</Button>;
    }
    return
{contents}
;
  }

  _renderMenuLinks () {
    const { page } = this.props;
    let links = [
      <Link key='/search' to='/search'>Search</Link>
    ];
    if (page) {

      page.sections.filter(section => 'pages' === section.type)
      .forEach(section => {
        section.pages.forEach(page => {
          page = page.id; // how the backend returns it
          let path = page.path ? `/${page.path}` : `/pages/${page._id}`;
          links.push(<Link key={path} to={path}>{page.name}</Link>);
        });
      });

      const calendarSections =
        page.sections.filter(section => 'calendar' === section.type);
      calendarSections.forEach(section => {
        const calendar = section.calendarId;
        let path = `/calendars/${calendar.path || calendar._id}`;
        links.push(<Link key={path} to={path}>{calendar.name}</Link>);
      });
      if (calendarSections.length === 0) {
        links.push(<Link key='/calendar' to='/calendar'>Calendar</Link>);
      }

      page.sections.filter(section => 'library' === section.type)
      .forEach(section => {
        const library = section.libraryId;
        let path = `/libraries/${library.path || library._id}`;
        links.push(<Link key={path} to={path}>{library.name}</Link>);
      });

    }
    return links;
  }

  _renderSplash () {
    const { site } = this.props;
    return (

          <Logo className="home__splash-logo" />

{site.name}

{site.slogan}

    );
  }

  _renderFooter () {
    const { page, session, site } = this.props;

    let socialLinks;
    if (site.socialUrls && site.socialUrls.length > 0) {
      socialLinks = site.socialUrls.map(url => {
        let contents;
        if (url.match(/facebook/)) {
          contents = <FacebookIcon />;
        } else if (url.match(/instagram/)) {
          contents = <InstagramIcon />;
        } else if (url.match(/twitter/)) {
          contents = <TwitterIcon />;
        } else if (url.match(/vimeo/)) {
          contents = <VimeoIcon />;
        } else if (url.match(/youtube/)) {
          contents = <YouTubeIcon />;
        } else {
          contents = site;
        }
        return {contents};
      });
    }

    let logo;
    if (site && site.logo) {
      logo = ;
    }

    const sessionControl = this._renderSession();

    let editControl;
    if (page && session && session.administrator) {
      editControl = <Link to={`/pages/${page._id}/edit`}>Edit</Link>;
    }

    return (

              {logo}

                {socialLinks}

              <Link to="/search"><Button>Search</Button></Link>

            {editControl}
            {sessionControl}

            <a href={`maps://?daddr=${encodeURIComponent(site.address)}`}>
              {site.address}

            <a href={`tel:${site.phone}`}>{site.phone}
            {site.copyright}

    );
  }

  _renderContents () {
    const { page, site } = this.props;
    const { showMenu } = this.state;

    let splash = this._renderSplash();

    let pageContents;
    if (page) {
      pageContents = <PageContents key="page" item={page} />;
    } else {
      pageContents = <Loading key="page" />;
    }

    let menuControlClasses = ["home__menu-control"];
    let menuClasses = ["home__menu"];
    let menuStyle = { backgroundColor: '#fff' };
    if (showMenu) {
      menuClasses.push("home__menu--active");
    }
    if (site && site.color) {
      menuStyle.backgroundColor = site.color;
      if (isDarkBackground(site.color)) {
        menuStyle.push("dark-background");
        if (showMenu) {
          menuControlClasses.push("dark-background");
        }
      }
    }
    const links = this._renderMenuLinks();
    const menu = (

        {links}

    );

    let footer = this._renderFooter();

    return [
      menu,

        <Button className={menuControlClasses.join(' ')} plain={true}
          onClick={showMenu ? undefined : this._showMenu}>
          menu
        </Button>
      ,
      splash,
      pageContents,
      footer
    ];
  }

  render () {
    const { site } = this.props;
    const { menuHeight, showMenu } = this.state;

    let contents, style;
    if (site) {
      contents = this._renderContents();
      if (! showMenu) {
        style = { transform: `translateY(-${menuHeight}px)` };
      }
    } else {
      contents = <Loading />;
    }

    return (

        {contents}

    );
  }
};

Home.propTypes = {
  page: PropTypes.object,
  session: PropTypes.shape({
    token: PropTypes.any
  }),
  site: PropTypes.object
};

Home.contextTypes = {
  router: PropTypes.any
};

const select = (state, props) => {
  let page;
  if (state.site && state.pages) {
    page = state.pages[state.site.homePageId._id];
  }
  return {
    page: page,
    session: state.session,
    site: state.site
  };
};

export default Stored(Home, select);
<commit_msg>Use 'Library' label on Home.<commit_after>"use strict";
import React, { Component, PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import { Link } from 'react-router';
import { getSite, getItem, deleteSession } from '../../actions';
import PageContents from '../page/PageContents';
import Section from '../../components/Section';
import FacebookIcon from '../../icons/Facebook';
import InstagramIcon from '../../icons/Instagram';
import TwitterIcon from '../../icons/Twitter';
import VimeoIcon from '../../icons/Vimeo';
import YouTubeIcon from '../../icons/YouTube';
import Button from '../../components/Button';
import Stored from '../../components/Stored';
import Loading from '../../components/Loading';
import Logo from '../../components/Logo';
import { isDarkBackground } from '../../utils/Color';

class Home extends Component {

  constructor () {
    super();
    this._onResize = this._onResize.bind(this);
    this._layout = this._layout.bind(this);
    this._signOut = this._signOut.bind(this);
    this._siteReady = this._siteReady.bind(this);
    this._showMenu = this._showMenu.bind(this);
    this._hideMenu = this._hideMenu.bind(this);
    this.state = { menuHeight: 0, showMenu: false };
  }

  componentDidMount () {
    const { site } = this.props;
    if (! site) {
      getSite()
      .then(this._siteReady)
      .catch(error => console.log('!!! Home catch', error));
    } else {
      this._siteReady(site)
      .catch(error => console.log('!!! Home re-catch', error));
    }
    window.addEventListener('resize', this._onResize);
  }

  componentWillReceiveProps (nextProps) {
    if (nextProps.page && ! this.props.page) {
      this.setState({ layoutNeeded: true });
    }
  }

  componentDidUpdate () {
    const { layoutNeeded } = this.state;
    if (layoutNeeded) {
      this.setState({ layoutNeeded: false });
      this._layout();
    }
  }

  componentWillUnmount () {
    window.removeEventListener('resize', this._onResize);
    clearTimeout(this._resizeTimer);
  }

  _siteReady (site) {
    const { page } = this.props;
    document.title = site.name;
    this._layout();
    if (page) {
      return Promise.resolve();
    } else if (! page && site.homePageId) {
      return getItem('pages', site.homePageId._id,
        { cache: true, populate: true });
    } else {
      return Promise.reject('No home page');
    }
  }

  _signOut () {
    deleteSession()
    .then(() => this.context.router.go('/'))
    .catch(error => console.log('!!! Home _signOut catch', error));
  }

  _onResize () {
    clearTimeout(this._resizeTimer);
    this._resizeTimer = setTimeout(this._layout, 20); // debounce
  }

  _layout () {
    if (window.innerWidth < 700 && this.refs.menuRef) {
      const rect = findDOMNode(this.refs.menuRef).getBoundingClientRect();
      this.setState({ menuHeight: rect.height, showMenu: false });
    } else {
      this.setState({ menuHeight: 0, showMenu: false });
    }
  }

  _showMenu (event) {
    this.setState({ showMenu: true });
  }

  _hideMenu (event) {
    this.setState({ showMenu: false });
  }

  _renderSession () {
    const { session } = this.props;
    let contents;
    if (session && session.token) {
      contents = [
        <Link key="name" to={`/users/${session.userId}/edit`}>
          {session.name}
        </Link>,

          Sign Out

      ];
    } else {
      contents = <Button path="/sign-in">Sign In</Button>;
    }
    return
{contents}
;
  }

  _renderMenuLinks () {
    const { page } = this.props;
    let links = [
      <Link key='/search' to='/search'>Search</Link>
    ];
    if (page) {

      page.sections.filter(section => 'pages' === section.type)
      .forEach(section => {
        section.pages.forEach(page => {
          page = page.id; // how the backend returns it
          let path = page.path ? `/${page.path}` : `/pages/${page._id}`;
          links.push(<Link key={path} to={path}>{page.name}</Link>);
        });
      });

      const calendarSections =
        page.sections.filter(section => 'calendar' === section.type);
      calendarSections.forEach(section => {
        const calendar = section.calendarId;
        let path = `/calendars/${calendar.path || calendar._id}`;
        links.push(<Link key={path} to={path}>{calendar.name}</Link>);
      });
      if (calendarSections.length === 0) {
        links.push(<Link key='/calendar' to='/calendar'>Calendar</Link>);
      }

      page.sections.filter(section => 'library' === section.type)
      .forEach(section => {
        const library = section.libraryId;
        let path = `/libraries/${library.path || library._id}`;
        links.push(<Link key={path} to={path}>Library</Link>);
      });

    }
    return links;
  }

  _renderSplash () {
    const { site } = this.props;
    return (

          <Logo className="home__splash-logo" />

{site.name}

{site.slogan}

    );
  }

  _renderFooter () {
    const { page, session, site } = this.props;

    let socialLinks;
    if (site.socialUrls && site.socialUrls.length > 0) {
      socialLinks = site.socialUrls.map(url => {
        let contents;
        if (url.match(/facebook/)) {
          contents = <FacebookIcon />;
        } else if (url.match(/instagram/)) {
          contents = <InstagramIcon />;
        } else if (url.match(/twitter/)) {
          contents = <TwitterIcon />;
        } else if (url.match(/vimeo/)) {
          contents = <VimeoIcon />;
        } else if (url.match(/youtube/)) {
          contents = <YouTubeIcon />;
        } else {
          contents = site;
        }
        return {contents};
      });
    }

    let logo;
    if (site && site.logo) {
      logo = ;
    }

    const sessionControl = this._renderSession();

    let editControl;
    if (page && session && session.administrator) {
      editControl = <Link to={`/pages/${page._id}/edit`}>Edit</Link>;
    }

    return (

              {logo}

                {socialLinks}

              <Link to="/search"><Button>Search</Button></Link>

            {editControl}
            {sessionControl}

            <a href={`maps://?daddr=${encodeURIComponent(site.address)}`}>
              {site.address}

            <a href={`tel:${site.phone}`}>{site.phone}
            {site.copyright}

    );
  }

  _renderContents () {
    const { page, site } = this.props;
    const { showMenu } = this.state;

    let splash = this._renderSplash();

    let pageContents;
    if (page) {
      pageContents = <PageContents key="page" item={page} />;
    } else {
      pageContents = <Loading key="page" />;
    }

    let menuControlClasses = ["home__menu-control"];
    let menuClasses = ["home__menu"];
    let menuStyle = { backgroundColor: '#fff' };
    if (showMenu) {
      menuClasses.push("home__menu--active");
    }
    if (site && site.color) {
      menuStyle.backgroundColor = site.color;
      if (isDarkBackground(site.color)) {
        menuStyle.push("dark-background");
        if (showMenu) {
          menuControlClasses.push("dark-background");
        }
      }
    }
    const links = this._renderMenuLinks();
    const menu = (

        {links}

    );

    let footer = this._renderFooter();

    return [
      menu,

        <Button className={menuControlClasses.join(' ')} plain={true}
          onClick={showMenu ? undefined : this._showMenu}>
          menu
        </Button>
      ,
      splash,
      pageContents,
      footer
    ];
  }

  render () {
    const { site } = this.props;
    const { menuHeight, showMenu } = this.state;

    let contents, style;
    if (site) {
      contents = this._renderContents();
      if (! showMenu) {
        style = { transform: `translateY(-${menuHeight}px)` };
      }
    } else {
      contents = <Loading />;
    }

    return (

        {contents}

    );
  }
};

Home.propTypes = {
  page: PropTypes.object,
  session: PropTypes.shape({
    token: PropTypes.any
  }),
  site: PropTypes.object
};

Home.contextTypes = {
  router: PropTypes.any
};

const select = (state, props) => {
  let page;
  if (state.site && state.pages) {
    page = state.pages[state.site.homePageId._id];
  }
  return {
    page: page,
    session: state.session,
    site: state.site
  };
};

export default Stored(Home, select);
<|endoftext|>


<commit_before><commit_msg>remove debug code<commit_after><|endoftext|>


<commit_before>import React from "react";
import PropTypes from "prop-types";
import { DatePickerAndroid, TimePickerAndroid } from "react-native";
import moment from "moment";

export default class CustomDatePickerAndroid extends React.PureComponent {
  static propTypes = {
    date: PropTypes.instanceOf(Date),
    mode: PropTypes.oneOf(["date", "time", "datetime"]),
    onCancel: PropTypes.func.isRequired,
    onConfirm: PropTypes.func.isRequired,
    onHideAfterConfirm: PropTypes.func,
    is24Hour: PropTypes.bool,
    isVisible: PropTypes.bool,
    datePickerModeAndroid: PropTypes.oneOf(["calendar", "spinner", "default"]),
    minimumDate: PropTypes.instanceOf(Date),
    maximumDate: PropTypes.instanceOf(Date)
  };

  static defaultProps = {
    date: new Date(),
    mode: "date",
    datePickerModeAndroid: "default",
    is24Hour: true,
    isVisible: false,
    onHideAfterConfirm: () => {}
  };

  componentDidUpdate = prevProps => {
    if (!prevProps.isVisible && this.props.isVisible) {
      if (this.props.mode === "date" || this.props.mode === "datetime") {
        this._showDatePickerAndroid();
      } else {
        this._showTimePickerAndroid();
      }
    }
  };

  componentDidMount = () => {
    if (this.props && this.props.isVisible) {
      if (this.props.mode === "date" || this.props.mode === "datetime") {
        this._showDatePickerAndroid();
      } else {
        this._showTimePickerAndroid();
      }
    }
  };

  _showDatePickerAndroid = async () => {
    try {
      const { action, year, month, day } = await DatePickerAndroid.open({
        date: this.props.date,
        minDate: this.props.minimumDate,
        maxDate: this.props.maximumDate,
        mode: this.props.datePickerModeAndroid
      });
      if (action !== DatePickerAndroid.dismissedAction) {
        let date;
        if (this.props.date && !isNaN(this.props.date.getTime())) {
          let hour = moment(this.props.date).hour();
          let minute = moment(this.props.date).minute();
          date = moment({ year, month, day, hour, minute }).toDate();
        } else {
          date = moment({ year, month, day }).toDate();
        }

        if (this.props.mode === "datetime") {
          // Prepopulate and show time picker
          const timeOptions = { is24Hour: this.props.is24Hour };
          if (this.props.date) {
            timeOptions.hour = moment(this.props.date).hour();
            timeOptions.minute = moment(this.props.date).minute();
          }
          const {
            action: timeAction,
            hour,
            minute
          } = await TimePickerAndroid.open(timeOptions);
          if (timeAction !== TimePickerAndroid.dismissedAction) {
            const selectedDate = new Date(year, month, day, hour, minute);
            this.props.onConfirm(selectedDate);
            this.props.onHideAfterConfirm(selectedDate);
          } else {
            this.props.onCancel();
          }
        } else {
          this.props.onConfirm(date);
          this.props.onHideAfterConfirm(date);
        }
      } else {
        this.props.onCancel();
      }
    } catch ({ code, message }) {
      console.warn("Cannot open date picker", message);
    }
  };

  _showTimePickerAndroid = async () => {
    try {
      const { action, hour, minute } = await TimePickerAndroid.open({
        hour: moment(this.props.date).hour(),
        minute: moment(this.props.date).minute(),
        is24Hour: this.props.is24Hour
      });
      if (action !== TimePickerAndroid.dismissedAction) {
        let date;
        if (this.props.date) {
          // This prevents losing the Date elements, see issue #71
          const year = moment(this.props.date).year();
          const month = moment(this.props.date).month();
          const day = moment(this.props.date).date();
          date = moment({ year, month, day, hour, minute }).toDate();
        } else {
          date = moment({ hour, minute }).toDate();
        }
        this.props.onConfirm(date);
        this.props.onHideAfterConfirm(date);
      } else {
        this.props.onCancel();
      }
    } catch ({ code, message }) {
      console.warn("Cannot open time picker", message);
    }
  };

  render() {
    return null;
  }
}
<commit_msg>Apply datePickerModeAndroid prop to TimePickerAndroid.<commit_after>import React from "react";
import PropTypes from "prop-types";
import { DatePickerAndroid, TimePickerAndroid } from "react-native";
import moment from "moment";

export default class CustomDatePickerAndroid extends React.PureComponent {
  static propTypes = {
    date: PropTypes.instanceOf(Date),
    mode: PropTypes.oneOf(["date", "time", "datetime"]),
    onCancel: PropTypes.func.isRequired,
    onConfirm: PropTypes.func.isRequired,
    onHideAfterConfirm: PropTypes.func,
    is24Hour: PropTypes.bool,
    isVisible: PropTypes.bool,
    datePickerModeAndroid: PropTypes.oneOf(["calendar", "spinner", "default"]),
    minimumDate: PropTypes.instanceOf(Date),
    maximumDate: PropTypes.instanceOf(Date)
  };

  static defaultProps = {
    date: new Date(),
    mode: "date",
    datePickerModeAndroid: "default",
    is24Hour: true,
    isVisible: false,
    onHideAfterConfirm: () => {}
  };

  componentDidUpdate = prevProps => {
    if (!prevProps.isVisible && this.props.isVisible) {
      if (this.props.mode === "date" || this.props.mode === "datetime") {
        this._showDatePickerAndroid();
      } else {
        this._showTimePickerAndroid();
      }
    }
  };

  componentDidMount = () => {
    if (this.props && this.props.isVisible) {
      if (this.props.mode === "date" || this.props.mode === "datetime") {
        this._showDatePickerAndroid();
      } else {
        this._showTimePickerAndroid();
      }
    }
  };

  _showDatePickerAndroid = async () => {
    try {
      const { action, year, month, day } = await DatePickerAndroid.open({
        date: this.props.date,
        minDate: this.props.minimumDate,
        maxDate: this.props.maximumDate,
        mode: this.props.datePickerModeAndroid
      });
      if (action !== DatePickerAndroid.dismissedAction) {
        let date;
        if (this.props.date && !isNaN(this.props.date.getTime())) {
          let hour = moment(this.props.date).hour();
          let minute = moment(this.props.date).minute();
          date = moment({ year, month, day, hour, minute }).toDate();
        } else {
          date = moment({ year, month, day }).toDate();
        }

        if (this.props.mode === "datetime") {
          // Prepopulate and show time picker
          const timeOptions = {
            is24Hour: this.props.is24Hour,
            mode: this.props.datePickerModeAndroid
          };
          if (this.props.date) {
            timeOptions.hour = moment(this.props.date).hour();
            timeOptions.minute = moment(this.props.date).minute();
          }
          const {
            action: timeAction,
            hour,
            minute
          } = await TimePickerAndroid.open(timeOptions);
          if (timeAction !== TimePickerAndroid.dismissedAction) {
            const selectedDate = new Date(year, month, day, hour, minute);
            this.props.onConfirm(selectedDate);
            this.props.onHideAfterConfirm(selectedDate);
          } else {
            this.props.onCancel();
          }
        } else {
          this.props.onConfirm(date);
          this.props.onHideAfterConfirm(date);
        }
      } else {
        this.props.onCancel();
      }
    } catch ({ code, message }) {
      console.warn("Cannot open date picker", message);
    }
  };

  _showTimePickerAndroid = async () => {
    try {
      const { action, hour, minute } = await TimePickerAndroid.open({
        hour: moment(this.props.date).hour(),
        minute: moment(this.props.date).minute(),
        is24Hour: this.props.is24Hour,
        mode: this.props.datePickerModeAndroid
      });
      if (action !== TimePickerAndroid.dismissedAction) {
        let date;
        if (this.props.date) {
          // This prevents losing the Date elements, see issue #71
          const year = moment(this.props.date).year();
          const month = moment(this.props.date).month();
          const day = moment(this.props.date).date();
          date = moment({ year, month, day, hour, minute }).toDate();
        } else {
          date = moment({ hour, minute }).toDate();
        }
        this.props.onConfirm(date);
        this.props.onHideAfterConfirm(date);
      } else {
        this.props.onCancel();
      }
    } catch ({ code, message }) {
      console.warn("Cannot open time picker", message);
    }
  };

  render() {
    return null;
  }
}
<|endoftext|>


<commit_before>/**
 CONTENTS PROPRIETARY AND CONFIDENTIAL

 Copyright © 2013 Knight Rider Consulting, Inc. All rights reserved.
 http://www.knightrider.com

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

/**
 *
 *    @author        Dale "Ducky" Lotts
 *    @since        2013-Jul-8
 */

angular.module('ui.bootstrap.datetimepicker', [])
    .constant('dateTimePickerConfig', {
        startView: 'day',
        minView: 'minute',
        minuteStep: 5,
        dropdownSelector: null
    })
    .directive('datetimepicker', ['dateTimePickerConfig', function (defaultConfig) {
        "use strict";

        return {
            restrict: 'E',
            require: 'ngModel',
            template: "
" +
                "
" +
                "
" +
                "
" +
                "           	" +
                "           {{ data.title }}	" +
                "           	" +
                "
" +
                "
" +
                "           {{ day }}	" +
                "
" +
                "
" +
                "
" +
                '
' +
                "           " +
                "              {{ dateValue.display }} " +
                "           	" +
                "
" +
                '
' +
                "           {{ dateValue.display }}	" +
                "
" +
                "
" +
                "

",
            scope: {
                ngModel: "=ngModel"
            },
            replace: true,
            link: function (scope, element, attrs, controller) {

                var directiveConfig = {};

                if (attrs.datetimepickerConfig) {
                    directiveConfig = scope.$eval(attrs.datetimepickerConfig);
                }

                var configuration = {};

                angular.extend(configuration, defaultConfig, directiveConfig);

                var dataFactory = {
                    year: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).startOf('year');
                        // View starts one year before the decade starts and ends one year after the decade ends
                        // i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
                        // Truncate the last digit from the current year and subtract 1 to get the start of the decade
                        var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
                        var startDate = moment.utc(selectedDate).year(startDecade - 1).startOf('year');
                        var activeYear = scope.ngModel ? moment(scope.ngModel).year() : 0;

                        var result = {
                            'currentView': 'year',
                            'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
                            'title': startDecade + '-' + (startDecade + 9),
                            'leftDate': moment.utc(startDate).subtract(9, 'year').valueOf(),
                            'rightDate': moment.utc(startDate).add(11, 'year').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 12; i++) {
                            var yearMoment = moment.utc(startDate).add(i, 'years');
                            var dateValue = {
                                'date': yearMoment.valueOf(),
                                'display': yearMoment.format('YYYY'),
                                'past': yearMoment.year() < startDecade,
                                'future': yearMoment.year() > startDecade + 9,
                                'active': yearMoment.year() === activeYear
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    month: function (unixDate) {

                        var startDate = moment.utc(unixDate).startOf('year');

                        var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM') : 0;

                        var result = {
                            'previousView': 'year',
                            'currentView': 'month',
                            'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
                            'currentDate': startDate.valueOf(),
                            'title': startDate.format('YYYY'),
                            'leftDate': moment.utc(startDate).subtract(1, 'year').valueOf(),
                            'rightDate': moment.utc(startDate).add(1, 'year').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 12; i++) {
                            var monthMoment = moment.utc(startDate).add(i, 'months');
                            var dateValue = {
                                'date': monthMoment.valueOf(),
                                'display': monthMoment.format('MMM'),
                                'active': monthMoment.format('YYYY-MMM') === activeDate
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    day: function (unixDate) {

                        var selectedDate = moment.utc(unixDate);
                        var startOfMonth = moment.utc(selectedDate).startOf('month');
                        var endOfMonth = moment.utc(selectedDate).endOf('month');

                        // ToDo: Update to account for starting on days other than Sunday.
                        var startDate = moment.utc(startOfMonth).subtract(startOfMonth.day(), 'days');

                        var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM-DD') : '';

                        var result = {
                            'previousView': 'month',
                            'currentView': 'day',
                            'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM'),
                            'leftDate': moment.utc(startOfMonth).subtract(1, 'months').valueOf(),
                            'rightDate': moment.utc(startOfMonth).add(1, 'months').valueOf(),
                            'dayNames': [],
                            'weeks': []
                        };

                        for (var dayNumber = 0; dayNumber < 7; dayNumber++) {
                            result.dayNames.push(moment.utc().day(dayNumber).format('dd'));
                        }

                        for (var i = 0; i < 6; i++) {
                            var week = { dates: [] };
                            for (var j = 0; j < 7; j++) {
                                var monthMoment = moment.utc(startDate).add((i * 7) + j, 'days');
                                var dateValue = {
                                    'date': monthMoment.valueOf(),
                                    'display': monthMoment.format('D'),
                                    'active': monthMoment.format('YYYY-MMM-DD') === activeDate,
                                    'past': monthMoment.isBefore(startOfMonth),
                                    'future': monthMoment.isAfter(endOfMonth)
                                };
                                week.dates.push(dateValue);
                            }
                            result.weeks.push(week);
                        }

                        return result;
                    },

                    hour: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).hour(0).minute(0).second(0);

                        var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H') : '';

                        var result = {
                            'previousView': 'day',
                            'currentView': 'hour',
                            'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM-DD'),
                            'leftDate': moment.utc(selectedDate).subtract(1, 'days').valueOf(),
                            'rightDate': moment.utc(selectedDate).add(1, 'days').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 24; i++) {
                            var hourMoment = moment.utc(selectedDate).add(i, 'hours');
                            var dateValue = {
                                'date': hourMoment.valueOf(),
                                'display': hourMoment.format('H:00'),
                                'active': hourMoment.format('YYYY-MM-DD H') === activeFormat
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    minute: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).minute(0).second(0);

                        var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H:mm') : '';

                        var result = {
                            'previousView': 'hour',
                            'currentView': 'minute',
                            'nextView': 'setTime',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM-DD H:mm'),
                            'leftDate': moment.utc(selectedDate).subtract(1, 'hours').valueOf(),
                            'rightDate': moment.utc(selectedDate).add(1, 'hours').valueOf(),
                            'dates': []
                        };

                        var limit = 60 / configuration.minuteStep;

                        for (var i = 0; i < limit; i++) {
                            var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute');
                            var dateValue = {
                                'date': hourMoment.valueOf(),
                                'display': hourMoment.format('H:mm'),
                                'active': hourMoment.format('YYYY-MM-DD H:mm') === activeFormat
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    setTime: function (unixDate) {
                        var tempDate = new Date(unixDate);
                        scope.ngModel = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));
                        if (configuration.dropdownSelector) {
                            jQuery(configuration.dropdownSelector).dropdown('toggle');
                        }
                        return dataFactory[scope.data.currentView](unixDate);
                    }
                };

                var headerClickHandler = function (event) {
                    scope.changeView(scope.data[event.data.viewProperty], scope.data[event.data.dateProperty], event);
                    scope.$apply();
                };

                // Work around issue where angular does not compile, fire, or update values ng-click events on the elements in the table header
                $('.left', element).on('click', { viewProperty: 'currentView', dateProperty: 'leftDate'}, headerClickHandler);
                $('.switch', element).on('click', { viewProperty: 'previousView', dateProperty: 'currentDate'}, headerClickHandler);
                $('.right', element).on('click', { viewProperty: 'currentView', dateProperty: 'rightDate'}, headerClickHandler);

                var getUTCTime = function () {
                    var tempDate = (scope.ngModel || new Date());
                    return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
                };

                scope.changeView = function (viewName, unixDate, event) {
                    if (event) {
                        event.stopPropagation();
                        event.preventDefault();
                    }

                    if (viewName && unixDate && dataFactory[viewName]) {
                        scope.data = dataFactory[viewName](unixDate);
                    }
                };

                scope.$watch('ngModel', function (newValue) {
                    scope.changeView(scope.data.currentView, getUTCTime());
                });

                scope.changeView(configuration.startView, getUTCTime());
            }
        };
    }]);
<commit_msg>Add jslint specific comments to top of file<commit_after>/*globals angular, moment, jQuery */
/*jslint vars:true */

/**
 CONTENTS PROPRIETARY AND CONFIDENTIAL

 Copyright © 2013 Knight Rider Consulting, Inc. All rights reserved.
 http://www.knightrider.com

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
 */

/**
 *
 *    @author        Dale "Ducky" Lotts
 *    @since        2013-Jul-8
 */

angular.module('ui.bootstrap.datetimepicker', [])
    .constant('dateTimePickerConfig', {
        startView: 'day',
        minView: 'minute',
        minuteStep: 5,
        dropdownSelector: null
    })
    .directive('datetimepicker', ['dateTimePickerConfig', function (defaultConfig) {
        "use strict";

        return {
            restrict: 'E',
            require: 'ngModel',
            template: "
" +
                "
" +
                "
" +
                "
" +
                "           	" +
                "           {{ data.title }}	" +
                "           	" +
                "
" +
                "
" +
                "           {{ day }}	" +
                "
" +
                "
" +
                "
" +
                '
' +
                "           " +
                "              {{ dateValue.display }} " +
                "           	" +
                "
" +
                '
' +
                "           {{ dateValue.display }}	" +
                "
" +
                "
" +
                "

",
            scope: {
                ngModel: "=ngModel"
            },
            replace: true,
            link: function (scope, element, attrs, controller) {

                var directiveConfig = {};

                if (attrs.datetimepickerConfig) {
                    directiveConfig = scope.$eval(attrs.datetimepickerConfig);
                }

                var configuration = {};

                angular.extend(configuration, defaultConfig, directiveConfig);

                var dataFactory = {
                    year: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).startOf('year');
                        // View starts one year before the decade starts and ends one year after the decade ends
                        // i.e. passing in a date of 1/1/2013 will give a range of 2009 to 2020
                        // Truncate the last digit from the current year and subtract 1 to get the start of the decade
                        var startDecade = (parseInt(selectedDate.year() / 10, 10) * 10);
                        var startDate = moment.utc(selectedDate).year(startDecade - 1).startOf('year');
                        var activeYear = scope.ngModel ? moment(scope.ngModel).year() : 0;

                        var result = {
                            'currentView': 'year',
                            'nextView': configuration.minView === 'year' ? 'setTime' : 'month',
                            'title': startDecade + '-' + (startDecade + 9),
                            'leftDate': moment.utc(startDate).subtract(9, 'year').valueOf(),
                            'rightDate': moment.utc(startDate).add(11, 'year').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 12; i++) {
                            var yearMoment = moment.utc(startDate).add(i, 'years');
                            var dateValue = {
                                'date': yearMoment.valueOf(),
                                'display': yearMoment.format('YYYY'),
                                'past': yearMoment.year() < startDecade,
                                'future': yearMoment.year() > startDecade + 9,
                                'active': yearMoment.year() === activeYear
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    month: function (unixDate) {

                        var startDate = moment.utc(unixDate).startOf('year');

                        var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM') : 0;

                        var result = {
                            'previousView': 'year',
                            'currentView': 'month',
                            'nextView': configuration.minView === 'month' ? 'setTime' : 'day',
                            'currentDate': startDate.valueOf(),
                            'title': startDate.format('YYYY'),
                            'leftDate': moment.utc(startDate).subtract(1, 'year').valueOf(),
                            'rightDate': moment.utc(startDate).add(1, 'year').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 12; i++) {
                            var monthMoment = moment.utc(startDate).add(i, 'months');
                            var dateValue = {
                                'date': monthMoment.valueOf(),
                                'display': monthMoment.format('MMM'),
                                'active': monthMoment.format('YYYY-MMM') === activeDate
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    day: function (unixDate) {

                        var selectedDate = moment.utc(unixDate);
                        var startOfMonth = moment.utc(selectedDate).startOf('month');
                        var endOfMonth = moment.utc(selectedDate).endOf('month');

                        // ToDo: Update to account for starting on days other than Sunday.
                        var startDate = moment.utc(startOfMonth).subtract(startOfMonth.day(), 'days');

                        var activeDate = scope.ngModel ? moment(scope.ngModel).format('YYYY-MMM-DD') : '';

                        var result = {
                            'previousView': 'month',
                            'currentView': 'day',
                            'nextView': configuration.minView === 'day' ? 'setTime' : 'hour',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM'),
                            'leftDate': moment.utc(startOfMonth).subtract(1, 'months').valueOf(),
                            'rightDate': moment.utc(startOfMonth).add(1, 'months').valueOf(),
                            'dayNames': [],
                            'weeks': []
                        };

                        for (var dayNumber = 0; dayNumber < 7; dayNumber++) {
                            result.dayNames.push(moment.utc().day(dayNumber).format('dd'));
                        }

                        for (var i = 0; i < 6; i++) {
                            var week = { dates: [] };
                            for (var j = 0; j < 7; j++) {
                                var monthMoment = moment.utc(startDate).add((i * 7) + j, 'days');
                                var dateValue = {
                                    'date': monthMoment.valueOf(),
                                    'display': monthMoment.format('D'),
                                    'active': monthMoment.format('YYYY-MMM-DD') === activeDate,
                                    'past': monthMoment.isBefore(startOfMonth),
                                    'future': monthMoment.isAfter(endOfMonth)
                                };
                                week.dates.push(dateValue);
                            }
                            result.weeks.push(week);
                        }

                        return result;
                    },

                    hour: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).hour(0).minute(0).second(0);

                        var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H') : '';

                        var result = {
                            'previousView': 'day',
                            'currentView': 'hour',
                            'nextView': configuration.minView === 'hour' ? 'setTime' : 'minute',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM-DD'),
                            'leftDate': moment.utc(selectedDate).subtract(1, 'days').valueOf(),
                            'rightDate': moment.utc(selectedDate).add(1, 'days').valueOf(),
                            'dates': []
                        };

                        for (var i = 0; i < 24; i++) {
                            var hourMoment = moment.utc(selectedDate).add(i, 'hours');
                            var dateValue = {
                                'date': hourMoment.valueOf(),
                                'display': hourMoment.format('H:00'),
                                'active': hourMoment.format('YYYY-MM-DD H') === activeFormat
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    minute: function (unixDate) {
                        var selectedDate = moment.utc(unixDate).minute(0).second(0);

                        var activeFormat = scope.ngModel ? moment(scope.ngModel).format('YYYY-MM-DD H:mm') : '';

                        var result = {
                            'previousView': 'hour',
                            'currentView': 'minute',
                            'nextView': 'setTime',
                            'currentDate': selectedDate.valueOf(),
                            'title': selectedDate.format('YYYY-MMM-DD H:mm'),
                            'leftDate': moment.utc(selectedDate).subtract(1, 'hours').valueOf(),
                            'rightDate': moment.utc(selectedDate).add(1, 'hours').valueOf(),
                            'dates': []
                        };

                        var limit = 60 / configuration.minuteStep;

                        for (var i = 0; i < limit; i++) {
                            var hourMoment = moment.utc(selectedDate).add(i * configuration.minuteStep, 'minute');
                            var dateValue = {
                                'date': hourMoment.valueOf(),
                                'display': hourMoment.format('H:mm'),
                                'active': hourMoment.format('YYYY-MM-DD H:mm') === activeFormat
                            };

                            result.dates.push(dateValue);
                        }

                        return result;
                    },

                    setTime: function (unixDate) {
                        var tempDate = new Date(unixDate);
                        scope.ngModel = new Date(tempDate.getTime() + (tempDate.getTimezoneOffset() * 60000));
                        if (configuration.dropdownSelector) {
                            jQuery(configuration.dropdownSelector).dropdown('toggle');
                        }
                        return dataFactory[scope.data.currentView](unixDate);
                    }
                };

                var headerClickHandler = function (event) {
                    scope.changeView(scope.data[event.data.viewProperty], scope.data[event.data.dateProperty], event);
                    scope.$apply();
                };

                // Work around issue where angular does not compile, fire, or update values ng-click events on the elements in the table header
                $('.left', element).on('click', { viewProperty: 'currentView', dateProperty: 'leftDate'}, headerClickHandler);
                $('.switch', element).on('click', { viewProperty: 'previousView', dateProperty: 'currentDate'}, headerClickHandler);
                $('.right', element).on('click', { viewProperty: 'currentView', dateProperty: 'rightDate'}, headerClickHandler);

                var getUTCTime = function () {
                    var tempDate = (scope.ngModel || new Date());
                    return tempDate.getTime() - (tempDate.getTimezoneOffset() * 60000);
                };

                scope.changeView = function (viewName, unixDate, event) {
                    if (event) {
                        event.stopPropagation();
                        event.preventDefault();
                    }

                    if (viewName && unixDate && dataFactory[viewName]) {
                        scope.data = dataFactory[viewName](unixDate);
                    }
                };

                scope.$watch('ngModel', function (newValue) {
                    scope.changeView(scope.data.currentView, getUTCTime());
                });

                scope.changeView(configuration.startView, getUTCTime());
            }
        };
    }]);
<|endoftext|>


<commit_before>$( document ).ready(function() {
    $.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
		/*
		$.each (data, function (index, ticketObject) {
			$.each (ticketObject, function (key, val){
				$("#wrapper").append("
" + key + " " + val + "
");
			})
		});
		*/
		$.each (data, function (index, ticketObject) {
			var ticketDiv = document.createElement("div");
			$(ticketDiv).addClass("ticketdiv");

			var ticketDivText = document.createElement("span");
			ticketDivText.innerHTML = ticketObject.Description;
			$(ticketDiv).append(ticketDivText);

			$("#wrapper").append(ticketDiv);
		});
	});
});<commit_msg>Added client and assigned to display.  Createed subdivs with classes<commit_after>$( document ).ready(function() {
    $.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
		/*
		$.each (data, function (index, ticketObject) {
			$.each (ticketObject, function (key, val){
				$("#wrapper").append("
" + key + " " + val + "
");
			})
		});
		*/
		$.each (data, function (index, ticketObject) {
			var ticketDiv = document.createElement("div");
			$(ticketDiv).addClass("ticketdiv");

			var clientDiv = document.createElement("div");
			$(clientDiv).addClass("clientdiv");

			var clientDivText = document.createElement("span");
			clientDivText.innerHTML = ticketObject.ClientName;
			$(clientDiv).append(clientDivText);

			var descriptionDiv = document.createElement("div");
			$(descriptionDiv).addClass("deescriptiondiv");

			var descriptionDivText = document.createElement("span");
			descriptionDivText.innerHTML = ticketObject.Description;
			$(descriptionDiv).append(descriptionDivText);

			var assignDiv = document.createElement("div");
			$(assignDiv).addClass("assigndiv");

			var assignDivText = document.createElement("span");
			assignDivText.innerHTML = ticketObject.AssignTech;
			$(assignDiv).append(assignDivText);

			$(ticketDiv).append(clientDiv);
			$(ticketDiv).append(descriptionDiv);
			$(ticketDiv).append(assignDiv);

			$("#wrapper").append(ticketDiv);
		});
	});
});<|endoftext|>


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

const path = require('path');

module.exports = function(context) {
  const setStyleCall = 'CallExpression[callee.name=setStyle]';
  const setStylesCall = 'CallExpression[callee.name=setStyles], CallExpression[callee.name=setImportantStyles]';
  const resetStylesCall = 'CallExpression[callee.name=resetStyles]';

  return {
    [setStyleCall]: function(node) {
      const filePath = context.getFilename();
      if (filePath.endsWith('src/style.js')) {
        return;
      }

      const arg = node.arguments[1];
      if (!arg) {
        return;
      }

      if (arg.type !== 'Literal' || typeof arg.value !== 'string') {
        return context.report({
          node: arg || node,
          message: 'property argument (the second argument) to setStyle must be a string literal',
        });
      }
    },

    [setStylesCall]: function(node) {
      const callName = node.callee.name;
      const arg = node.arguments[1];

      if (!arg) {
        return;
      }

      if (arg.type !== 'ObjectExpression') {
        if (arg.type === 'CallExpression') {
          const {callee} = arg;
          if (callee.type === 'Identifier' && callee.name === 'assertDoesNotContainDisplay') {
            return;
          }
        }

        return context.report({
          node: arg || node,
          message: `styles argument (the second argument) to ${callName} must be an object literal. You may also pass in an explicit call to assertDoesNotContainDisplay`,
        });
      }

      const {properties} = arg;
      for (let i = 0; i < properties.length; i++) {
        const prop = properties[i];

        if (prop.computed) {
          context.report({
            node: prop,
            message: 'Style names must not be computed',
          });
          continue;
        }
      }
    },

    [resetStylesCall]: function(node) {
      const arg = node.arguments[1];

      if (!arg) {
        return;
      }

      if (arg.type !== 'ArrayExpression') {
        return context.report({
          node: arg || node,
          message: `styles argument (the second argument) to resetStyles must be an array literal`,
        });
      }

      const {elements} = arg;
      for (let i = 0; i < elements.length; i++) {
        const el = elements[i];

        if (el.type !== 'Literal' || typeof el.value !== 'string') {
          context.report({
            node: el,
            message: 'Style names must be string literals',
          });
          continue;
        }
      }
    },
  };
};
<commit_msg>Forbid setStyle manipulations on display style (#17982)<commit_after>/**
 * Copyright 2018 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

const path = require('path');

module.exports = function(context) {
  const setStyleCall = 'CallExpression[callee.name=setStyle]';
  const setStylesCall = 'CallExpression[callee.name=setStyles], CallExpression[callee.name=setImportantStyles]';
  const resetStylesCall = 'CallExpression[callee.name=resetStyles]';

  const displayMessage = [
    'Do not set the display property using setStyle.',
    'Only the `toggle` helper in `src/style.js` is permitted to change the `display: none` style of an element.',
    'Or use `setInitialDisplay` to setup an initial `display: block`, `inline-block`, etc., if it is not possible to do so in CSS.',
  ].join('\n\t');

  return {
    [setStyleCall]: function(node) {
      const filePath = context.getFilename();
      if (filePath.endsWith('src/style.js')) {
        return;
      }

      const arg = node.arguments[1];
      if (!arg) {
        return;
      }

      if (arg.type !== 'Literal' || typeof arg.value !== 'string') {
        return context.report({
          node: arg || node,
          message: 'property argument (the second argument) to setStyle must be a string literal',
        });
      }

      if (arg.value === 'display') {
        context.report({
          node: arg,
          message: displayMessage,
        });
      }
    },

    [setStylesCall]: function(node) {
      const callName = node.callee.name;
      const arg = node.arguments[1];

      if (!arg) {
        return;
      }

      if (arg.type !== 'ObjectExpression') {
        if (arg.type === 'CallExpression') {
          const {callee} = arg;
          if (callee.type === 'Identifier' && callee.name === 'assertDoesNotContainDisplay') {
            return;
          }
        }

        return context.report({
          node: arg || node,
          message: `styles argument (the second argument) to ${callName} must be an object literal. You may also pass in an explicit call to assertDoesNotContainDisplay`,
        });
      }

      const {properties} = arg;
      for (let i = 0; i < properties.length; i++) {
        const prop = properties[i];

        if (prop.computed) {
          context.report({
            node: prop,
            message: 'Style names must not be computed',
          });
          continue;
        }

        const {key} = prop;
        // `"display": "none"`, and `display: none` use two different AST keys.
        if (key.value === 'display' || key.name === 'display') {
          context.report({
            node: prop,
            message: displayMessage,
          });
        }
      }
    },

    [resetStylesCall]: function(node) {
      const arg = node.arguments[1];

      if (!arg) {
        return;
      }

      if (arg.type !== 'ArrayExpression') {
        return context.report({
          node: arg || node,
          message: `styles argument (the second argument) to resetStyles must be an array literal`,
        });
      }

      const {elements} = arg;
      for (let i = 0; i < elements.length; i++) {
        const el = elements[i];

        if (el.type !== 'Literal' || typeof el.value !== 'string') {
          context.report({
            node: el,
            message: 'Style names must be string literals',
          });
          continue;
        }

        if (el.value === 'display') {
          context.report({
            node: el,
            message: displayMessage,
          });
        }
      }
    },
  };
};
<|endoftext|>


<commit_before>/**
 * Phune Gaming SDK for JavaScript v{{ VERSION }}
 *
 * @file Handles the communication between the Phune Gaming platform and the games.
 * @license MIT License <http://opensource.org/licenses/MIT>
 * @copyright Copyright (c) 2014 Present Technologies
 */
(function(window, document) {
    'use strict';

    /**
     * Constructs a new instance of the Phune Gaming SDK.
     *
     * @constructor PG
     * @classdesc Phune Gaming SDK
     */
    var PG = function() {

        var origin = '*', // (document.location.origin) ? document.location.origin : document.location.protocol + '//' + document.location.host,
            result = {
                WON: 'won',
                LOST: 'lost',
                DRAW: 'draw'
            },
            message, lastMessage = null,
            player, opponent;

        /**
         * Get the game results with the correct value based on the winnerPlayerId param.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} winnerPlayerId The id of the player that won the match.
         * @returns {string} A game result valid value.
         */
        var getGameResult = function(winnerPlayerId) {
            if (typeof winnerPlayerId === 'undefined') {
                return;
            }

            if (winnerPlayerId === player.id) {
                return result.WON;
            } else if (winnerPlayerId === opponent.id) {
                return result.LOST;
            }

            return result.DRAW;
        };

        /**
         * The game should build the user interface and get ready to start playing.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {Object} player The current player details.
         * @param {Object} opponent The opponent details.
         * @param {string} deviceType Indicates the type of the device where the game is running. Possible values are 'MOBILE' and 'TV'.
         */
        var onMatchPrepare = function(player, opponent, deviceType) {
            throw new Error('onMatchPrepare is not implemented.');
        };

        /**
         * The game can now configure additional match details.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} allowedTime Time allowed for the player to configure the game and start the match.
         */
        var onGameLobby = function(allowedTime) {
            throw new Error('onGameLobby is not implemented.');
        };

        /**
         * The match start confirmation. Only now is the player allowed to play the game.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {number} timeToPlay Time allowed for the player to make a move.
         */
        var onMatchStart = function(playerIdToPlayNext, timeToPlay) {
            throw new Error('onMatchStart is not implemented.');
        };

        /**
         * Acknowledgment to a valid move.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdWhoSentTheMove The identifier of the player that sent the move.
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {Object} moveDetails The move details.
         * @param {Object} moveResults The results of the move validation.
         * @param {string} [gameResult] If the move ended the game, this indicates its results. Possible values are 'won', 'lost' or 'draw'.
         */
        var onMoveValid = function(playerIdWhoSentTheMove, playerIdToPlayNext, moveDetails, moveResults, gameResult) {
            throw new Error('onMoveValid is not implemented.');
        };

        /**
         * Acknowledgment to an invalid move sent by the current player.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {Object} moveResults The results of the move validation.
         */
        var onMoveInvalid = function(playerIdToPlayNext, moveResults) {
            throw new Error('onMoveInvalid is not implemented.');
        };

        /**
         * A message from the server-side rules was received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerWhoSentTheMessage The identifier of the player that sent the message.
         * @param {Object} messageDetails Message specific to a game and unknown to the platform. The developer is advised to have multiple message types with different bodies in order to achieve different goals.
         * @param {Object} messageResults The result returned by the server-side rules.
         */
        var onServerMessage = function(playerIdWhoSentTheMessage, messageDetails, messageResults) {
            throw new Error('onServerMessage is not implemented.');
        };

        /**
         * A message sent directly from another player was received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {Object} messageDetails Message specific to a game and unknown to the platform. The developer is advised to have multiple message types with different bodies in order to achieve different goals.
         */
        var onPlayerMessage = function(messageDetails) {
            throw new Error('onPlayerMessage is not implemented.');
        };

        /**
         * Called by the platform when a match end event is received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {string} gameResult The game results. Possible values are 'won', 'lost' or 'draw'.
         */
        var onMatchEnd = function(gameResult) {
            throw new Error('onMatchEnd is not implemented.');
        };

        /**
         * A keyboard or TV remote control key was pressed.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {string} key The key that was pressed. Possible values are 'left', 'right', 'up', 'down' or 'enter'.
         */
        var onKeyPress = function(key) {
            throw new Error('onKeyPress is not implemented.');
        };

        return {
            /**
             * Initialize the Phune Gaming SDK.
             *
             * @public
             * @memberof PG
             * @param {Object} params Object containing all the options to configure the SDK. This includes the mandatory callback functions: onMatchPrepare, onMatchStart, onMoveValid, onMoveInvalid, onMatchEnd, as well as the optional callback functions: onServerMessage, onPlayerMessage and onKeyPress.
             */
            init: function(params) {
                onMatchPrepare = params.onMatchPrepare || onMatchPrepare;
                onGameLobby = params.onGameLobby || onGameLobby;
                onMatchStart = params.onMatchStart || onMatchStart;
                onMoveValid = params.onMoveValid || onMoveValid;
                onMoveInvalid = params.onMoveInvalid || onMoveInvalid;
                onMatchEnd = params.onMatchEnd || onMatchEnd;
                onServerMessage = params.onServerMessage || onServerMessage;
                onPlayerMessage = params.onPlayerMessage || onPlayerMessage;
                onKeyPress = params.onKeyPress || onKeyPress;

                window.addEventListener('message', function(msg) {
                    // if (msg.origin !== origin) {
                    //     console.warn('Origin not recognized: ' + msg.origin);
                    //     return;
                    // }
                    switch (msg.data.type) {
                    case 'matchPrepare':
                        player = msg.data.player;
                        opponent = msg.data.opponent;
                        onMatchPrepare(player, opponent, msg.data.deviceType);
                        break;
                    case 'enterGameLobby':
                        onGameLobby(msg.data.timeout);
                        break;
                    case 'matchStart':
                        onMatchStart(msg.data.nextPlayerId, msg.data.timeout);
                        break;
                    case 'matchMoveValid':
                        onMoveValid(msg.data.playerId, msg.data.nextPlayerId, msg.data.content, msg.data.evaluationContent, getGameResult(msg.data.winnerPlayerId));
                        break;
                    case 'matchMoveInvalid':
                        onMoveInvalid(msg.data.nextPlayerId, msg.data.evaluationContent);
                        break;
                    case 'matchEnd':
                        onMatchEnd(getGameResult(msg.data.winnerPlayerId));
                        break;
                    case 'serverMessage':
                        onServerMessage(msg.data.playerId, msg.data.content, msg.data.result);
                        break;
                    case 'playerMessage':
                        onPlayerMessage(msg.data.content);
                        break;
                    case 'keyPress':
                        onKeyPress(msg.data.value);
                        break;
                    }
                });

                // Inform the platform that the game loaded successfully.
                window.parent.postMessage({
                    type: 'loaded'
                }, origin);
            },

            /**
             * Informs the server that the client is ready to start the match.
             *
             * @public
             * @memberof PG
             */
            ready: function() {
                window.parent.postMessage({
                    type: 'ready'
                }, origin);
            },

            /**
             * Informs the server that the game configuration phase ended and the game can start.
             *
             * @public
             * @memberof PG
             */
            exitGameLobby: function() {
                window.parent.postMessage({
                    type: 'exitGameLobby'
                }, origin);
            },

            /**
             * Asks the platform to show the game menu.
             *
             * @public
             * @memberof PG
             */
            showMenu: function() {
                window.parent.postMessage({
                    type: 'showMenu'
                }, origin);
            },

            /**
             * Send a move to the platform server.
             *
             * @public
             * @memberof PG
             * @param {Object} moveDetails The move details.
             * @param {function(Object): boolean} [validate] Optional function to validate the move before sending it to server.
             * @returns {boolean} True when the move is valid or when no validation function was provided.
             */
            sendMove: function(moveDetails, validate) {
                if (validate && !validate(moveDetails)) {
                    return false;
                }

                window.parent.postMessage({
                    type: 'move',
                    content: moveDetails
                }, origin);

                return true;
            },

            /**
             * Send a message to the server-side rules. This message is specific to a game and will not be processed by the platform itself.
             * The response from the server could either be sent to you only or to all players.
             *
             * @public
             * @memberof PG
             * @param {Object} messageDetails The content of the message to be sent.
             * @param {boolean} isAnswerPublic Whether the reply from the server's rules should be sent to all players or not.
             * @param {boolean} [serializeRequest] Whether the messages should be processed in order of arrival or can be executed in parallel.
             */
            sendMessageToServer: function(messageDetails, isAnswerPublic, serializeRequest) {
                window.parent.postMessage({
                    type: 'serverMessage',
                    publicAnswer: isAnswerPublic,
                    requiresConcurrencyControl: (serializeRequest) ? serializeRequest : true,
                    content: messageDetails
                }, origin);
            },

            /**
             * Peer-to-peer message sent directly to another player.
             *
             * @public
             * @memberof PG
             * @param {Object} messageDetails The content of the message to be sent.
             * @param {number} [sendTimeIntervalLimit] Do not allow sending more than one message within the specified time in milliseconds.
             *                 If this is called more than once during this interval only the last message will be sent.
             */
            sendMessageToPlayer: function(messageDetails, sendTimeIntervalLimit) {
                if (!sendTimeIntervalLimit || lastMessage === null) {
                    message = {
                        type: 'playerMessage',
                        content: messageDetails
                    };
                    window.parent.postMessage(message, origin);

                    if (sendTimeIntervalLimit) {
                        setTimeout(function() {
                            if (lastMessage !== message) {
                                message = lastMessage;
                                lastMessage = null;
                                window.parent.postMessage(message, origin);
                            } else {
                                lastMessage = null;
                            }
                        }, sendTimeIntervalLimit);
                    }
                }
                if (sendTimeIntervalLimit) {
                    lastMessage = message;
                }
            }
        };
    };
    window.PG = new PG();

}(window, document));
<commit_msg>Clean up some param names<commit_after>/**
 * Phune Gaming SDK for JavaScript v{{ VERSION }}
 *
 * @file Handles the communication between the Phune Gaming platform and the games.
 * @license MIT License <http://opensource.org/licenses/MIT>
 * @copyright Copyright (c) 2014 Present Technologies
 */
(function(window, document) {
    'use strict';

    /**
     * Constructs a new instance of the Phune Gaming SDK.
     *
     * @constructor PG
     * @classdesc Phune Gaming SDK
     */
    var PG = function() {

        var origin = '*', // (document.location.origin) ? document.location.origin : document.location.protocol + '//' + document.location.host,
            result = {
                WON: 'won',
                LOST: 'lost',
                DRAW: 'draw'
            },
            message, lastMessage = null,
            player, opponent;

        /**
         * Get the match result with the correct value based on the winnerPlayerId param.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} winnerPlayerId The id of the player that won the match.
         * @returns {string} A match result valid value.
         */
        var getMatchResult = function(winnerPlayerId) {
            if (typeof winnerPlayerId === 'undefined') {
                return;
            }

            if (winnerPlayerId === player.id) {
                return result.WON;
            } else if (winnerPlayerId === opponent.id) {
                return result.LOST;
            }

            return result.DRAW;
        };

        /**
         * The game should build the user interface and get ready to start playing.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {Object} player The current player details.
         * @param {Object} opponent The opponent details.
         * @param {string} deviceType Indicates the type of the device where the game is running. Possible values are 'MOBILE' and 'TV'.
         */
        var onMatchPrepare = function(player, opponent, deviceType) {
            throw new Error('onMatchPrepare is not implemented.');
        };

        /**
         * The game can now configure additional match details.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} allowedTime Time allowed for the player to configure the game and start the match.
         */
        var onGameLobby = function(allowedTime) {
            throw new Error('onGameLobby is not implemented.');
        };

        /**
         * The match start confirmation. Only now is the player allowed to play the game.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {number} timeToPlay Time allowed for the player to make a move.
         */
        var onMatchStart = function(playerIdToPlayNext, timeToPlay) {
            throw new Error('onMatchStart is not implemented.');
        };

        /**
         * Acknowledgment to a valid move.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdWhoSentTheMove The identifier of the player that sent the move.
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {Object} moveDetails The move details.
         * @param {Object} moveEvaluation The result of the move validation.
         * @param {string} [matchResult] If the move ended the match, this indicates its result. Possible values are 'won', 'lost' or 'draw'.
         */
        var onMoveValid = function(playerIdWhoSentTheMove, playerIdToPlayNext, moveDetails, moveEvaluation, matchResult) {
            throw new Error('onMoveValid is not implemented.');
        };

        /**
         * Acknowledgment to an invalid move sent by the current player.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerIdToPlayNext The identifier of the player to whom the next move belongs.
         * @param {Object} moveEvaluation The result of the move validation.
         */
        var onMoveInvalid = function(playerIdToPlayNext, moveEvaluation) {
            throw new Error('onMoveInvalid is not implemented.');
        };

        /**
         * A message from the server-side rules was received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {number} playerWhoSentTheMessage The identifier of the player that sent the message.
         * @param {Object} messageDetails Message specific to a game and unknown to the platform. The developer is advised to have multiple message types with different bodies in order to achieve different goals.
         * @param {Object} messageResult The result returned by the server-side rules.
         */
        var onServerMessage = function(playerIdWhoSentTheMessage, messageDetails, messageResult) {
            throw new Error('onServerMessage is not implemented.');
        };

        /**
         * A message sent directly from another player was received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {Object} messageDetails Message specific to a game and unknown to the platform. The developer is advised to have multiple message types with different bodies in order to achieve different goals.
         */
        var onPlayerMessage = function(messageDetails) {
            throw new Error('onPlayerMessage is not implemented.');
        };

        /**
         * Called by the platform when a match end event is received.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {string} matchResult The match result. Possible values are 'won', 'lost' or 'draw'.
         */
        var onMatchEnd = function(matchResult) {
            throw new Error('onMatchEnd is not implemented.');
        };

        /**
         * A keyboard or TV remote control key was pressed.
         *
         * @private
         * @abstract
         * @memberof PG
         * @param {string} key The key that was pressed. Possible values are 'left', 'right', 'up', 'down' or 'enter'.
         */
        var onKeyPress = function(key) {
            throw new Error('onKeyPress is not implemented.');
        };

        return {
            /**
             * Initialize the Phune Gaming SDK.
             *
             * @public
             * @memberof PG
             * @param {Object} params Object containing all the options to configure the SDK. This includes the mandatory callback functions: onMatchPrepare, onMatchStart, onMoveValid, onMoveInvalid, onMatchEnd, as well as the optional callback functions: onServerMessage, onPlayerMessage and onKeyPress.
             */
            init: function(params) {
                onMatchPrepare = params.onMatchPrepare || onMatchPrepare;
                onGameLobby = params.onGameLobby || onGameLobby;
                onMatchStart = params.onMatchStart || onMatchStart;
                onMoveValid = params.onMoveValid || onMoveValid;
                onMoveInvalid = params.onMoveInvalid || onMoveInvalid;
                onMatchEnd = params.onMatchEnd || onMatchEnd;
                onServerMessage = params.onServerMessage || onServerMessage;
                onPlayerMessage = params.onPlayerMessage || onPlayerMessage;
                onKeyPress = params.onKeyPress || onKeyPress;

                window.addEventListener('message', function(msg) {
                    // if (msg.origin !== origin) {
                    //     console.warn('Origin not recognized: ' + msg.origin);
                    //     return;
                    // }
                    switch (msg.data.type) {
                    case 'matchPrepare':
                        player = msg.data.player;
                        opponent = msg.data.opponent;
                        onMatchPrepare(player, opponent, msg.data.deviceType);
                        break;
                    case 'enterGameLobby':
                        onGameLobby(msg.data.timeout);
                        break;
                    case 'matchStart':
                        onMatchStart(msg.data.nextPlayerId, msg.data.timeout);
                        break;
                    case 'matchMoveValid':
                        onMoveValid(msg.data.playerId, msg.data.nextPlayerId, msg.data.content, msg.data.evaluationContent, getMatchResult(msg.data.winnerPlayerId));
                        break;
                    case 'matchMoveInvalid':
                        onMoveInvalid(msg.data.nextPlayerId, msg.data.evaluationContent);
                        break;
                    case 'matchEnd':
                        onMatchEnd(getMatchResult(msg.data.winnerPlayerId));
                        break;
                    case 'serverMessage':
                        onServerMessage(msg.data.playerId, msg.data.content, msg.data.result);
                        break;
                    case 'playerMessage':
                        onPlayerMessage(msg.data.content);
                        break;
                    case 'keyPress':
                        onKeyPress(msg.data.value);
                        break;
                    }
                });

                // Inform the platform that the game loaded successfully.
                window.parent.postMessage({
                    type: 'loaded'
                }, origin);
            },

            /**
             * Informs the server that the client is ready to start the match.
             *
             * @public
             * @memberof PG
             */
            ready: function() {
                window.parent.postMessage({
                    type: 'ready'
                }, origin);
            },

            /**
             * Informs the server that the game configuration phase ended and the game can start.
             *
             * @public
             * @memberof PG
             */
            exitGameLobby: function() {
                window.parent.postMessage({
                    type: 'exitGameLobby'
                }, origin);
            },

            /**
             * Asks the platform to show the game menu.
             *
             * @public
             * @memberof PG
             */
            showMenu: function() {
                window.parent.postMessage({
                    type: 'showMenu'
                }, origin);
            },

            /**
             * Send a move to the platform server.
             *
             * @public
             * @memberof PG
             * @param {Object} moveDetails The move details.
             * @param {function(Object): boolean} [validate] Optional function to validate the move before sending it to server.
             * @returns {boolean} True when the move is valid or when no validation function was provided.
             */
            sendMove: function(moveDetails, validate) {
                if (validate && !validate(moveDetails)) {
                    return false;
                }

                window.parent.postMessage({
                    type: 'move',
                    content: moveDetails
                }, origin);

                return true;
            },

            /**
             * Send a message to the server-side rules. This message is specific to a game and will not be processed by the platform itself.
             * The response from the server could either be sent to you only or to all players.
             *
             * @public
             * @memberof PG
             * @param {Object} messageDetails The content of the message to be sent.
             * @param {boolean} isAnswerPublic Whether the reply from the server's rules should be sent to all players or not.
             * @param {boolean} [serializeRequest] Whether the messages should be processed in order of arrival or can be executed in parallel.
             */
            sendMessageToServer: function(messageDetails, isAnswerPublic, serializeRequest) {
                window.parent.postMessage({
                    type: 'serverMessage',
                    publicAnswer: isAnswerPublic,
                    requiresConcurrencyControl: (serializeRequest) ? serializeRequest : true,
                    content: messageDetails
                }, origin);
            },

            /**
             * Peer-to-peer message sent directly to another player.
             *
             * @public
             * @memberof PG
             * @param {Object} messageDetails The content of the message to be sent.
             * @param {number} [sendTimeIntervalLimit] Do not allow sending more than one message within the specified time in milliseconds.
             *                 If this is called more than once during this interval only the last message will be sent.
             */
            sendMessageToPlayer: function(messageDetails, sendTimeIntervalLimit) {
                if (!sendTimeIntervalLimit || lastMessage === null) {
                    message = {
                        type: 'playerMessage',
                        content: messageDetails
                    };
                    window.parent.postMessage(message, origin);

                    if (sendTimeIntervalLimit) {
                        setTimeout(function() {
                            if (lastMessage !== message) {
                                message = lastMessage;
                                lastMessage = null;
                                window.parent.postMessage(message, origin);
                            } else {
                                lastMessage = null;
                            }
                        }, sendTimeIntervalLimit);
                    }
                }
                if (sendTimeIntervalLimit) {
                    lastMessage = message;
                }
            }
        };
    };
    window.PG = new PG();

}(window, document));
<|endoftext|>


<commit_before><commit_msg>Tooltip component add title slots<commit_after><|endoftext|>