<commit_before>/*global describe, it, before, beforeEach, afterEach */
'use strict';
const expect = require('chai').expect;
const fm = require('@arangodb/foxx/manager');
const request = require('@arangodb/request');
const fs = require('fs');
const internal = require('internal');
const basePath = fs.makeAbsolute(fs.join(internal.startupPath, 'common', 'test-data', 'apps'));
const arango = require('@arangodb').arango;
const baseUrl = arango.getEndpoint().replace('tcp://', 'http://') + '/_db/_system';

describe('Foxx service path handling', () => {
  const mount = '/unittest/paths';
  before(function () {
    // Sanity tests
    expect(encodeURIComponent('ö')).not.to.equal('ö');
    expect(encodeURIComponent('+')).not.to.equal('+');
    expect(encodeURIComponent('/')).not.to.equal('/');
    expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/'));
  });
  beforeEach(function () {
    try {
      fm.uninstall(mount, {force: true});
    } catch (e) {}
    fm.install(basePath, mount);
  });

  afterEach(function () {
    fm.uninstall(mount, {force: true});
  });

  it('supports plain URLs', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'world'});
  });

  it('decodes encoded parts', () => {
    const res = request.get(`${baseUrl}/${mount}/${encodeURIComponent('hellö')}/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({encoded: 'world'});
  });

  it('does not double-decode encoded parts', () => {
    const res = request.get(`${baseUrl}/${mount}/${encodeURIComponent(encodeURIComponent('hellö'))}/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({crazy: 'world'});
  });

  it('does not decode plus signs in parts', () => {
    const res = request.get(`${baseUrl}/${mount}/h+llo/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({plus: 'world'});
  });

  it('decodes encoded path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent('wörld')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'wörld'});
  });

  it('does not double-decode encoded path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent(encodeURIComponent('wörld'))}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: encodeURIComponent('wörld')});
  });

  it('does not decode plus signs in path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/w+rld`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'w+rld'});
  });

  it('distinguishes between real slashes and encoded slashes', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent('not/slash')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'not/slash'});
  });

  it('does not decode suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/${encodeURIComponent('hello/world')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: encodeURIComponent('hello/world')});
  });

  it('does not decode plus signs in suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/${encodeURIComponent('hello+world')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: encodeURIComponent('hello+world')});
  });

  it('does not try to resolve dots in suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/../..`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: '../..'});
  });

  it('does not try to resolve dots during routing', () => {
    const res = request.get(`${baseUrl}/${mount}/../${mount}/hello`);
    expect(res).to.have.property('status', 404);
  });

  it('serves static files', () => {
    const res = request.get(`${baseUrl}/${mount}/static/public.txt`, {json: false});
    expect(res.body).to.equal('Hello');
  });

  it('does not serve static files outside statics path using dots', () => {
    const res = request.get(`${baseUrl}/${mount}/static/../secret.txt`, {json: false});
    expect(res).to.have.property('status', 404);
  });

  it('does not serve static files outside statics path using escaped dots', () => {
    const DOTDOT = '%2E%2E';
    expect(decodeURIComponent(DOTDOT)).to.equal('..');
    const res = request.get(`${baseUrl}/${mount}/static/${DOTDOT}/secret.txt`, {json: false});
    expect(res).to.have.property('status', 404);
  });
});
<commit_msg>fix test :S<commit_after>/*global describe, it, before, beforeEach, afterEach */
'use strict';
const expect = require('chai').expect;
const fm = require('@arangodb/foxx/manager');
const request = require('@arangodb/request');
const fs = require('fs');
const internal = require('internal');
const basePath = fs.makeAbsolute(fs.join(internal.startupPath, 'common', 'test-data', 'apps'));
const arango = require('@arangodb').arango;
const baseUrl = arango.getEndpoint().replace('tcp://', 'http://') + '/_db/_system';

describe('Foxx service path handling', () => {
  const mount = '/unittest/paths';
  before(function () {
    // Sanity tests
    expect(encodeURIComponent('ö')).not.to.equal('ö');
    expect(encodeURIComponent('+')).not.to.equal('+');
    expect(encodeURIComponent('/')).not.to.equal('/');
    expect(encodeURIComponent(encodeURIComponent('/'))).not.to.equal(encodeURIComponent('/'));
  });
  beforeEach(function () {
    try {
      fm.uninstall(mount, {force: true});
    } catch (e) {}
    fm.install(fs.join(basePath, 'paths'), mount);
  });

  afterEach(function () {
    fm.uninstall(mount, {force: true});
  });

  it('supports plain URLs', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'world'});
  });

  it('decodes encoded parts', () => {
    const res = request.get(`${baseUrl}/${mount}/${encodeURIComponent('hellö')}/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({encoded: 'world'});
  });

  it('does not double-decode encoded parts', () => {
    const res = request.get(`${baseUrl}/${mount}/${encodeURIComponent(encodeURIComponent('hellö'))}/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({crazy: 'world'});
  });

  it('does not decode plus signs in parts', () => {
    const res = request.get(`${baseUrl}/${mount}/h+llo/world`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({plus: 'world'});
  });

  it('decodes encoded path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent('wörld')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'wörld'});
  });

  it('does not double-decode encoded path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent(encodeURIComponent('wörld'))}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: encodeURIComponent('wörld')});
  });

  it('does not decode plus signs in path params', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/w+rld`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'w+rld'});
  });

  it('distinguishes between real slashes and encoded slashes', () => {
    const res = request.get(`${baseUrl}/${mount}/hello/${encodeURIComponent('not/slash')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({raw: 'not/slash'});
  });

  it('does not decode suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/${encodeURIComponent('hello/world')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: encodeURIComponent('hello/world')});
  });

  it('does not decode plus signs in suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/${encodeURIComponent('hello+world')}`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: encodeURIComponent('hello+world')});
  });

  it('does not try to resolve dots in suffixes', () => {
    const res = request.get(`${baseUrl}/${mount}/suffix/../..`);
    expect(res).to.have.property('status', 200);
    expect(res.json).to.eql({suffix: '../..'});
  });

  it('does not try to resolve dots during routing', () => {
    const res = request.get(`${baseUrl}/${mount}/../${mount}/hello`);
    expect(res).to.have.property('status', 404);
  });

  it('serves static files', () => {
    const res = request.get(`${baseUrl}/${mount}/static/public.txt`, {json: false});
    expect(res.body).to.equal('Hello');
  });

  it('does not serve static files outside statics path using dots', () => {
    const res = request.get(`${baseUrl}/${mount}/static/../secret.txt`, {json: false});
    expect(res).to.have.property('status', 404);
  });

  it('does not serve static files outside statics path using escaped dots', () => {
    const DOTDOT = '%2E%2E';
    expect(decodeURIComponent(DOTDOT)).to.equal('..');
    const res = request.get(`${baseUrl}/${mount}/static/${DOTDOT}/secret.txt`, {json: false});
    expect(res).to.have.property('status', 404);
  });
});
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import _ from 'lodash'
import { scrollToTop } from '../../vendor/scrolling'
import { loadNotifications } from '../../actions/notifications'
import StreamContainer from '../../containers/StreamContainer'
import { LOAD_STREAM_SUCCESS } from '../../constants/action_types'
import { SESSION_KEYS } from '../../constants/application_types'
import {
  BubbleIcon,
  HeartIcon,
  RepostIcon,
  RelationshipIcon,
} from '../../components/notifications/NotificationIcons'
import { TabListLinks } from '../../components/tabs/TabList'
import { Paginator } from '../../components/streams/Paginator'
import Session from '../../../src/vendor/session'
import { MainView } from '../../components/views/MainView'

class Notifications extends Component {

  static propTypes = {
    pathname: PropTypes.string,
    params: PropTypes.shape({
      category: PropTypes.string,
    }),
  }

  static preRender = (store, routerState) => {
    const { category } = routerState.params
    const params = {}
    if (category) {
      params.category = category
    }
    return store.dispatch(loadNotifications(params))
  }

  componentWillMount() {
    this.saveCategory()
    this.state = { isReloading: false }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.streamType === LOAD_STREAM_SUCCESS) {
      this.setState({ isReloading: false })
    }
  }

  componentDidUpdate() {
    this.saveCategory()
  }

  onClickTab = ({ type }) => {
    if (this.state.activeTabType === type) {
      scrollToTop()
      this.setState({ isReloading: true })
    }
    if (this.streamContainer) {
      this.streamContainer.refs.wrappedInstance.setAction(
        loadNotifications({ category: type })
      )
    }
    this.setState({ activeTabType: type })
  }

  saveCategory() {
    const { category } = this.props.params
    if (category) {
      Session.setItem(SESSION_KEYS.NOTIFICATIONS_FILTER, category)
    } else {
      Session.removeItem(SESSION_KEYS.NOTIFICATIONS_FILTER)
    }
  }

  render() {
    const { pathname } = this.props
    const { category } = this.props.params
    const { isReloading } = this.state
    const params = {}
    if (category) {
      params.category = category
    }
    const tabs = [
      { to: '/notifications', type: 'all', children: 'All' },
      { to: '/notifications/comments', type: 'comments', children: <BubbleIcon /> },
      { to: '/notifications/mentions', type: 'mentions', children: '@' },
      { to: '/notifications/loves', type: 'loves', children: <HeartIcon /> },
      { to: '/notifications/reposts', type: 'reposts', children: <RepostIcon /> },
      { to: '/notifications/relationships', type: 'relationships', children: <RelationshipIcon /> },
    ]
    return (
      <MainView className="Notifications">
        <TabListLinks
          activePath={pathname}
          className="IconTabList NotificationsContainerTabs"
          onTabClick={this.onClickTab}
          tabClasses="IconTab"
          tabs={tabs}
        />
        {
          isReloading ?
            <Paginator
              className="NotificationReload"
              isHidden={false}
              totalPages={0}
              totalPagesRemaining={0}
            /> :
            null
        }
        <StreamContainer
          action={loadNotifications(params)}
          className="asFullWidth"
          key={`notificationView_${params.category}`}
          ref={(comp) => { this.streamContainer = comp }}
          scrollSessionKey={`notifications_${category || 'all'}`}
        />
      </MainView>
    )
  }
}

function mapStateToProps(state, ownProps) {
  return {
    pathname: ownProps.location.pathname,
    streamType: _.get(state, 'stream.type'),
  }
}

export default connect(mapStateToProps)(Notifications)

<commit_msg>Refactor Notifications to not use refs.<commit_after>import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import _ from 'lodash'
import { scrollToTop } from '../../vendor/scrolling'
import { loadNotifications } from '../../actions/notifications'
import StreamContainer from '../../containers/StreamContainer'
import { LOAD_STREAM_SUCCESS } from '../../constants/action_types'
import { SESSION_KEYS } from '../../constants/application_types'
import {
  BubbleIcon,
  HeartIcon,
  RepostIcon,
  RelationshipIcon,
} from '../../components/notifications/NotificationIcons'
import { TabListLinks } from '../../components/tabs/TabList'
import { Paginator } from '../../components/streams/Paginator'
import Session from '../../../src/vendor/session'
import { MainView } from '../../components/views/MainView'

class Notifications extends Component {

  static propTypes = {
    category: PropTypes.string,
    pathname: PropTypes.string,
    params: PropTypes.shape({
      category: PropTypes.string,
    }),
    streamAction: PropTypes.object,
  }

  static preRender = (store, routerState) =>
    store.dispatch(loadNotifications(routerState.params))

  componentWillMount() {
    this.saveCategory()
    this.state = { isReloading: false }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.streamType === LOAD_STREAM_SUCCESS) {
      this.setState({ isReloading: false })
    }
  }

  componentDidUpdate() {
    this.saveCategory()
  }

  onClickTab = ({ type }) => {
    if (this.state.activeTabType === type) {
      scrollToTop()
      this.setState({ isReloading: true })
    }
    this.setState({ activeTabType: type })
  }

  saveCategory() {
    const { category } = this.props
    if (category) {
      Session.setItem(SESSION_KEYS.NOTIFICATIONS_FILTER, category)
    } else {
      Session.removeItem(SESSION_KEYS.NOTIFICATIONS_FILTER)
    }
  }

  render() {
    const { category, pathname, streamAction } = this.props
    const { isReloading } = this.state
    const tabs = [
      { to: '/notifications', type: 'all', children: 'All' },
      { to: '/notifications/comments', type: 'comments', children: <BubbleIcon /> },
      { to: '/notifications/mentions', type: 'mentions', children: '@' },
      { to: '/notifications/loves', type: 'loves', children: <HeartIcon /> },
      { to: '/notifications/reposts', type: 'reposts', children: <RepostIcon /> },
      { to: '/notifications/relationships', type: 'relationships', children: <RelationshipIcon /> },
    ]
    return (
      <MainView className="Notifications">
        <TabListLinks
          activePath={pathname}
          className="IconTabList NotificationsContainerTabs"
          onTabClick={this.onClickTab}
          tabClasses="IconTab"
          tabs={tabs}
        />
        {
          isReloading ?
            <Paginator
              className="NotificationReload"
              isHidden={false}
              totalPages={0}
              totalPagesRemaining={0}
            /> :
            null
        }
        <StreamContainer
          action={streamAction}
          className="asFullWidth"
          key={`notificationView_${category}`}
          scrollSessionKey={`notifications_${category || 'all'}`}
        />
      </MainView>
    )
  }
}

function mapStateToProps(state, ownProps) {
  const category = _.get(ownProps, 'params.category')
  return {
    category,
    pathname: ownProps.location.pathname,
    streamAction: loadNotifications({ category }),
    streamType: _.get(state, 'stream.type'),
  }
}

export default connect(mapStateToProps)(Notifications)

<|endoftext|>


<commit_before>import { window, document } from 'ssr-window';
import $ from 'dom7';
import Template7 from 'template7';
import Utils from '../../utils/utils';
import vdom from './vdom';
import patch from './patch';

class Framework7Component {
  constructor(app, options, extendContext = {}) {
    const id = Utils.id();
    const self = Utils.merge(
      this,
      extendContext,
      {
        $,
        $$: $,
        $dom7: $,
        $app: app,
        $f7: app,
        $options: Utils.extend({ id }, options),
        $id: options.id || id,
      }
    );
    const { $options } = self;

    // Root data and methods
    Object.defineProperty(self, '$root', {
      enumerable: true,
      configurable: true,
      get() {
        let root = Utils.merge({}, app.data, app.methods);
        if (window && window.Proxy) {
          root = new window.Proxy(root, {
            set(target, name, val) {
              app.data[name] = val;
            },
            deleteProperty(target, name) {
              delete app.data[name];
              delete app.methods[name];
            },
            has(target, name) {
              return (name in app.data || name in app.methods);
            },
          });
        }
        return root;
      },
      set() {},
    });

    // Apply context
    ('beforeCreate created beforeMount mounted beforeDestroy destroyed updated').split(' ').forEach((cycleKey) => {
      if ($options[cycleKey]) $options[cycleKey] = $options[cycleKey].bind(self);
    });

    if ($options.data) {
      $options.data = $options.data.bind(self);
      // Data
      Utils.extend(self, $options.data());
    }
    if ($options.render) $options.render = $options.render.bind(self);
    if ($options.methods) {
      Object.keys($options.methods).forEach((methodName) => {
        self[methodName] = $options.methods[methodName].bind(self);
      });
    }

    // Bind Events
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $options.on[eventName] = $options.on[eventName].bind(self);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $options.once[eventName] = $options.once[eventName].bind(self);
      });
    }

    // Before create hook
    if ($options.beforeCreate) $options.beforeCreate();

    // Render
    let html = self.$render();

    // Make Dom
    if (html && typeof html === 'string') {
      html = html.trim();
      self.$vnode = vdom(html, self, app, true);
      self.el = document.createElement('div');
      patch(self.el, self.$vnode);
    } else if (html) {
      self.el = html;
    }
    self.$el = $(self.el);

    // Set styles scope ID
    if ($options.style) {
      self.$styleEl = document.createElement('style');
      self.$styleEl.innerHTML = $options.style;
      if ($options.styleScoped) {
        self.el.setAttribute(`data-f7-${$options.id}`, '');
      }
    }

    self.$attachEvents();

    // Created callback
    if ($options.created) $options.created();

    // Store component instance
    self.el.f7Component = self;

    return self;
  }

  $attachEvents() {
    const self = this;
    const { $options, $el } = self;
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $el.on(Utils.eventNameToColonCase(eventName), $options.on[eventName]);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $el.once(Utils.eventNameToColonCase(eventName), $options.once[eventName]);
      });
    }
  }

  $detachEvents() {
    const self = this;
    const { $options, $el } = self;
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $el.off(Utils.eventNameToColonCase(eventName), $options.on[eventName]);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $el.off(Utils.eventNameToColonCase(eventName), $options.once[eventName]);
      });
    }
  }

  $render() {
    const self = this;
    const { $options } = self;
    let html = '';
    if ($options.render) {
      html = $options.render();
    } else if ($options.template) {
      if (typeof $options.template === 'string') {
        try {
          html = Template7.compile($options.template)(self);
        } catch (err) {
          throw err;
        }
      } else {
        // Supposed to be function
        html = $options.template(self);
      }
    }
    return html;
  }

  $forceUpdate() {
    const self = this;
    let html = self.$render();

    // Make Dom
    if (html && typeof html === 'string') {
      html = html.trim();
      const newVNode = vdom(html, self, self.$app);
      self.$vnode = patch(self.$vnode, newVNode);
    }
  }

  $setState(mergeState) {
    const self = this;
    Utils.merge(self, mergeState);
    self.$forceUpdate();
  }

  $mount(mountMethod) {
    const self = this;
    if (self.$options.beforeMount) self.$options.beforeMount();
    if (self.$styleEl) $('head').append(self.$styleEl);
    if (mountMethod) mountMethod(self.el);
    if (self.$options.mounted) self.$options.mounted();
  }

  $destroy() {
    const self = this;
    if (self.$options.beforeDestroy) self.$options.beforeDestroy();
    if (self.$styleEl) $(self.$styleEl).remove();
    self.$detachEvents();
    if (self.$options.destroyed) self.$options.destroyed();
    // Delete component instance
    if (self.el && self.el.f7Component) {
      self.el.f7Component = null;
      delete self.el.f7Component;
    }
    // Patch with empty node
    if (self.$vnode) {
      self.$vnode = patch(self.$vnode, { sel: self.$vnode.sel, data: {} });
    }
    Utils.deleteProps(self);
  }
}

export default Framework7Component;
<commit_msg>Core: Router component - fix when root element not DIV<commit_after>import { window, document } from 'ssr-window';
import $ from 'dom7';
import Template7 from 'template7';
import Utils from '../../utils/utils';
import vdom from './vdom';
import patch from './patch';

class Framework7Component {
  constructor(app, options, extendContext = {}) {
    const id = Utils.id();
    const self = Utils.merge(
      this,
      extendContext,
      {
        $,
        $$: $,
        $dom7: $,
        $app: app,
        $f7: app,
        $options: Utils.extend({ id }, options),
        $id: options.id || id,
      }
    );
    const { $options } = self;

    // Root data and methods
    Object.defineProperty(self, '$root', {
      enumerable: true,
      configurable: true,
      get() {
        let root = Utils.merge({}, app.data, app.methods);
        if (window && window.Proxy) {
          root = new window.Proxy(root, {
            set(target, name, val) {
              app.data[name] = val;
            },
            deleteProperty(target, name) {
              delete app.data[name];
              delete app.methods[name];
            },
            has(target, name) {
              return (name in app.data || name in app.methods);
            },
          });
        }
        return root;
      },
      set() {},
    });

    // Apply context
    ('beforeCreate created beforeMount mounted beforeDestroy destroyed updated').split(' ').forEach((cycleKey) => {
      if ($options[cycleKey]) $options[cycleKey] = $options[cycleKey].bind(self);
    });

    if ($options.data) {
      $options.data = $options.data.bind(self);
      // Data
      Utils.extend(self, $options.data());
    }
    if ($options.render) $options.render = $options.render.bind(self);
    if ($options.methods) {
      Object.keys($options.methods).forEach((methodName) => {
        self[methodName] = $options.methods[methodName].bind(self);
      });
    }

    // Bind Events
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $options.on[eventName] = $options.on[eventName].bind(self);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $options.once[eventName] = $options.once[eventName].bind(self);
      });
    }

    // Before create hook
    if ($options.beforeCreate) $options.beforeCreate();

    // Render
    let html = self.$render();

    // Make Dom
    if (html && typeof html === 'string') {
      html = html.trim();
      self.$vnode = vdom(html, self, app, true);
      self.el = document.createElement(self.$vnode.sel || 'div');
      patch(self.el, self.$vnode);
    } else if (html) {
      self.el = html;
    }
    self.$el = $(self.el);

    // Set styles scope ID
    if ($options.style) {
      self.$styleEl = document.createElement('style');
      self.$styleEl.innerHTML = $options.style;
      if ($options.styleScoped) {
        self.el.setAttribute(`data-f7-${$options.id}`, '');
      }
    }

    self.$attachEvents();

    // Created callback
    if ($options.created) $options.created();

    // Store component instance
    self.el.f7Component = self;

    return self;
  }

  $attachEvents() {
    const self = this;
    const { $options, $el } = self;
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $el.on(Utils.eventNameToColonCase(eventName), $options.on[eventName]);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $el.once(Utils.eventNameToColonCase(eventName), $options.once[eventName]);
      });
    }
  }

  $detachEvents() {
    const self = this;
    const { $options, $el } = self;
    if ($options.on) {
      Object.keys($options.on).forEach((eventName) => {
        $el.off(Utils.eventNameToColonCase(eventName), $options.on[eventName]);
      });
    }
    if ($options.once) {
      Object.keys($options.once).forEach((eventName) => {
        $el.off(Utils.eventNameToColonCase(eventName), $options.once[eventName]);
      });
    }
  }

  $render() {
    const self = this;
    const { $options } = self;
    let html = '';
    if ($options.render) {
      html = $options.render();
    } else if ($options.template) {
      if (typeof $options.template === 'string') {
        try {
          html = Template7.compile($options.template)(self);
        } catch (err) {
          throw err;
        }
      } else {
        // Supposed to be function
        html = $options.template(self);
      }
    }
    return html;
  }

  $forceUpdate() {
    const self = this;
    let html = self.$render();

    // Make Dom
    if (html && typeof html === 'string') {
      html = html.trim();
      const newVNode = vdom(html, self, self.$app);
      self.$vnode = patch(self.$vnode, newVNode);
    }
  }

  $setState(mergeState) {
    const self = this;
    Utils.merge(self, mergeState);
    self.$forceUpdate();
  }

  $mount(mountMethod) {
    const self = this;
    if (self.$options.beforeMount) self.$options.beforeMount();
    if (self.$styleEl) $('head').append(self.$styleEl);
    if (mountMethod) mountMethod(self.el);
    if (self.$options.mounted) self.$options.mounted();
  }

  $destroy() {
    const self = this;
    if (self.$options.beforeDestroy) self.$options.beforeDestroy();
    if (self.$styleEl) $(self.$styleEl).remove();
    self.$detachEvents();
    if (self.$options.destroyed) self.$options.destroyed();
    // Delete component instance
    if (self.el && self.el.f7Component) {
      self.el.f7Component = null;
      delete self.el.f7Component;
    }
    // Patch with empty node
    if (self.$vnode) {
      self.$vnode = patch(self.$vnode, { sel: self.$vnode.sel, data: {} });
    }
    Utils.deleteProps(self);
  }
}

export default Framework7Component;
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

Menubar.File = function ( editor ) {

	var container = new UI.Panel();
	container.setClass( 'menu' );

	var title = new UI.Panel();
	title.setClass( 'title' );
	title.setTextContent( 'File' );
	container.add( title );

	var options = new UI.Panel();
	options.setClass( 'options' );
	container.add( options );

	// New

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'New' );
	option.onClick( function () {

		if ( confirm( 'Are you sure?' ) ) {

			editor.config.setKey(
				'camera/position', [ 500, 250, 500 ],
				'camera/target', [ 0, 0, 0 ]
			);

			editor.storage.clear( function () {

				location.href = location.pathname;

			} );

		}

	} );
	options.add( option );

	//

	options.add( new UI.HorizontalRule() );

	// Import

	var fileInput = document.createElement( 'input' );
	fileInput.type = 'file';
	fileInput.addEventListener( 'change', function ( event ) {

		editor.loader.loadFile( fileInput.files[ 0 ] );

	} );

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Import' );
	option.onClick( function () {

		fileInput.click();

	} );
	options.add( option );

	//

	options.add( new UI.HorizontalRule() );

	// Export Geometry

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Geometry' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected.' );
			return;

		}

		var geometry = object.geometry;

		if ( geometry === undefined ) {

			alert( 'The selected object doesn\'t have geometry.' );
			return;

		}

		var output = geometry.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export Object

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Object' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected' );
			return;

		}

		var output = object.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export Scene

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Scene' );
	option.onClick( function () {

		var output = editor.scene.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export OBJ

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export OBJ' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected.' );
			return;

		}

		var geometry = object.geometry;

		if ( geometry === undefined ) {

			alert( 'The selected object doesn\'t have geometry.' );
			return;

		}

		var exporter = new THREE.OBJExporter();

		exportString( exporter.parse( geometry ) );

	} );
	options.add( option );

	// Export STL

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export STL' );
	option.onClick( function () {

		var exporter = new THREE.STLExporter();

		exportString( exporter.parse( editor.scene ) );

	} );
	options.add( option );

	/*
	//

	options.add( new UI.HorizontalRule() );

	// Publish

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Publish' );
	option.onClick( function () {

		alert( 'Not yet...' );

	} );
	options.add( option );
	*/

	/*
	// Test

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Test' );
	option.onClick( function () {

		var text = new UI.Text( 'blah' );
		editor.showDialog( text );

	} );
	options.add( option );
	*/

	//

	var exportString = function ( output ) {

		var blob = new Blob( [ output ], { type: 'text/plain' } );
		var objectURL = URL.createObjectURL( blob );

		window.open( objectURL, '_blank' );
		window.focus();

	};

	return container;

};
<commit_msg>Editor: Updated File/Export OBJ<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

Menubar.File = function ( editor ) {

	var container = new UI.Panel();
	container.setClass( 'menu' );

	var title = new UI.Panel();
	title.setClass( 'title' );
	title.setTextContent( 'File' );
	container.add( title );

	var options = new UI.Panel();
	options.setClass( 'options' );
	container.add( options );

	// New

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'New' );
	option.onClick( function () {

		if ( confirm( 'Are you sure?' ) ) {

			editor.config.setKey(
				'camera/position', [ 500, 250, 500 ],
				'camera/target', [ 0, 0, 0 ]
			);

			editor.storage.clear( function () {

				location.href = location.pathname;

			} );

		}

	} );
	options.add( option );

	//

	options.add( new UI.HorizontalRule() );

	// Import

	var fileInput = document.createElement( 'input' );
	fileInput.type = 'file';
	fileInput.addEventListener( 'change', function ( event ) {

		editor.loader.loadFile( fileInput.files[ 0 ] );

	} );

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Import' );
	option.onClick( function () {

		fileInput.click();

	} );
	options.add( option );

	//

	options.add( new UI.HorizontalRule() );

	// Export Geometry

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Geometry' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected.' );
			return;

		}

		var geometry = object.geometry;

		if ( geometry === undefined ) {

			alert( 'The selected object doesn\'t have geometry.' );
			return;

		}

		var output = geometry.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export Object

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Object' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected' );
			return;

		}

		var output = object.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export Scene

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export Scene' );
	option.onClick( function () {

		var output = editor.scene.toJSON();
		output = JSON.stringify( output, null, '\t' );
		output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );

		exportString( output );

	} );
	options.add( option );

	// Export OBJ

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export OBJ' );
	option.onClick( function () {

		var object = editor.selected;

		if ( object === null ) {

			alert( 'No object selected.' );
			return;

		}

		var exporter = new THREE.OBJExporter();

		exportString( exporter.parse( object ) );

	} );
	options.add( option );

	// Export STL

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Export STL' );
	option.onClick( function () {

		var exporter = new THREE.STLExporter();

		exportString( exporter.parse( editor.scene ) );

	} );
	options.add( option );

	/*
	//

	options.add( new UI.HorizontalRule() );

	// Publish

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Publish' );
	option.onClick( function () {

		alert( 'Not yet...' );

	} );
	options.add( option );
	*/

	/*
	// Test

	var option = new UI.Panel();
	option.setClass( 'option' );
	option.setTextContent( 'Test' );
	option.onClick( function () {

		var text = new UI.Text( 'blah' );
		editor.showDialog( text );

	} );
	options.add( option );
	*/

	//

	var exportString = function ( output ) {

		var blob = new Blob( [ output ], { type: 'text/plain' } );
		var objectURL = URL.createObjectURL( blob );

		window.open( objectURL, '_blank' );
		window.focus();

	};

	return container;

};
<|endoftext|>


<commit_before>FullScreenPokemon.FullScreenPokemon.settings.input = {
    "InputWritrArgs": {
        "aliases": {
            // Keyboard aliases
            "left":   [65, 37],     // a,     left
            "right":  [68, 39],     // d,     right
            "up":     [87, 38, 32], // w,     up,    space
            "down":   [83, 40],     // s,     down
            "a":      [90, 13],     // z,     enter
            "b":      [88, 8],      // x,     backspace
            "pause":  [80, 17],      // p, ctrl
            // Mouse aliases
            "rightclick": [3],
        },
        "triggers": {
            "onkeydown": {
                "left": FullScreenPokemon.FullScreenPokemon.prototype.keyDownLeft,
                "right": FullScreenPokemon.FullScreenPokemon.prototype.keyDownRight,
                "up": FullScreenPokemon.FullScreenPokemon.prototype.keyDownUp,
                "down": FullScreenPokemon.FullScreenPokemon.prototype.keyDownDown,
                "a": FullScreenPokemon.FullScreenPokemon.prototype.keyDownA,
                "b": FullScreenPokemon.FullScreenPokemon.prototype.keyDownB,
                "pause": FullScreenPokemon.FullScreenPokemon.prototype.togglePauseMenu,
                "mute": FullScreenPokemon.FullScreenPokemon.prototype.keyDownMute,
            },
            "onkeyup": {
                "left": FullScreenPokemon.FullScreenPokemon.prototype.keyUpLeft,
                "right": FullScreenPokemon.FullScreenPokemon.prototype.keyUpRight,
                "up": FullScreenPokemon.FullScreenPokemon.prototype.keyUpUp,
                "down": FullScreenPokemon.FullScreenPokemon.prototype.keyUpDown,
                "a": FullScreenPokemon.FullScreenPokemon.prototype.keyUpA,
                "b": FullScreenPokemon.FullScreenPokemon.prototype.keyUpB,
                "pause": FullScreenPokemon.FullScreenPokemon.prototype.keyUpPause
            },
            "onmousedown": {
                "rightclick": FullScreenPokemon.FullScreenPokemon.prototype.mouseDownRight
            },
            "oncontextmenu": {},
            "ondevicemotion": {
                //"devicemotion": FullScreenPokemon.FullScreenPokemon.prototype.deviceMotion
            }
        }
    }
};<commit_msg>Matched inputs to VBA<commit_after>FullScreenPokemon.FullScreenPokemon.settings.input = {
    "InputWritrArgs": {
        "aliases": {
            // Keyboard aliases
            "left":   [65, 37],     // a,     left
            "right":  [68, 39],     // d,     right
            "up":     [87, 38],     // w,     up
            "down":   [83, 40],     // s,     down
            "a":      [90, 13],     // z
            "b":      [88, 8],      // x,     backspace
            "pause":  [13, 17],     // enter, ctrl
            // Mouse aliases
            "rightclick": [3],
        },
        "triggers": {
            "onkeydown": {
                "left": FullScreenPokemon.FullScreenPokemon.prototype.keyDownLeft,
                "right": FullScreenPokemon.FullScreenPokemon.prototype.keyDownRight,
                "up": FullScreenPokemon.FullScreenPokemon.prototype.keyDownUp,
                "down": FullScreenPokemon.FullScreenPokemon.prototype.keyDownDown,
                "a": FullScreenPokemon.FullScreenPokemon.prototype.keyDownA,
                "b": FullScreenPokemon.FullScreenPokemon.prototype.keyDownB,
                "pause": FullScreenPokemon.FullScreenPokemon.prototype.togglePauseMenu,
                "mute": FullScreenPokemon.FullScreenPokemon.prototype.keyDownMute,
            },
            "onkeyup": {
                "left": FullScreenPokemon.FullScreenPokemon.prototype.keyUpLeft,
                "right": FullScreenPokemon.FullScreenPokemon.prototype.keyUpRight,
                "up": FullScreenPokemon.FullScreenPokemon.prototype.keyUpUp,
                "down": FullScreenPokemon.FullScreenPokemon.prototype.keyUpDown,
                "a": FullScreenPokemon.FullScreenPokemon.prototype.keyUpA,
                "b": FullScreenPokemon.FullScreenPokemon.prototype.keyUpB,
                "pause": FullScreenPokemon.FullScreenPokemon.prototype.keyUpPause
            },
            "onmousedown": {
                "rightclick": FullScreenPokemon.FullScreenPokemon.prototype.mouseDownRight
            },
            "oncontextmenu": {},
            "ondevicemotion": {
                //"devicemotion": FullScreenPokemon.FullScreenPokemon.prototype.deviceMotion
            }
        }
    }
};<|endoftext|>


<commit_before>define([
	"dojo/_base/kernel", // dojo.config.isDebug dojo.deprecated
	".",
	"./_WidgetBase",
	"./_OnDijitClickMixin",
	"./_FocusMixin",
	"./_base",
	"dojo/_base/lang", // dojo.hitch
	"dojo/_base/connect"	// dojo.connect
], function(dojo, dijit){

// module:
//		dijit/_Widget
// summary:
//		Old base for widgets.   New widgets should extend _WidgetBase instead

dijit._connectToDomNode = function(/*Event*/ event){
	// summary:
	//		If user connects to a widget method === this function, then they will
	//		instead actually be connecting the equivalent event on this.domNode
};

dojo.declare("dijit._Widget", [dijit._WidgetBase, dijit._OnDijitClickMixin, dijit._FocusMixin], {
	// summary:
	//		Base class for all Dijit widgets.
	//
	//		Extends _WidgetBase, adding support for:
	//			- declaratively/programatically specifying widget initialization parameters like
	//				onMouseMove="foo" that call foo when this.domNode gets a mousemove event
	//			- ondijitclick
	//				Support new dojoAttachEvent="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
	//			- focus related functions
	//				In particular, the onFocus()/onBlur() callbacks.   Driven internally by
	//				dijit/_base/focus.js.
	//			- deprecated methods
	//			- onShow(), onHide(), onClose()
	//
	//		Also, by loading code in dijit/_base, turns on:
	//			- browser sniffing (putting browser id like .dj_ie on <html> node)
	//			- high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)

	////////////////// DEFERRED CONNECTS ///////////////////

	onClick: dijit._connectToDomNode,
	/*=====
	onClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onDblClick: dijit._connectToDomNode,
	/*=====
	onDblClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse double click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onKeyDown: dijit._connectToDomNode,
	/*=====
	onKeyDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being pressed down.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyPress: dijit._connectToDomNode,
	/*=====
	onKeyPress: function(event){
		// summary:
		//		Connect to this function to receive notifications of printable keys being typed.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyUp: dijit._connectToDomNode,
	/*=====
	onKeyUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being released.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onMouseDown: dijit._connectToDomNode,
	/*=====
	onMouseDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is pressed down.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseMove: dijit._connectToDomNode,
	/*=====
	onMouseMove: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOut: dijit._connectToDomNode,
	/*=====
	onMouseOut: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOver: dijit._connectToDomNode,
	/*=====
	onMouseOver: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseLeave: dijit._connectToDomNode,
	/*=====
	onMouseLeave: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseEnter: dijit._connectToDomNode,
	/*=====
	onMouseEnter: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseUp: dijit._connectToDomNode,
	/*=====
	onMouseUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is released.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/

	constructor: function(params){
		// extract parameters like onMouseMove that should connect directly to this.domNode
		this._toConnect = {};
		for(var name in params){
			if(this[name] === dijit._connectToDomNode){
				this._toConnect[name] = params[name];
				delete params[name];
			}
		}
	},

	postCreate: function(){
		this.inherited(arguments);

		// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
		for(var name in this._toConnect){
			this.on(name, this._toConnect[name]);
		}
		delete this._toConnect;
	},

	on: function(/*String*/ type, /*Function*/ func){
		type = type.replace(/^on/, "");
		if(this["on" + type.charAt(0).toUpperCase() + type.substr(1)] === dijit._connectToDomNode){
			// Use dojo.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
			// Also, need to specify context as "this" rather than the default context of the DOMNode
			return dojo.connect(this.domNode, type.toLowerCase(), this, func);
		}else{
			return this.inherited(arguments);
		}
	},

	_setFocusedAttr: function(val){
		// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
		// (but since it's a private variable we aren't required to keep supporting it).
		this._focused = val;
		this._set("focused", val);
	},

	////////////////// DEPRECATED METHODS ///////////////////

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		// summary:
		//		Deprecated.  Use set() instead.
		// tags:
		//		deprecated
		dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
		this.set(attr, value);
	},

	attr: function(/*String|Object*/name, /*Object?*/value){
		// summary:
		//		Set or get properties on a widget instance.
		//	name:
		//		The property to get or set. If an object is passed here and not
		//		a string, its keys are used as names of attributes to be set
		//		and the value of the object as values to set in the widget.
		//	value:
		//		Optional. If provided, attr() operates as a setter. If omitted,
		//		the current value of the named property is returned.
		// description:
		//		This method is deprecated, use get() or set() directly.

		// Print deprecation warning but only once per calling function
		if(dojo.config.isDebug){
			var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
				caller = (arguments.callee.caller || "unknown caller").toString();
			if(!alreadyCalledHash[caller]){
				dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
				caller, "", "2.0");
				alreadyCalledHash[caller] = true;
			}
		}

		var args = arguments.length;
		if(args >= 2 || typeof name === "object"){ // setter
			return this.set.apply(this, arguments);
		}else{ // getter
			return this.get(name);
		}
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	_onShow: function(){
		// summary:
		//		Internal method called when this widget is made visible.
		//		See `onShow` for details.
		this.onShow();
	},

	onShow: function(){
		// summary:
		//		Called when this widget becomes the selected pane in a
		//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
		//		`dijit.layout.AccordionContainer`, etc.
		//
		//		Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
		// tags:
		//		callback
	},

	onHide: function(){
		// summary:
			//		Called when another widget becomes the selected pane in a
			//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
			//		`dijit.layout.AccordionContainer`, etc.
			//
			//		Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
			// tags:
			//		callback
	},

	onClose: function(){
		// summary:
		//		Called when this widget is being displayed as a popup (ex: a Calendar popped
		//		up from a DateTextBox), and it is hidden.
		//		This is called from the dijit.popup code, and should not be called directly.
		//
		//		Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
		//		Callback if a user tries to close the child.   Child will be closed if this function returns true.
		// tags:
		//		extension

		return true;		// Boolean
	}
});

return dijit._Widget;
});
<commit_msg>In async mode, avoid loading modules we aren't using directly.   Refs #12432 !strict.<commit_after>define([
	"dojo/_base/kernel", // dojo.config.isDebug dojo.deprecated
	".",
	"./_WidgetBase",
	"./_OnDijitClickMixin",
	"./_FocusMixin",
	"dojo/_base/lang", // dojo.hitch
	"dojo/_base/connect",	// dojo.connect
	"dojo/uacss",		// brower sniffing
	"dijit/hccss",		// high contrast mode sniffing
	"./_base/manager",	// dijit.byId, etc.
	"./_base/wai"		// for back-compat, remove for 2.0
], function(dojo, dijit){

// module:
//		dijit/_Widget
// summary:
//		Old base for widgets.   New widgets should extend _WidgetBase instead

dijit._connectToDomNode = function(/*Event*/ event){
	// summary:
	//		If user connects to a widget method === this function, then they will
	//		instead actually be connecting the equivalent event on this.domNode
};

dojo.declare("dijit._Widget", [dijit._WidgetBase, dijit._OnDijitClickMixin, dijit._FocusMixin], {
	// summary:
	//		Base class for all Dijit widgets.
	//
	//		Extends _WidgetBase, adding support for:
	//			- declaratively/programatically specifying widget initialization parameters like
	//				onMouseMove="foo" that call foo when this.domNode gets a mousemove event
	//			- ondijitclick
	//				Support new dojoAttachEvent="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
	//			- focus related functions
	//				In particular, the onFocus()/onBlur() callbacks.   Driven internally by
	//				dijit/_base/focus.js.
	//			- deprecated methods
	//			- onShow(), onHide(), onClose()
	//
	//		Also, by loading code in dijit/_base, turns on:
	//			- browser sniffing (putting browser id like .dj_ie on <html> node)
	//			- high contrast mode sniffing (add .dijit_a11y class to <body> if machine is in high contrast mode)

	////////////////// DEFERRED CONNECTS ///////////////////

	onClick: dijit._connectToDomNode,
	/*=====
	onClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onDblClick: dijit._connectToDomNode,
	/*=====
	onDblClick: function(event){
		// summary:
		//		Connect to this function to receive notifications of mouse double click events.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onKeyDown: dijit._connectToDomNode,
	/*=====
	onKeyDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being pressed down.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyPress: dijit._connectToDomNode,
	/*=====
	onKeyPress: function(event){
		// summary:
		//		Connect to this function to receive notifications of printable keys being typed.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onKeyUp: dijit._connectToDomNode,
	/*=====
	onKeyUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of keys being released.
		// event:
		//		key Event
		// tags:
		//		callback
	},
	=====*/
	onMouseDown: dijit._connectToDomNode,
	/*=====
	onMouseDown: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is pressed down.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseMove: dijit._connectToDomNode,
	/*=====
	onMouseMove: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOut: dijit._connectToDomNode,
	/*=====
	onMouseOut: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseOver: dijit._connectToDomNode,
	/*=====
	onMouseOver: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseLeave: dijit._connectToDomNode,
	/*=====
	onMouseLeave: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves off of this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseEnter: dijit._connectToDomNode,
	/*=====
	onMouseEnter: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse moves onto this widget.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/
	onMouseUp: dijit._connectToDomNode,
	/*=====
	onMouseUp: function(event){
		// summary:
		//		Connect to this function to receive notifications of when the mouse button is released.
		// event:
		//		mouse Event
		// tags:
		//		callback
	},
	=====*/

	constructor: function(params){
		// extract parameters like onMouseMove that should connect directly to this.domNode
		this._toConnect = {};
		for(var name in params){
			if(this[name] === dijit._connectToDomNode){
				this._toConnect[name] = params[name];
				delete params[name];
			}
		}
	},

	postCreate: function(){
		this.inherited(arguments);

		// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
		for(var name in this._toConnect){
			this.on(name, this._toConnect[name]);
		}
		delete this._toConnect;
	},

	on: function(/*String*/ type, /*Function*/ func){
		type = type.replace(/^on/, "");
		if(this["on" + type.charAt(0).toUpperCase() + type.substr(1)] === dijit._connectToDomNode){
			// Use dojo.connect() rather than on() to get handling for "onmouseenter" on non-IE, etc.
			// Also, need to specify context as "this" rather than the default context of the DOMNode
			return dojo.connect(this.domNode, type.toLowerCase(), this, func);
		}else{
			return this.inherited(arguments);
		}
	},

	_setFocusedAttr: function(val){
		// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
		// (but since it's a private variable we aren't required to keep supporting it).
		this._focused = val;
		this._set("focused", val);
	},

	////////////////// DEPRECATED METHODS ///////////////////

	setAttribute: function(/*String*/ attr, /*anything*/ value){
		// summary:
		//		Deprecated.  Use set() instead.
		// tags:
		//		deprecated
		dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
		this.set(attr, value);
	},

	attr: function(/*String|Object*/name, /*Object?*/value){
		// summary:
		//		Set or get properties on a widget instance.
		//	name:
		//		The property to get or set. If an object is passed here and not
		//		a string, its keys are used as names of attributes to be set
		//		and the value of the object as values to set in the widget.
		//	value:
		//		Optional. If provided, attr() operates as a setter. If omitted,
		//		the current value of the named property is returned.
		// description:
		//		This method is deprecated, use get() or set() directly.

		// Print deprecation warning but only once per calling function
		if(dojo.config.isDebug){
			var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
				caller = (arguments.callee.caller || "unknown caller").toString();
			if(!alreadyCalledHash[caller]){
				dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " +
				caller, "", "2.0");
				alreadyCalledHash[caller] = true;
			}
		}

		var args = arguments.length;
		if(args >= 2 || typeof name === "object"){ // setter
			return this.set.apply(this, arguments);
		}else{ // getter
			return this.get(name);
		}
	},

	////////////////// MISCELLANEOUS METHODS ///////////////////

	_onShow: function(){
		// summary:
		//		Internal method called when this widget is made visible.
		//		See `onShow` for details.
		this.onShow();
	},

	onShow: function(){
		// summary:
		//		Called when this widget becomes the selected pane in a
		//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
		//		`dijit.layout.AccordionContainer`, etc.
		//
		//		Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
		// tags:
		//		callback
	},

	onHide: function(){
		// summary:
			//		Called when another widget becomes the selected pane in a
			//		`dijit.layout.TabContainer`, `dijit.layout.StackContainer`,
			//		`dijit.layout.AccordionContainer`, etc.
			//
			//		Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`.
			// tags:
			//		callback
	},

	onClose: function(){
		// summary:
		//		Called when this widget is being displayed as a popup (ex: a Calendar popped
		//		up from a DateTextBox), and it is hidden.
		//		This is called from the dijit.popup code, and should not be called directly.
		//
		//		Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses.
		//		Callback if a user tries to close the child.   Child will be closed if this function returns true.
		// tags:
		//		extension

		return true;		// Boolean
	}
});

// For back-compat, remove in 2.0.
if(!dojo.isAsync){
	dojo.ready(0, function(){
		require(["dijit/_base/focus", "dijit/_base/place", "dijit/_base/popup", "dijit/_base/scroll",
			"dijit/_base/typematic", "dijit/_base/window"]);
	})
}

return dijit._Widget;
});
<|endoftext|>


<commit_before>psc.namespace('admin');

psc.admin.UserAdmin = (function ($) {
  var user;

  var provisionableSites = new Array();
  PROVISIONABLE_SITES.each(function(site) {
    provisionableSites.push(site.identifier)
  });

  function selectRole(roleKey) {
    $('a.role').removeClass('selected');
    $('#role-' + roleKey).addClass('selected');

    if (roleKey === 'multiple-roles') {
      startEditingMultiple();
      $('input.roles-to-edit').change(selectRoleCheckbox);

    } else {
      startEditing(roleKey);
    }
  }

  function selectRoleTab(evt) {
    var roleKey = evt.target.id.substring("role-".length)
    selectRole(roleKey);
    syncAllVsOne();
    return false;
  }

  function selectRoleCheckbox(evt) {
    var checked = _($('input.roles-to-edit:checked')).map(function(elt) {
      return $(elt).val();
    });

    addRoleAndScopesToMultipleTemplate(checked);
    syncAllVsOne();
    return false;
  }

  function determineProvisionableStudies(role) {
    var isTM = _(role.uses || []).indexOf("template_management") >= 0;
    var isSP = _(role.uses || []).indexOf("site_participation") >= 0;
    if (isTM && isSP) {
      return PROVISIONABLE_STUDIES['template_management+site_participation'];
    } else if (isTM) {
      return PROVISIONABLE_STUDIES['template_management'];
    } else {
      // use participation even for roles that don't have uses
      return PROVISIONABLE_STUDIES['site_participation'];
    }
  }

  function startEditing(roleKey) {
    var role = _.detect(PROVISIONABLE_ROLES, function (role) { return role.key === roleKey });
    if (role) {
      var enableRoleControl = isControlEnabled('role-control', role);
      var enableSitesControl = isControlEnabled('sites-control', role);

      $('#role-editor-pane').html(resigTemplate('role_editor_template', {
          role: role, sites: PROVISIONABLE_SITES, studies: determineProvisionableStudies(role),
          enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl
        })).
        find('input.role-group-membership').attr('checked', !!user.memberships[role.key]).
        click(updateGroupMembership).end().
        parent().attr('role', roleKey);
      registerScopeControls('#role-editor-pane', role, 'site', 'sites');
      registerScopeControls('#role-editor-pane', role, 'study', 'studies');
    } else {
      alert("No such role " + roleKey);
    }
  }

  function startEditingMultiple(roleKeys) {
    roleKeys = roleKeys || []
    var roles = _.select(PROVISIONABLE_ROLES, function (role) { return _.include(roleKeys, role.key)});

    var enableRoleControl = _(roles).any(function(r) {return isControlEnabled('role-control', r)});
    var enableSitesControl = _(roles).any(function(r) {return isControlEnabled('sites-control', r)});
    $('#role-editor-pane').html(resigTemplate('multiple_role_editor_template', {
      roles: roles, sites: PROVISIONABLE_SITES,
      studies: uniqueStudies(_(roles).map(function(r) {return determineProvisionableStudies(r)}).flatten()),
      enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl,
      utils: {mapRoleKeys: mapRoleKeys, mapRoleNames: mapRoleNames, escapeIdSpaces: escapeIdSpaces}
    }));
  }

  function addRoleAndScopesToMultipleTemplate(roleKeys) {
    roleKeys = roleKeys || []
    var roles = _.select(PROVISIONABLE_ROLES, function (role) { return _.include(roleKeys, role.key)});

    var enableRoleControl = _(roles).any(function(r) {return isControlEnabled('role-control', r)});
    var enableSitesControl = _(roles).any(function(r) {return isControlEnabled('sites-control', r)});
    $('#scope-container').html(resigTemplate('role_and_scope_assignment_template', {
      roles: roles, sites: PROVISIONABLE_SITES,
      studies: uniqueStudies(_(roles).map(function(r) {return determineProvisionableStudies(r)}).flatten()),
      enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl,
      utils: {mapRoleKeys: mapRoleKeys, mapRoleNames: mapRoleNames, escapeIdSpaces: escapeIdSpaces}
    }))

    registerMultipleGroupControl('#role-editor-pane', roles);
    registerMultipleScopeControls('#role-editor-pane', roles, 'site', 'sites')
    registerMultipleScopeControls('#role-editor-pane', roles, 'study', 'studies')

  }

  function uniqueStudies(studies) {
    var unique = [];
    _(studies).each(function(s) {
      if (!_(unique).pluck('identifier').include(s.identifier)) {
        unique.push(s)
      }
    });
    return unique;
  }

  function isControlEnabled(controlKey, role) {
      var enableRoleControl = true;
      var enableSitesControl = true;
      var provisionableStudies = new Array();
      determineProvisionableStudies(role).each(function(study) {
          provisionableStudies.push(study.identifier)
      });
      var mem = user.memberships[role.key]
      if (mem != null) {
        // Disable role membership control when the user we are provisioning is a
        // member of sites and/or studies that are not in the list of provisionable studies.
        if (mem['sites'] != null && mem['studies'] != null) {
          if (!(isSubsetOfProvision(provisionableSites, mem['sites'])
              && isSubsetOfProvision(provisionableStudies, mem['studies']))) {
            enableRoleControl = false;
          }
        } else if (mem['studies'] != null) {
          if (!isSubsetOfProvision(provisionableStudies, mem['studies'])) {
            enableRoleControl = false;
          }
        } else if (mem['sites'] != null) {
          if (!isSubsetOfProvision(provisionableSites, mem['sites'])) {
            enableRoleControl = false;
          }
        }

        // Disable sites membership control when the user we are provisioning is a
        // member of all-sites and the all-sites membership is not in the list of provisionable sites.
        if (!_.isEmpty(provisionableSites) && !_.isEmpty(mem['sites'])) {
          if (!_.include(provisionableSites,'__ALL__') && _.include(mem['sites'],'__ALL__')) {
            enableSitesControl = false;
          }
        }
      }

    switch(controlKey) {
      case 'role-control': return enableRoleControl;
      case 'sites-control': return enableSitesControl;
      default: return null;
    }
  }

  function isSubsetOfProvision(provisionableList, membershipList) {
    if(!_.isEmpty(provisionableList) && !_.isEmpty(membershipList)) {
      return _.all(membershipList, function(ele){
        return _.include(provisionableList,ele)
      })
    }
  }

  function updateGroupMembership(evt) {
    var r = evt.target.value;
    if ($(evt.target).attr('checked')) {
      user.add(r);
    } else {
      user.remove(r);
    }
  }

  function registerMultipleGroupControl(pane, roles) {
    var state = determineTristateCheckboxState(roles);

    var input = $(pane).find('#multiple-group-membership:first');

    $(input).tristate({initialState: state});

    updateIntermediateStateLabel(state, pane, '#partial-multiple-group-membership-info', roles);

    $(input).bind('tristate-state-change', _(updateMultipleGroupMemberships).bind(this, mapRoleKeys(roles)));
    $(input).bind('tristate-state-change', _(function (pane, roles, evt) {
      var state = $(evt.target).attr('state');
      updateIntermediateStateLabel(state, pane, '#partial-multiple-group-membership-info', roles);
    }).bind(this, pane, roles));
  }

  function updateMultipleGroupMemberships(roleKeys, evt) {
    console.log("Updating user obj", roleKeys, evt)

    var state = $(evt.target).attr('state');

    _(roleKeys).each(function(k) {
      switch(state) {
      case 'checked':
        user.add(k); break;
      case 'unchecked':
        user.remove(k); break;
      default:
        console.log("State does not exist", state);
      }
    });
  }

  function updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue) {
    var label = $(pane).find(label + ':first');

    if (state == 'intermediate') {
      var roleKeys = mapRoleKeys(roles);
      var scope = buildScopeObject(scopeType, scopeValue);
      var matchingRoleKeys = user.matchingMemberships(roleKeys, scope);
      var matchingRoles = _(PROVISIONABLE_ROLES).select(function (role) {return _(matchingRoleKeys).include(role.key)});
      var nonMatchingRoles = _(roles).reject(function (role) {return _(matchingRoleKeys).include(role.key)});
      $(label).html('(Applies to ' +  mapRoleNames(matchingRoles).join(', ') + ', but not ' + mapRoleNames(nonMatchingRoles).join(', ') + ')').show();
    } else {
      $(label).hide().empty();
    }
  }

  function determineTristateCheckboxState(roles, scopeType, scopeValue) {
    var roleKeys = mapRoleKeys(roles);
    var scope = buildScopeObject(scopeType, scopeValue);

    var status = user.membershipsStatus(roleKeys, scope);
    console.log("Tri-state checkbox status", status, scopeType, scopeValue);
    switch(status) {
      case 'FULL':    return 'checked';
      case 'PARTIAL': return 'intermediate';
      case 'NONE':    return 'unchecked';
      default:
        console.log("Memberships status unknown: ", status);
        return 'unchecked';
    }
  }

  function registerScopeControls(pane, role, scopeType, scopeTypePlural) {
    $(pane).find('input.scope-' + scopeType).each(function (i, input) {
      var qMembership = {}; qMembership[scopeType] = $(input).attr(scopeType + '-identifier');
      $(input).attr(
          'checked',
          user.hasMembership(role.key, qMembership));
    }).click(_(updateMembershipScope).bind(this, role.key, scopeType, scopeTypePlural));
  }

  function registerMultipleScopeControls(pane, roles, scopeType, scopeTypePlural) {
    $(pane).find('input.scope-' + scopeType).each(function (i, input) {
      var scopeValue = $(input).attr(scopeType + '-identifier');
      var state = determineTristateCheckboxState(roles, scopeType, scopeValue);
      var label = '#partial-scope-' + scopeType + '-' + escapeIdSpaces(scopeValue) + '-info';
      $(input).tristate({initialState: state});
      updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue);
    }).bind('tristate-state-change', _(function(roles, scopeType, scopeTypePlural, evt) {
      updateMultipleMembershipScope(_(roles).map(function(r){return r.key}), scopeType, scopeTypePlural, evt);
      var scopeValue = $(evt.target).attr(scopeType + '-identifier');
      var state = determineTristateCheckboxState(roles, scopeType, scopeValue);
      var label = '#partial-scope-' + scopeType + '-' + escapeIdSpaces(scopeValue) + '-info';
      updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue);
    }).bind(this, roles, scopeType, scopeTypePlural));
  }

  function buildScopeObject(scopeType, scopeValue) {
    var scope = null;
    if (scopeType) {
      scope = {}; scope[scopeType] = scopeValue;
    }
    return scope;
  }

  function mapRoleKeys(roles) {
    return roles.map(function(r){return r.key});
  }

  function mapRoleNames(roles) {
    return _(roles).map(function(r){return r.name});
  }

  function escapeIdSpaces(id) {
    return id.replace(' ', '--space--');
  }

  function updateMembershipScope(roleKey, scopeType, scopeListName, evt) {
    console.log("Updating user obj", roleKey, scopeType, scopeListName, evt);
    var scope = {}; scope[scopeListName] = [ evt.target.getAttribute(scopeType + '-identifier') ];
    if ($(evt.target).attr('checked')) {
      user.add(roleKey, scope);
    } else {
      user.remove(roleKey, scope);
    }
  }

  function updateMultipleMembershipScope(roleKeys, scopeType, scopeListName, evt) {
    var scope = {}; scope[scopeListName] = [ evt.target.getAttribute(scopeType + '-identifier') ];
    var state = $(evt.target).attr('state')

    console.log("Updating user obj", roleKeys, scopeType, scopeListName, scope, state, evt);

    _(roleKeys).each(function(roleKey) {
      switch(state) {
      case 'checked':
        user.add(roleKey, scope); break;
      case 'unchecked':
        user.remove(roleKey, scope); break;
      default:
        console.log("State does not exist", state);
      }
    });
  }

  function syncRoleTabOnChange(evt, data) {
    if (user.hasMembership(data.role)) {
      $('a#role-' + data.role).addClass('member');
    } else {
      $('a#role-' + data.role).removeClass('member');
    }
  }

  function syncRoleEditorOnChange(evt, data) {
    var role = data.role;
    var editorRole = $('#role-editor').attr('role');
    if (role !== editorRole && editorRole.indexOf(',') !== -1) { return; }
    var input;
    if (data.scopeType) {
      input = $('#role-editor input#scope-' + data.scopeType + '-' + data.scopeIdentifier);
      setTimeout(syncAllVsOne, 0);
    } else {
      input = $('#role-editor input#group-' + data.role);
      if ($(input).length <= 0) {
        input = $('#role-editor input#multiple-group-membership');
      }
    }
    input.attr('checked', data.kind === 'add');
    if (input.attr('state')) {
      input.attr('state', data.kind === 'add' ? 'checked' : 'unchecked');
      $(input).parents('.row').find('.partial-membership').empty();
    }
  }

  function syncAllVsOne() {
    _(['scope-study', 'scope-site']).each(function (scopeClass) {
      var isAll = $('#role-editor input.all.' + scopeClass + ":checked").length > 0;
      if (isAll) {
        var oneSelector = '#role-editor input.one.' + scopeClass;

        // TODO: Find way to get tri-state checkboxes without using the state attribute.
        var regular = $(oneSelector).not('[state]').
          filter(':checked').click().end()

        var tristate = $(oneSelector + '[state]').
          tristate('state', 'unchecked')

        regular.add(tristate).
          attr("disabled", true).
          closest('div.row').addClass('disabled');
      } else {
        $('#role-editor input.one.' + scopeClass).
          attr("disabled", false).
          closest('div.row').removeClass('disabled');
      }
    });
  }

  function syncUsername() {
    user.username = $('input#username').val();
  }

  function toggleRoleView(evt, action) {
    if(!action) {
      action = $('#show-all-toggle:contains(Show)').length > 0 ? 'hide' : 'show';
    }

    if (action === 'show') {
      $('#show-all-toggle').text('Show Suite Roles');
      $("div.role-tab[role-type='suite']").hide();
    } else {
      $('#show-all-toggle').text('Hide Suite Roles')
      $("div.role-tab[role-type='suite']").show();
    }
    return false;
  }

  return {
    init: function (u) {
      user = u;
      _(user.memberships).each(function (scopes, roleKey) {
        $('#role-' + roleKey).addClass('member');
      }, this);
      var firstRole;
      if ($("div.role-tab[role-type='psc'] a.role.member").length > 0) {
        firstRole = $('a.role.member')[0].id.substring("role-".length);
      } else {
        firstRole = 'study_subject_calendar_manager';
      }
      selectRole(firstRole);
      $('a.role').click(selectRoleTab);
      $(user).bind('membership-change', syncRoleTabOnChange).
        bind('membership-change', syncRoleEditorOnChange);
      $('input#username').keyup(syncUsername);
      syncAllVsOne();
      toggleRoleView(null, 'show');
      $('#show-all-toggle').click(toggleRoleView);
    },

    serializeRoleChanges: function () {
      if (user.username) {
        var changes = {}; changes[user.username] = user.changes;
        return YAHOO.lang.JSON.stringify(changes);
      } else {
        return "{}";
      }
    }
  };
})(jQuery);
<commit_msg>Consolidate updateGroupMembership and updateMultipleGroupMemberships functions into one function.<commit_after>psc.namespace('admin');

psc.admin.UserAdmin = (function ($) {
  var user;

  var provisionableSites = new Array();
  PROVISIONABLE_SITES.each(function(site) {
    provisionableSites.push(site.identifier)
  });

  function selectRole(roleKey) {
    $('a.role').removeClass('selected');
    $('#role-' + roleKey).addClass('selected');

    if (roleKey === 'multiple-roles') {
      startEditingMultiple();
      $('input.roles-to-edit').change(selectRoleCheckbox);

    } else {
      startEditing(roleKey);
    }
  }

  function selectRoleTab(evt) {
    var roleKey = evt.target.id.substring("role-".length)
    selectRole(roleKey);
    syncAllVsOne();
    return false;
  }

  function selectRoleCheckbox(evt) {
    var checked = _($('input.roles-to-edit:checked')).map(function(elt) {
      return $(elt).val();
    });

    addRoleAndScopesToMultipleTemplate(checked);
    syncAllVsOne();
    return false;
  }

  function determineProvisionableStudies(role) {
    var isTM = _(role.uses || []).indexOf("template_management") >= 0;
    var isSP = _(role.uses || []).indexOf("site_participation") >= 0;
    if (isTM && isSP) {
      return PROVISIONABLE_STUDIES['template_management+site_participation'];
    } else if (isTM) {
      return PROVISIONABLE_STUDIES['template_management'];
    } else {
      // use participation even for roles that don't have uses
      return PROVISIONABLE_STUDIES['site_participation'];
    }
  }

  function startEditing(roleKey) {
    var role = _.detect(PROVISIONABLE_ROLES, function (role) { return role.key === roleKey });
    if (role) {
      var enableRoleControl = isControlEnabled('role-control', role);
      var enableSitesControl = isControlEnabled('sites-control', role);

      $('#role-editor-pane').html(resigTemplate('role_editor_template', {
          role: role, sites: PROVISIONABLE_SITES, studies: determineProvisionableStudies(role),
          enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl
        })).
        find('input.role-group-membership').attr('checked', !!user.memberships[role.key]).
        click(_(updateGroupMembership).bind(this, function(evt) {return $(evt.target).attr('checked')})).end().
        parent().attr('role', roleKey);
      registerScopeControls('#role-editor-pane', role, 'site', 'sites');
      registerScopeControls('#role-editor-pane', role, 'study', 'studies');
    } else {
      alert("No such role " + roleKey);
    }
  }

  function startEditingMultiple(roleKeys) {
    roleKeys = roleKeys || []
    var roles = _.select(PROVISIONABLE_ROLES, function (role) { return _.include(roleKeys, role.key)});

    var enableRoleControl = _(roles).any(function(r) {return isControlEnabled('role-control', r)});
    var enableSitesControl = _(roles).any(function(r) {return isControlEnabled('sites-control', r)});
    $('#role-editor-pane').html(resigTemplate('multiple_role_editor_template', {
      roles: roles, sites: PROVISIONABLE_SITES,
      studies: uniqueStudies(_(roles).map(function(r) {return determineProvisionableStudies(r)}).flatten()),
      enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl,
      utils: {mapRoleKeys: mapRoleKeys, mapRoleNames: mapRoleNames, escapeIdSpaces: escapeIdSpaces}
    }));
  }

  function addRoleAndScopesToMultipleTemplate(roleKeys) {
    roleKeys = roleKeys || []
    var roles = _.select(PROVISIONABLE_ROLES, function (role) { return _.include(roleKeys, role.key)});

    var enableRoleControl = _(roles).any(function(r) {return isControlEnabled('role-control', r)});
    var enableSitesControl = _(roles).any(function(r) {return isControlEnabled('sites-control', r)});
    $('#scope-container').html(resigTemplate('role_and_scope_assignment_template', {
      roles: roles, sites: PROVISIONABLE_SITES,
      studies: uniqueStudies(_(roles).map(function(r) {return determineProvisionableStudies(r)}).flatten()),
      enableRoleControl: enableRoleControl, enableSitesControl: enableSitesControl,
      utils: {mapRoleKeys: mapRoleKeys, mapRoleNames: mapRoleNames, escapeIdSpaces: escapeIdSpaces}
    }))

    registerMultipleGroupControl('#role-editor-pane', roles);
    registerMultipleScopeControls('#role-editor-pane', roles, 'site', 'sites')
    registerMultipleScopeControls('#role-editor-pane', roles, 'study', 'studies')

  }

  function uniqueStudies(studies) {
    var unique = [];
    _(studies).each(function(s) {
      if (!_(unique).pluck('identifier').include(s.identifier)) {
        unique.push(s)
      }
    });
    return unique;
  }

  function isControlEnabled(controlKey, role) {
      var enableRoleControl = true;
      var enableSitesControl = true;
      var provisionableStudies = new Array();
      determineProvisionableStudies(role).each(function(study) {
          provisionableStudies.push(study.identifier)
      });
      var mem = user.memberships[role.key]
      if (mem != null) {
        // Disable role membership control when the user we are provisioning is a
        // member of sites and/or studies that are not in the list of provisionable studies.
        if (mem['sites'] != null && mem['studies'] != null) {
          if (!(isSubsetOfProvision(provisionableSites, mem['sites'])
              && isSubsetOfProvision(provisionableStudies, mem['studies']))) {
            enableRoleControl = false;
          }
        } else if (mem['studies'] != null) {
          if (!isSubsetOfProvision(provisionableStudies, mem['studies'])) {
            enableRoleControl = false;
          }
        } else if (mem['sites'] != null) {
          if (!isSubsetOfProvision(provisionableSites, mem['sites'])) {
            enableRoleControl = false;
          }
        }

        // Disable sites membership control when the user we are provisioning is a
        // member of all-sites and the all-sites membership is not in the list of provisionable sites.
        if (!_.isEmpty(provisionableSites) && !_.isEmpty(mem['sites'])) {
          if (!_.include(provisionableSites,'__ALL__') && _.include(mem['sites'],'__ALL__')) {
            enableSitesControl = false;
          }
        }
      }

    switch(controlKey) {
      case 'role-control': return enableRoleControl;
      case 'sites-control': return enableSitesControl;
      default: return null;
    }
  }

  function isSubsetOfProvision(provisionableList, membershipList) {
    if(!_.isEmpty(provisionableList) && !_.isEmpty(membershipList)) {
      return _.all(membershipList, function(ele){
        return _.include(provisionableList,ele)
      })
    }
  }

  function updateGroupMembership(isCheckedFn, evt) {
    if (!isCheckedFn) {return;}

    var roleKeys = evt.target.value ? evt.target.value.split(',') : [];

    _(roleKeys).each(function(k) {
      if (isCheckedFn(evt)) {
        user.add(k);
      } else {
        user.remove(k);
      }
    });
  }

  function registerMultipleGroupControl(pane, roles) {
    var state = determineTristateCheckboxState(roles);

    var input = $(pane).find('#multiple-group-membership:first');

    $(input).tristate({initialState: state});

    updateIntermediateStateLabel(state, pane, '#partial-multiple-group-membership-info', roles);

    $(input).bind('tristate-state-change',
        _(updateGroupMembership).bind(this, function(evt) {return $(evt.target).tristate('state') == 'checked'}));
    $(input).bind('tristate-state-change', _(function (pane, roles, evt) {
      var state = $(evt.target).attr('state');
      updateIntermediateStateLabel(state, pane, '#partial-multiple-group-membership-info', roles);
    }).bind(this, pane, roles));
  }

  function updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue) {
    var label = $(pane).find(label + ':first');

    if (state == 'intermediate') {
      var roleKeys = mapRoleKeys(roles);
      var scope = buildScopeObject(scopeType, scopeValue);
      var matchingRoleKeys = user.matchingMemberships(roleKeys, scope);
      var matchingRoles = _(PROVISIONABLE_ROLES).select(function (role) {return _(matchingRoleKeys).include(role.key)});
      var nonMatchingRoles = _(roles).reject(function (role) {return _(matchingRoleKeys).include(role.key)});
      $(label).html('(Applies to ' +  mapRoleNames(matchingRoles).join(', ') + ', but not ' + mapRoleNames(nonMatchingRoles).join(', ') + ')').show();
    } else {
      $(label).hide().empty();
    }
  }

  function determineTristateCheckboxState(roles, scopeType, scopeValue) {
    var roleKeys = mapRoleKeys(roles);
    var scope = buildScopeObject(scopeType, scopeValue);

    var status = user.membershipsStatus(roleKeys, scope);
    console.log("Tri-state checkbox status", status, scopeType, scopeValue);
    switch(status) {
      case 'FULL':    return 'checked';
      case 'PARTIAL': return 'intermediate';
      case 'NONE':    return 'unchecked';
      default:
        console.log("Memberships status unknown: ", status);
        return 'unchecked';
    }
  }

  function registerScopeControls(pane, role, scopeType, scopeTypePlural) {
    $(pane).find('input.scope-' + scopeType).each(function (i, input) {
      var qMembership = {}; qMembership[scopeType] = $(input).attr(scopeType + '-identifier');
      $(input).attr(
          'checked',
          user.hasMembership(role.key, qMembership));
    }).click(_(updateMembershipScope).bind(this, role.key, scopeType, scopeTypePlural));
  }

  function registerMultipleScopeControls(pane, roles, scopeType, scopeTypePlural) {
    $(pane).find('input.scope-' + scopeType).each(function (i, input) {
      var scopeValue = $(input).attr(scopeType + '-identifier');
      var state = determineTristateCheckboxState(roles, scopeType, scopeValue);
      var label = '#partial-scope-' + scopeType + '-' + escapeIdSpaces(scopeValue) + '-info';
      $(input).tristate({initialState: state});
      updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue);
    }).bind('tristate-state-change', _(function(roles, scopeType, scopeTypePlural, evt) {
      updateMultipleMembershipScope(_(roles).map(function(r){return r.key}), scopeType, scopeTypePlural, evt);
      var scopeValue = $(evt.target).attr(scopeType + '-identifier');
      var state = determineTristateCheckboxState(roles, scopeType, scopeValue);
      var label = '#partial-scope-' + scopeType + '-' + escapeIdSpaces(scopeValue) + '-info';
      updateIntermediateStateLabel(state, pane, label, roles, scopeType, scopeValue);
    }).bind(this, roles, scopeType, scopeTypePlural));
  }

  function buildScopeObject(scopeType, scopeValue) {
    var scope = null;
    if (scopeType) {
      scope = {}; scope[scopeType] = scopeValue;
    }
    return scope;
  }

  function mapRoleKeys(roles) {
    return roles.map(function(r){return r.key});
  }

  function mapRoleNames(roles) {
    return _(roles).map(function(r){return r.name});
  }

  function escapeIdSpaces(id) {
    return id.replace(' ', '--space--');
  }

  function updateMembershipScope(roleKey, scopeType, scopeListName, evt) {
    console.log("Updating user obj", roleKey, scopeType, scopeListName, evt);
    var scope = {}; scope[scopeListName] = [ evt.target.getAttribute(scopeType + '-identifier') ];
    if ($(evt.target).attr('checked')) {
      user.add(roleKey, scope);
    } else {
      user.remove(roleKey, scope);
    }
  }

  function updateMultipleMembershipScope(roleKeys, scopeType, scopeListName, evt) {
    var scope = {}; scope[scopeListName] = [ evt.target.getAttribute(scopeType + '-identifier') ];
    var state = $(evt.target).attr('state')

    console.log("Updating user obj", roleKeys, scopeType, scopeListName, scope, state, evt);

    _(roleKeys).each(function(roleKey) {
      switch(state) {
      case 'checked':
        user.add(roleKey, scope); break;
      case 'unchecked':
        user.remove(roleKey, scope); break;
      default:
        console.log("State does not exist", state);
      }
    });
  }

  function syncRoleTabOnChange(evt, data) {
    if (user.hasMembership(data.role)) {
      $('a#role-' + data.role).addClass('member');
    } else {
      $('a#role-' + data.role).removeClass('member');
    }
  }

  function syncRoleEditorOnChange(evt, data) {
    var role = data.role;
    var editorRole = $('#role-editor').attr('role');
    if (role !== editorRole && editorRole.indexOf(',') !== -1) { return; }
    var input;
    if (data.scopeType) {
      input = $('#role-editor input#scope-' + data.scopeType + '-' + data.scopeIdentifier);
      setTimeout(syncAllVsOne, 0);
    } else {
      input = $('#role-editor input#group-' + data.role);
      if ($(input).length <= 0) {
        input = $('#role-editor input#multiple-group-membership');
      }
    }
    input.attr('checked', data.kind === 'add');
    if (input.attr('state')) {
      input.attr('state', data.kind === 'add' ? 'checked' : 'unchecked');
      $(input).parents('.row').find('.partial-membership').empty();
    }
  }

  function syncAllVsOne() {
    _(['scope-study', 'scope-site']).each(function (scopeClass) {
      var isAll = $('#role-editor input.all.' + scopeClass + ":checked").length > 0;
      if (isAll) {
        var oneSelector = '#role-editor input.one.' + scopeClass;

        // TODO: Find way to get tri-state checkboxes without using the state attribute.
        var regular = $(oneSelector).not('[state]').
          filter(':checked').click().end()

        var tristate = $(oneSelector + '[state]').
          tristate('state', 'unchecked')

        regular.add(tristate).
          attr("disabled", true).
          closest('div.row').addClass('disabled');
      } else {
        $('#role-editor input.one.' + scopeClass).
          attr("disabled", false).
          closest('div.row').removeClass('disabled');
      }
    });
  }

  function syncUsername() {
    user.username = $('input#username').val();
  }

  function toggleRoleView(evt, action) {
    if(!action) {
      action = $('#show-all-toggle:contains(Show)').length > 0 ? 'hide' : 'show';
    }

    if (action === 'show') {
      $('#show-all-toggle').text('Show Suite Roles');
      $("div.role-tab[role-type='suite']").hide();
    } else {
      $('#show-all-toggle').text('Hide Suite Roles')
      $("div.role-tab[role-type='suite']").show();
    }
    return false;
  }

  return {
    init: function (u) {
      user = u;
      _(user.memberships).each(function (scopes, roleKey) {
        $('#role-' + roleKey).addClass('member');
      }, this);
      var firstRole;
      if ($("div.role-tab[role-type='psc'] a.role.member").length > 0) {
        firstRole = $('a.role.member')[0].id.substring("role-".length);
      } else {
        firstRole = 'study_subject_calendar_manager';
      }
      selectRole(firstRole);
      $('a.role').click(selectRoleTab);
      $(user).bind('membership-change', syncRoleTabOnChange).
        bind('membership-change', syncRoleEditorOnChange);
      $('input#username').keyup(syncUsername);
      syncAllVsOne();
      toggleRoleView(null, 'show');
      $('#show-all-toggle').click(toggleRoleView);
    },

    serializeRoleChanges: function () {
      if (user.username) {
        var changes = {}; changes[user.username] = user.changes;
        return YAHOO.lang.JSON.stringify(changes);
      } else {
        return "{}";
      }
    }
  };
})(jQuery);
<|endoftext|>


<commit_before>var request = require('request');
var cheerio = require('cheerio');
var h = require('../../lib/helpers.js');

exports.setup = function (server) {
	server.get({path: '/cinema', version: '1.0.0'}, getMovies);
};

var getMovies = function (req, res, next) {
	res.charSet = 'utf8';

	var url = 'http://kvikmyndir.is/bio/syningatimar/';

	request(url, function (error, response, body) {
		if (error) return callback(err);

		var $, obj = {
			results: []
		};

		try {
			$ = cheerio.load( body );
		} catch (e) {
			exports.logError( e );
		}

		var movies = $('.Kvikmyndir_TimeTable #divbox').find('.utanumMynd_new');

		movies.each(function() {
			var movie = $(this);
			var st = [];
			var showtimes = movie.find('[id^="myndbio"]');

			showtimes.each(function() {
				var showtime = {
					theater: $(this).find('#bio a').text().trim(),
					schedule: []
				};

				$(this).find('.syningartimi_item').each(function() {
					showtime.schedule.push( $(this).text().trim() );
				});

				st.push( showtime );
			});

			obj.results.push({
				title: movie.find('#mynd_titill a').html().trim(),
				released: movie.find('.mynd_titill_artal').text().match(/\d+/g)[0].trim(),
				restricted: movie.find('.mynd_aldurstakmark img').attr('alt').trim(),
				imdb: movie.find('.imdbEinkunn').text().trim(),
				image: 'http://kvikmyndir.is' + movie.find('img.poster').attr('src').trim(),
				showtimes: st
			});
		});

		res.json(200, obj);
		return next();
	});
};<commit_msg>Commented<commit_after>var request = require('request');
var cheerio = require('cheerio');
var h = require('../../lib/helpers.js');

exports.setup = function (server) {
	server.get({path: '/cinema', version: '1.0.0'}, getMovies);
};

/**
 * Fetches movies for show today in Icelandic cinemas.
 * response - JSON: Movie data within an 'results' array.
 */
var getMovies = function (req, res, next) {
	res.charSet = 'utf8';

	var url = 'http://kvikmyndir.is/bio/syningatimar/';

	request(url, function (error, response, body) {
		if (error) return callback(err);

		var $, obj = {
			results: []
		};

		try {
			$ = cheerio.load( body );
		} catch (e) {
			exports.logError( e );
		}

		var movies = $('.Kvikmyndir_TimeTable #divbox').find('.utanumMynd_new');

		movies.each(function() {
			var movie = $(this);
			var st = [];
			var showtimes = movie.find('[id^="myndbio"]');

			showtimes.each(function() {
				var showtime = {
					theater: $(this).find('#bio a').text().trim(),
					schedule: []
				};

				$(this).find('.syningartimi_item').each(function() {
					showtime.schedule.push( $(this).text().trim() );
				});

				st.push( showtime );
			});

			obj.results.push({
				title: movie.find('#mynd_titill a').html().trim(),
				released: movie.find('.mynd_titill_artal').text().match(/\d+/g)[0].trim(),
				restricted: movie.find('.mynd_aldurstakmark img').attr('alt').trim(),
				imdb: movie.find('.imdbEinkunn').text().trim(),
				image: 'http://kvikmyndir.is' + movie.find('img.poster').attr('src').trim(),
				showtimes: st
			});
		});

		res.json(200, obj);
		return next();
	});
};<|endoftext|>


<commit_before>//----------------------------------------------------------------------------
//  Copyright (C) 2014  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

// toggle display of all code cells
"using strict";

var toggle_codecells_extension = (function() {

     var show=true;

     function toggle(){
         if (show){
             $('div.input').hide();
         }else{
             $('div.input').show();
         }
         show = !show;
     }

    /**
    * Add run control buttons to toolbar and initialize codecells
    *
    */
    IPython.toolbar.add_buttons_group([
                {
                    id : 'toggle_codecells',
                    label : 'Toggle codecell display',
                    icon : 'icon-list-alt',
                    callback : toggle
                }
          ]);

})(); <commit_msg>Add newline<commit_after>//----------------------------------------------------------------------------
//  Copyright (C) 2014  The IPython Development Team
//
//  Distributed under the terms of the BSD License.  The full license is in
//  the file COPYING, distributed as part of this software.
//----------------------------------------------------------------------------

// toggle display of all code cells
"using strict";

var toggle_codecells_extension = (function() {

     var show=true;

     function toggle(){
         if (show){
             $('div.input').hide();
         }else{
             $('div.input').show();
         }
         show = !show;
     }

    /**
    * Add run control buttons to toolbar and initialize codecells
    *
    */
    IPython.toolbar.add_buttons_group([
                {
                    id : 'toggle_codecells',
                    label : 'Toggle codecell display',
                    icon : 'icon-list-alt',
                    callback : toggle
                }
          ]);

})();
<|endoftext|>