<commit_before>import _ from 'messages'
import * as complexMatcher from 'complex-matcher'
import ceil from 'lodash/ceil'
import classNames from 'classnames'
import debounce from 'lodash/debounce'
import Icon from 'icon'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
import Tags from 'tags'
import Tooltip from 'tooltip'
import React, { Component } from 'react'
import { editVm, addTag, removeTag, startVm, stopVm } from 'xo'
import { Link } from 'react-router'
import { Row, Col } from 'grid'
import { Text } from 'editable'
import {
  BlockLink,
  connectStore,
  formatSize,
  invoke,
  osFamily
} from 'utils'
import {
  createFilter,
  createGetObject,
  createPager,
  createSelector,
  hosts,
  pools,
  tags,
  vms
} from 'selectors'

import {
  Button,
  DropdownButton,
  MenuItem,
  Pagination
} from 'react-bootstrap-4/lib'

import styles from './index.css'

@connectStore({
  container: createGetObject((state, props) => props.vm.$container)
})
class VmItem extends Component {
  componentWillMount () {
    this.setState({ collapsed: true })
  }

  _addTag = tag => addTag(this.props.vm.id, tag)
  _removeTag = tag => removeTag(this.props.vm.id, tag)
  _setNameDescription = nameDescription => editVm(this.props.vm, { name_description: nameDescription })
  _setNameLabel = nameLabel => editVm(this.props.vm, { name_label: nameLabel })
  _start = () => startVm(this.props.vm)
  _stop = () => stopVm(this.props.vm)
  _toggleCollapse = () => this.setState({ collapsed: !this.state.collapsed })

  render () {
    const { vm, container, expandAll } = this.props
    return

      <BlockLink to={`/vms/${vm.id}`}>
        <Row>
          <Col mediumSize={9} largeSize={5} className={styles.itemContent}>
            <input type='checkbox'></input>

            <Tooltip
              content={isEmpty(vm.current_operations)
                ? _(`powerState${vm.power_state}`)
                :
{_(`powerState${vm.power_state}`)}{' ('}{map(vm.current_operations)[0]}{')'}

              }
            >
              {isEmpty(vm.current_operations)
                ? <Icon icon={`${vm.power_state.toLowerCase()}`} />
                : <Icon icon='busy' />
              }
            </Tooltip>

            <Text onChange={this._setNameLabel}>{vm.name_label}</Text>
          </Col>
          <Col mediumSize={4} className={classNames(styles.itemContent, 'hidden-md-down')}>

              {vm.power_state === 'Running'
                ?
                  <Tooltip content={_('stopVmLabel')}>

                      <Icon icon='vm-stop' size='1' />

                  </Tooltip>

                :
                  <Tooltip content={_('startVmLabel')}>

                      <Icon icon='vm-start' size='1' />

                  </Tooltip>

              }

            {vm.os_version && vm.os_version.distro ? <Icon icon={osFamily(vm.os_version.distro)} /> : }

            <Text onChange={this._setNameDescription}>
              {vm.name_description}</Text>
          </Col>
          <Col mediumSize={2} className={styles.itemContent}>
            {container.type === 'host'
              ? <Link to={`/hosts/${container.id}`}>{container.name_label}</Link>
              : <Link to={`/pools/${container.id}`}>{container.name_label}</Link>
            }
          </Col>
          <Col mediumSize={1} className={styles.itemExpandRow}>

              <Icon icon='nav' fixedWidth />

          </Col>
        </Row>
      </BlockLink>
      {!this.state.collapsed || expandAll
        ? <Row>
          <Col mediumSize={4} className={styles.itemExpanded}>

              {vm.CPUs.number}x <Icon icon='cpu' />
              {formatSize(vm.memory.size)} <Icon icon='memory' />
              {isEmpty(vm.snapshots)
                ? null
                : {vm.snapshots.length}x <Icon icon='vm-snapshot' />
              }
              {vm.docker ? <Icon icon='vm-docker' /> : null}

          </Col>
          <Col largeSize={4} className={styles.itemExpanded}>
            {map(vm.addresses, address => {address})}
          </Col>
          <Col mediumSize={4}>

              <Tags labels={vm.tags} onDelete={this._removeTag} onAdd={this._addTag} />

          </Col>
        </Row>
        : null
      }

  }
}

const VMS_PER_PAGE = 10

@connectStore({
  pools,
  hosts,
  vms,
  tags
})
export default class Home extends Component {
  static contextTypes = {
    router: React.PropTypes.object
  }

  constructor (props) {
    super(props)

    this.state = {
      expandAll: false,
      displayActions: false,
      activePage: 1
    }

    this.getFilteredVms = createFilter(
      () => this.props.vms,
      createSelector(
        () => this.filter,
        complexMatcher.create
      ),
      true
    )

    this.getCurrentPageVms = createPager(
      this.getFilteredVms,
      () => this.state.activePage,
      VMS_PER_PAGE
    )
  }

  get filter () {
    return this.props.location.query.s
  }

  set filter (value) {
    this.context.router.push({
      ...this.props.location,
      query: { s: value }
    })
    this.setPage(1)
  }

  componentWillMount () {
    if (this.filter == null) {
      this.filter = 'power_state:running '
    }
  }

  _onFilterChange = invoke(
    debounce(filter => { this.filter = filter }, 500),
    setFilter => event => setFilter(event.target.value)
  )

  setFilter (filter) {
    this.refs.filter.value = filter
    this.refs.filter.focus()
    this.filter = filter
  }

  _checkAll = () => this.setState({
    displayActions: !this.state.displayActions
  })
  _expandAll = () => this.setState({ expandAll: !this.state.expandAll })
  _filterBusy = () => this.setFilter('current_operations:"" ')
  _filterHalted = () => this.setFilter('!power_state:running ')
  _filterHvm = () => this.setFilter('virtualizationMode:hvm ')
  _filterNone = () => this.setFilter('')
  _filterRunning = () => this.setFilter('power_state:running ')
  _filterTags = () => this.setFilter('tags:')

  setPage = (activePage) => this.setState({ activePage })
  handleSelect = (_, selectedEvent) => this.setPage(selectedEvent.eventKey)

  render () {
    const { vms } = this.props
    if (isEmpty(vms)) {
      return
There are no VMs

    }

    const { pools, hosts, tags } = this.props
    const { activePage } = this.state
    const filteredVms = this.getFilteredVms()
    const currentPageVms = this.getCurrentPageVms()
    return

      <Row className={styles.itemRowHeader}>
        <Col mediumSize={6}>

              <DropdownButton id='filter' bsStyle='secondary' title={_('homeFilters')}>
                <MenuItem onClick={this._filterRunning}>
                  {_('homeFilterRunningVms')}
                </MenuItem>
                <MenuItem onClick={this._filterHalted}>
                  {_('homeFilterNonRunningVms')}
                </MenuItem>
                <MenuItem onClick={this._filterBusy}>
                  {_('homeFilterPendingVms')}
                </MenuItem>
                <MenuItem onClick={this._filterHvm}>
                  {_('homeFilterHvmGuests')}
                </MenuItem>
                <MenuItem onClick={this._filterTags}>
                  {_('homeFilterTags')}
                </MenuItem>
              </DropdownButton>

            <input
              autoFocus
              className='form-control'
              defaultValue={this.filter}
              onChange={this._onFilterChange}
              ref='filter'
              type='text'
            />

              <button
                className='btn btn-secondary'
                onClick={this._filterNone}>
                <Icon icon='clear-search' />
              </button>

        </Col>
        <Col mediumSize={6} className='text-xs-right'>
          <Link
            className='btn btn-success'
            to='/new/vm'>
            <Icon icon='vm-new' /> {_('homeNewVm')}
          </Link>
        </Col>
      </Row>

        <Row className={styles.itemContainerHeader}>
          <Col mediumSize={2}>
            <button className='btn btn-link'>
              <input type='checkbox' onChange={this._checkAll}></input>
              {this.state.displayActions
                ?    xx<Icon icon='vm' /> selected
                :    {filteredVms.length}x <Icon icon='vm' /> {`(on ${vms.length})`}
              }
            </button>
          </Col>
          <Col mediumSize={10} className='text-xs-right'>
          {this.state.displayActions
            ?

              <Button className='btn btn-secondary'><Icon icon='vm-stop' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-start' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-reboot' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-migrate' /></Button>
              <Button className='btn btn-secondary dropdown-toggle'>More</Button>

            :

              {pools.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='pool' /> {_('homeAllPools')} ({pools.length})
                </button>
                : null
              }

              {hosts.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='host' /> {_('homeAllHosts')} ({hosts.length})
                </button>
                : null
              }

              {tags.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='tags' /> {_('homeAllTags')} ({tags.length})
                </button>
                : null
              }

              <button className='btn btn-link dropdown-toggle'>
                <Icon icon='filters' /> {_('homeSort')}
              </button>

              <button className='btn btn-secondary'
                onClick={this._expandAll}>
                <Icon icon='nav' />
              </button>

          }
          </Col>
        </Row>
        {map(currentPageVms, vm =>
          <VmItem vm={vm} key={vm.id} expandAll={this.state.expandAll} />
        )}

      {filteredVms.length > VMS_PER_PAGE && <Row>

            <Pagination
              first
              last
              prev
              next
              ellipsis
              boundaryLinks
              maxButtons={5}
              items={ceil(filteredVms.length / VMS_PER_PAGE)}
              activePage={activePage}
              onSelect={this.handleSelect} />

      </Row>}

  }
}
<commit_msg>fix(home): number of VMs per page to 20<commit_after>import _ from 'messages'
import * as complexMatcher from 'complex-matcher'
import ceil from 'lodash/ceil'
import classNames from 'classnames'
import debounce from 'lodash/debounce'
import Icon from 'icon'
import isEmpty from 'lodash/isEmpty'
import map from 'lodash/map'
import Tags from 'tags'
import Tooltip from 'tooltip'
import React, { Component } from 'react'
import { editVm, addTag, removeTag, startVm, stopVm } from 'xo'
import { Link } from 'react-router'
import { Row, Col } from 'grid'
import { Text } from 'editable'
import {
  BlockLink,
  connectStore,
  formatSize,
  invoke,
  osFamily
} from 'utils'
import {
  createFilter,
  createGetObject,
  createPager,
  createSelector,
  hosts,
  pools,
  tags,
  vms
} from 'selectors'

import {
  Button,
  DropdownButton,
  MenuItem,
  Pagination
} from 'react-bootstrap-4/lib'

import styles from './index.css'

@connectStore({
  container: createGetObject((state, props) => props.vm.$container)
})
class VmItem extends Component {
  componentWillMount () {
    this.setState({ collapsed: true })
  }

  _addTag = tag => addTag(this.props.vm.id, tag)
  _removeTag = tag => removeTag(this.props.vm.id, tag)
  _setNameDescription = nameDescription => editVm(this.props.vm, { name_description: nameDescription })
  _setNameLabel = nameLabel => editVm(this.props.vm, { name_label: nameLabel })
  _start = () => startVm(this.props.vm)
  _stop = () => stopVm(this.props.vm)
  _toggleCollapse = () => this.setState({ collapsed: !this.state.collapsed })

  render () {
    const { vm, container, expandAll } = this.props
    return

      <BlockLink to={`/vms/${vm.id}`}>
        <Row>
          <Col mediumSize={9} largeSize={5} className={styles.itemContent}>
            <input type='checkbox'></input>

            <Tooltip
              content={isEmpty(vm.current_operations)
                ? _(`powerState${vm.power_state}`)
                :
{_(`powerState${vm.power_state}`)}{' ('}{map(vm.current_operations)[0]}{')'}

              }
            >
              {isEmpty(vm.current_operations)
                ? <Icon icon={`${vm.power_state.toLowerCase()}`} />
                : <Icon icon='busy' />
              }
            </Tooltip>

            <Text onChange={this._setNameLabel}>{vm.name_label}</Text>
          </Col>
          <Col mediumSize={4} className={classNames(styles.itemContent, 'hidden-md-down')}>

              {vm.power_state === 'Running'
                ?
                  <Tooltip content={_('stopVmLabel')}>

                      <Icon icon='vm-stop' size='1' />

                  </Tooltip>

                :
                  <Tooltip content={_('startVmLabel')}>

                      <Icon icon='vm-start' size='1' />

                  </Tooltip>

              }

            {vm.os_version && vm.os_version.distro ? <Icon icon={osFamily(vm.os_version.distro)} /> : }

            <Text onChange={this._setNameDescription}>
              {vm.name_description}</Text>
          </Col>
          <Col mediumSize={2} className={styles.itemContent}>
            {container.type === 'host'
              ? <Link to={`/hosts/${container.id}`}>{container.name_label}</Link>
              : <Link to={`/pools/${container.id}`}>{container.name_label}</Link>
            }
          </Col>
          <Col mediumSize={1} className={styles.itemExpandRow}>

              <Icon icon='nav' fixedWidth />

          </Col>
        </Row>
      </BlockLink>
      {!this.state.collapsed || expandAll
        ? <Row>
          <Col mediumSize={4} className={styles.itemExpanded}>

              {vm.CPUs.number}x <Icon icon='cpu' />
              {formatSize(vm.memory.size)} <Icon icon='memory' />
              {isEmpty(vm.snapshots)
                ? null
                : {vm.snapshots.length}x <Icon icon='vm-snapshot' />
              }
              {vm.docker ? <Icon icon='vm-docker' /> : null}

          </Col>
          <Col largeSize={4} className={styles.itemExpanded}>
            {map(vm.addresses, address => {address})}
          </Col>
          <Col mediumSize={4}>

              <Tags labels={vm.tags} onDelete={this._removeTag} onAdd={this._addTag} />

          </Col>
        </Row>
        : null
      }

  }
}

const VMS_PER_PAGE = 20

@connectStore({
  pools,
  hosts,
  vms,
  tags
})
export default class Home extends Component {
  static contextTypes = {
    router: React.PropTypes.object
  }

  constructor (props) {
    super(props)

    this.state = {
      expandAll: false,
      displayActions: false,
      activePage: 1
    }

    this.getFilteredVms = createFilter(
      () => this.props.vms,
      createSelector(
        () => this.filter,
        complexMatcher.create
      ),
      true
    )

    this.getCurrentPageVms = createPager(
      this.getFilteredVms,
      () => this.state.activePage,
      VMS_PER_PAGE
    )
  }

  get filter () {
    return this.props.location.query.s
  }

  set filter (value) {
    this.context.router.push({
      ...this.props.location,
      query: { s: value }
    })
    this.setPage(1)
  }

  componentWillMount () {
    if (this.filter == null) {
      this.filter = 'power_state:running '
    }
  }

  _onFilterChange = invoke(
    debounce(filter => { this.filter = filter }, 500),
    setFilter => event => setFilter(event.target.value)
  )

  setFilter (filter) {
    this.refs.filter.value = filter
    this.refs.filter.focus()
    this.filter = filter
  }

  _checkAll = () => this.setState({
    displayActions: !this.state.displayActions
  })
  _expandAll = () => this.setState({ expandAll: !this.state.expandAll })
  _filterBusy = () => this.setFilter('current_operations:"" ')
  _filterHalted = () => this.setFilter('!power_state:running ')
  _filterHvm = () => this.setFilter('virtualizationMode:hvm ')
  _filterNone = () => this.setFilter('')
  _filterRunning = () => this.setFilter('power_state:running ')
  _filterTags = () => this.setFilter('tags:')

  setPage = (activePage) => this.setState({ activePage })
  handleSelect = (_, selectedEvent) => this.setPage(selectedEvent.eventKey)

  render () {
    const { vms } = this.props
    if (isEmpty(vms)) {
      return
There are no VMs

    }

    const { pools, hosts, tags } = this.props
    const { activePage } = this.state
    const filteredVms = this.getFilteredVms()
    const currentPageVms = this.getCurrentPageVms()
    return

      <Row className={styles.itemRowHeader}>
        <Col mediumSize={6}>

              <DropdownButton id='filter' bsStyle='secondary' title={_('homeFilters')}>
                <MenuItem onClick={this._filterRunning}>
                  {_('homeFilterRunningVms')}
                </MenuItem>
                <MenuItem onClick={this._filterHalted}>
                  {_('homeFilterNonRunningVms')}
                </MenuItem>
                <MenuItem onClick={this._filterBusy}>
                  {_('homeFilterPendingVms')}
                </MenuItem>
                <MenuItem onClick={this._filterHvm}>
                  {_('homeFilterHvmGuests')}
                </MenuItem>
                <MenuItem onClick={this._filterTags}>
                  {_('homeFilterTags')}
                </MenuItem>
              </DropdownButton>

            <input
              autoFocus
              className='form-control'
              defaultValue={this.filter}
              onChange={this._onFilterChange}
              ref='filter'
              type='text'
            />

              <button
                className='btn btn-secondary'
                onClick={this._filterNone}>
                <Icon icon='clear-search' />
              </button>

        </Col>
        <Col mediumSize={6} className='text-xs-right'>
          <Link
            className='btn btn-success'
            to='/new/vm'>
            <Icon icon='vm-new' /> {_('homeNewVm')}
          </Link>
        </Col>
      </Row>

        <Row className={styles.itemContainerHeader}>
          <Col mediumSize={2}>
            <button className='btn btn-link'>
              <input type='checkbox' onChange={this._checkAll}></input>
              {this.state.displayActions
                ?    xx<Icon icon='vm' /> selected
                :    {filteredVms.length}x <Icon icon='vm' /> {`(on ${vms.length})`}
              }
            </button>
          </Col>
          <Col mediumSize={10} className='text-xs-right'>
          {this.state.displayActions
            ?

              <Button className='btn btn-secondary'><Icon icon='vm-stop' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-start' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-reboot' /></Button>
              <Button className='btn btn-secondary'><Icon icon='vm-migrate' /></Button>
              <Button className='btn btn-secondary dropdown-toggle'>More</Button>

            :

              {pools.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='pool' /> {_('homeAllPools')} ({pools.length})
                </button>
                : null
              }

              {hosts.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='host' /> {_('homeAllHosts')} ({hosts.length})
                </button>
                : null
              }

              {tags.length
                ? <button className='btn btn-link dropdown-toggle'>
                  <Icon icon='tags' /> {_('homeAllTags')} ({tags.length})
                </button>
                : null
              }

              <button className='btn btn-link dropdown-toggle'>
                <Icon icon='filters' /> {_('homeSort')}
              </button>

              <button className='btn btn-secondary'
                onClick={this._expandAll}>
                <Icon icon='nav' />
              </button>

          }
          </Col>
        </Row>
        {map(currentPageVms, vm =>
          <VmItem vm={vm} key={vm.id} expandAll={this.state.expandAll} />
        )}

      {filteredVms.length > VMS_PER_PAGE && <Row>

            <Pagination
              first
              last
              prev
              next
              ellipsis
              boundaryLinks
              maxButtons={5}
              items={ceil(filteredVms.length / VMS_PER_PAGE)}
              activePage={activePage}
              onSelect={this.handleSelect} />

      </Row>}

  }
}
<|endoftext|>


<commit_before>/* **************************************************************

   Copyright 2013 Zoovy, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

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

//    !!! ->   TODO: replace 'username' in the line below with the merchants username.     <- !!!

var seo_robots = function(_app) {
	var r = {

	vars : {
		pages : [],
		pagesLoaded : false,
		counter : 0,
		counterReset : 50
		},

////////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

	callbacks : {
//executed when extension is loaded. should include any validation that needs to occur.
		init : {
			onSuccess : function()	{
				var r = false; //return false if extension won't load for some reason (account config, dependencies, etc).

					dump(_robots);
				if(_robots._robotGreeting){
					_app.ext.seo_robots.u.welcomeRobot(_robots._robotGreeting);
					}
				else {
					_robots.hello = _app.ext.seo_robots.u.welcomeRobot;
					}
				//Replace the _robots.next default functionality with some real stuff

				_robots.next = function(page){
					if(typeof page !== 'undefined' || _app.ext.seo_robots.vars.pagesLoaded){
						if(typeof page !== 'undefined'){
							try{
								page = JSON.parse(page);
								}
							catch(e){
								dump("Could not parse page, let's assume it's to be used as a string");
								}
							}
						else if(_app.ext.seo_robots.vars.pages.length){
							page = _app.ext.seo_robots.vars.pages.splice(0,1)[0];
							}
						else{
							_robots.status = function(){return -1};
							return false;
							}

						//Let's clean up some memory, shall we?
						_app.ext.store_swc.vars.counter++;
						if(_app.ext.store_swc.vars.counter >= _app.ext.store_swc.vars.counterReset){
							_app.ext.store_swc.vars.counter -= _app.ext.store_swc.vars.counterReset;
							for(var datapointer in _app.data){
								if(datapointer.indexOf('appProductGet|') == 0 ||
										datapointer.indexOf('relatedProducts|') == 0 ||
										datapointer.indexOf('appFilteredSearch') == 0){
									_app.model.destroy(datapointer);
									}
								}
							}
						var status = 100;
						if(typeof page == 'string' && page.indexOf('#!') == 0){
							_app.ext.quickstart.vars.showContentFinished = false;
							_app.ext.quickstart.vars.showContentCompleteFired = false;
							window.location.hash = page;
							}
						else if(typeof page == 'object'){
							$('#globalMessaging').intervaledEmpty();
							showContent('',page);
							}
						else{
							status = 404;
							}
						_robots.status = function(){
							if(status == 100 && _app.ext.quickstart.vars.showContentFinished && _app.ext.quickstart.vars.showContentCompleteFired){
								status = 200;
								}
							return status;
							}
						}
					else {
						_robots.status = function(){
							if(_app.ext.seo_robots.vars.pagesLoaded){
								return 204;
								}
							else {
								return 100;
								}
							}
						}
					};
				_robots.ready = function(){return _app.ext.seo_robots.vars.pagesLoaded};
				_robots.pop = function(){
					var page = _app.ext.seo_robots.vars.pages.splice(0,1)[0];
					if(typeof page == 'object'){
						page = JSON.stringify(page);
						}
					if(typeof page == 'undefined'){ page = '#';}
					return page;
					};

				//if there is any functionality required for this extension to load, put it here. such as a check for async google, the FB object, etc. return false if dependencies are not present. don't check for other extensions.
				r = true;

				return r;
				},
			onError : function()	{
//errors will get reported for this callback as part of the extensions loading.  This is here for extra error handling purposes.
//you may or may not need it.
				_app.u.dump('BEGIN admin_orders.callbacks.init.onError');
				}
			}
		}, //callbacks

////////////////////////////////////   ACTION    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//actions are functions triggered by a user interaction, such as a click/tap.
//these are going the way of the do do, in favor of app events. new extensions should have few (if any) actions.
		a : {

			}, //Actions

////////////////////////////////////   RENDERFORMATS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//renderFormats are what is used to actually output data.
//on a data-bind, format: is equal to a renderformat. extension: tells the rendering engine where to look for the renderFormat.
//that way, two render formats named the same (but in different extensions) don't overwrite each other.
		renderFormats : {

			}, //renderFormats
////////////////////////////////////   UTIL [u]   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//utilities are typically functions that are exected by an event or action.
//any functions that are recycled should be here.
		u : {
			welcomeRobot : function(botStr){
				dump("WELCOMING ROBOT");
				_app.ext.quickstart.vars.cachedPageCount = 0;
				var request = {
					"_cmd" : "appSEOFetch"
					};
				request._tag = {
					'datapointer' : 'appSEOFetch',
					'callback' : function(rd){
						for(var i in _app.data[rd.datapointer]['@OBJECTS']){
							var p = _app.data[rd.datapointer]['@OBJECTS'][i];

							switch(p.type){
								case "pid":
									_app.ext.seo_robots.vars.pages.push({
										pageType : "product",
										pid : p.id
										});
									break;
								case "navcat" :
									_app.ext.seo_robots.vars.pages.push({
										pageType : "category",
										navcat : p.id
										});
									break;
								case "list" :
									dump("LIST "+p.id+" SKIPPED IN PAGE BUILDING");
									break;
								default :
									dump("Unrecognized pageInfo type: "+p.type+" full obj follows:");
									dump(p);
									break;
								}
							}
						_app.ext.seo_robots.vars.pagesLoaded = true;
						}
					};
				_app.model.addDispatchToQ(request, 'immutable');
				_app.model.dispatchThis('immutable');

				return "1.0";
				}
			}, //u [utilities]

//app-events are added to an element through data-app-event="extensionName|functionName"
//right now, these are not fully supported, but they will be going forward.
//they're used heavily in the admin.html file.
//while no naming convention is stricly forced,
//when adding an event, be sure to do off('click.appEventName') and then on('click.appEventName') to ensure the same event is not double-added if app events were to get run again over the same template.
		e : {
			} //e [app Events]
		} //r object.
	return r;
	}<commit_msg>_robots.pop support amt<commit_after>/* **************************************************************

   Copyright 2013 Zoovy, Inc.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

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

//    !!! ->   TODO: replace 'username' in the line below with the merchants username.     <- !!!

var seo_robots = function(_app) {
	var r = {

	vars : {
		pages : [],
		pagesLoaded : false,
		counter : 0,
		counterReset : 50
		},

////////////////////////////////////   CALLBACKS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

	callbacks : {
//executed when extension is loaded. should include any validation that needs to occur.
		init : {
			onSuccess : function()	{
				var r = false; //return false if extension won't load for some reason (account config, dependencies, etc).

					dump(_robots);
				if(_robots._robotGreeting){
					_app.ext.seo_robots.u.welcomeRobot(_robots._robotGreeting);
					}
				else {
					_robots.hello = _app.ext.seo_robots.u.welcomeRobot;
					}
				//Replace the _robots.next default functionality with some real stuff

				_robots.next = function(page){
					if(typeof page !== 'undefined' || _app.ext.seo_robots.vars.pagesLoaded){
						if(typeof page !== 'undefined'){
							try{
								page = JSON.parse(page);
								}
							catch(e){
								dump("Could not parse page, let's assume it's to be used as a string");
								}
							}
						else if(_app.ext.seo_robots.vars.pages.length){
							page = _app.ext.seo_robots.vars.pages.splice(0,1)[0];
							}
						else{
							_robots.status = function(){return -1};
							return false;
							}

						//Let's clean up some memory, shall we?
						_app.ext.store_swc.vars.counter++;
						if(_app.ext.store_swc.vars.counter >= _app.ext.store_swc.vars.counterReset){
							_app.ext.store_swc.vars.counter -= _app.ext.store_swc.vars.counterReset;
							for(var datapointer in _app.data){
								if(datapointer.indexOf('appProductGet|') == 0 ||
										datapointer.indexOf('relatedProducts|') == 0 ||
										datapointer.indexOf('appFilteredSearch') == 0){
									_app.model.destroy(datapointer);
									}
								}
							}
						var status = 100;
						if(typeof page == 'string' && page.indexOf('#!') == 0){
							_app.ext.quickstart.vars.showContentFinished = false;
							_app.ext.quickstart.vars.showContentCompleteFired = false;
							window.location.hash = page;
							}
						else if(typeof page == 'object'){
							$('#globalMessaging').intervaledEmpty();
							showContent('',page);
							}
						else{
							status = 404;
							}
						_robots.status = function(){
							if(status == 100 && _app.ext.quickstart.vars.showContentFinished && _app.ext.quickstart.vars.showContentCompleteFired){
								status = 200;
								}
							return status;
							}
						}
					else {
						_robots.status = function(){
							if(_app.ext.seo_robots.vars.pagesLoaded){
								return 204;
								}
							else {
								return 100;
								}
							}
						}
					};
				_robots.ready = function(){return _app.ext.seo_robots.vars.pagesLoaded};
				_robots.pop = function(amt){
					amt = amt || 1;
					if(amt < 0){amt = 1;}
					var pageArr = _app.ext.seo_robots.vars.pages.splice(0,amt);
					var r = '';
					for(var i = 0; i < pageArr.length; i++){
						var page = pageArr[i];
						if(typeof page == 'object'){
							page = JSON.stringify(page);
							}
						r += page + '\n';
						}
					return r;
					};

				//if there is any functionality required for this extension to load, put it here. such as a check for async google, the FB object, etc. return false if dependencies are not present. don't check for other extensions.
				r = true;

				return r;
				},
			onError : function()	{
//errors will get reported for this callback as part of the extensions loading.  This is here for extra error handling purposes.
//you may or may not need it.
				_app.u.dump('BEGIN admin_orders.callbacks.init.onError');
				}
			}
		}, //callbacks

////////////////////////////////////   ACTION    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//actions are functions triggered by a user interaction, such as a click/tap.
//these are going the way of the do do, in favor of app events. new extensions should have few (if any) actions.
		a : {

			}, //Actions

////////////////////////////////////   RENDERFORMATS    \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//renderFormats are what is used to actually output data.
//on a data-bind, format: is equal to a renderformat. extension: tells the rendering engine where to look for the renderFormat.
//that way, two render formats named the same (but in different extensions) don't overwrite each other.
		renderFormats : {

			}, //renderFormats
////////////////////////////////////   UTIL [u]   \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

//utilities are typically functions that are exected by an event or action.
//any functions that are recycled should be here.
		u : {
			welcomeRobot : function(botStr){
				dump("WELCOMING ROBOT");
				_app.ext.quickstart.vars.cachedPageCount = 0;
				var request = {
					"_cmd" : "appSEOFetch"
					};
				request._tag = {
					'datapointer' : 'appSEOFetch',
					'callback' : function(rd){
						for(var i in _app.data[rd.datapointer]['@OBJECTS']){
							var p = _app.data[rd.datapointer]['@OBJECTS'][i];

							switch(p.type){
								case "pid":
									_app.ext.seo_robots.vars.pages.push({
										pageType : "product",
										pid : p.id
										});
									break;
								case "navcat" :
									_app.ext.seo_robots.vars.pages.push({
										pageType : "category",
										navcat : p.id
										});
									break;
								case "list" :
									dump("LIST "+p.id+" SKIPPED IN PAGE BUILDING");
									break;
								default :
									dump("Unrecognized pageInfo type: "+p.type+" full obj follows:");
									dump(p);
									break;
								}
							}
						_app.ext.seo_robots.vars.pagesLoaded = true;
						}
					};
				_app.model.addDispatchToQ(request, 'immutable');
				_app.model.dispatchThis('immutable');

				return "1.0";
				}
			}, //u [utilities]

//app-events are added to an element through data-app-event="extensionName|functionName"
//right now, these are not fully supported, but they will be going forward.
//they're used heavily in the admin.html file.
//while no naming convention is stricly forced,
//when adding an event, be sure to do off('click.appEventName') and then on('click.appEventName') to ensure the same event is not double-added if app events were to get run again over the same template.
		e : {
			} //e [app Events]
		} //r object.
	return r;
	}<|endoftext|>


<commit_before>(function(angular){
    "use strict";

    var courseSlug = /[^/]+$/.extract(location.pathname);
    var app = angular.module('admin', ['ngRoute', 'ngResource', 'ngSanitize', 'youtube']);

    app.config(['$httpProvider', '$sceDelegateProvider',
        function ($httpProvider, $sceDelegateProvider) {
            $httpProvider.defaults.xsrfCookieName = 'csrftoken';
            $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
            $sceDelegateProvider.resourceUrlWhitelist([
                /^https?:\/\/(www\.)?youtube\.com\/.*/,
                'data:text/html, <html style="background: white">'
            ]);
            window.sce = $sceDelegateProvider;
        }
    ]);

    app.directive('contenteditable', function(){
        return {
            "restrict": 'A',
            "require": '?ngModel',
            "link": function(scope, element, attrs, ngModel) {
                if(!ngModel) return;
                ngModel.$render = function(){ element.html(ngModel.$viewValue || ''); };
                element.on('blur keyup change', function() { scope.$apply(read); });
                function read() {
                    var html = element.html();
                    if( attrs.stripBr && html.match(/ *<br\/?> */) ){
                      html = "";
                    }
                    ngModel.$setViewValue(html);
                }
            }
        };
    });

    /**
     * Controllers
     */
    app.controller('CourseEdit',['$scope', 'CourseDataFactory', '$http', 'youtubePlayerApi',
        function($scope, CourseDataFactory, $http, youtubePlayerApi){
            $scope.course = {};
            $scope.video = {
                'name': null,
                'youtube_id': null,
                'save': function() {
                    if(this.youtube_id_temp) {
                        this.youtube_id = this.youtube_id_temp;
                        $scope.course.intro_video = this;
                        $scope.course.$save().then((function(){
                            youtubePlayerApi.player.cueVideoById(this.youtube_id);
                        }).bind(this));
                    }
                },
                'reset': function() {
                    this.youtube_id = $scope.course.intro_video.youtube_id;
                }
            };

            var fields = ['application', 'requirement', 'abstract', 'structure', 'workload'];

            var build_data_for_modals = function(field){
                return {
                    'status': '',
                    'window': field,
                    'data': angular.copy($scope.course[field]),
                    'reset': function(){
                        this.data = angular.copy($scope.course[field]);
                    },
                    'save': function(){
                        var self = this;
                        var old = angular.copy($scope.course[field]);
                        $scope.course[field] = self.data;
                        $scope.course.$save()
                            .then(function(){
                                self.status = 'saved';
                            }).catch(function(){
                                self.status = 'error';
                                $scope.course[field] = old;
                            });
                    }
                };
            };

            CourseDataFactory.then(function(course){
                $scope.course = angular.copy(course);
                $scope.modals = fields.map(build_data_for_modals);
                if($scope.course.intro_video){
                    $scope.video.name = $scope.course.intro_video.name;
                    $scope.video.youtube_id = $scope.course.intro_video.youtube_id;

                    youtubePlayerApi.events = {
                        'onReady': function(player){
                            player.target.cueVideoById($scope.video.youtube_id);
                        }
                    };
                }
                youtubePlayerApi.loadPlayer();
                // reindex $scope.modals
                fields.forEach(function(e,i){$scope.modals[e]=$scope.modals[i];});
            });
        }
    ]);

    app.controller('LessonList',['$scope', '$rootScope', 'LessonListFactory', '$http',
        function($scope, $rootScope, LessonListFactory, $http){

            $scope.select = function (lesson) {
                $rootScope.selectedLesson = lesson;
                return lesson;
            };
            $scope.countActivities = function(l) {
                if( l.units ) {
                    return l.units.reduce(function(p,s){return p + (s.activity ? 1 : 0); }, 0);
                }
                return 0;
            };
            $scope.countVideos = function(l) {
                if( l.units ) {
                    return l.units.reduce(function(p,s){return p + (s.video ? 1 : 0); }, 0);
                }
                return 0;
            };

            LessonListFactory.then(function(lessons){
                $scope.lessons = lessons;
            });
        }
    ]);

    app.controller('LessonEdit',['$scope', '$rootScope', 'LessonListFactory', '$http',
        function($scope, $rootScope, LessonListFactory, $http){
            $scope.active = 'content';

            var selectedUnitIndex = 0;
            $scope.selectUnit = function(index) {
                selectedUnitIndex = index;
            };
            $scope.deleteUnit = function(index) {
                $rootScope.selectedLesson.units.splice(index, 1);
            };
            $scope.addUnit = function() {
                var pos = $rootScope.selectedLesson.units.length;
                $rootScope.selectedLesson.units.push({
                    "activity": null,
                    "id": null,
                    "position": pos,
                    "title": "",
                    "video": {
                        "id": null,
                        "name":"",
                        "youtube_id":"",
                    }
                });
            };
            $scope.selectedUnit = function() {
                if($rootScope.selectedLesson)
                    return $rootScope.selectedLesson.units[selectedUnitIndex];
            };
            $scope.activity = function() {
                if($scope.selectedUnit() && $scope.selectedUnit().activity)
                    return $scope.selectedUnit().activity;
            };
            $scope.typeIs = function(type) {
                return $scope.activity() && $scope.activity().type === type;
            };
            $scope.changeTypeTo = function(type) {
                if($scope.activity()) {
                    $scope.activity().type = type;
                }
            };

            LessonListFactory.then(function(lessons){
                window.lessons = lessons;
                $scope.lessons = lessons;
            });
        }
    ]);

    /**
     * Factories
     */
    app.factory('CourseDataFactory', ['$rootScope', '$q', '$resource',
        function($rootScope, $q, $resource, $window) {
            var Course = $resource('/api/course/:courseSlug/',{'courseSlug': courseSlug});
            var deferred = $q.defer();

            Course.get(function(course){
                deferred.resolve(course);
            });
            return deferred.promise;
        }
    ]);

    app.factory('LessonListFactory', ['$rootScope', '$q', '$resource',
        function($rootScope, $q, $resource, $window) {
            var resourceConfig = {
                'get':  {
                    'method':'GET',
                    'params':{'course__slug': courseSlug}
                },
                'query':{
                    'method':'GET',
                    'params':{'course__slug': courseSlug},
                    'isArray': true
                },
                'update':{
                    'method': 'PUT'
                }
            };
            var LessonList = $resource('/api/lessons/:id', {'id':'@id'}, resourceConfig);
            var deferred = $q.defer();

            LessonList.query(function(lessons){
                deferred.resolve(lessons);
            });
            return deferred.promise;
        }
    ]);

})(angular);<commit_msg>enable user to create units in admin page<commit_after>(function(angular){
    "use strict";

    var courseSlug = /[^/]+$/.extract(location.pathname);
    var app = angular.module('admin', ['ngRoute', 'ngResource', 'ngSanitize', 'youtube']);

    app.config(['$httpProvider', '$sceDelegateProvider',
        function ($httpProvider, $sceDelegateProvider) {
            $httpProvider.defaults.xsrfCookieName = 'csrftoken';
            $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
            $sceDelegateProvider.resourceUrlWhitelist([
                /^https?:\/\/(www\.)?youtube\.com\/.*/,
                'data:text/html, <html style="background: white">'
            ]);
            window.sce = $sceDelegateProvider;
        }
    ]);

    app.directive('contenteditable', function(){
        return {
            "restrict": 'A',
            "require": '?ngModel',
            "link": function(scope, element, attrs, ngModel) {
                if(!ngModel) return;
                ngModel.$render = function(){ element.html(ngModel.$viewValue || ''); };
                element.on('blur keyup change', function() { scope.$apply(read); });
                function read() {
                    var html = element.html();
                    if( attrs.stripBr && html.match(/ *<br\/?> */) ){
                      html = "";
                    }
                    ngModel.$setViewValue(html);
                }
            }
        };
    });

    /**
     * Controllers
     */
    app.controller('CourseEdit',['$scope', 'CourseDataFactory', '$http', 'youtubePlayerApi',
        function($scope, CourseDataFactory, $http, youtubePlayerApi){
            $scope.course = {};
            $scope.video = {
                'name': null,
                'youtube_id': null,
                'save': function() {
                    if(this.youtube_id_temp) {
                        this.youtube_id = this.youtube_id_temp;
                        $scope.course.intro_video = this;
                        $scope.course.$save().then((function(){
                            youtubePlayerApi.player.cueVideoById(this.youtube_id);
                        }).bind(this));
                    }
                },
                'reset': function() {
                    this.youtube_id = $scope.course.intro_video.youtube_id;
                }
            };

            var fields = ['application', 'requirement', 'abstract', 'structure', 'workload'];

            var build_data_for_modals = function(field){
                return {
                    'status': '',
                    'window': field,
                    'data': angular.copy($scope.course[field]),
                    'reset': function(){
                        this.data = angular.copy($scope.course[field]);
                    },
                    'save': function(){
                        var self = this;
                        var old = angular.copy($scope.course[field]);
                        $scope.course[field] = self.data;
                        $scope.course.$save()
                            .then(function(){
                                self.status = 'saved';
                            }).catch(function(){
                                self.status = 'error';
                                $scope.course[field] = old;
                            });
                    }
                };
            };

            CourseDataFactory.then(function(course){
                $scope.course = angular.copy(course);
                $scope.modals = fields.map(build_data_for_modals);
                if($scope.course.intro_video){
                    $scope.video.name = $scope.course.intro_video.name;
                    $scope.video.youtube_id = $scope.course.intro_video.youtube_id;

                    youtubePlayerApi.events = {
                        'onReady': function(player){
                            player.target.cueVideoById($scope.video.youtube_id);
                        }
                    };
                }
                youtubePlayerApi.loadPlayer();
                // reindex $scope.modals
                fields.forEach(function(e,i){$scope.modals[e]=$scope.modals[i];});
            });
        }
    ]);

    app.controller('LessonList',['$scope', '$rootScope', 'LessonListFactory', '$http',
        function($scope, $rootScope, LessonListFactory, $http){

            $scope.select = function (lesson) {
                $rootScope.selectedLesson = lesson;
                return lesson;
            };
            $scope.countActivities = function(l) {
                if( l.units ) {
                    return l.units.reduce(function(p,s){return p + (s.activity ? 1 : 0); }, 0);
                }
                return 0;
            };
            $scope.countVideos = function(l) {
                if( l.units ) {
                    return l.units.reduce(function(p,s){return p + (s.video ? 1 : 0); }, 0);
                }
                return 0;
            };

            LessonListFactory.then(function(lessons){
                $scope.lessons = lessons;
            });
        }
    ]);

    app.controller('LessonEdit',['$scope', '$rootScope', 'LessonListFactory', '$http',
        function($scope, $rootScope, LessonListFactory, $http){
            $scope.active = 'content';

            var selectedUnitIndex = 0;
            $scope.selectUnit = function(index) {
                selectedUnitIndex = index;
            };
            $scope.deleteUnit = function(index) {
                $rootScope.selectedLesson.units.splice(index, 1);
            };
            $scope.addUnit = function() {
                var pos = $rootScope.selectedLesson.units.length;
                $rootScope.selectedLesson.units.push({
                    "activity": null,
                    "id": null,
                    "position": pos,
                    "title": "",
                    "video": {
                        "id": null,
                        "name":"",
                        "youtube_id":"",
                    }
                });
            };
            $scope.selectedUnit = function() {
                if($rootScope.selectedLesson)
                    return $rootScope.selectedLesson.units[selectedUnitIndex];
            };
            $scope.activity = function() {
                if($scope.selectedUnit() && $scope.selectedUnit().activity)
                    return $scope.selectedUnit().activity;
            };
            $scope.typeIs = function(type) {
                return $scope.activity() && $scope.activity().type === type;
            };
            $scope.changeTypeTo = function(type) {
                if(!$scope.activity()) {
                    $scope.selectedUnit().activity = {
                        "type": type,
                        "data": {"question":""}
                    };
                } else {
                    $scope.activity().type = type;
                }
            };
            $scope.addAlternative = function(){
                if(!$scope.activity().data.alternatives) {
                    $scope.activity().data.alternatives = [""];
                } else {
                    $scope.activity().data.alternatives.push("");
                }
            }
            LessonListFactory.then(function(lessons){
                $scope.lessons = lessons;
            });
        }
    ]);

    /**
     * Factories
     */
    app.factory('CourseDataFactory', ['$rootScope', '$q', '$resource',
        function($rootScope, $q, $resource, $window) {
            var Course = $resource('/api/course/:courseSlug/',{'courseSlug': courseSlug});
            var deferred = $q.defer();

            Course.get(function(course){
                deferred.resolve(course);
            });
            return deferred.promise;
        }
    ]);

    app.factory('LessonListFactory', ['$rootScope', '$q', '$resource',
        function($rootScope, $q, $resource, $window) {
            var resourceConfig = {
                'get':  {
                    'method':'GET',
                    'params':{'course__slug': courseSlug}
                },
                'query':{
                    'method':'GET',
                    'params':{'course__slug': courseSlug},
                    'isArray': true
                },
                'update':{
                    'method': 'PUT'
                }
            };
            var LessonList = $resource('/api/lessons/:id', {'id':'@id'}, resourceConfig);
            var deferred = $q.defer();

            LessonList.query(function(lessons){
                deferred.resolve(lessons);
            });
            return deferred.promise;
        }
    ]);

})(angular);<|endoftext|>


<commit_before>$(function() {
	// Launch WebSocket to update the widgets
	var websocket = new WebSocket('ws://' + ws_host + ':' + ws_port + '/websocket')
	websocket.onopen = function() {
		// Websocket opened
	};
	websocket.onmessage = function (event) {
		data = JSON.parse(event.data);
		$('#' + data['label']).html($(data['content']).html());
	}
});<commit_msg>Client: Robustify websocket handling code<commit_after>'use strict';

$(function() {
  try {
    // Launch WebSocket to update the widgets
    var websocket = new WebSocket('ws://' + ws_host + ':' + ws_port + '/websocket');
    websocket.onopen = function (event) {
      // Websocket opened
    };
    websocket.onmessage = function (event) {
      if (event.data) {
        var data = JSON.parse(event.data);
        $('#' + data['label']).html($(data['content']).html());
      }
    };
    websocket.onclose = function (event) {
      // Websocket closed
    };
  } catch (err) {
    console.log('Error while opening websocket: ' + err);
  }
});<|endoftext|>


<commit_before>/*

This is the editable plugin for Mike Boers' nitrogen web framework.

Requirments:

	jquery-ui
	jquery.blockUI.js

Optional:
	jquery.autodate.js
	jquery.markdownEditor.js
	jquery.textarearesizer.js

TODO:
	- preview disabled unless there are changes
	- add flash on invalid form
	- preview should stop moving away from page
	- unsaved changes should stop moving away from page

*/

(function($, undefined) {

var assertHoverClass = function($$) {
	if (!$$.data().crudHoverSetup) {
		$$.hover(function() {
			$$.addClass('crud-hover')
		}, function() {
			$$.removeClass('crud-hover')
		})
		$$.data().crudHoverSetup = true
	}
}

$.widget('nitrogen.crud', {

	options: {

		allowCreate: true,
		allowUpdate: true,
		allowDelete: true,

		// Extra data to send along with the API request.
		// Useful for when you need more info for an API mdoel adapter callback.
		extraData: {}
	},

	_create: function()
	{
		// For backwards compatibility
		if (this.options.deleteable !== undefined) {
			this.options.allowDelete = this.options.deleteable
		}

		var $$ = this.widget()

		// This is what we will restore to.
		this.originalChildren = $$.children()

		if (this.options.id) {
			this._setupIdle()
		} else if (this.options.allowCreate) {
			console.warning('Creation has not been fixed yet.')
			// this.edit()
		} else {
			throw "no ID and no create permissions"
		}
	},

	// Get functions whose context is bound to this object.
	_bound: function(name) {
		if (!this._boundFunctions) {
			this._boundFunctions = {}
		}
		if (!this._boundFunctions[name]) {
			var self = this
			var func = this[name]
			this._boundFunctions[name] = function() {
				return func.apply(self, arguments)
			}
		}
		return this._boundFunctions[name]
	},

	_setState: function(state, obj) {
		var $$ = obj ? obj : this.widget()
		var oldDate = this.state
		if (oldDate) {
			$$.removeClass('crud-state-' + this.state)
		}
		this.state = state
		$$.addClass('crud')
		$$.addClass('crud-state-' + state)
		assertHoverClass($$)
		return oldDate
	},

	_getFormData: function() {
		var data = {}
		$.each(this.form.serializeArray(), function(k, v) {
			data[this.name] = this.value;
		});
		return data;
	},

	_getRequestData: function() {
		return $.extend({}, this.options, this.options.extraData, this._getFormData())
	},

	_setupIdle: function() {

		this._setState('idle')

		var $$ = this.widget();

		this.buttons = $('
')
			.appendTo($$)

		if (this.options.allowUpdate) {
			$('Edit')
				.button({icons: {primary: 'silk-icon silk-icon-pencil'}})
				.click(this._bound('edit'))
				.appendTo(this.buttons)
		}

		if (this.options.allowDelete) {
			$('Delete')
				.button({icons: {primary: 'silk-icon silk-icon-delete'}})
				.click(this._bound('delete'))
				.appendTo(this.buttons)
		}

		this.buttons.buttonset()

	},

	edit: function()
	{
		var self = this
		var $$ = this.widget()

		if (this.state == 'preview') {
			this.preview.remove()
			$$.unblock()
			$$.show()
			this._setState('edit')
			return
		}

		var oldState = this._setState('getForm')

		// Block out the UI while we get the form.
		$$.block({
			message: 'Retrieving form. Please wait...'
		});

		// Get the form.
		var data = $.extend({}, this.options, this.options.extraData, {
			method: 'get_form',
			id: this.options.id ? this.options.id : 0
		});

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: data,
			success: this._bound('_setupForm'),
			error: function() {
				self._setState(oldState)
				$$.unblock()
				alert('There was an error while contacting the server.')
			},
			dataType: 'json'
		});
	},

	// This takes an object which must have a `form` property.
	_setupForm: function(res)
	{
		var $$ = this.widget()
		$$.unblock()
		$$.empty()

		this._setState('edit')

		this.form = $('<form />')
			.appendTo($$)
			.html(res.form) // Must come after the append.

		// Save the current values for cancel warning.
		// XXX: do not do this for the edit button on a preview, or after
		// submitting invalid data.
		if (this.originalData === undefined)
			this.originalData = this._getFormData()

		var versionControls = $('
History:
')
			.appendTo(this.form)
		$('<select class="version-menu"><option value="">None</option></select>')
			.attr('disabled', true)
			.appendTo(versionControls)
		$('<input type="checkbox" />Commit on Save')
			.appendTo(versionControls)

		var buttons = $('
')
			.appendTo(this.form);
		$('Preview')
			.button({icons: {primary: 'silk-icon silk-icon-eye'}})
			.click(this._bound('preview'))
			.appendTo(buttons)
		$('Save')
			.button({icons: {primary: 'silk-icon silk-icon-tick'}})
			.click(this._bound('save'))
			.appendTo(buttons)
		$('Cancel')
			.button({icons: {primary: 'silk-icon silk-icon-cross'}})
			.click(this._bound('cancel'))
			.appendTo(buttons)
		buttons.buttonset()
	},

	cancel: function()
	{
		if (this.state != 'edit')
			throw 'not editing'

		var changed = this._isDifferentData(this._getFormData())
		if (changed && !confirm("There are unsaved changes.\n\nAre you sure you want to cancel?"))
			return

		var $$ = this.widget()

		if (this.options.id) { // an UPDATE
			// Restore the markup to what it was.
			$$.empty()
			$$.append(this.originalChildren)
			this._setupIdle()
		} else { // a CREATE
			$$.remove()
		}
	},

	save: function(e, mode)
	{

		var self = this
		var $$ = this.widget()

		mode = mode ? mode : 'save'
		var isPreview = mode == 'preview'

		if (this.state != 'edit' && this.state != 'preview')
			throw 'not editing'

		var oldState = this._setState(isPreview ? 'preview' : 'saving')

		var blockTarget = mode == 'apply' ? this.preview : $$
		blockTarget.block(isPreview ?
			'Building preview. Please wait...' :
			'Saving. Please wait...'
		)

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: $.extend(this._getRequestData(), {
				method: isPreview ? 'preview' : 'submit_form',
				id: this.options.id ? this.options.id : 0
			}),
			success: function(res) {
				switch(mode) {
					case 'apply':
						self.preview.remove()
						// NO BREAK
					case 'save':
						self._handleSaveResponse(res)
						break
					case 'preview':
						self._handlePreviewResponse(res)
						break
					default:
						throw 'unrecognized mode: ' + mode
				}
			},
			error: function() {
				self._setState(oldState)
				blockTarget.unblock()
				alert('There was an error while contacting the server.')
			},
			dataType: 'json'
		})
	},

	preview: function(e) {
		this.save(e, 'preview')
	},

	_handleSaveResponse: function(res)
	{
		if (!res.valid) {
			this._setupForm(res);
		}
		else
		{
			if (res.id)
				// So the next will be an update.
				this.options.id = res.id
			var $$ = this.widget()
			$(res.partial)
				.insertAfter($$)
				.crud(this.options)
			$$.remove()
		}
	},

	_handlePreviewResponse: function(res)
	{
		if (!res.valid) {
			this._setupForm(res);
		}
		else
		{
			$$ = this.widget()
			this.preview = $(res.partial)
				.insertAfter($$)
			$$.hide()

			this._setState('preview')
			this._setState('preview', this.preview)

			var $buttons = $('
')
				.appendTo(this.preview)
			$('Save')
				.button({icons: {primary: 'silk-icon silk-icon-tick'}})
				.click(this._bound('apply'))
				.appendTo($buttons);
			$('Edit')
				.button({icons: {primary: 'silk-icon silk-icon-pencil'}})
				.click(this._bound('edit'))
				.appendTo($buttons);
			$('Revert')
				.button({icons: {primary: 'silk-icon silk-icon-cross'}})
				.click(this._bound('revert'))
				.appendTo($buttons);
			$buttons.buttonset();

			var self = this
			var pulse = function()
			{
				if (self.state == 'preview') {
					self.preview.toggleClass('crud-preview-pulse', 750, null, pulse)
				}
			}
			pulse()

		}
	},

	apply: function(e) {
		if (this.state != 'preview')
			throw 'not previewing'
		this.save(e, 'apply')

	},

	_isDifferentData: function(data)
	{
		for (var key in data) {
			if (data[key] != this.originalData[key]) {
				return true
			}
		}
		return false
	},

	revert: function() {

		if (this.state != 'preview')
			throw 'not editing'

		var changed = this._isDifferentData(this._getFormData())
		if (changed && !confirm("There are unsaved changes.\n\nAre you sure you want revert changes?"))
			return

		this.preview.remove()

		var $$ = this.widget()

		if (this.options.id) { // an UPDATE
			// Restore the markup to what it was.
			$$.empty()
			$$.append(this.originalChildren)
			$$.show()
			this._setupIdle()
		} else { // a CREATE
			$$.remove()
		}

	},

	// Note that this is NOT "destroy". This actually removes the data.
	// This also needs to be quoted for those browsers that care that much.
	'delete': function()
	{
		var $$ = this.widget()

		if (!confirm("Are you sure you want to delete this?\n\nIt cannot be recovered."))
			return

		this._setState('deleting')

		$$.block({
			message: 'Deleting. Please wait...'
		});

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: {
				method: 'delete',
				id: this.options.id
			},
			success: function(res) {
				$$.remove();
			}
		});
	}

});

})(jQuery);<commit_msg>CRUD creation works again.<commit_after>/*

This is the editable plugin for Mike Boers' nitrogen web framework.

Requirments:

	jquery-ui
	jquery.blockUI.js

Optional:
	jquery.autodate.js
	jquery.markdownEditor.js
	jquery.textarearesizer.js

TODO:
	- preview disabled unless there are changes
	- add flash on invalid form
	- preview should stop moving away from page
	- unsaved changes should stop moving away from page

*/

(function($, undefined) {

var assertHoverClass = function($$) {
	if (!$$.data().crudHoverSetup) {
		$$.hover(function() {
			$$.addClass('crud-hover')
		}, function() {
			$$.removeClass('crud-hover')
		})
		$$.data().crudHoverSetup = true
	}
}

$.widget('nitrogen.crud', {

	options: {

		allowCreate: true,
		allowUpdate: true,
		allowDelete: true,

		// Extra data to send along with the API request.
		// Useful for when you need more info for an API mdoel adapter callback.
		extraData: {}
	},

	_create: function()
	{
		// For backwards compatibility
		if (this.options.deleteable !== undefined) {
			this.options.allowDelete = this.options.deleteable
		}

		var $$ = this.widget()

		// This is what we will restore to.
		this.originalChildren = $$.children()

		if (this.options.id) {
			this._setupIdle()
		} else if (this.options.allowCreate) {
			this.edit()
		} else {
			throw "no ID and no create permissions"
		}
	},

	// Get functions whose context is bound to this object.
	_bound: function(name) {
		if (!this._boundFunctions) {
			this._boundFunctions = {}
		}
		if (!this._boundFunctions[name]) {
			var self = this
			var func = this[name]
			this._boundFunctions[name] = function() {
				return func.apply(self, arguments)
			}
		}
		return this._boundFunctions[name]
	},

	_setState: function(state, obj) {
		var $$ = obj ? obj : this.widget()
		var oldDate = this.state
		if (oldDate) {
			$$.removeClass('crud-state-' + this.state)
		}
		this.state = state
		$$.addClass('crud')
		$$.addClass('crud-state-' + state)
		assertHoverClass($$)
		return oldDate
	},

	_getFormData: function() {
		var data = {}
		$.each(this.form.serializeArray(), function(k, v) {
			data[this.name] = this.value;
		});
		return data;
	},

	_getRequestData: function() {
		return $.extend({}, this.options, this.options.extraData, this._getFormData())
	},

	_setupIdle: function() {

		this._setState('idle')

		var $$ = this.widget();

		this.buttons = $('
')
			.appendTo($$)

		if (this.options.allowUpdate) {
			$('Edit')
				.button({icons: {primary: 'silk-icon silk-icon-pencil'}})
				.click(this._bound('edit'))
				.appendTo(this.buttons)
		}

		if (this.options.allowDelete) {
			$('Delete')
				.button({icons: {primary: 'silk-icon silk-icon-delete'}})
				.click(this._bound('delete'))
				.appendTo(this.buttons)
		}

		this.buttons.buttonset()

	},

	edit: function()
	{
		var self = this
		var $$ = this.widget()

		if (this.state == 'preview') {
			this.preview.remove()
			$$.unblock()
			$$.show()
			this._setState('edit')
			return
		}

		var oldState = this._setState('getForm')

		// Block out the UI while we get the form.
		$$.block({
			message: 'Retrieving form. Please wait...'
		});

		// Get the form.
		var data = $.extend({}, this.options, this.options.extraData, {
			method: 'get_form',
			id: this.options.id ? this.options.id : 0
		});

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: data,
			success: this._bound('_setupForm'),
			error: function() {
				self._setState(oldState)
				$$.unblock()
				alert('There was an error while contacting the server.')
			},
			dataType: 'json'
		});
	},

	// This takes an object which must have a `form` property.
	_setupForm: function(res)
	{
		var $$ = this.widget()
		$$.unblock()
		$$.empty()

		this._setState('edit')

		this.form = $('<form />')
			.appendTo($$)
			.html(res.form) // Must come after the append.

		// Save the current values for cancel warning.
		// XXX: do not do this for the edit button on a preview, or after
		// submitting invalid data.
		if (this.originalData === undefined)
			this.originalData = this._getFormData()

		var versionControls = $('
History:
')
			.appendTo(this.form)
		$('<select class="version-menu"><option value="">None</option></select>')
			.attr('disabled', true)
			.appendTo(versionControls)
		$('<input type="checkbox" />Commit on Save')
			.appendTo(versionControls)

		var buttons = $('
')
			.appendTo(this.form);
		$('Preview')
			.button({icons: {primary: 'silk-icon silk-icon-eye'}})
			.click(this._bound('preview'))
			.appendTo(buttons)
		$('Save')
			.button({icons: {primary: 'silk-icon silk-icon-tick'}})
			.click(this._bound('save'))
			.appendTo(buttons)
		$('Cancel')
			.button({icons: {primary: 'silk-icon silk-icon-cross'}})
			.click(this._bound('cancel'))
			.appendTo(buttons)
		buttons.buttonset()
	},

	cancel: function()
	{
		if (this.state != 'edit')
			throw 'not editing'

		var changed = this._isDifferentData(this._getFormData())
		if (changed && !confirm("There are unsaved changes.\n\nAre you sure you want to cancel?"))
			return

		var $$ = this.widget()

		if (this.options.id) { // an UPDATE
			// Restore the markup to what it was.
			$$.empty()
			$$.append(this.originalChildren)
			this._setupIdle()
		} else { // a CREATE
			$$.remove()
		}
	},

	save: function(e, mode)
	{

		var self = this
		var $$ = this.widget()

		mode = mode ? mode : 'save'
		var isPreview = mode == 'preview'

		if (this.state != 'edit' && this.state != 'preview')
			throw 'not editing'

		var oldState = this._setState(isPreview ? 'preview' : 'saving')

		var blockTarget = mode == 'apply' ? this.preview : $$
		blockTarget.block(isPreview ?
			'Building preview. Please wait...' :
			'Saving. Please wait...'
		)

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: $.extend(this._getRequestData(), {
				method: isPreview ? 'preview' : 'submit_form',
				id: this.options.id ? this.options.id : 0
			}),
			success: function(res) {
				switch(mode) {
					case 'apply':
						self.preview.remove()
						// NO BREAK
					case 'save':
						self._handleSaveResponse(res)
						break
					case 'preview':
						self._handlePreviewResponse(res)
						break
					default:
						throw 'unrecognized mode: ' + mode
				}
			},
			error: function() {
				self._setState(oldState)
				blockTarget.unblock()
				alert('There was an error while contacting the server.')
			},
			dataType: 'json'
		})
	},

	preview: function(e) {
		this.save(e, 'preview')
	},

	_handleSaveResponse: function(res)
	{
		if (!res.valid) {
			this._setupForm(res);
		}
		else
		{
			if (res.id)
				// So the next will be an update.
				this.options.id = res.id
			var $$ = this.widget()
			$(res.partial)
				.insertAfter($$)
				.crud(this.options)
			$$.remove()
		}
	},

	_handlePreviewResponse: function(res)
	{
		if (!res.valid) {
			this._setupForm(res);
		}
		else
		{
			$$ = this.widget()
			this.preview = $(res.partial)
				.insertAfter($$)
			$$.hide()

			this._setState('preview')
			this._setState('preview', this.preview)

			var $buttons = $('
')
				.appendTo(this.preview)
			$('Save')
				.button({icons: {primary: 'silk-icon silk-icon-tick'}})
				.click(this._bound('apply'))
				.appendTo($buttons);
			$('Edit')
				.button({icons: {primary: 'silk-icon silk-icon-pencil'}})
				.click(this._bound('edit'))
				.appendTo($buttons);
			$('Revert')
				.button({icons: {primary: 'silk-icon silk-icon-cross'}})
				.click(this._bound('revert'))
				.appendTo($buttons);
			$buttons.buttonset();

			var self = this
			var pulse = function()
			{
				if (self.state == 'preview') {
					self.preview.toggleClass('crud-preview-pulse', 750, null, pulse)
				}
			}
			pulse()

		}
	},

	apply: function(e) {
		if (this.state != 'preview')
			throw 'not previewing'
		this.save(e, 'apply')

	},

	_isDifferentData: function(data)
	{
		for (var key in data) {
			if (data[key] != this.originalData[key]) {
				return true
			}
		}
		return false
	},

	revert: function() {

		if (this.state != 'preview')
			throw 'not editing'

		var changed = this._isDifferentData(this._getFormData())
		if (changed && !confirm("There are unsaved changes.\n\nAre you sure you want revert changes?"))
			return

		this.preview.remove()

		var $$ = this.widget()

		if (this.options.id) { // an UPDATE
			// Restore the markup to what it was.
			$$.empty()
			$$.append(this.originalChildren)
			$$.show()
			this._setupIdle()
		} else { // a CREATE
			$$.remove()
		}

	},

	// Note that this is NOT "destroy". This actually removes the data.
	// This also needs to be quoted for those browsers that care that much.
	'delete': function()
	{
		var $$ = this.widget()

		if (!confirm("Are you sure you want to delete this?\n\nIt cannot be recovered."))
			return

		this._setState('deleting')

		$$.block({
			message: 'Deleting. Please wait...'
		});

		$.ajax({
			type: "POST",
			url: this.options.url,
			data: {
				method: 'delete',
				id: this.options.id
			},
			success: function(res) {
				$$.remove();
			}
		});
	}

});

})(jQuery);<|endoftext|>


<commit_before>function updateChart (data) {
	var list = [];
	var records = data.records;
	var len = records;
	for (var i = 0; i < len; i++) {
		list.push([records[i].Date, records[i].Power])
	}
	$.jqplot('chart1', [list], {
		title:'Predicted Power Usage for the next 24 Hours',
		axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}},
		series:[{lineWidth:4, markerOptions:{style:'square'}}]
	});
}

function getData () {
	$.getJSON("/data", updateChart);
}
<commit_msg>told it where to plot<commit_after>function updateChart (data) {
	var list = [];
	var records = data.records;
	var len = records;
	for (var i = 0; i < len; i++) {
		list.push([records[i].Date, records[i].Power])
	}
	$.jqplot('futureplot', [list], {
		title:'Predicted Power Usage for the next 24 Hours',
		axes:{xaxis:{renderer:$.jqplot.DateAxisRenderer}},
		series:[{lineWidth:4, markerOptions:{style:'square'}}]
	});
}

function getData () {
	$.getJSON("/data", updateChart);
}
<|endoftext|>


<commit_before>/* In the Margins Side Panel Functions */
/* Based on https://github.com/AndreaLombardo/BootSideMenu */
function getSide(listClasses){
    var side;
    for (var i = 0; i<listClasses.length; i++) {
        if (listClasses[i]=='sidebar-left') {
            side = "left";
            break;
        } else if (listClasses[i]=='sidebar-right') {
            side = "right";
            break;
        } else {
            side = null;
        }
    }
    return side;
}

function doAnimation(container, containerWidth, sidebarSide, sidebarStatus) {
    var toggler = container.children()[1];
    if (sidebarStatus == "opened") {
        if (sidebarSide == "left") {
            container.animate({
                left: -(containerWidth+2)
            });
            toggleArrow("left");
        } else if (sidebarSide == "right") {
            container.animate({
                right: -(containerWidth +2)
            });
            toggleArrow("right");
        }
        container.attr('data-status', 'closed');
    } else {
        if (sidebarSide == "left") {
            container.animate({
                left:0
            });
            toggleArrow("right");
        } else if (sidebarSide == "right") {
            container.animate({
                right:0
            });
            toggleArrow("left");
        }
        container.attr('data-status', 'opened');
    }
}

function toggleArrow(side){
    if (side=="left") {
        $("#toggler").children(".glyphicon-chevron-right").css('display', 'block');
        $("#toggler").children(".glyphicon-chevron-left").css('display', 'none');
    } else if (side=="right") {
        $("#toggler").children(".glyphicon-chevron-left").css('display', 'block');
        $("#toggler").children(".glyphicon-chevron-right").css('display', 'none');
    }
}

/* In the Margins API Functions */
function callAPI(slug, type) {

    // No reason not to hard code this here since we only need the slug
    var url = "http://scalar.usc.edu/works/lexos/rdf/node/"+slug+"?rec=0&format=json";

    // Ajax call
    $.ajax({
        type:"GET",
        url:url,
        dataType:'jsonp',
        success: function(data){
            processData(data, type, url);
        },
        error: handleError
    });
}

function handleError(XMLHttpRequest, textStatus, errorThrown) {
    $('#status').html(textstatus+'"
"'+errorThrown).show();
}

function processData(data, type, url) {
    for (var nodeProp in data) {
        node = data[nodeProp]
        content_path = node['http://rdfs.org/sioc/ns#content'];
        if (node['http://purl.org/dc/terms/title'] != null) {
            var title = node['http://purl.org/dc/terms/title'][0].value;
            var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (node['http://simile.mit.edu/2003/10/ontologies/artstor#url'] != null) {
            var video_url = node['http://simile.mit.edu/2003/10/ontologies/artstor#url'][0].value;
            //var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (node['http://simile.mit.edu/2003/10/ontologies/artstor#url'] != null) {
            var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (content_path != null) {
            var content = content_path[0].value;
            content = content.replace(new RegExp('\r?\n\r?\n','g'), '

'); // Replace line breaks
        }
    }
    displayITMcontent(content, title, url, type, video_url);
}

function displayITMcontent(content, title, url, type, video_url) {
    // Fork here based on type
    switch (type) {
        case "panel":
        $("#panel-content").remove();
        titleLink = '
'+title+'
';
        $("#itm-content").append('
'+titleLink+content+'

');
        // Next two lines determine the panel height and change on window resize
        var height = $("#panel-content").visibleHeight()+"px";
        $("#panel-content").css("height", height);
        $("#panel-status").hide();
        break;

        case "dialog":
        titleLink = ''+title+'';
        $('#ITM-modal .modal-title').html(titleLink);
        msg = "
This is just a sample modal. Ultimately, it will open a settings dialog, but for now it can be used as a trigger to display In the Margins content. Click the Show Video button to see some sample video content.
";
        $('#ITM-modal .modal-body').empty();
        $('#ITM-modal .modal-body').append(msg);
        $('#ITM-modal .modal-body').append(content);
        $("#dialog-status").hide();
        break;

        // Works only with YouTube videos
        case "video-dialog":
        var youtube_url = video_url.replace("https://www.youtube.com/watch?v=", "https://www.youtube.com/embed/");
        titleLink = ''+title+'';
        $('#ITM-modal .modal-title').html(titleLink);
        msg = "
This is just a sample modal. Ultimately, it will open a settings dialog, but for now it can be used as a trigger to display In the Margins content. Click the Show Video button to see some sample video content.
";
        $('#ITM-modal .modal-body').empty();
        $('#ITM-modal .modal-body').html("");
        $('#ITM-modal .modal-body').append('');
        $("#dialog-status").hide();
        break;
    }
}

/* Gets the height of the viewport relative to a specified element.
   See http://stackoverflow.com/questions/24768795/get-the-visible-height-of-a-div-with-jquery */
$.fn.visibleHeight = function() {
    var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop;
    scrollTop = $(window).scrollTop();
    scrollBot = scrollTop + $(window).height();
    elTop = this.offset().top;
    elBottom = elTop + this.outerHeight();
    visibleTop = elTop < scrollTop ? scrollTop : elTop;
    visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    return visibleBottom - visibleTop
}

/* Document Ready Functions */
$(document).ready(function() {

    /* Get the viewport height after the window is resized */
    $(window).on('scroll resize', getVisible);

    /* ITM Panel Setup */
    var container = $("#toggler").parent();
    var containerWidth = container.width();
    container.css({left:-(containerWidth+2)});
    container.attr('data-status', 'closed');

    /* ITM Panel Toggle Events */
    /* Note: This function currentl only works with side-panel toggle icon.
       To enable the use of other triggers, a class trigger should be used. */
    $('#toggler').click(function() {
        var container = $(this).parent();
        var listClasses = $(container[0]).attr('class').split(/\s+/); //IE9 Fix
        var side = getSide(listClasses);
        var containerWidth = container.width();
        var status = container.attr('data-status');
        var dataSlug = $("#ITMPanel-itm-content").attr('data-slug');
        var slug = (dataSlug == "") ? "index" : dataSlug;
        if(!status) {
            status = "opened";
        }
        doAnimation(container, containerWidth, side, status);
        if (status == "closed") {
            $("#panel-status").show();
            callAPI(slug, "panel");
        }
    });

    /* Populate a Bootstrap modal */
    $('#ITM-modal').on('show.bs.modal', function() {
        $("#dialog-status").show();
        callAPI("best-practices", "dialog");
    });

    /* Empty a Bootstrap modal */
    $('#ITM-modal').on('hide.bs.modal', function() {
        $('#ITM-modal').removeData('bs.modal');
        icon = $('#dialog-status').parent().html();
        $('#ITM-modal .modal-body').html("");
        $('#ITM-modal .modal-body').html(icon);
        $("#dialog-status").hide();
        callAPI("best-practices", "dialog");
    });

    /* Handle Show Video Button in Bootstrap Modal */
    $('#show-video').click(function() {
        callAPI("how-to-read-a-dendrogram", "video-dialog")
    });
});

/* Initialise Bootstrap Modal */
$('#ITM-modal').modal();
<commit_msg>Disabled window resize<commit_after>/* In the Margins Side Panel Functions */
/* Based on https://github.com/AndreaLombardo/BootSideMenu */
function getSide(listClasses){
    var side;
    for (var i = 0; i<listClasses.length; i++) {
        if (listClasses[i]=='sidebar-left') {
            side = "left";
            break;
        } else if (listClasses[i]=='sidebar-right') {
            side = "right";
            break;
        } else {
            side = null;
        }
    }
    return side;
}

function doAnimation(container, containerWidth, sidebarSide, sidebarStatus) {
    var toggler = container.children()[1];
    if (sidebarStatus == "opened") {
        if (sidebarSide == "left") {
            container.animate({
                left: -(containerWidth+2)
            });
            toggleArrow("left");
        } else if (sidebarSide == "right") {
            container.animate({
                right: -(containerWidth +2)
            });
            toggleArrow("right");
        }
        container.attr('data-status', 'closed');
    } else {
        if (sidebarSide == "left") {
            container.animate({
                left:0
            });
            toggleArrow("right");
        } else if (sidebarSide == "right") {
            container.animate({
                right:0
            });
            toggleArrow("left");
        }
        container.attr('data-status', 'opened');
    }
}

function toggleArrow(side){
    if (side=="left") {
        $("#toggler").children(".glyphicon-chevron-right").css('display', 'block');
        $("#toggler").children(".glyphicon-chevron-left").css('display', 'none');
    } else if (side=="right") {
        $("#toggler").children(".glyphicon-chevron-left").css('display', 'block');
        $("#toggler").children(".glyphicon-chevron-right").css('display', 'none');
    }
}

/* In the Margins API Functions */
function callAPI(slug, type) {

    // No reason not to hard code this here since we only need the slug
    var url = "http://scalar.usc.edu/works/lexos/rdf/node/"+slug+"?rec=0&format=json";

    // Ajax call
    $.ajax({
        type:"GET",
        url:url,
        dataType:'jsonp',
        success: function(data){
            processData(data, type, url);
        },
        error: handleError
    });
}

function handleError(XMLHttpRequest, textStatus, errorThrown) {
    $('#status').html(textstatus+'"
"'+errorThrown).show();
}

function processData(data, type, url) {
    for (var nodeProp in data) {
        node = data[nodeProp]
        content_path = node['http://rdfs.org/sioc/ns#content'];
        if (node['http://purl.org/dc/terms/title'] != null) {
            var title = node['http://purl.org/dc/terms/title'][0].value;
            var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (node['http://simile.mit.edu/2003/10/ontologies/artstor#url'] != null) {
            var video_url = node['http://simile.mit.edu/2003/10/ontologies/artstor#url'][0].value;
            //var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (node['http://simile.mit.edu/2003/10/ontologies/artstor#url'] != null) {
            var url = node['http://purl.org/dc/terms/isVersionOf'][0].value;
        }
        if (content_path != null) {
            var content = content_path[0].value;
            content = content.replace(new RegExp('\r?\n\r?\n','g'), '

'); // Replace line breaks
        }
    }
    displayITMcontent(content, title, url, type, video_url);
}

function displayITMcontent(content, title, url, type, video_url) {
    // Fork here based on type
    switch (type) {
        case "panel":
        $("#panel-content").remove();
        titleLink = '
'+title+'
';
        $("#itm-content").append('
'+titleLink+content+'

');
        // Next two lines determine the panel height and change on window resize
        //var height = $("#panel-content").visibleHeight()+"px";
        //$("#panel-content").css("height", height);
        $("#panel-status").hide();
        break;

        case "dialog":
        titleLink = ''+title+'';
        $('#ITM-modal .modal-title').html(titleLink);
        msg = "
This is just a sample modal. Ultimately, it will open a settings dialog, but for now it can be used as a trigger to display In the Margins content. Click the Show Video button to see some sample video content.
";
        $('#ITM-modal .modal-body').empty();
        $('#ITM-modal .modal-body').append(msg);
        $('#ITM-modal .modal-body').append(content);
        $("#dialog-status").hide();
        break;

        // Works only with YouTube videos
        case "video-dialog":
        var youtube_url = video_url.replace("https://www.youtube.com/watch?v=", "https://www.youtube.com/embed/");
        titleLink = ''+title+'';
        $('#ITM-modal .modal-title').html(titleLink);
        msg = "
This is just a sample modal. Ultimately, it will open a settings dialog, but for now it can be used as a trigger to display In the Margins content. Click the Show Video button to see some sample video content.
";
        $('#ITM-modal .modal-body').empty();
        $('#ITM-modal .modal-body').html("");
        $('#ITM-modal .modal-body').append('');
        $("#dialog-status").hide();
        break;
    }
}

/* Gets the height of the viewport relative to a specified element.
   See http://stackoverflow.com/questions/24768795/get-the-visible-height-of-a-div-with-jquery */
$.fn.visibleHeight = function() {
    var elBottom, elTop, scrollBot, scrollTop, visibleBottom, visibleTop;
    scrollTop = $(window).scrollTop();
    scrollBot = scrollTop + $(window).height();
    elTop = this.offset().top;
    elBottom = elTop + this.outerHeight();
    visibleTop = elTop < scrollTop ? scrollTop : elTop;
    visibleBottom = elBottom > scrollBot ? scrollBot : elBottom;
    return visibleBottom - visibleTop
}

/* Document Ready Functions */
$(document).ready(function() {

    /* Get the viewport height after the window is resized */
    //$(window).on('scroll resize', getVisible);

    /* ITM Panel Setup */
    var container = $("#toggler").parent();
    var containerWidth = container.width();
    container.css({left:-(containerWidth+2)});
    container.attr('data-status', 'closed');

    /* ITM Panel Toggle Events */
    /* Note: This function currentl only works with side-panel toggle icon.
       To enable the use of other triggers, a class trigger should be used. */
    $('#toggler').click(function() {
        var container = $(this).parent();
        var listClasses = $(container[0]).attr('class').split(/\s+/); //IE9 Fix
        var side = getSide(listClasses);
        var containerWidth = container.width();
        var status = container.attr('data-status');
        var dataSlug = $("#ITMPanel-itm-content").attr('data-slug');
        var slug = (dataSlug == "") ? "index" : dataSlug;
        if(!status) {
            status = "opened";
        }
        doAnimation(container, containerWidth, side, status);
        if (status == "closed") {
            $("#panel-status").show();
            callAPI(slug, "panel");
        }
    });

    /* Populate a Bootstrap modal */
    $('#ITM-modal').on('show.bs.modal', function() {
        $("#dialog-status").show();
        callAPI("best-practices", "dialog");
    });

    /* Empty a Bootstrap modal */
    $('#ITM-modal').on('hide.bs.modal', function() {
        $('#ITM-modal').removeData('bs.modal');
        icon = $('#dialog-status').parent().html();
        $('#ITM-modal .modal-body').html("");
        $('#ITM-modal .modal-body').html(icon);
        $("#dialog-status").hide();
        callAPI("best-practices", "dialog");
    });

    /* Handle Show Video Button in Bootstrap Modal */
    $('#show-video').click(function() {
        callAPI("how-to-read-a-dendrogram", "video-dialog")
    });
});

/* Initialise Bootstrap Modal */
$('#ITM-modal').modal();
<|endoftext|>