<commit_before>import PropTypes from 'prop-types'
import React from 'react'
import GlobalSearch from 'part:@sanity/components/globalsearch/default'
import globalSearchStyles from 'part:@sanity/components/globalsearch/default-style'
import schema from 'part:@sanity/base/schema?'
import client from 'part:@sanity/base/client?'
import Preview from 'part:@sanity/base/preview?'
import locationStore from 'part:@sanity/base/location'
import {IntentLink} from 'part:@sanity/base/router'
import {union, flatten} from 'lodash'
import styles from './styles/Search.css'

class Search extends React.Component {

  static propTypes = {
    onSelect: PropTypes.func
  }

  static defaultProps = {
    onSelect() {}
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      topItems: [],
      items: []
    }
  }

  handleSearch = q => {
    if (!client) {
      console.error('Sanity client is missing. (Search is disabled)') // eslint-disable-line
      return
    }

    // Get all fields that we want to search in (text and string)
    const searchableFields = flatten(
      schema.getTypeNames()
        .map(typeName => schema.get(typeName))
        .filter(type => type.type && type.type.name === 'object')
        .map(type => type.fields
            .filter(field => field.type.jsonType === 'string')
            .map(field => field.name)
          )
        )

    const uniqueFields = union(searchableFields)
    const constraints = uniqueFields.map(field => `${field} match $term`)
    const query = `*[${constraints.join(' || ')}][0...10]`

    this.setState({
      isSearching: true
    })

    client.fetch(query, {term: q})
      .then(hits => {
        this.setState({
          isSearching: false,
          isOpen: true,
          // we need this filtering because the search my return documents of types not in schema
          items: hits.filter(hit => schema.has(hit._type))
        })
      })

    this.setState({
      isOpen: true
    })
  }

  getTopItems = () => {
    // We use 3 last edited items until we have logic for most used etc.

    const prefixedTypeNames = schema.getTypeNames().map(str => `"${str}"`)

    const query = `*[_type in [${prefixedTypeNames.join(', ')}]] | order(_updatedAt desc) [0...3]`

    client.fetch(query, {})
      .then(response => {
        this.setState({
          topItems: response
        })
      })
  }

  handleFocus = () => {
    this.getTopItems()

    this.setState({
      active: true,
      isOpen: true
    })
  }
  handleClose = () => {
    this.setState({
      isOpen: false
    })
  }

  handleBlur = () => {
    this.setState({
      isOpen: false
    })
  }

  renderItem = (item, options) => {
    const type = schema.get(item._type)
    return (
      <IntentLink intent="edit" params={{id: item._id, type: type.name}} className={globalSearchStyles.link}>
        <Preview
          value={item}
          layout="default"
          type={type}
        />
      </IntentLink>
    )
  }

  render() {

    if (!schema) {
      return
No schema

    }

    return (

        <GlobalSearch
          onSearch={this.handleSearch}
          isSearching={this.state.isSearching}
          onBlur={this.handleBlur}
          renderItem={this.renderItem}
          onFocus={this.handleFocus}
          onClose={this.handleClose}
          isOpen={this.state.isOpen}
          label="Search"
          items={(this.state.items.length > 0 && this.state.items) || this.state.topItems}
          placeholder="Search…"
          listContainerClassName={styles.listContainer}
        />

    )
  }
}

export default Search
<commit_msg>[default-layout] Support substring search<commit_after>import PropTypes from 'prop-types'
import React from 'react'
import GlobalSearch from 'part:@sanity/components/globalsearch/default'
import globalSearchStyles from 'part:@sanity/components/globalsearch/default-style'
import schema from 'part:@sanity/base/schema?'
import client from 'part:@sanity/base/client?'
import Preview from 'part:@sanity/base/preview?'
import locationStore from 'part:@sanity/base/location'
import {IntentLink} from 'part:@sanity/base/router'
import {union, flatten} from 'lodash'
import styles from './styles/Search.css'

class Search extends React.Component {

  static propTypes = {
    onSelect: PropTypes.func
  }

  static defaultProps = {
    onSelect() {}
  }

  constructor(props) {
    super(props)

    this.state = {
      isOpen: false,
      topItems: [],
      items: []
    }
  }

  handleSearch = q => {
    if (!client) {
      console.error('Sanity client is missing. (Search is disabled)') // eslint-disable-line
      return
    }

    // Get all fields that we want to search in (text and string)
    const searchableFields = flatten(
      schema.getTypeNames()
        .map(typeName => schema.get(typeName))
        .filter(type => type.type && type.type.name === 'object')
        .map(type => type.fields
            .filter(field => field.type.jsonType === 'string')
            .map(field => field.name)
          )
        )

    const uniqueFields = union(searchableFields)
    const constraints = uniqueFields.map(field => `${field} match $term`)
    const query = `*[${constraints.join(' || ')}][0...10]`

    this.setState({
      isSearching: true
    })

    client.fetch(query, {term: `${q}*`})
      .then(hits => {
        this.setState({
          isSearching: false,
          isOpen: true,
          // we need this filtering because the search my return documents of types not in schema
          items: hits.filter(hit => schema.has(hit._type))
        })
      })

    this.setState({
      isOpen: true
    })
  }

  getTopItems = () => {
    // We use 3 last edited items until we have logic for most used etc.

    const prefixedTypeNames = schema.getTypeNames().map(str => `"${str}"`)

    const query = `*[_type in [${prefixedTypeNames.join(', ')}]] | order(_updatedAt desc) [0...3]`

    client.fetch(query, {})
      .then(response => {
        this.setState({
          topItems: response
        })
      })
  }

  handleFocus = () => {
    this.getTopItems()

    this.setState({
      active: true,
      isOpen: true
    })
  }
  handleClose = () => {
    this.setState({
      isOpen: false
    })
  }

  handleBlur = () => {
    this.setState({
      isOpen: false
    })
  }

  renderItem = (item, options) => {
    const type = schema.get(item._type)
    return (
      <IntentLink intent="edit" params={{id: item._id, type: type.name}} className={globalSearchStyles.link}>
        <Preview
          value={item}
          layout="default"
          type={type}
        />
      </IntentLink>
    )
  }

  render() {

    if (!schema) {
      return
No schema

    }

    return (

        <GlobalSearch
          onSearch={this.handleSearch}
          isSearching={this.state.isSearching}
          onBlur={this.handleBlur}
          renderItem={this.renderItem}
          onFocus={this.handleFocus}
          onClose={this.handleClose}
          isOpen={this.state.isOpen}
          label="Search"
          items={(this.state.items.length > 0 && this.state.items) || this.state.topItems}
          placeholder="Search…"
          listContainerClassName={styles.listContainer}
        />

    )
  }
}

export default Search
<|endoftext|>


<commit_before>'use strict';

module.exports = function () {

  return console.log("TextRTF whatsup");

};
<commit_msg>Removed unused JS<commit_after><|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics',
  name: 'DAOControllerView',
  extends: 'foam.u2.View',

  requires: [
    'foam.comics.SearchMode',
    'foam.comics.DAOController',
    'foam.comics.DAOUpdateControllerView',
    'foam.u2.view.ScrollTableView',
    'foam.u2.dialog.Popup'
  ],

  imports: [
    'createControllerView? as importedCreateControllerView',
    'data? as importedData',
    'stack',
    'summaryView? as importedSummaryView',
    'updateView? as importedUpdateView',
    'window'
  ],

  exports: [
    'as controllerView',
    'data.selection as selection',
    'data.data as dao',
    'data.filteredTableColumns as filteredTableColumns',
    'data.searchColumns as searchColumns',
    'dblclick'
  ],

  css: `
    ^ {
      /* The following three lines are a cross-browser
         equivalent to width: fit-content; in Chrome */
      width: intrinsic;
      width: -moz-max-content;
      width: -webkit-max-content;
      margin: 24px auto 0 auto;
    }

    ^top-row {
      align-items: center;
      margin-bottom: 10px;
    }

    ^separate {
      display: flex;
      justify-content: space-between;
    }

    ^title-container > * {
      color: /*%BLACK%*/ #1e1f21;
      margin: 0;
    }

    ^container {
      display: flex;
    }

    ^ .actions {
      display: inline-block;
      margin-bottom: 8px;
    }

    ^ .actions button + button {
      margin-left: 8px;
    }

    ^full-search-container {
      flex: 0 0 250px;
      margin-right: 10px;
    }

    ^ .foam-u2-view-TableView {
      width: 1024px;
    }
  `,

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.comics.DAOController',
      name: 'data',
      expression: function(importedData) {
        return importedData;
      }
    },
    {
      name: 'cls',
      expression: function(data) {
        return data.cls_;
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      factory: function() {
        return this.data.summaryView || this.importedSummaryView || {
          class: 'foam.u2.view.ScrollTableView'
        };
      }
    },
    {
      name: 'createControllerView',
      expression: function() {
        return this.importedCreateControllerView || {
          class: 'foam.comics.DAOCreateControllerView'
        };
      }
    },
    {
      name: 'updateView',
      expression: function() {
        return this.importedUpdateView || {
          class: 'foam.comics.DAOUpdateControllerView'
        };
      }
    }
  ],

  reactions: [
    ['data', 'action.create', 'onCreate'],
    ['data', 'edit', 'onEdit'],
    ['data', 'finished', 'onFinished'],
    ['data', 'export', 'onExport']
  ],

  methods: [
    function initE() {
      var self = this;

      this.data.border.add(
        this.E()
          .addClass(this.myClass())
          .start()
            .addClass(this.myClass('top-row'))
            .addClass(this.myClass('separate'))
            .start()
              .addClass(this.myClass('title-container'))
              .start('h1')
                .add(this.data.title$)
              .end()
              .start()
                .add(this.data.subtitle$)
              .end()
            .end()
            .callIfElse(self.data.createLabel, function() {
              this.tag(self.data.primaryAction, {
                label$: self.data.createLabel$,
                size: 'LARGE'
              });
            }, function() {
              this.start().tag(self.data.primaryAction, { size: 'LARGE' }).end();
            })
          .end()
          .start()
            .addClass(this.myClass('container'))
            .callIf(this.data.searchMode === this.SearchMode.FULL, function() {
              this.start()
                .hide(self.data.searchHidden$)
                .addClass(self.myClass('full-search-container'))
                .add(self.cls.PREDICATE.clone().copyFrom({
                  view: { class: 'foam.u2.view.ReciprocalSearch' }
                }))
              .end();
            })
            .start()
              .start()
                .addClass(this.myClass('separate'))
                .callIf(this.data.searchMode === this.SearchMode.SIMPLE, function() {
                  this
                    .start()
                      .add(self.cls.PREDICATE.clone().copyFrom({
                        view: { class: 'foam.u2.view.SimpleSearch' }
                      }))
                    .end();
                })
                .start()
                  .addClass('actions')
                  .show(self.mode$.map((m) => m === foam.u2.DisplayMode.RW))
                  .start()
                    .forEach(self.cls.getAxiomsByClass(foam.core.Action).filter((action) => {
                      return action.name !== self.data.primaryAction.name;
                    }), function(action) {
                      this.tag(action, { buttonStyle: 'TERTIARY' });
                    })
                    .add()
                  .end()
                .end()
              .end()
              .start()
                .style({ 'overflow-x': 'auto' })
                .tag(this.summaryView, {
                  data$: this.data.filteredDAO$,
                  multiSelectEnabled: !! this.data.relationship,
                  selectedObjects$: this.data.selectedObjects$
                })
              .end()
            .end()
          .end());

      this.add(this.data.border);
    },

    function dblclick(obj) {
      if ( this.data.dblclick ) {
        this.data.dblclick(obj);
      } else {
        this.onEdit(null, null, obj.id);
      }
    }
  ],

  listeners: [
    function onCreate() {
      this.stack.push({
        class: this.createControllerView.class,
        detailView: this.data.detailView
      }, this);
    },

    function onEdit(s, edit, id) {
      this.stack.push({
        class: this.updateView.class,
        detailView: this.data.detailView,
        editEnabled: this.data.editEnabled,
        key: id
      }, this);
    },

    function onFinished() {
      this.stack.back();
    },

    function onExport(dao) {
      this.add(this.Popup.create().tag({
        class: 'foam.u2.ExportModal',
        exportData: dao.src.filteredDAO
      }));
    }
  ]
});
<commit_msg>Visually align the right side of the table and the primary action<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.comics',
  name: 'DAOControllerView',
  extends: 'foam.u2.View',

  requires: [
    'foam.comics.SearchMode',
    'foam.comics.DAOController',
    'foam.comics.DAOUpdateControllerView',
    'foam.u2.view.ScrollTableView',
    'foam.u2.dialog.Popup'
  ],

  imports: [
    'createControllerView? as importedCreateControllerView',
    'data? as importedData',
    'stack',
    'summaryView? as importedSummaryView',
    'updateView? as importedUpdateView',
    'window'
  ],

  exports: [
    'as controllerView',
    'data.selection as selection',
    'data.data as dao',
    'data.filteredTableColumns as filteredTableColumns',
    'data.searchColumns as searchColumns',
    'dblclick'
  ],

  css: `
    ^ {
      /* The following three lines are a cross-browser
         equivalent to width: fit-content; in Chrome */
      width: intrinsic;
      width: -moz-max-content;
      width: -webkit-max-content;
      margin: 24px auto 0 auto;
    }

    ^top-row {
      align-items: center;
      margin-bottom: 10px;
    }

    ^separate {
      display: flex;
      justify-content: space-between;
    }

    ^title-container > * {
      color: /*%BLACK%*/ #1e1f21;
      margin: 0;
    }

    ^container {
      display: flex;
      justify-content: space-between;
    }

    ^ .actions {
      display: inline-block;
      margin-bottom: 8px;
    }

    ^ .actions button + button {
      margin-left: 8px;
    }

    ^full-search-container {
      flex: 0 0 250px;
    }

    ^ .foam-u2-view-TableView {
      width: 1024px;
    }
  `,

  properties: [
    {
      class: 'FObjectProperty',
      of: 'foam.comics.DAOController',
      name: 'data',
      expression: function(importedData) {
        return importedData;
      }
    },
    {
      name: 'cls',
      expression: function(data) {
        return data.cls_;
      }
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      factory: function() {
        return this.data.summaryView || this.importedSummaryView || {
          class: 'foam.u2.view.ScrollTableView'
        };
      }
    },
    {
      name: 'createControllerView',
      expression: function() {
        return this.importedCreateControllerView || {
          class: 'foam.comics.DAOCreateControllerView'
        };
      }
    },
    {
      name: 'updateView',
      expression: function() {
        return this.importedUpdateView || {
          class: 'foam.comics.DAOUpdateControllerView'
        };
      }
    }
  ],

  reactions: [
    ['data', 'action.create', 'onCreate'],
    ['data', 'edit', 'onEdit'],
    ['data', 'finished', 'onFinished'],
    ['data', 'export', 'onExport']
  ],

  methods: [
    function initE() {
      var self = this;

      this.data.border.add(
        this.E()
          .addClass(this.myClass())
          .start()
            .addClass(this.myClass('top-row'))
            .addClass(this.myClass('separate'))
            .start()
              .addClass(this.myClass('title-container'))
              .start('h1')
                .add(this.data.title$)
              .end()
              .start()
                .add(this.data.subtitle$)
              .end()
            .end()
            .callIfElse(self.data.createLabel, function() {
              this.tag(self.data.primaryAction, {
                label$: self.data.createLabel$,
                size: 'LARGE'
              });
            }, function() {
              this.start().tag(self.data.primaryAction, { size: 'LARGE' }).end();
            })
          .end()
          .start()
            .addClass(this.myClass('container'))
            .callIf(this.data.searchMode === this.SearchMode.FULL, function() {
              this.start()
                .hide(self.data.searchHidden$)
                .addClass(self.myClass('full-search-container'))
                .add(self.cls.PREDICATE.clone().copyFrom({
                  view: { class: 'foam.u2.view.ReciprocalSearch' }
                }))
              .end();
            })
            .start()
              .start()
                .addClass(this.myClass('separate'))
                .callIf(this.data.searchMode === this.SearchMode.SIMPLE, function() {
                  this
                    .start()
                      .add(self.cls.PREDICATE.clone().copyFrom({
                        view: { class: 'foam.u2.view.SimpleSearch' }
                      }))
                    .end();
                })
                .start()
                  .addClass('actions')
                  .show(self.mode$.map((m) => m === foam.u2.DisplayMode.RW))
                  .start()
                    .forEach(self.cls.getAxiomsByClass(foam.core.Action).filter((action) => {
                      return action.name !== self.data.primaryAction.name;
                    }), function(action) {
                      this.tag(action, { buttonStyle: 'TERTIARY' });
                    })
                    .add()
                  .end()
                .end()
              .end()
              .start()
                .style({ 'overflow-x': 'auto' })
                .tag(this.summaryView, {
                  data$: this.data.filteredDAO$,
                  multiSelectEnabled: !! this.data.relationship,
                  selectedObjects$: this.data.selectedObjects$
                })
              .end()
            .end()
          .end());

      this.add(this.data.border);
    },

    function dblclick(obj) {
      if ( this.data.dblclick ) {
        this.data.dblclick(obj);
      } else {
        this.onEdit(null, null, obj.id);
      }
    }
  ],

  listeners: [
    function onCreate() {
      this.stack.push({
        class: this.createControllerView.class,
        detailView: this.data.detailView
      }, this);
    },

    function onEdit(s, edit, id) {
      this.stack.push({
        class: this.updateView.class,
        detailView: this.data.detailView,
        editEnabled: this.data.editEnabled,
        key: id
      }, this);
    },

    function onFinished() {
      this.stack.back();
    },

    function onExport(dao) {
      this.add(this.Popup.create().tag({
        class: 'foam.u2.ExportModal',
        exportData: dao.src.filteredDAO
      }));
    }
  ]
});
<|endoftext|>


<commit_before>/*global define*/

define(['jquery', 'underscore', 'backbone', 'templates', 'helpers/gauge-helper', 'marionette'], function($, _, Backbone, JST, gaugeHelper) {
    'use strict';

    var OsdDashView = Backbone.Marionette.ItemView.extend({
        className: 'col-lg-3 col-md-3 col-sm-6 col-xs-6 custom-gutter',
        template: JST['app/scripts/templates/osd-dash.ejs'],
        headlineTemplate: _.template('<%- ok %>/<%- total %>'),
        subtextTemplate: _.template('<%- percentage %>% out'),
        ui: {
            'headline': '.headline',
            'subline': '.subline',
            'subtext': '.subtext'
        },
        modelEvents: {
            'change': 'modelChanged'
        },
        initialize: function() {
            _.bindAll(this, 'set', 'modelChanged');
            this.App = Backbone.Marionette.getOption(this, 'App');
            if (this.App) {
                this.listenTo(this.App.vent, 'status:update', this.set);
            }
            this.model = new Backbone.Model();
            gaugeHelper(this);
        },
        set: function(model) {
            var attr = model.attributes.osd;
            console.log(model.attributes);
            this.model.set({
                ok: attr.ok.count,
                warn: attr.warn.count,
                critical: attr.critical.count
            });
        },
        modelChanged: function(model) {
            var ok = model.get('ok');
            var warn = model.get('warn');
            var critical = model.get('critical');
            var count = 0;
            if (_.isNumber(ok)) {
                count += ok;
            }
            if (_.isNumber(warn)) {
                count += warn;
            }
            if (_.isNumber(critical)) {
                count += critical;
            }
            this.ui.subline.text('In and Up');
            if (warn || critical) {
                var percentage = Math.floor(((warn + critical) / count) * 100);
                this.ui.subtext.text(this.subtextTemplate({
                    percentage: percentage
                }));
            }
            this.ui.headline.text(this.headlineTemplate({
                ok: ok,
                total: count
            }));
        }
    });

    return OsdDashView;
});
<commit_msg>Use round instead of floor<commit_after>/*global define*/

define(['jquery', 'underscore', 'backbone', 'templates', 'helpers/gauge-helper', 'marionette'], function($, _, Backbone, JST, gaugeHelper) {
    'use strict';

    var OsdDashView = Backbone.Marionette.ItemView.extend({
        className: 'col-lg-3 col-md-3 col-sm-6 col-xs-6 custom-gutter',
        template: JST['app/scripts/templates/osd-dash.ejs'],
        headlineTemplate: _.template('<%- ok %>/<%- total %>'),
        subtextTemplate: _.template('<%- percentage %>% out'),
        ui: {
            'headline': '.headline',
            'subline': '.subline',
            'subtext': '.subtext'
        },
        modelEvents: {
            'change': 'modelChanged'
        },
        initialize: function() {
            _.bindAll(this, 'set', 'modelChanged');
            this.App = Backbone.Marionette.getOption(this, 'App');
            if (this.App) {
                this.listenTo(this.App.vent, 'status:update', this.set);
            }
            this.model = new Backbone.Model();
            gaugeHelper(this);
        },
        set: function(model) {
            var attr = model.attributes.osd;
            console.log(model.attributes);
            this.model.set({
                ok: attr.ok.count,
                warn: attr.warn.count,
                critical: attr.critical.count
            });
        },
        modelChanged: function(model) {
            var ok = model.get('ok');
            var warn = model.get('warn');
            var critical = model.get('critical');
            var count = 0;
            if (_.isNumber(ok)) {
                count += ok;
            }
            if (_.isNumber(warn)) {
                count += warn;
            }
            if (_.isNumber(critical)) {
                count += critical;
            }
            this.ui.subline.text('In and Up');
            if (warn || critical) {
                var percentage = Math.round(((warn + critical) / count) * 100);
                this.ui.subtext.text(this.subtextTemplate({
                    percentage: percentage
                }));
            }
            this.ui.headline.text(this.headlineTemplate({
                ok: ok,
                total: count
            }));
        }
    });

    return OsdDashView;
});
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOMServer from 'react-dom/server';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './index.css';

export const getRegex = () => /@([a-z][0-9a-z-_]+(?:\:([a-z][0-9a-z-]+))?)/g; // eslint-disable-line no-useless-escape
export const replaceMentions = (string, mentions) => {
  return string.replace(getRegex(), (match, mentionName, teamName) => {
    const mention = mentions.find(m => [m.userName, m.groupName].includes(mentionName));
    if (mention) {
      const isGroup = !!teamName;
      return ReactDOMServer.renderToStaticMarkup(<Mention isGroup={isGroup}>{mention.name}</Mention>);
    }
    return match;
  });
};
export const userMentionType = PropTypes.shape({
  userName: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  email: PropTypes.string.isRequired,
  avatarURL: PropTypes.string,
});
export const groupMentionType = PropTypes.shape({
  groupName: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  avatarURL: PropTypes.string,
});

const Mention = props => {
  const { children, className, isGroup, ...rest } = props;
  return <span {...rest} className={classnames(styles.mention, className, { 'is-group': isGroup })}>
    {children}
  </span>;
};

Mention.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
  isGroup: PropTypes.bool,
};

export default Mention;
<commit_msg>Add id type to mention<commit_after>import React from 'react';
import ReactDOMServer from 'react-dom/server';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import styles from './index.css';

export const getRegex = () => /@([a-z][0-9a-z-_]+(?:\:([a-z][0-9a-z-]+))?)/g; // eslint-disable-line no-useless-escape
export const replaceMentions = (string, mentions) => {
  return string.replace(getRegex(), (match, mentionName, teamName) => {
    const mention = mentions.find(m => [m.userName, m.groupName].includes(mentionName));
    if (mention) {
      const isGroup = !!teamName;
      return ReactDOMServer.renderToStaticMarkup(<Mention isGroup={isGroup}>{mention.name}</Mention>);
    }
    return match;
  });
};
export const userMentionType = PropTypes.shape({
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  userName: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  email: PropTypes.string.isRequired,
  avatarURL: PropTypes.string,
});
export const groupMentionType = PropTypes.shape({
  id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
  groupName: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
  avatarURL: PropTypes.string,
});

const Mention = props => {
  const { children, className, isGroup, ...rest } = props;
  return <span {...rest} className={classnames(styles.mention, className, { 'is-group': isGroup })}>
    {children}
  </span>;
};

Mention.propTypes = {
  children: PropTypes.node,
  className: PropTypes.string,
  isGroup: PropTypes.bool,
};

export default Mention;
<|endoftext|>


<commit_before>//https://learn.jquery.com/plugins/basic-plugin-creation/

(function ($) {
  $.fn.ssAC = function (options) {

    //extend jQuery contains to be case-insensitive
    $.expr[':'].containsIC = function (n, i, m) {
      return jQuery(n).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
    };

    //init global variables
    var _self = this;
    this.selectedLI = null;
    this.ssSL = $(this);
    // This is the easiest way to have default options.
    var settings = $.extend({
      startsWith: false,
      width: _self.ssSL.outerWidth(true)
    }, options);

    ////////////////////////////////
    // CONSTRUCT NEW DOM ELEMENTS //
    ////////////////////////////////

    //configure text box
    this.ssTB = $.parseHTML("<input type='text' style='" + _self.ssSL.attr("style") + "' class='" + _self.ssSL.attr("class") + "' />")
    $(this.ssTB).css("width", settings.width); //force correct width if not stipulated
    _self.ssSL.after(_self.ssTB);
    _self.ssSL.hide(); //hide select list

    //convert OPTION to LI
    var li = "";
    _self.ssSL.find('option').each(function (i, v) {
      var value = $(v);
      li = li + "
" + value.text() + "
";
    })
    //create DIV list
    this.ssDIV = $.parseHTML("

" + li + "

");
    _self.ssSL.after(_self.ssDIV);
    $(_self.ssDIV).hide();

    ////////////
    // EVENTS //
    ////////////

    //textbox click - open list
    $(_self.ssTB).on('click', function () {
      $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB })
      _self.SITV();
    })

    //textbox - ignore left and right arrows due to unwanted caret movement in textbox
    $(_self.ssTB).on('keydown', function (e) {
      if (e.keyCode === 38 || e.keyCode === 40) {
        e.preventDefault();
      }
    })

    //textbox - handle keys
    $(_self.ssTB).on('keyup', function (e) {
      //UP & DOWN if list NOT showing
      if ((e.keyCode === 38 || e.keyCode === 40) && !$(_self.ssDIV).is(":visible")) {
        $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB });
        _self.SITV();
      }
      else if (e.keyCode === 38) { //arrow up
        _self.MOVE(-1);
      }
      else if (e.keyCode === 40) { //arrow down
        _self.MOVE(1);
      }
      else if (e.keyCode === 13 || e.keyCode === 27) { //ENTER & TAB
        _self.SelectIndex(0);
        $(_self.ssDIV).hide();
      }
      else if (e.keyCode === 9 || e.keyCode === 37 || e.keyCode === 39 || e.keyCode === 16 || e.keyCode === 17 || e.keyCode === 18) {
        //ignore: tab, leftarrow, rightarrow, shift, ctrl, alt
      }
      else {
        if (e.keyCode !== 46) {
          $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB })
          _self.SITV();
        }
        var searchString = $(_self.ssTB).val().toLowerCase();
        var searchLength = searchString.length;
        var fList;
        //filter (anywhere or start)
        if (settings.startsWith) {
          fList = $(_self.ssDIV).find("li").filter(function () {
            return $(this).text().slice(0, searchLength).toLowerCase() == searchString;
          });
        }
        else {
          fList = $(_self.ssDIV).find('li:containsIC("' + searchString + '")');
        }

        $(_self.ssDIV).find('li').hide();
        fList.show();
        _self.selectedLI = fList.first();
        _self.SelectIndex(1);
      }
      e.preventDefault();
    })

    //textbox - blur event (hide div list)
    $(_self.ssTB).on('blur', function (e) {
      $(_self.ssDIV).hide();
      _self.SelectIndex(0);
      $(_self.ssDIV).find('li[value]').show();
    })

    //click list item in DIV (delegate)
    $(_self.ssDIV).on('mousedown', 'li', function (e) {
      _self.selectedLI = $(this);
      _self.SelectIndex(0);
      $(_self.ssDIV).hide();
    });

    ///////////////
    // FUNCTIONS //
    ///////////////
    //MOVE
    this.MOVE = function (n) {
      if ($(_self.ssDIV).find('li:visible').length <= 1) { return; }

      if (_self.liIndex() === -1) {
        if (n > 0) { _self.selectedLI = $(_self.ssDIV).find('li:visible').first(); }
        else { _self.selectedLI = $(_self.ssDIV).find('li:visible').last(); }
        _self.SelectIndex(1);
        return;
      }
      else if (n > 0) {
        _self.selectedLI = $(_self.selectedLI).nextAll(':visible').first();
        if (_self.liIndex() === -1) { _self.selectedLI = $(_self.ssDIV).find('li:visible').first(); }
      }
      else if (n < 0) {
        _self.selectedLI = $(_self.selectedLI).prevAll(':visible').first();
        if (_self.liIndex() === -1) { _self.selectedLI = $(_self.ssDIV).find('li:visible').last(); }
      }
      _self.SelectIndex(1);
    }

    //SELECT INDEX
    this.SelectIndex = function (i) {
      $(_self.ssDIV).find('li').removeClass('ssSI');
      $(_self.selectedLI).addClass('ssSI');
      $(_self.ssSL).val(_self.liValue());
      if (i !== 1) $(_self.ssTB).val(_self.liText());
      _self.SITV();
    }

    //scroll in to view
    this.SITV = function () {
      $(_self.ssDIV).find('li:empty').hide();
      var div = $(_self.ssDIV);
      var sli = $(_self.selectedLI);
      if (sli.position() === undefined) return;

      if (sli.position()["top"] + sli.height() > div.height() + div.scrollTop()) {
        div.animate({ scrollTop: sli.position()["top"] - div.height() + sli.height() * 2 }, 100)
      } else if (sli.position()["top"] < div.scrollTop()) {
        div.animate({ scrollTop: sli.position()["top"] }, 100)
      }
    }

    //get selected text
    this.liText = function () {
      if (_self.selectedLI === null) { return ""; }
      else { return $(_self.selectedLI[0]).text(); }
    }
    //get selected value
    this.liValue = function () {
      if (_self.selectedLI === null) { return ""; }
      else { return $(_self.selectedLI[0]).attr("value"); }
    }
    //get selected index
    this.liIndex = function () {
      if (_self.selectedLI === null) { return -1; }
      else { return $(_self.ssDIV).find(_self.selectedLI[0]).index('li'); }
    }

    //set selected item
    if ($(_self.ssSL).find('[selected]').length) {
      var value = $(_self.ssSL).find('[selected]').val();
      _self.selectedLI = $(_self.ssDIV).find("li[value='" + value + "']");
      _self.SelectIndex(0);
    }

    return this;
  };

}(jQuery));
<commit_msg>Make functions private.<commit_after>//https://learn.jquery.com/plugins/basic-plugin-creation/

(function ($) {
  $.fn.ssAC = function (options) {

    //extend jQuery contains to be case-insensitive
    $.expr[':'].containsIC = function (n, i, m) {
      return jQuery(n).text().toUpperCase().indexOf(m[3].toUpperCase()) >= 0;
    };

    //init global variables
    var _self = this;
    this.selectedLI = null;
    this.ssSL = $(this);
    // This is the easiest way to have default options.
    var settings = $.extend({
      startsWith: false,
      width: _self.ssSL.outerWidth(true)
    }, options);

    ////////////////////////////////
    // CONSTRUCT NEW DOM ELEMENTS //
    ////////////////////////////////

    //configure text box
    this.ssTB = $.parseHTML("<input type='text' style='" + _self.ssSL.attr("style") + "' class='" + _self.ssSL.attr("class") + "' />")
    $(this.ssTB).css("width", settings.width); //force correct width if not stipulated
    _self.ssSL.after(_self.ssTB);
    _self.ssSL.hide(); //hide select list

    //convert OPTION to LI
    var li = "";
    _self.ssSL.find('option').each(function (i, v) {
      var value = $(v);
      li = li + "
" + value.text() + "
";
    })
    //create DIV list
    this.ssDIV = $.parseHTML("

" + li + "

");
    _self.ssSL.after(_self.ssDIV);
    $(_self.ssDIV).hide();

    ////////////
    // EVENTS //
    ////////////

    //textbox click - open list
    $(_self.ssTB).on('click', function () {
      $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB })
      ScrollIntoView(_self);
    })

    //textbox - ignore left and right arrows due to unwanted caret movement in textbox
    $(_self.ssTB).on('keydown', function (e) {
      if (e.keyCode === 38 || e.keyCode === 40) {
        e.preventDefault();
      }
    })

    //textbox - handle keys
    $(_self.ssTB).on('keyup', function (e) {
      //UP & DOWN if list NOT showing
      if ((e.keyCode === 38 || e.keyCode === 40) && !$(_self.ssDIV).is(":visible")) {
        $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB });
        ScrollIntoView(_self);
      }
      else if (e.keyCode === 38) { //arrow up
        MOVE(_self, -1);
      }
      else if (e.keyCode === 40) { //arrow down
        MOVE(_self, 1);
      }
      else if (e.keyCode === 13 || e.keyCode === 27) { //ENTER & TAB
        SelectIndex(_self, 0);
        $(_self.ssDIV).hide();
      }
      else if (e.keyCode === 9 || e.keyCode === 37 || e.keyCode === 39 || e.keyCode === 16 || e.keyCode === 17 || e.keyCode === 18) {
        //ignore: tab, leftarrow, rightarrow, shift, ctrl, alt
      }
      else {
        if (e.keyCode !== 46) {
          $(_self.ssDIV).show().position({ my: "left top+5", at: "left bottom", of: _self.ssTB })
          ScrollIntoView(_self);
        }
        var searchString = $(_self.ssTB).val().toLowerCase();
        var searchLength = searchString.length;
        var fList;
        //filter (anywhere or start)
        if (settings.startsWith) {
          fList = $(_self.ssDIV).find("li").filter(function () {
            return $(this).text().slice(0, searchLength).toLowerCase() === searchString;
          });
        }
        else {
          fList = $(_self.ssDIV).find('li:containsIC("' + searchString + '")');
        }

        $(_self.ssDIV).find('li').hide();
        fList.show();
        _self.selectedLI = fList.first();
        SelectIndex(_self, 1);
      }
      e.preventDefault();
    })

    //textbox - blur event (hide div list)
    $(_self.ssTB).on('blur', function (e) {
      $(_self.ssDIV).hide();
      SelectIndex(_self, 0);
      $(_self.ssDIV).find('li[value]').show();
    })

    //click list item in DIV (delegate)
    $(_self.ssDIV).on('mousedown', 'li', function (e) {
      _self.selectedLI = $(this);
      SelectIndex(_self, 0);
      $(_self.ssDIV).hide();
    });

    ///////////////
    // FUNCTIONS //
    ///////////////
    //MOVE
    function MOVE(_self, n) {
      if ($(_self.ssDIV).find('li:visible').length <= 1) { return; }

      if (liIndex(_self) === -1) {
        if (n > 0) { _self.selectedLI = $(_self.ssDIV).find('li:visible').first(); }
        else { _self.selectedLI = $(_self.ssDIV).find('li:visible').last(); }
        _self.SelectIndex(1);
        return;
      }
      else if (n > 0) {
        _self.selectedLI = $(_self.selectedLI).nextAll(':visible').first();
        if (liIndex(_self) === -1) { _self.selectedLI = $(_self.ssDIV).find('li:visible').first(); }
      }
      else if (n < 0) {
        _self.selectedLI = $(_self.selectedLI).prevAll(':visible').first();
        if (liIndex(_self) === -1) { _self.selectedLI = $(_self.ssDIV).find('li:visible').last(); }
      }
      SelectIndex(_self, 1);
    }

    //SELECT INDEX
    function SelectIndex(_self, i) {
      $(_self.ssDIV).find('li').removeClass('ssSI');
      $(_self.selectedLI).addClass('ssSI');
      $(_self.ssSL).val(liValue(_self));
      if (i !== 1) $(_self.ssTB).val(liText(_self));
      ScrollIntoView(_self);
    }

    //scroll in to view
    function ScrollIntoView(_self) {
      $(_self.ssDIV).find('li:empty').hide();
      var div = $(_self.ssDIV);
      var sli = $(_self.selectedLI);
      if (sli.position() === undefined) return;

      if (sli.position()["top"] + sli.height() > div.height() + div.scrollTop()) {
        div.animate({ scrollTop: sli.position()["top"] - div.height() + sli.height() * 2 }, 100)
      } else if (sli.position()["top"] < div.scrollTop()) {
        div.animate({ scrollTop: sli.position()["top"] }, 100)
      }
    }

    //get selected text
    function liText(_self) {
      if (_self.selectedLI === null) { return ""; }
      else { return $(_self.selectedLI[0]).text(); }
    }
    //get selected value
    function liValue(_self) {
      if (_self.selectedLI === null) { return ""; }
      else { return $(_self.selectedLI[0]).attr("value"); }
    }
    //get selected index
    function liIndex(_self) {
      if (_self.selectedLI === null) { return -1; }
      else { return $(_self.ssDIV).find(_self.selectedLI[0]).index('li'); }
    }

    //set selected item
    if ($(_self.ssSL).find('[selected]').length) {
      var value = $(_self.ssSL).find('[selected]').val();
      _self.selectedLI = $(_self.ssDIV).find("li[value='" + value + "']");
      SelectIndex(_self, 0);
    }

    return this;
  };

}(jQuery));
<|endoftext|>


<commit_before>/*
This REST server provides NeXt-compatible topology JSON. When it's running, it keeps on generating some new nodes/links, imitating real things that are going on in a real network.
Run this server PRIOR TO your NeXt app; otherwise you'll get errors.
*/

// fetch predefined topology from external module
var topology = require('./topology-data');
var topologyData = topology.response;
var liveTopologyTimer;
var http = require('http');

// app configuration
var appConfig = {
	'live': false, // turn on live network?
	'baseNodeNumber': topologyData.nodes.length,
	'aplicableMultiplicity': 5, // maximum number of host per 1 router,
	'permissibleInterval': 0.25, // (baseNodeNumber * aplicableMultiplicity) +- permissibleInterval%
	'timeout': 1000, // timeout of live topology processing
	'debug': true, // debug mode (log messages)
	'port': 5555, // port to be listened to
	'lastNodeId': topologyData.nodes.length-1,
	'lastLinkId': topologyData.links.length-1
};

// debug stuff
var debug = {
	'added': 0, // amount nodes added
	'removed': 0 // amount nodes removed
};

// create a server
var server = http.createServer();

// custom log function (for scalability)
var log = function(message){
	if(appConfig.debug)
		console.log(message);
};

var findArrayKeyByNodeId = function(nodeId){
	for(var i=0;i<topologyData.nodes.length;i++)
		if(topologyData.nodes[i].id == nodeId) return i;
	return -1;
};

var findArrayKeyByLinkId = function(linkId){
	for(var i=0;i<topologyData.links.length;i++)
		if(topologyData.links[i].id == linkId) return i;
	return -1;
};

// add a random node
var addRandomNode = function(){
	appConfig.lastNodeId++;
	appConfig.lastLinkId++;
	// new node
	var newNodeArrayKey = topologyData.nodes.length;
	topologyData.nodes.push({
		"id": appConfig.lastNodeId,
		"name": 'host:' + appConfig.lastNodeId,
		"x": Math.floor(Math.random() * 800 + 10),
		"y": Math.floor(Math.random() * 400 + 10)
	});
	// new link
	var newLinkedElementId = Math.floor(Math.random() * newNodeArrayKey);
	var newLinkedNodeId = topologyData.nodes[newLinkedElementId].id;
	topologyData.links.push({
		"id": appConfig.lastLinkId,
		"source": newLinkedNodeId,
		"target": appConfig.lastNodeId
	});
	log("linked: " + appConfig.lastNodeId + " <- " + newLinkedNodeId);
	topologyData.nodes[newNodeArrayKey].group = topologyData.nodes[newLinkedElementId].group;
	topologyData.nodeSet[topologyData.nodes[newNodeArrayKey].group].nodes.push(appConfig.lastNodeId);
	debug.added++;
};

// remove node
var removeNode = function(nodeArrayId){
	if(nodeArrayId in topologyData.nodes) {
		var node = topologyData.nodes[nodeArrayId];
		// remove node id from nodeset
		topologyData.nodeSet[node.group].nodes.splice(topologyData.nodeSet[node.group].nodes.indexOf(node.id),1);
		topologyData.nodes.splice(nodeArrayId, 1);
		var linkId;
		for(var i = 0; i < topologyData.links.length;){
			var currentLink = topologyData.links[i];
			if (currentLink.source == node.id || currentLink.target == node.id) {
				linkId = currentLink.id;
				if (currentLink.source == node.id && currentLink.target > currentLink.source){
					removeNode(findArrayKeyByNodeId(currentLink.target));
				}
				else if(currentLink.target == node.id && currentLink.source > currentLink.target){
					removeNode(findArrayKeyByNodeId(currentLink.target));
				}
				removeLink(findArrayKeyByLinkId(linkId));
			}
			else
				i++;
		}
	}
};

// remove link
var removeLink = function(linkArrayId){
	if(linkArrayId in topologyData.links) {
		topologyData.links.splice(linkArrayId, 1);
	}
};

// remove a random node from
var removeRandomNode = function(){
	// node that will be removed
	var toBeRemoved = Math.floor((Math.random() * (topologyData.nodes.length - appConfig.baseNodeNumber)) + appConfig.baseNodeNumber);
	log('removing a node #' + toBeRemoved);
	removeNode(toBeRemoved);
	debug.removed++;
};

// algorithm of live topology
var liveTopologyProcessing = function(){
	// pre settings
	var point = appConfig.baseNodeNumber * appConfig.aplicableMultiplicity;
	var start = point * (1 - appConfig.permissibleInterval);
	var end = point * (1 + appConfig.permissibleInterval);

	var nodesNumber = topologyData.nodes.length;
	if(nodesNumber < start){
		addRandomNode();
	}
	else if(nodesNumber > end){
		removeRandomNode()
	}
	else{
		var currentAction = Math.floor(Math.random() * 3);
		if(currentAction == 0){
			removeRandomNode();
		}
		else{
			addRandomNode();
		}
	}
	log('live running; ' + "total nodes: " + topologyData.nodes.length);
};

// initialize live network
if(appConfig.live){
	liveTopologyTimer = setInterval(liveTopologyProcessing,appConfig.timeout);
	log('live started');
}

// Server API
server.on('request',function(req,res){
	// start live network
	if(req.url == '/start'){
		if(!appConfig.live) {
			appConfig.live = true;
			liveTopologyTimer = setInterval(liveTopologyProcessing, appConfig.timeout);
			log('live started');
		}
		res.writeHead(200, {'Access-Control-Allow-Origin': '*'});
		res.end(JSON.stringify({'command': 'start', 'result': 'ok'}));
	}
	// stop live network
	else if(req.url == '/stop'){
		if(appConfig.live){
			appConfig.live = false;
			clearInterval(liveTopologyTimer);
			log('live stopped');
		}
		res.writeHead(200,{'Access-Control-Allow-Origin': '*'});
		res.end(JSON.stringify({'command':'stop','result':'ok'}));
	}
	// display status
	else if(req.url == '/status'){
		res.writeHead(200,{'Access-Control-Allow-Origin': '*'});
		res.end(JSON.stringify({'command':'status','result':appConfig.live}));
		log('live: ' + appConfig.live);
	}
	// default response: topology
	else if(req.url == '/topology'){
		res.writeHead(200,{'Access-Control-Allow-Origin': '*'});
		res.end(JSON.stringify(topologyData));
		log('request for topology');
	}
});

// start listening to income connections
server.listen(appConfig.port);
<commit_msg>fix: Access-Control-Allow-Headers<commit_after>/*
This REST server provides NeXt-compatible topology JSON. When it's running, it keeps on generating some new nodes/links, imitating real things that are going on in a real network.
Run this server PRIOR TO your NeXt app; otherwise you'll get errors.
*/

// fetch predefined topology from external module
var topology = require('./topology-data');
var topologyData = topology.response;
var liveTopologyTimer;
var http = require('http');

// app configuration
var appConfig = {
	'live': false, // turn on live network?
	'baseNodeNumber': topologyData.nodes.length,
	'aplicableMultiplicity': 5, // maximum number of host per 1 router,
	'permissibleInterval': 0.25, // (baseNodeNumber * aplicableMultiplicity) +- permissibleInterval%
	'timeout': 1000, // timeout of live topology processing
	'debug': true, // debug mode (log messages)
	'port': 5555, // port to be listened to
	'lastNodeId': topologyData.nodes.length-1,
	'lastLinkId': topologyData.links.length-1
};

// debug stuff
var debug = {
	'added': 0, // amount nodes added
	'removed': 0 // amount nodes removed
};

// create a server
var server = http.createServer();

// custom log function (for scalability)
var log = function(message){
	if(appConfig.debug)
		console.log(message);
};

var findArrayKeyByNodeId = function(nodeId){
	for(var i=0;i<topologyData.nodes.length;i++)
		if(topologyData.nodes[i].id == nodeId) return i;
	return -1;
};

var findArrayKeyByLinkId = function(linkId){
	for(var i=0;i<topologyData.links.length;i++)
		if(topologyData.links[i].id == linkId) return i;
	return -1;
};

// add a random node
var addRandomNode = function(){
	appConfig.lastNodeId++;
	appConfig.lastLinkId++;
	// new node
	var newNodeArrayKey = topologyData.nodes.length;
	topologyData.nodes.push({
		"id": appConfig.lastNodeId,
		"name": 'host:' + appConfig.lastNodeId,
		"x": Math.floor(Math.random() * 800 + 10),
		"y": Math.floor(Math.random() * 400 + 10)
	});
	// new link
	var newLinkedElementId = Math.floor(Math.random() * newNodeArrayKey);
	var newLinkedNodeId = topologyData.nodes[newLinkedElementId].id;
	topologyData.links.push({
		"id": appConfig.lastLinkId,
		"source": newLinkedNodeId,
		"target": appConfig.lastNodeId
	});
	log("linked: " + appConfig.lastNodeId + " <- " + newLinkedNodeId);
	topologyData.nodes[newNodeArrayKey].group = topologyData.nodes[newLinkedElementId].group;
	topologyData.nodeSet[topologyData.nodes[newNodeArrayKey].group].nodes.push(appConfig.lastNodeId);
	debug.added++;
};

// remove node
var removeNode = function(nodeArrayId){
	if(nodeArrayId in topologyData.nodes) {
		var node = topologyData.nodes[nodeArrayId];
		// remove node id from nodeset
		topologyData.nodeSet[node.group].nodes.splice(topologyData.nodeSet[node.group].nodes.indexOf(node.id),1);
		topologyData.nodes.splice(nodeArrayId, 1);
		var linkId;
		for(var i = 0; i < topologyData.links.length;){
			var currentLink = topologyData.links[i];
			if (currentLink.source == node.id || currentLink.target == node.id) {
				linkId = currentLink.id;
				if (currentLink.source == node.id && currentLink.target > currentLink.source){
					removeNode(findArrayKeyByNodeId(currentLink.target));
				}
				else if(currentLink.target == node.id && currentLink.source > currentLink.target){
					removeNode(findArrayKeyByNodeId(currentLink.target));
				}
				removeLink(findArrayKeyByLinkId(linkId));
			}
			else
				i++;
		}
	}
};

// remove link
var removeLink = function(linkArrayId){
	if(linkArrayId in topologyData.links) {
		topologyData.links.splice(linkArrayId, 1);
	}
};

// remove a random node from
var removeRandomNode = function(){
	// node that will be removed
	var toBeRemoved = Math.floor((Math.random() * (topologyData.nodes.length - appConfig.baseNodeNumber)) + appConfig.baseNodeNumber);
	log('removing a node #' + toBeRemoved);
	removeNode(toBeRemoved);
	debug.removed++;
};

// algorithm of live topology
var liveTopologyProcessing = function(){
	// pre settings
	var point = appConfig.baseNodeNumber * appConfig.aplicableMultiplicity;
	var start = point * (1 - appConfig.permissibleInterval);
	var end = point * (1 + appConfig.permissibleInterval);

	var nodesNumber = topologyData.nodes.length;
	if(nodesNumber < start){
		addRandomNode();
	}
	else if(nodesNumber > end){
		removeRandomNode()
	}
	else{
		var currentAction = Math.floor(Math.random() * 3);
		if(currentAction == 0){
			removeRandomNode();
		}
		else{
			addRandomNode();
		}
	}
	log('live running; ' + "total nodes: " + topologyData.nodes.length);
};

// initialize live network
if(appConfig.live){
	liveTopologyTimer = setInterval(liveTopologyProcessing,appConfig.timeout);
	log('live started');
}

// Server API
server.on('request',function(req,res){
	// start live network
	if(req.url == '/start'){
		if(!appConfig.live) {
			appConfig.live = true;
			liveTopologyTimer = setInterval(liveTopologyProcessing, appConfig.timeout);
			log('live started');
		}
		res.writeHead(200,
			{'Access-Control-Allow-Origin': '*',
			'Access-Control-Allow-Methods': 'GET',
			'Access-Control-Allow-Headers': 'Content-Type',
			'Access-Control-Max-Age': '86400'
			});
		res.end(JSON.stringify({'command': 'start', 'result': 'ok'}));
	}
	// stop live network
	else if(req.url == '/stop'){
		if(appConfig.live){
			appConfig.live = false;
			clearInterval(liveTopologyTimer);
			log('live stopped');
		}
		res.writeHead(200,
			{'Access-Control-Allow-Origin': '*',
				'Access-Control-Allow-Methods': 'GET',
				'Access-Control-Allow-Headers': 'Content-Type',
				'Access-Control-Max-Age': '86400'
			});
		res.end(JSON.stringify({'command':'stop','result':'ok'}));
	}
	// display status
	else if(req.url == '/status'){
		res.writeHead(200,
			{'Access-Control-Allow-Origin': '*',
				'Access-Control-Allow-Methods': 'GET',
				'Access-Control-Allow-Headers': 'Content-Type',
				'Access-Control-Max-Age': '86400'
			});
		res.end(JSON.stringify({'command':'status','result':appConfig.live}));
		log('live: ' + appConfig.live);
	}
	// default response: topology
	else if(req.url == '/topology'){
		res.writeHead(200,
			{'Access-Control-Allow-Origin': '*',
				'Access-Control-Allow-Methods': 'GET',
				'Access-Control-Allow-Headers': 'Content-Type',
				'Access-Control-Max-Age': '86400'
			});
		res.end(JSON.stringify(topologyData));
		log('request for topology');
	}
});

// start listening to income connections
server.listen(appConfig.port);
<|endoftext|>


<commit_before>
<commit_msg>Delete blog.js<commit_after><|endoftext|>


<commit_before>
/**
  @class

  `XT.Model` is an abstract class designed to operate with `XT.DataSource`.
  It should be subclassed for any specific implentation. Subtypes should
  include a `recordType` the data source will use to retreive the record.

  To create a new model include `isNew` in the options like so:
    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = new XM.Contact({firstName: 'Randy'}, {isNew: true});

  To load an existing record include a guid in the options like so:
    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = new XM.Contact({guid: 1});
    m.fetch();

    OR

    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = XM.Contact();
    m.fetch({id: 1});

  @extends Backbone.RelationalModel
  @param {Object} attributes
  @param {Object} options
*/
XT.Model = Backbone.RelationalModel.extend(
  /** @scope XT.Model.prototype */ {

  /**
  Set to true if you want an id fetched from the server when the `isNew` option
  is passed on a new model.

  @type {Boolean}
  */
  autoFetchId: true,

  idAttribute: "guid",

  /**
  A hash structure that defines data access.

  @type {Hash}
  */
  privileges: {},

  /**
  Indicates whethere the model is read only.

  @type {Boolean}
  */
  readOnly: false,

  /**
  An array of attribute names designating attributes that are not editable.
  Use `setReadOnly` to edit this array.

  @seealso `setReadOnly`
  @seealso `isReadOnly`
  @type {Array}
  */
  readOnlyAttributes: [],

  /**
  Specify the name of a data source model here.

  @type {String}
  */
  recordType: null,

  /**
  An array of required attributes. A validate on the entire model will fail
  until all the required attributes have values.

  @type {Array}
  */
  requiredAttributes: [],

  // ..........................................................
  // METHODS
  //

  /**
  Update status when any attributes change.
  */
  attributeChanged: function() {
    if (this.get('dataState') === 'read') {
      this.set('dataState', 'update');
    }
  },

  /**
    Returns whether the current record can be updated based on privilege
    settings.

    @returns {Boolean}
  */
  canUpdate: function() {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);
    return klass.canUpdate(this);
  },

  /**
    Returns whether the current record can be deleted based on privilege
    settings.

    @returns {Boolean}
  */
  canDelete: function() {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);
    return klass.canDelete(this);
  },

  /**
  Reimplemented.
  */
  clear: function(options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.clear.call(this, options);
  },

  /**
  Reimplemented.
  */
  destroy: function(options) {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);

    if (kclass.canDelete(this)) {
      options = options ? _.clone(options) : {};
      options.wait = true;
      model.attributes.dataState = 'delete';
      return Backbone.Model.prototype.destroy.call(this, options);
    }
    console.log('Insufficient privileges to destroy');
    return false;
  },

  /*
  Reimplemented.
  */
  fetch: function(options) {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);

    if (klass.canRead()) {
       return Backbone.Model.prototype.fetch.call(this, options);
    }
    console.log('Insufficient privileges to fetch');
    return false;
  },

  /**
  Set the id on this record an id from the server.
  */
  fetchId: function() {
    var that = this;
    var options = {};

    if (!_.isEmpty(this.id)) return false;

    // Callback
    options.success = function(resp, status, xhr) {
      that.set(that.idAttribute, resp);
    };

    // Dispatch
    XT.dataSource.dispatch('XT.Model', 'fetchId', this.recordType, options);
  },

  initialize: function() {
    var options = arguments[1];
    var that = this;

    // Validate record type
    if (_.isEmpty(this.recordType)) throw 'No record type defined';

    // Initialize for new record.
    if (options && options.isNew) {
      this.attributes.dataState = 'create';
      if (this.autoFetchId) this.fetchId();
    }

    // Id Attribute should be required and read only
    if (this.idAttribute) this.readOnlyAttributes.push(this.idAttribute);
    if (this.idAttribute) this.requiredAttributes.push(this.idAttribute);

    // Set up state change handler
    this.on('change', this.attributeChanged);

    // Set up destroy handler
    this.on('destroy', function() {
      that.clear({silent: true});
    });
  },

  /**
  Returns true when dataState is `create` or `update`.
  */
  isDirty: function() {
    var dataState = this.get('dataState');
    return dataState === 'create' || dataState === 'update';
  },

  /**
  Reimplemented. A model is new if the dataState is `create`.
  */
  isNew: function() {
    return this.get('dataState') === 'create';
  },

  /**
  Return whether the model is in a read-only state. If an attribute name
  is passed, returns whether that attribute is read-only.

  @seealso `setReadOnly`
  @seealso `readOnly`
  @param {String} attribute
  */
  isReadOnly: function(attr) {
    if (!_.isString(attr) || this.readOnly) {
      return  model.readOnly;
    }
    return _.contains(this.readOnly, attr);
  },

  /**
  Reimplemented to check for required attributes.
  */
  isValid: function() {
    var options = {};
    options.checkRequired = true;

    return this.validate(model.attributes, options);
  },

  /**
  Reimplemented.
  */
  save: function(key, value, options) {
    if (this.isDirty()) {
      options = options ? _.clone(options) : {};
      options.checkRequired = true;
      return Backbone.Model.prototype.save.call(this, key, value, options);
    }

    console.log('No changes to save');
    return false;
  },

  /**
  Reimplemented.
  */
  set: function(key, value, options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.set.call(this, key, value, options);
  },

  /**
  Set the entire model, or a specific model attribute to readOnly. Privilege
  enforcement supercedes read-only settings.

  Examples:

  model.setReadOnly() // sets model to read only
  model.setReadOnly(false) // sets model to be editable
  model.setReadOnly('name') // sets 'name' attribute to read-only
  model.setReadOnly('name', false) // sets 'name' attribute to be editable

  @seealso `isReadOnly`
  @seealso `readOnly`
  @param {String|Boolean} Attribute to set, or boolean if setting the model
  @param {Boolean} boolean - default = true.
  */
  setReadOnly: function(key, value) {
    // handle attribute
    if (_.isString(key)) {
      value = _.isBoolean(value) ? value : true;
      if (value && !_.contains(this.readOnlyAttributes, key)) {
        this.readOnlyAttributes.push(key);
      } else if (!value && _.contains(this.readOnlyAttributes, key)) {
        this.readOnlyAttributes = _.without(this.readOnlyAttributes, key);
      }

    // handle model
    } else {
      key = _.isBoolean(key) ? key : true;
      this.readOnly = key;
    }

    return this;
  },

  /**
  Sync to xTuple datasource.
  */
  sync: function(method, model, options) {
    options = options ? _.clone(options) : {};
    var id = options.id || model.id;
    var success = options.success;
    var recordType = this.recordType;

    model.off('change', model.attributeChanged);
    options.success = function(resp, status, xhr) {
      if (success) success(resp, status, xhr);
      model.on('change', model.attributeChanged);
    };

    // Read
    if (method === 'read' && recordType && id && success) {
      return XT.dataSource.retrieveRecord(recordType, id, options);

    // Write
    } else if (method === 'create' || method === 'update' || method === 'delete') {
      return XT.dataSource.commitRecord(model, options);
    }

    return false;
  },

  /**
  Reimplemented.
  */
  unset: function(attr, options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.unset.call(this, attr, options);
  },

  /**
  Default validation checks required fields and read-only.
  Reimplement your own custom validation code here, but make sure
  to call back to the superclass at the top of your function using:

  return XT.Model.prototype.validate.call(this, attributes, options);

  @param {Object} attributes
  @param {Object} options
  */
  validate: function(attributes, options) {
    var attr;
    var err;
    options = options || {};

    // Check required.
    if (options.checkRequired) {
      for (i = 0; i < this.requiredAttributes.length; i++) {
        if (_.isEmpty(attributes[this.requiredAttributes[i]])) {
          return "'" + this.required[i] + "' is required.";
        }
      }
    }

    // Check read-only.
    if (options.checkReadOnly) {
      err = this.readOnly ? "Record is in a read only state." :
                "Can not change read only attribute {attr}.";
      for (attr in attributes) {
        if (_.contains(this.readOnlyAttributes, attr) &&
            !_.isEqual(attributes[attr], this.previous(attr))) {
          return err.replace("{attr}", attr);
        }
      }
    }

    // Check privileges.
    if (options.checkPrivileges && !this.canUpdate()) {
      return "Insufficient privileges to update";
    }
  }

});

// ..........................................................
// CLASS METHODS
//

enyo.mixin( /** @scope XT.Model */ XT.Model, {

  /**
    Use this function to find out whether a user can create records before
    instantiating one.

    @returns {Boolean}
  */
  canCreate: function() {
    return XT.Model.canDo.call(this, 'create');
  },

  /**
    Use this function to find out whether a user can read this record type
    before any have been loaded.

    @returns {Boolean}
  */
  canRead: function() {
    var privs = this.privileges;
    var sessionPrivs = XT.session.privileges;
    var isGranted = false;

    // If no privileges, nothing to check.
    if (_.isEmpty(privs)) return true;

    if (sessionPrivs && sessionPrivs.get) {
      // Check global read privilege.
      isGranted = privs.all && privs.all.read ?
                  sessionPrivs.get(privs.all.read) : false;

      // Check global update privilege.
      if (!isGranted) {
        isGranted = privs.all && privs.all.update ?
                    sessionPrivs.get(privs.all.update) : false;
      }

      // Check personal view privilege.
      if (!isGranted) {
        isGranted = privs.personal && privs.personal.read ?
                    sessionPrivs.get(privs.personal.read) : false;
      }

      // Check personal update privilege.
      if (!isGranted) {
        isGranted = privs.personal && privs.personal.update ?
                    sessionPrivs.get(privs.personal.update) : false;
      }
    }

    return isGranted;
  },

  /**
    Returns whether a user has access to update a record of this type. If a
    record is passed that involves personal privileges, it will validate
    whether that particular record is updatable.

    @returns {Boolean}
  */
  canUpdate: function(model) {
    return XT.Model.canDo.call(this, 'update', model);
  },

  /**
    Returns whether a user has access to delete a record of this type. If a
    record is passed that involves personal privileges, it will validate
    whether that particular record is deletable.

    @returns {Boolean}
  */
  canDelete: function(model) {
    return XT.Model.canDo.call(this, 'delete', model);
  },

  /**
  Check privilege on `action`. If `model` is passed, checks personal
  privileges on the model where applicable.

  @param {String} Action
  @param {XT.Model} Model
  */
  canDo: function(action, model) {
    var privs = this.privileges,
        sessionPrivs = XT.session.privileges,
        isGrantedAll = false,
        isGrantedPersonal = false,
        userName = XT.session.details.username;

    // If no privileges, nothing to check
    if (_.isEmpty(privs)) return true;

    if (sessionPrivs && sessionPrivs.get) {
      // Check global privileges
      if (privs.all && privs.all[action]) {
        isGrantedAll = sessionPrivs.get(privs.all[action]);
      }

      // Check personal privileges
      if (!isGrantedAll && privs.personal && privs.personal[action]) {
        isGrantedPersonal = sessionPrivs.get(privs.personal[action]);
      }
    }

    // If only personal privileges, check the personal attribute list to
    // see if we can update.
    if (!isGrantedAll && isGrantedPersonal && model) {
      var i = 0;
      var username = XT.session.details.username;
      var props = privs.personal && privs.personal.properties ?
                  privs.personal.properties : [];

      isGrantedPersonal = false;
      while (!isGrantedPersonal && i < props.length) {
        isGrantedPersonal = model.previous(props[i].get('username')) === username;
        i++;
      }
    }

    return isGrantedAll || isGrantedPersonal;
  }

});

<commit_msg>fix local references<commit_after>
/**
  @class

  `XT.Model` is an abstract class designed to operate with `XT.DataSource`.
  It should be subclassed for any specific implentation. Subtypes should
  include a `recordType` the data source will use to retreive the record.

  To create a new model include `isNew` in the options like so:
    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = new XM.Contact({firstName: 'Randy'}, {isNew: true});

  To load an existing record include a guid in the options like so:
    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = new XM.Contact({guid: 1});
    m.fetch();

    OR

    XM.Contact = XT.Model.extend({recordType: 'XM.Contact'});
    m = XM.Contact();
    m.fetch({id: 1});

  @extends Backbone.RelationalModel
  @param {Object} attributes
  @param {Object} options
*/
XT.Model = Backbone.RelationalModel.extend(
  /** @scope XT.Model.prototype */ {

  /**
  Set to true if you want an id fetched from the server when the `isNew` option
  is passed on a new model.

  @type {Boolean}
  */
  autoFetchId: true,

  idAttribute: "guid",

  /**
  A hash structure that defines data access.

  @type {Hash}
  */
  privileges: {},

  /**
  Indicates whethere the model is read only.

  @type {Boolean}
  */
  readOnly: false,

  /**
  An array of attribute names designating attributes that are not editable.
  Use `setReadOnly` to edit this array.

  @seealso `setReadOnly`
  @seealso `isReadOnly`
  @type {Array}
  */
  readOnlyAttributes: [],

  /**
  Specify the name of a data source model here.

  @type {String}
  */
  recordType: null,

  /**
  An array of required attributes. A validate on the entire model will fail
  until all the required attributes have values.

  @type {Array}
  */
  requiredAttributes: [],

  // ..........................................................
  // METHODS
  //

  /**
  Update status when any attributes change.
  */
  attributeChanged: function() {
    if (this.get('dataState') === 'read') {
      this.set('dataState', 'update');
    }
  },

  /**
    Returns whether the current record can be updated based on privilege
    settings.

    @returns {Boolean}
  */
  canUpdate: function() {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);
    return klass.canUpdate(this);
  },

  /**
    Returns whether the current record can be deleted based on privilege
    settings.

    @returns {Boolean}
  */
  canDelete: function() {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);
    return klass.canDelete(this);
  },

  /**
  Reimplemented.
  */
  clear: function(options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.clear.call(this, options);
  },

  /**
  Reimplemented.
  */
  destroy: function(options) {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);

    if (kclass.canDelete(this)) {
      options = options ? _.clone(options) : {};
      options.wait = true;
      this.attributes.dataState = 'delete';
      return Backbone.Model.prototype.destroy.call(this, options);
    }
    console.log('Insufficient privileges to destroy');
    return false;
  },

  /*
  Reimplemented.
  */
  fetch: function(options) {
    var klass = Backbone.Relational.store.getObjectByName(this.recordType);

    if (klass.canRead()) {
       return Backbone.Model.prototype.fetch.call(this, options);
    }
    console.log('Insufficient privileges to fetch');
    return false;
  },

  /**
  Set the id on this record an id from the server.
  */
  fetchId: function() {
    var that = this;
    var options = {};

    if (!_.isEmpty(this.id)) return false;

    // Callback
    options.success = function(resp, status, xhr) {
      that.set(that.idAttribute, resp);
    };

    // Dispatch
    XT.dataSource.dispatch('XT.Model', 'fetchId', this.recordType, options);
  },

  initialize: function() {
    var options = arguments[1];
    var that = this;

    // Validate record type
    if (_.isEmpty(this.recordType)) throw 'No record type defined';

    // Initialize for new record.
    if (options && options.isNew) {
      this.attributes.dataState = 'create';
      if (this.autoFetchId) this.fetchId();
    }

    // Id Attribute should be required and read only
    if (this.idAttribute) this.readOnlyAttributes.push(this.idAttribute);
    if (this.idAttribute) this.requiredAttributes.push(this.idAttribute);

    // Set up state change handler
    this.on('change', this.attributeChanged);

    // Set up destroy handler
    this.on('destroy', function() {
      that.clear({silent: true});
    });
  },

  /**
  Returns true when dataState is `create` or `update`.
  */
  isDirty: function() {
    var dataState = this.get('dataState');
    return dataState === 'create' || dataState === 'update';
  },

  /**
  Reimplemented. A model is new if the dataState is `create`.
  */
  isNew: function() {
    return this.get('dataState') === 'create';
  },

  /**
  Return whether the model is in a read-only state. If an attribute name
  is passed, returns whether that attribute is read-only.

  @seealso `setReadOnly`
  @seealso `readOnly`
  @param {String} attribute
  */
  isReadOnly: function(attr) {
    if (!_.isString(attr) || this.readOnly) {
      return  this.readOnly;
    }
    return _.contains(this.readOnly, attr);
  },

  /**
  Reimplemented to check for required attributes.
  */
  isValid: function() {
    var options = {};
    options.checkRequired = true;

    return this.validate(this.attributes, options);
  },

  /**
  Reimplemented.
  */
  save: function(key, value, options) {
    if (this.isDirty()) {
      options = options ? _.clone(options) : {};
      options.checkRequired = true;
      return Backbone.Model.prototype.save.call(this, key, value, options);
    }

    console.log('No changes to save');
    return false;
  },

  /**
  Reimplemented.
  */
  set: function(key, value, options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.set.call(this, key, value, options);
  },

  /**
  Set the entire model, or a specific model attribute to readOnly. Privilege
  enforcement supercedes read-only settings.

  Examples:

  model.setReadOnly() // sets model to read only
  model.setReadOnly(false) // sets model to be editable
  model.setReadOnly('name') // sets 'name' attribute to read-only
  model.setReadOnly('name', false) // sets 'name' attribute to be editable

  @seealso `isReadOnly`
  @seealso `readOnly`
  @param {String|Boolean} Attribute to set, or boolean if setting the model
  @param {Boolean} boolean - default = true.
  */
  setReadOnly: function(key, value) {
    // handle attribute
    if (_.isString(key)) {
      value = _.isBoolean(value) ? value : true;
      if (value && !_.contains(this.readOnlyAttributes, key)) {
        this.readOnlyAttributes.push(key);
      } else if (!value && _.contains(this.readOnlyAttributes, key)) {
        this.readOnlyAttributes = _.without(this.readOnlyAttributes, key);
      }

    // handle model
    } else {
      key = _.isBoolean(key) ? key : true;
      this.readOnly = key;
    }

    return this;
  },

  /**
  Sync to xTuple datasource.
  */
  sync: function(method, model, options) {
    options = options ? _.clone(options) : {};
    var id = options.id || model.id;
    var success = options.success;
    var recordType = this.recordType;

    model.off('change', model.attributeChanged);
    options.success = function(resp, status, xhr) {
      if (success) success(resp, status, xhr);
      model.on('change', model.attributeChanged);
    };

    // Read
    if (method === 'read' && recordType && id && success) {
      return XT.dataSource.retrieveRecord(recordType, id, options);

    // Write
    } else if (method === 'create' || method === 'update' || method === 'delete') {
      return XT.dataSource.commitRecord(model, options);
    }

    return false;
  },

  /**
  Reimplemented.
  */
  unset: function(attr, options) {
    options = options ? _.clone(options) : {};
    options.checkReadOnly = true;
    options.checkPrivileges = true;
    return Backbone.RelationalModel.prototype.unset.call(this, attr, options);
  },

  /**
  Default validation checks required fields and read-only.
  Reimplement your own custom validation code here, but make sure
  to call back to the superclass at the top of your function using:

  return XT.Model.prototype.validate.call(this, attributes, options);

  @param {Object} attributes
  @param {Object} options
  */
  validate: function(attributes, options) {
    var attr;
    var err;
    options = options || {};

    // Check required.
    if (options.checkRequired) {
      for (i = 0; i < this.requiredAttributes.length; i++) {
        if (_.isEmpty(attributes[this.requiredAttributes[i]])) {
          return "'" + this.required[i] + "' is required.";
        }
      }
    }

    // Check read-only.
    if (options.checkReadOnly) {
      err = this.readOnly ? "Record is in a read only state." :
                "Can not change read only attribute {attr}.";
      for (attr in attributes) {
        if (_.contains(this.readOnlyAttributes, attr) &&
            !_.isEqual(attributes[attr], this.previous(attr))) {
          return err.replace("{attr}", attr);
        }
      }
    }

    // Check privileges.
    if (options.checkPrivileges && !this.canUpdate()) {
      return "Insufficient privileges to update";
    }
  }

});

// ..........................................................
// CLASS METHODS
//

enyo.mixin( /** @scope XT.Model */ XT.Model, {

  /**
    Use this function to find out whether a user can create records before
    instantiating one.

    @returns {Boolean}
  */
  canCreate: function() {
    return XT.Model.canDo.call(this, 'create');
  },

  /**
    Use this function to find out whether a user can read this record type
    before any have been loaded.

    @returns {Boolean}
  */
  canRead: function() {
    var privs = this.privileges;
    var sessionPrivs = XT.session.privileges;
    var isGranted = false;

    // If no privileges, nothing to check.
    if (_.isEmpty(privs)) return true;

    if (sessionPrivs && sessionPrivs.get) {
      // Check global read privilege.
      isGranted = privs.all && privs.all.read ?
                  sessionPrivs.get(privs.all.read) : false;

      // Check global update privilege.
      if (!isGranted) {
        isGranted = privs.all && privs.all.update ?
                    sessionPrivs.get(privs.all.update) : false;
      }

      // Check personal view privilege.
      if (!isGranted) {
        isGranted = privs.personal && privs.personal.read ?
                    sessionPrivs.get(privs.personal.read) : false;
      }

      // Check personal update privilege.
      if (!isGranted) {
        isGranted = privs.personal && privs.personal.update ?
                    sessionPrivs.get(privs.personal.update) : false;
      }
    }

    return isGranted;
  },

  /**
    Returns whether a user has access to update a record of this type. If a
    record is passed that involves personal privileges, it will validate
    whether that particular record is updatable.

    @returns {Boolean}
  */
  canUpdate: function(model) {
    return XT.Model.canDo.call(this, 'update', model);
  },

  /**
    Returns whether a user has access to delete a record of this type. If a
    record is passed that involves personal privileges, it will validate
    whether that particular record is deletable.

    @returns {Boolean}
  */
  canDelete: function(model) {
    return XT.Model.canDo.call(this, 'delete', model);
  },

  /**
  Check privilege on `action`. If `model` is passed, checks personal
  privileges on the model where applicable.

  @param {String} Action
  @param {XT.Model} Model
  */
  canDo: function(action, model) {
    var privs = this.privileges,
        sessionPrivs = XT.session.privileges,
        isGrantedAll = false,
        isGrantedPersonal = false,
        userName = XT.session.details.username;

    // If no privileges, nothing to check
    if (_.isEmpty(privs)) return true;

    if (sessionPrivs && sessionPrivs.get) {
      // Check global privileges
      if (privs.all && privs.all[action]) {
        isGrantedAll = sessionPrivs.get(privs.all[action]);
      }

      // Check personal privileges
      if (!isGrantedAll && privs.personal && privs.personal[action]) {
        isGrantedPersonal = sessionPrivs.get(privs.personal[action]);
      }
    }

    // If only personal privileges, check the personal attribute list to
    // see if we can update.
    if (!isGrantedAll && isGrantedPersonal && model) {
      var i = 0;
      var username = XT.session.details.username;
      var props = privs.personal && privs.personal.properties ?
                  privs.personal.properties : [];

      isGrantedPersonal = false;
      while (!isGrantedPersonal && i < props.length) {
        isGrantedPersonal = model.previous(props[i].get('username')) === username;
        i++;
      }
    }

    return isGrantedAll || isGrantedPersonal;
  }

});

<|endoftext|>


<commit_before>/**
 * @fileOverview Read OP tests.
 */
var chai = require('chai');
var expect = chai.expect;

var testHelper = require('../test-helpers.lib');

var read = module.exports = {};

read.test = function(params) {
  describe('Read OPs', function() {
    this.timeout(5000);

    testHelper.setupReq();

    // before create a record
    testHelper.createItem(params);

    // after delete record
    testHelper.deleteItem(params);

    describe('Read list of records', function() {
      it('Should read all records', function(done) {
        this.req.get(params.endpoint)
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }

            expect(res.body).to.be.an('array');
            expect(res.body.length).to.be.at.least(1);
            params.readList.response(res, done);
          });
      });
    });

    describe('Read a single record', function() {
      it('Should read one record', function(done) {
        this.req.get(params.endpoint + '/' + this.item[params.idAttr])
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }

            expect(res.body).to.be.an('object');
            params.readItem.response(res, done);
          });
      });
    });

    describe('Read filtered records', function() {
      it('Email filter :: should have right results count', function(done) {
        var filterObj = {};
        filterObj[params.stringAttr] = params.fixture[params.stringAttr];

        this.req.get(params.endpoint)
          .query(filterObj)
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }
            expect(res.body).to.have.length(1);
            params.readFilter.response(res, done);
          });
      });
    });
  });
};
<commit_msg>fix copy on read filter tester<commit_after>/**
 * @fileOverview Read OP tests.
 */
var chai = require('chai');
var expect = chai.expect;

var testHelper = require('../test-helpers.lib');

var read = module.exports = {};

read.test = function(params) {
  describe('Read OPs', function() {
    this.timeout(5000);

    testHelper.setupReq();

    // before create a record
    testHelper.createItem(params);

    // after delete record
    testHelper.deleteItem(params);

    describe('Read list of records', function() {
      it('Should read all records', function(done) {
        this.req.get(params.endpoint)
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }

            expect(res.body).to.be.an('array');
            expect(res.body.length).to.be.at.least(1);
            params.readList.response(res, done);
          });
      });
    });

    describe('Read a single record', function() {
      it('Should read one record', function(done) {
        this.req.get(params.endpoint + '/' + this.item[params.idAttr])
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }

            expect(res.body).to.be.an('object');
            params.readItem.response(res, done);
          });
      });
    });

    describe('Read filtered records', function() {
      it('query filter :: should have right results count', function(done) {
        var filterObj = {};
        filterObj[params.stringAttr] = params.fixture[params.stringAttr];

        this.req.get(params.endpoint)
          .query(filterObj)
          .expect(200)
          .end(function(err, res) {
            if (err) {
              console.error('ERROR. Body:', res.body);
              done(err);
              return;
            }
            expect(res.body).to.have.length(1);
            params.readFilter.response(res, done);
          });
      });
    });
  });
};
<|endoftext|>


<commit_before>import Ember from 'ember';
import intercom from 'intercom';

const {
  get,
  Service,
  assign: emberAssign,
  computed,
  assert,
  run: { scheduleOnce }
} = Ember;

// Handle multi browser support for Ember 2.4+
const assign = Object.assign || emberAssign;

export default Service.extend({
  api: intercom,

  _userNameProp: computed('config.userProperties.nameProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.nameProp')}`);
  }),

  _userEmailProp: computed('config.userProperties.emailProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.emailProp')}`);
  }),

  _userCreatedAtProp: computed('config.userProperties.createdAtProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.createdAtProp')}`);
  }),

  user: {
    name: null,
    email: null
  },

  _hasUserContext: computed('user', '_userNameProp', '_userEmailProp', '_userCreatedAtProp', function() {
    return !!get(this, 'user') &&
           !!get(this, '_userNameProp') &&
           !!get(this, '_userEmailProp');
  }),

  _intercomBootConfig: computed('_hasUserContext', function() {
    let appId = get(this, 'config.appId');
    assert('You must supply an "ENV.intercom.appId" in your "config/environment.js" file.', appId);

    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    let obj = {
      app_id: appId
    };

    if (get(this, '_hasUserContext')) {
      obj.name = get(this, '_userNameProp');
      obj.email = get(this, '_userEmailProp');
      if (get(this, '_userCreatedAtProp')) {
        obj.created_at = get(this, '_userCreatedAtProp');
      }
    }
    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    return obj;
  }),

  start(bootConfig = {}) {
    let _bootConfig = get(this, '_intercomBootConfig');
    assign(_bootConfig, bootConfig);
    scheduleOnce('afterRender', () => {
      return this.get('api')('boot', _bootConfig);
    });
  },

  stop() {
    scheduleOnce('afterRender', () => this.get('api')('shutdown'));
  },

  update(properties = {}) {
    scheduleOnce('afterRender', () => this.get('api')('update', properties));
  }
});
<commit_msg>Use merge in favor of assign, for ie9 compat<commit_after>import Ember from 'ember';
import intercom from 'intercom';

const {
  get,
  merge,
  Service,
  computed,
  assert,
  run: { scheduleOnce }
} = Ember;

export default Service.extend({
  api: intercom,

  _userNameProp: computed('config.userProperties.nameProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.nameProp')}`);
  }),

  _userEmailProp: computed('config.userProperties.emailProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.emailProp')}`);
  }),

  _userCreatedAtProp: computed('config.userProperties.createdAtProp', function() {
    return get(this, `user.${get(this, 'config.userProperties.createdAtProp')}`);
  }),

  user: {
    name: null,
    email: null
  },

  _hasUserContext: computed('user', '_userNameProp', '_userEmailProp', '_userCreatedAtProp', function() {
    return !!get(this, 'user') &&
           !!get(this, '_userNameProp') &&
           !!get(this, '_userEmailProp');
  }),

  _intercomBootConfig: computed('_hasUserContext', function() {
    let appId = get(this, 'config.appId');
    assert('You must supply an "ENV.intercom.appId" in your "config/environment.js" file.', appId);

    // jscs:disable requireCamelCaseOrUpperCaseIdentifiers
    let obj = {
      app_id: appId
    };

    if (get(this, '_hasUserContext')) {
      obj.name = get(this, '_userNameProp');
      obj.email = get(this, '_userEmailProp');
      if (get(this, '_userCreatedAtProp')) {
        obj.created_at = get(this, '_userCreatedAtProp');
      }
    }
    // jscs:enable requireCamelCaseOrUpperCaseIdentifiers

    return obj;
  }),

  start(bootConfig = {}) {
    let _bootConfig = merge(get(this, '_intercomBootConfig'), bootConfig);
    scheduleOnce('afterRender', () => {
      return this.get('api')('boot', _bootConfig);
    });
  },

  stop() {
    scheduleOnce('afterRender', () => this.get('api')('shutdown'));
  },

  update(properties = {}) {
    scheduleOnce('afterRender', () => this.get('api')('update', properties));
  }
});
<|endoftext|>


<commit_before><commit_msg>New listing<commit_after><|endoftext|>


<commit_before>#!/usr/bin/env node

var tty = require('tty')
var cursor = require('../')(process.stdout)

// listen for the queryPosition report on stdin
process.stdin.resume()
tty.setRawMode(true)

process.stdin.once('data', function (b) {
  var match = /\[(\d+)\;(\d+)R$/.exec(b.toString())
  if (match) {
    var xy = match.slice(1, 3).reverse().map(Number)
    console.error(xy)
  }

  // cleanup and close stdin
  tty.setRawMode(false)
  process.stdin.pause()
})

// send the query position request code to stdout
cursor.queryPosition()
<commit_msg>cursorPosition example: fix node v0.8.x warnings<commit_after>#!/usr/bin/env node

var tty = require('tty')
var cursor = require('../')(process.stdout)

// listen for the queryPosition report on stdin
process.stdin.resume()
raw(true)

process.stdin.once('data', function (b) {
  var match = /\[(\d+)\;(\d+)R$/.exec(b.toString())
  if (match) {
    var xy = match.slice(1, 3).reverse().map(Number)
    console.error(xy)
  }

  // cleanup and close stdin
  raw(false)
  process.stdin.pause()
})

// send the query position request code to stdout
cursor.queryPosition()

function raw (mode) {
  if (process.stdin.setRawMode) {
    process.stdin.setRawMode(mode)
  } else {
    tty.setRawMode(mode)
  }
}
<|endoftext|>