<commit_before>'use strict';

/**
 * Module dependencies
 */

module.exports = mongoose => {
  require('mongoose-float').loadType(mongoose);

  const SchemaTypes = mongoose.Schema.Types;

  SchemaTypes.Decimal.prototype.cast = function (value) {
    return value.toString();
  };

  return {
    convertType: mongooseType => {
      switch (mongooseType.toLowerCase()) {
        case 'string':
        case 'password':
        case 'text':
        case 'email':
          return 'String';
        case 'integer':
        case 'biginteger':
          return 'Number';
        case 'float':
          return 'Float';
        case 'decimal':
          return 'Decimal';
        case 'date':
        case 'time':
        case 'datetime':
        case 'timestamp':
          return Date;
        case 'boolean':
          return 'Boolean';
        case 'binary':
          return 'Buffer';
        case 'uuid':
          return 'ObjectId';
        case 'enumeration':
          return 'String';
        case 'json':
          return 'Mixed';
        default:

      }
    }
  };
};
<commit_msg>Remove trivial decimal implementation and use float type instead #550<commit_after>'use strict';

/**
 * Module dependencies
 */

module.exports = mongoose => {
  require('mongoose-float').loadType(mongoose);

  const SchemaTypes = mongoose.Schema.Types;

  // Note: The decimal format isn't well supported by MongoDB.
  // It's recommended to use Float or Number type instead.
  //
  // SchemaTypes.Decimal.prototype.cast = function (value) {
  //   return value.toString();
  // };

  return {
    convertType: mongooseType => {
      switch (mongooseType.toLowerCase()) {
        case 'string':
        case 'password':
        case 'text':
        case 'email':
          return 'String';
        case 'integer':
        case 'biginteger':
          return 'Number';
        case 'float':
        case 'decimal':
          return 'Float';
        case 'date':
        case 'time':
        case 'datetime':
        case 'timestamp':
          return Date;
        case 'boolean':
          return 'Boolean';
        case 'binary':
          return 'Buffer';
        case 'uuid':
          return 'ObjectId';
        case 'enumeration':
          return 'String';
        case 'json':
          return 'Mixed';
        default:

      }
    }
  };
};
<|endoftext|>


<commit_before>'use strict';

var mean = require('meanio');

exports.render = function(req, res) {

  var modules = [];
  // Preparing angular modules list with dependencies
  for (var name in mean.modules) {
    modules.push({
      name: name,
      module: 'mean.' + name,
      angularDependencies: mean.modules[name].angularDependencies
    });
  }

  function isAdmin() {
    return req.user && req.user.roles.indexOf('admin') !== -1;
  }

  // Send some basic starting info to the view
  res.render('index', {
    user: req.user ? {
      name: req.user.name,
      _id: req.user._id,
      username: req.user.username,
      roles: req.user.roles
    } : {},
    modules: modules,
    isAdmin: isAdmin,
    adminEnabled: isAdmin() && mean.moduleEnabled('mean-admin')
  });
};
<commit_msg>Added user email in global user object<commit_after>'use strict';

var mean = require('meanio');

exports.render = function(req, res) {

  var modules = [];
  // Preparing angular modules list with dependencies
  for (var name in mean.modules) {
    modules.push({
      name: name,
      module: 'mean.' + name,
      angularDependencies: mean.modules[name].angularDependencies
    });
  }

  function isAdmin() {
    return req.user && req.user.roles.indexOf('admin') !== -1;
  }

  // Send some basic starting info to the view
  res.render('index', {
    user: req.user ? {
      name: req.user.name,
      _id: req.user._id,
      username: req.user.username,
      email: req.user.email,
      roles: req.user.roles
    } : {},
    modules: modules,
    isAdmin: isAdmin,
    adminEnabled: isAdmin() && mean.moduleEnabled('mean-admin')
  });
};
<|endoftext|>


<commit_before>import '../src/styles/global.scss';

export AppBar, { appBarHeight } from './components/AppBar';
export AppFooter, {
  appFooterHeightShort,
  appFooterHeightTall
} from './components/AppFooter';
export AppSwitcher from './components/AppSwitcher';
export Avatar from './components/Avatar';
export Chip from './components/Chip';
export Lozenge from './components/Lozenge';
export Truncate from './components/Truncate';
export DelayedProgress from './components/DelayedProgress';
export NavigationSideBar, {
  sectionsShape as navigationSidebarSectionsShape
} from './components/NavigationSideBar';
export DiscoverySideBar, {
  sectionsShape as discoverySidebarSectionsShape,
  DiscoverySideBarContainerPure as DiscoverySideBarPure
} from './components/DiscoverySideBar';
export FullScreenDialog from './components/FullScreenDialog';
export ProfileMenu from './components/ProfileMenu';
export RaisedTextField from './components/RaisedTextField';
export TopBar, { topBarHeight } from './components/TopBar';
export FilePicker from './components/FilePicker';
export FileProgressDialog from './components/FilePicker/FileProgressDialog';
export ProgressDialog from './components/ProgressDialog';
export * as formComponents from './components/formComponents';
export AppContainer from './components/AppContainer';
export OAuthLoginButton from './components/OAuthLoginButton';
export { Table, PaginatedTable, Column, LOADING } from './components/DataTable';
export MenuColumn from './components/DataTable/MenuColumn';
export {
  withVeritoneSDKThemeProvider,
  VeritoneSDKThemeProvider,
  defaultVSDKTheme
} from './helpers/withVeritoneSDKThemeProvider';
export SearchPill from './components/SearchPill';
export HorizontalScroll from './components/HorizontalScroll';
export GeoPicker from './components/GeoPicker';
export ExpandableInputField from './components/ExpandableInputField';
export SearchBar from './components/SearchBar';
export BoundingPolyOverlay from './components/BoundingPolyOverlay/Overlay';
export OverlayPositioningProvider from './components/BoundingPolyOverlay/OverlayPositioningProvider';
export { Interval, defaultIntervals } from 'helpers/date';
export StatusPill from './components/StatusPill';
export ModalHeader from './components/ModalHeader';
export SelectionButton from './components/SelectionButton';
export MediaPlayer from './components/MediaPlayer';
export RestartMediaButton from './components/MediaPlayer/RestartMediaButton';
export Lightbox from './components/share-components/Lightbox';
<commit_msg>Expose VideoSource<commit_after>import '../src/styles/global.scss';

export AppBar, { appBarHeight } from './components/AppBar';
export AppFooter, {
  appFooterHeightShort,
  appFooterHeightTall
} from './components/AppFooter';
export AppSwitcher from './components/AppSwitcher';
export Avatar from './components/Avatar';
export Chip from './components/Chip';
export Lozenge from './components/Lozenge';
export Truncate from './components/Truncate';
export DelayedProgress from './components/DelayedProgress';
export NavigationSideBar, {
  sectionsShape as navigationSidebarSectionsShape
} from './components/NavigationSideBar';
export DiscoverySideBar, {
  sectionsShape as discoverySidebarSectionsShape,
  DiscoverySideBarContainerPure as DiscoverySideBarPure
} from './components/DiscoverySideBar';
export FullScreenDialog from './components/FullScreenDialog';
export ProfileMenu from './components/ProfileMenu';
export RaisedTextField from './components/RaisedTextField';
export TopBar, { topBarHeight } from './components/TopBar';
export FilePicker from './components/FilePicker';
export FileProgressDialog from './components/FilePicker/FileProgressDialog';
export ProgressDialog from './components/ProgressDialog';
export * as formComponents from './components/formComponents';
export AppContainer from './components/AppContainer';
export OAuthLoginButton from './components/OAuthLoginButton';
export { Table, PaginatedTable, Column, LOADING } from './components/DataTable';
export MenuColumn from './components/DataTable/MenuColumn';
export {
  withVeritoneSDKThemeProvider,
  VeritoneSDKThemeProvider,
  defaultVSDKTheme
} from './helpers/withVeritoneSDKThemeProvider';
export SearchPill from './components/SearchPill';
export HorizontalScroll from './components/HorizontalScroll';
export GeoPicker from './components/GeoPicker';
export ExpandableInputField from './components/ExpandableInputField';
export SearchBar from './components/SearchBar';
export BoundingPolyOverlay from './components/BoundingPolyOverlay/Overlay';
export OverlayPositioningProvider from './components/BoundingPolyOverlay/OverlayPositioningProvider';
export { Interval, defaultIntervals } from 'helpers/date';
export StatusPill from './components/StatusPill';
export ModalHeader from './components/ModalHeader';
export SelectionButton from './components/SelectionButton';
export MediaPlayer from './components/MediaPlayer';
export RestartMediaButton from './components/MediaPlayer/RestartMediaButton';
export Lightbox from './components/share-components/Lightbox';
export VideoSource from './components/MediaPlayer/VideoSource';
<|endoftext|>


<commit_before>const fs = require('fs')
const path = require('path')
const webpack = require('webpack')

const {
  getMainEntry,
  getH5Options
} = require('@dcloudio/uni-cli-shared')

const {
  getGlobalUsingComponentsCode
} = require('@dcloudio/uni-cli-shared/lib/pages')

const modifyVueLoader = require('../vue-loader')

const WebpackHtmlAppendPlugin = require('../../packages/webpack-html-append-plugin')

const WebpackUniAppPlugin = require('../../packages/webpack-uni-app-loader/plugin/index')

function resolve (dir) {
  return path.resolve(__dirname, '../../', dir)
}

const {
  title,
  publicPath,
  template,
  devServer
} = getH5Options()

const runtimePath = '@dcloudio/uni-mp-weixin/dist/mp.js'
const wxsPath = '@dcloudio/uni-mp-weixin/dist/wxs.js'
const uniCloudPath = path.resolve(__dirname, '../../packages/uni-cloud/dist/index.js')

function getProvides () {
  return {
    'uniCloud': [uniCloudPath, 'default'],
    'wx.nextTick': [runtimePath, 'nextTick'],
    'Page': [runtimePath, 'Page'],
    'Component': [runtimePath, 'Component'],
    'Behavior': [runtimePath, 'Behavior'],
    'getDate': [wxsPath, 'getDate'],
    'getRegExp': [wxsPath, 'getRegExp']
  }
}

const plugins = [
  new WebpackUniAppPlugin(),
  new webpack.ProvidePlugin(getProvides())
]

if (process.env.NODE_ENV !== 'production') {
  plugins.push(new WebpackHtmlAppendPlugin(
    `
        <script>
        ${fs.readFileSync(path.resolve(__dirname, './auto-reload.js'), 'utf8')}
        </script>
        `
  ))
}

const vueConfig = {
  parallel: false, // 因为传入了自定义 compiler，避免参数丢失，禁用parallel
  publicPath,
  pages: {
    index: {
      // page 的入口
      entry: path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()),
      // 模板来源
      template,
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时，
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title,
      // 在这个页面中包含的块，默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index'],
      baseUrl: publicPath
    }
  }
}

if (devServer && Object.keys(devServer).length) {
  vueConfig.devServer = devServer
}

module.exports = {
  vueConfig,
  webpackConfig (webpackConfig) {
    let useBuiltIns = 'usage'

    const statCode = process.env.UNI_USING_STAT ? `import '@dcloudio/uni-stat';` : ''

    try {
      const babelConfig = require(path.resolve(process.env.UNI_CLI_CONTEXT, 'babel.config.js'))
      useBuiltIns = babelConfig.presets[0][1].useBuiltIns
    } catch (e) {}

    const beforeCode = (useBuiltIns === 'entry' ? `import '@babel/polyfill';` : '') +
      `import 'uni-pages';import 'uni-${process.env.UNI_PLATFORM}';`

    return {
      devtool: process.env.NODE_ENV === 'production' ? false : 'cheap-module-eval-source-map',
      resolve: {
        extensions: ['.nvue'],
        alias: {
          'vue-router': resolve('packages/h5-vue-router'),
          'uni-h5': require.resolve('@dcloudio/uni-h5'),
          'uni-qh': path.resolve(__dirname, 'qh-api.js')
        }
      },
      module: {
        rules: [{
          test: path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()),
          use: [{
            loader: 'wrap-loader',
            options: {
              before: [
                beforeCode + statCode + getGlobalUsingComponentsCode()
              ]
            }
          }]
        }, {
          test: /App\.vue$/,
          use: {
            loader: 'wrap-loader',
            options: {
              before: [`<template><App :keepAliveInclude="keepAliveInclude"/></template>`]
            }
          }
        }, { // 解析组件，css 等
          resourceQuery: /vue&type=script/,
          use: [{
            loader: path.resolve(__dirname,
              '../../packages/webpack-uni-app-loader/using-components')
          }]
        }, {
          resourceQuery: /vue&type=template/,
          use: [{
            loader: resolve('packages/webpack-uni-app-loader/filter-modules-template.js')
          }, {
            loader: '@dcloudio/vue-cli-plugin-uni/packages/webpack-uni-app-loader/page-meta'
          }]
        }, {
          resourceQuery: [/lang=wxs/, /blockType=wxs/],
          use: [{
            loader: resolve('packages/webpack-uni-filter-loader')
          }]
        }]
      },
      resolveLoader: {
        alias: {
          'vue-style-loader': resolve('packages/h5-vue-style-loader')
        }
      },
      plugins
    }
  },
  chainWebpack (webpackConfig, vueOptions, api) {
    webpackConfig.plugins.delete('copy')

    if (!process.env.UNI_OPT_PREFETCH) {
      webpackConfig.plugins.delete('prefetch-index')
    }
    if (!process.env.UNI_OPT_PRELOAD) {
      webpackConfig.plugins.delete('preload-index')
    }

    modifyVueLoader(webpackConfig, {
      isH5: true,
      hotReload: true
    }, require('./compiler-options'), api)

    if (process.env.NODE_ENV === 'production') {
      require('./cssnano-options')(webpackConfig)
    }
  }
}
<commit_msg>feat(h5): add format-log<commit_after>const fs = require('fs')
const path = require('path')
const webpack = require('webpack')

const {
  getMainEntry,
  getH5Options
} = require('@dcloudio/uni-cli-shared')

const {
  getGlobalUsingComponentsCode
} = require('@dcloudio/uni-cli-shared/lib/pages')

const modifyVueLoader = require('../vue-loader')

const WebpackHtmlAppendPlugin = require('../../packages/webpack-html-append-plugin')

const WebpackUniAppPlugin = require('../../packages/webpack-uni-app-loader/plugin/index')

function resolve (dir) {
  return path.resolve(__dirname, '../../', dir)
}

const {
  title,
  publicPath,
  template,
  devServer
} = getH5Options()

const runtimePath = '@dcloudio/uni-mp-weixin/dist/mp.js'
const wxsPath = '@dcloudio/uni-mp-weixin/dist/wxs.js'
const uniCloudPath = path.resolve(__dirname, '../../packages/uni-cloud/dist/index.js')

function getProvides () {
  return {
    '__f__': [path.resolve(__dirname, '../format-log.js'), 'default'],
    'uniCloud': [uniCloudPath, 'default'],
    'wx.nextTick': [runtimePath, 'nextTick'],
    'Page': [runtimePath, 'Page'],
    'Component': [runtimePath, 'Component'],
    'Behavior': [runtimePath, 'Behavior'],
    'getDate': [wxsPath, 'getDate'],
    'getRegExp': [wxsPath, 'getRegExp']
  }
}

const plugins = [
  new WebpackUniAppPlugin(),
  new webpack.ProvidePlugin(getProvides())
]

if (process.env.NODE_ENV !== 'production') {
  plugins.push(new WebpackHtmlAppendPlugin(
    `
        <script>
        ${fs.readFileSync(path.resolve(__dirname, './auto-reload.js'), 'utf8')}
        </script>
        `
  ))
}

const vueConfig = {
  parallel: false, // 因为传入了自定义 compiler，避免参数丢失，禁用parallel
  publicPath,
  pages: {
    index: {
      // page 的入口
      entry: path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()),
      // 模板来源
      template,
      // 在 dist/index.html 的输出
      filename: 'index.html',
      // 当使用 title 选项时，
      // template 中的 title 标签需要是 <title><%= htmlWebpackPlugin.options.title %></title>
      title,
      // 在这个页面中包含的块，默认情况下会包含
      // 提取出来的通用 chunk 和 vendor chunk。
      chunks: ['chunk-vendors', 'chunk-common', 'index'],
      baseUrl: publicPath
    }
  }
}

if (devServer && Object.keys(devServer).length) {
  vueConfig.devServer = devServer
}

module.exports = {
  vueConfig,
  webpackConfig (webpackConfig) {
    let useBuiltIns = 'usage'

    const statCode = process.env.UNI_USING_STAT ? `import '@dcloudio/uni-stat';` : ''

    try {
      const babelConfig = require(path.resolve(process.env.UNI_CLI_CONTEXT, 'babel.config.js'))
      useBuiltIns = babelConfig.presets[0][1].useBuiltIns
    } catch (e) {}

    const beforeCode = (useBuiltIns === 'entry' ? `import '@babel/polyfill';` : '') +
      `import 'uni-pages';import 'uni-${process.env.UNI_PLATFORM}';`

    return {
      devtool: process.env.NODE_ENV === 'production' ? false : 'cheap-module-eval-source-map',
      resolve: {
        extensions: ['.nvue'],
        alias: {
          'vue-router': resolve('packages/h5-vue-router'),
          'uni-h5': require.resolve('@dcloudio/uni-h5'),
          'uni-qh': path.resolve(__dirname, 'qh-api.js')
        }
      },
      module: {
        rules: [{
          test: path.resolve(process.env.UNI_INPUT_DIR, getMainEntry()),
          use: [{
            loader: 'wrap-loader',
            options: {
              before: [
                beforeCode + statCode + getGlobalUsingComponentsCode()
              ]
            }
          }]
        }, {
          test: /App\.vue$/,
          use: {
            loader: 'wrap-loader',
            options: {
              before: [`<template><App :keepAliveInclude="keepAliveInclude"/></template>`]
            }
          }
        }, { // 解析组件，css 等
          resourceQuery: /vue&type=script/,
          use: [{
            loader: path.resolve(__dirname,
              '../../packages/webpack-uni-app-loader/using-components')
          }]
        }, {
          resourceQuery: /vue&type=template/,
          use: [{
            loader: resolve('packages/webpack-uni-app-loader/filter-modules-template.js')
          }, {
            loader: '@dcloudio/vue-cli-plugin-uni/packages/webpack-uni-app-loader/page-meta'
          }]
        }, {
          resourceQuery: [/lang=wxs/, /blockType=wxs/],
          use: [{
            loader: resolve('packages/webpack-uni-filter-loader')
          }]
        }]
      },
      resolveLoader: {
        alias: {
          'vue-style-loader': resolve('packages/h5-vue-style-loader')
        }
      },
      plugins
    }
  },
  chainWebpack (webpackConfig, vueOptions, api) {
    webpackConfig.plugins.delete('copy')

    if (!process.env.UNI_OPT_PREFETCH) {
      webpackConfig.plugins.delete('prefetch-index')
    }
    if (!process.env.UNI_OPT_PRELOAD) {
      webpackConfig.plugins.delete('preload-index')
    }

    modifyVueLoader(webpackConfig, {
      isH5: true,
      hotReload: true
    }, require('./compiler-options'), api)

    if (process.env.NODE_ENV === 'production') {
      require('./cssnano-options')(webpackConfig)
    }
  }
}
<|endoftext|>


<commit_before>// {
//   text: String,
//   location: String,
//   timestamp: EJSON.Date,
//   flagged: Boolean,
//   answered: Boolean,
//   poster: {
//     name: String,
//     emailMd5: String (hash),
//     id: String (ObjectId)
//   }
// }
Questions = new Meteor.Collection("questions");

if (Meteor.isServer) {
  Meteor.startup(function () {
    if (!Questions.find().count())
      _.each([{
        text: "lorem posum realtime ipsum minimongo short one",
        location: "front door",
        timestamp: (new Date),
        flagged: true,
        answered: false,
        poster: {
          name: "Realtime monkey",
          emailMd5: "5cc411461e4b68aa2d0400a7eeccf949",
          id: ""
        }
      }, {
        text: "Echo Park excepteur Wes Anderson Pinterest, fugiat trust fund Cosby sweater McSweeney's 3 wolf moon XOXO organic bitters Portland Brooklyn. Put a bird on it Tonx cred, cardigan mustache keytar scenester. Irure laborum occupy Etsy craft beer. Swag Marfa distillery, nulla leggings disrupt gastropub small batch Thundercats dolore hoodie Brooklyn. Cosby sweater Shoreditch try-hard irony gluten-free. Church-key hella kogi, Truffaut enim next level chia pork belly photo booth fap Tonx. Blue Bottle mlkshk nesciunt mixtape sriracha typewriter, locavore retro meggings dolor Cosby sweater fanny pack.",
        location: "roof",
        timestamp: new Date(new Date - 1000*10*60),
        flagged: false,
        answered: false,
        poster: {
          name: "Hipster Hacker",
          emailMd5: "5cc411461e4b68aa2d0400a7eeccf949",
          id: ""
        }
      }], function (x) {
        Questions.insert(x);
      });
  });

  Meteor.publish("questions", function () {
    return Questions.find();
  });
} else {
  Meteor.subscribe("questions");
}

Meteor.methods({
  'hasPermissions': function (userId, doc) {
    try {
      var creatorId = doc.poster.userId;
      if (creatorId === "" || !creatorId || creatorId === userId) {
        return true;
      }
      return (/^.*@meteor.com$/.test(Users.findOne(userId).services.github.email));
    } catch (e) {
      return false;
    }
  }
});

<commit_msg>Publish users info<commit_after>// {
//   text: String,
//   location: String,
//   timestamp: EJSON.Date,
//   flagged: Boolean,
//   answered: Boolean,
//   poster: {
//     name: String,
//     emailMd5: String (hash),
//     id: String (ObjectId)
//   }
// }
Questions = new Meteor.Collection("questions");

if (Meteor.isServer) {
  Meteor.startup(function () {
    if (!Questions.find().count())
      _.each([{
        text: "lorem posum realtime ipsum minimongo short one",
        location: "front door",
        timestamp: (new Date),
        flagged: true,
        answered: false,
        poster: {
          name: "Realtime monkey",
          emailMd5: "5cc411461e4b68aa2d0400a7eeccf949",
          id: ""
        }
      }, {
        text: "Echo Park excepteur Wes Anderson Pinterest, fugiat trust fund Cosby sweater McSweeney's 3 wolf moon XOXO organic bitters Portland Brooklyn. Put a bird on it Tonx cred, cardigan mustache keytar scenester. Irure laborum occupy Etsy craft beer. Swag Marfa distillery, nulla leggings disrupt gastropub small batch Thundercats dolore hoodie Brooklyn. Cosby sweater Shoreditch try-hard irony gluten-free. Church-key hella kogi, Truffaut enim next level chia pork belly photo booth fap Tonx. Blue Bottle mlkshk nesciunt mixtape sriracha typewriter, locavore retro meggings dolor Cosby sweater fanny pack.",
        location: "roof",
        timestamp: new Date(new Date - 1000*10*60),
        flagged: false,
        answered: false,
        poster: {
          name: "Hipster Hacker",
          emailMd5: "5cc411461e4b68aa2d0400a7eeccf949",
          id: ""
        }
      }], function (x) {
        Questions.insert(x);
      });
  });

  Meteor.publish("questions", function () {
    return Questions.find();
  });
  Meteor.publish("userData", function () {
    return Meteor.users.find({_id: this.userId});
  });
} else {
  Meteor.subscribe("questions");
  Meteor.subscribe("userData");
}

Meteor.methods({
  'hasPermissions': function (userId, doc) {
    try {
      var creatorId = doc.poster.userId;
      if (creatorId === "" || !creatorId || creatorId === userId) {
        return true;
      }
      return (/^.*@meteor.com$/.test(Users.findOne(userId).services.github.email));
    } catch (e) {
      return false;
    }
  }
});

<|endoftext|>


<commit_before>function drawNlogN(){
  var numPoints = 20,
      points = _.sortBy(uniformRandom(numPoints), f('x'))

  points.forEach(function(d, i){ d.i = i })

  var svg = d3.select('#nlogn').html('')
    .append('svg')
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

  var lineG = svg.append('g')

  var circles = svg.append('g').selectAll('circle')
      .data(points).enter()
    .append('circle').classed('point', true)
      .attr('r', 5)
      .attr('cx', f('x'))
      .attr('cy', f('y'))
      .each(function(d){ d.circle = d3.select(this) })
      .on('mouseover', function(d, i){
        if (i == activeI) iteratePoint()
      })

  svg.append('defs').append('clipPath').attr('id', 'xoclip').append('rect')
      .attr({height: height, width: 0, 'fill-opacity': 0})
    .transition().duration(3).ease('linear')
      .attr({width: width})
    .each('end', iteratePoint)

  lineG.append('path').classed('xorder', true)
      .attr('d', 'M' + points.map(f('p')).join('L'))
      .attr('clip-path', 'url(#xoclip)')

  var rect = lineG.append('rect')
      .attr({height: height, width: 0})

  var activePoints = lineG.append('path').classed('cur-hull', true)

  var topPoints = [points[0], points[1]]
  var curI = 2
  var lookingBack = false
  var activeI = curI

  function iteratePoint(){
    if (curI > points.length  - 1) return

    var curPoint = points[curI]

    if (!lookingBack){
      activeI = _.last(topPoints).i
      lookingBack = true

      activePoints.attr('d',  'M' + topPoints
          .concat(_.last(topPoints))
        .map(f('p')).join('L'))
      .transition().duration(1000).attr('d', 'M' + topPoints
          .concat(curPoint)
        .map(f('p')).join('L'))
      .each('end', checkForAngleDraw)

      rect.transition().duration(1000).attr('width', curPoint.x)

    } else{
      var a = topPoints[topPoints.length - 2]
      var b = topPoints[topPoints.length - 1]
      var keep = a ? !lessThan180(a, b, curPoint) : true

      b.g ? b.g.remove() : ''
      if (!keep && topPoints.length > 1){
        topPoints.pop()
        b.circle.classed('passed-point', true)
        activeI = _.last(topPoints).i

        activePoints.attr('d',  'M' + topPoints
            .concat(b)
            .concat(curPoint)
          .map(f('p')).join('L'))
        .transition().duration(1000).attr('d', 'M' + topPoints
            .concat({p: midPoint(a, curPoint)})
            .concat(curPoint)
          .map(f('p')).join('L'))
        .each('end', checkForAngleDraw)

      } else{
        lookingBack = false
        topPoints.push(curPoint)
        curI++
        activeI = curI
        curPoint = points[curI]
      }

    }

    circles.attr('r', function(d, i){ return i < curI ? 5 : 5 })
    topPoints.concat(curPoint).forEach(function(d){
      d.circle.attr('r', 10).classed('top-point', true)
    })

    circles.classed('next-point', function(d, i){
      return i === activeI
    })

    function checkForAngleDraw(){
      drawAngle(curPoint, topPoints[topPoints.length - 1], topPoints[topPoints.length - 2])
    }
  }

  circles.classed('next-point', function(d, i){
    return i === curI
  })

  // svg.append('rect')
  //     .attr({width: width, height: height})
  //     .on('mousemove', function(){
  //       var p3 = d3.mouse(this)
  //       var p3 = {x: p3[0], y: p3[1]}
  //       mPath.attr('d', ['M', p2.x, ',', p2.y, 'L', p3.x, ',', p3.y].join(''))
  //       //console.log(calcAngle(p1, p2, p3))
  //       ang = isLessThan180(p1, p2, p3)
  //       aText.text(ang)
  //     })
  // var p1 = {x: 200, y: 200}
  // var p2 = {x: 300, y: 200}
  // svg.append('path').classed('mline', true)
  //     .attr('d', ['M', p1.x, ',', p1.y, 'L', p2.x, ',', p2.y].join(''))

  // var mPath = svg.append('path').classed('mline', true)
  // var aText = svg.append('text').style('fill', 'white')
  //     .attr({dy: '3em', dx: '3em'})

  addResetButton(svg, drawNlogN)
}
drawNlogN()<commit_msg>nn ends nicely<commit_after>function drawNlogN(){
  var numPoints = 20,
      points = _.sortBy(uniformRandom(numPoints), f('x'))

  points.forEach(function(d, i){ d.i = i })

  var svg = d3.select('#nlogn').html('')
    .append('svg')
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

  var lineG = svg.append('g')

  var circles = svg.append('g').selectAll('circle')
      .data(points).enter()
    .append('circle').classed('point', true)
      .attr('r', 5)
      .attr('cx', f('x'))
      .attr('cy', f('y'))
      .each(function(d){ d.circle = d3.select(this) })
      .on('mouseover', function(d, i){
        if (i == activeI) iteratePoint()
      })

  svg.append('defs').append('clipPath').attr('id', 'xoclip').append('rect')
      .attr({height: height, width: 0, 'fill-opacity': 0})
    .transition().duration(3).ease('linear')
      .attr({width: width})
    .each('end', iteratePoint)

  lineG.append('path').classed('xorder', true)
      .attr('d', 'M' + points.map(f('p')).join('L'))
      .attr('clip-path', 'url(#xoclip)')

  var rect = lineG.append('rect')
      .attr({height: height, width: 0})

  var activePoints = lineG.append('path').classed('cur-hull', true)

  var topPoints = [points[0], points[1]]
  var curI = 2
  var lookingBack = false
  var activeI = curI

  function iteratePoint(){
    if (curI > points.length  - 1) return

    var curPoint = points[curI]

    if (!lookingBack){
      activeI = _.last(topPoints).i
      lookingBack = true

      activePoints.attr('d',  'M' + topPoints
          .concat(_.last(topPoints))
        .map(f('p')).join('L'))
      .transition().duration(1000).attr('d', 'M' + topPoints
          .concat(curPoint)
        .map(f('p')).join('L'))
      .each('end', checkForAngleDraw)

      rect.transition().duration(1000).attr('width', curPoint.x)

    } else{
      var a = topPoints[topPoints.length - 2]
      var b = topPoints[topPoints.length - 1]
      var keep = a ? !lessThan180(a, b, curPoint) : true

      b.g ? b.g.remove() : ''
      if (!keep && topPoints.length > 1){
        topPoints.pop()
        b.circle.classed('passed-point', true)
        activeI = _.last(topPoints).i

        activePoints.attr('d',  'M' + topPoints
            .concat(b)
            .concat(curPoint)
          .map(f('p')).join('L'))
        .transition().duration(1000).attr('d', 'M' + topPoints
            .concat({p: midPoint(a, curPoint)})
            .concat(curPoint)
          .map(f('p')).join('L'))
        .each('end', checkForAngleDraw)

      } else{
        lookingBack = false
        topPoints.push(curPoint)
        curI++
        activeI = curI
        curPoint = points[curI]
      }

    }

    circles
        .classed('next-point', function(d, i){ return i === activeI })
        .attr('r', function(d, i){ return i < curI ? 5 : 5 })

    if (!curPoint){
      rect.transition().duration(1000).attr('width', width)
      return  //exit early if we're done
    }

    topPoints.concat(curPoint).forEach(function(d){
      d.circle.attr('r', 10).classed('top-point', true)
    })

    function checkForAngleDraw(){
      drawAngle(curPoint, topPoints[topPoints.length - 1], topPoints[topPoints.length - 2])
    }
  }

  circles.classed('next-point', function(d, i){
    return i === curI
  })

  // svg.append('rect')
  //     .attr({width: width, height: height})
  //     .on('mousemove', function(){
  //       var p3 = d3.mouse(this)
  //       var p3 = {x: p3[0], y: p3[1]}
  //       mPath.attr('d', ['M', p2.x, ',', p2.y, 'L', p3.x, ',', p3.y].join(''))
  //       //console.log(calcAngle(p1, p2, p3))
  //       ang = isLessThan180(p1, p2, p3)
  //       aText.text(ang)
  //     })
  // var p1 = {x: 200, y: 200}
  // var p2 = {x: 300, y: 200}
  // svg.append('path').classed('mline', true)
  //     .attr('d', ['M', p1.x, ',', p1.y, 'L', p2.x, ',', p2.y].join(''))

  // var mPath = svg.append('path').classed('mline', true)
  // var aText = svg.append('text').style('fill', 'white')
  //     .attr({dy: '3em', dx: '3em'})

  addResetButton(svg, drawNlogN)
}
drawNlogN()<|endoftext|>


<commit_before>var io = require('./io');
var templates = require('./templates');
var ObjectId = require('modm').ObjectId;

function createRequest (method, link) {

    var data = link.data || {};

    // template id is mandatory
    if (!data.t) {
        return;
    }

    var request = {
        role: link.session._rid,
        options: {},
        templateId: data.t
    };

    // query
    request.query = data.q || {};
    request.query._tp = data.t;

    // update
    if (data.d && data.d.constructor.name === 'Object') {

        // set type
        if (method === 'insert') {
            data.d._tp = data.t;
        }

        request.data = data.d;
    }

    // options
    if (data.o && data.o.constructor.name === 'Object') {
        request.options = data.o;
    }

    // fields
    if (data.f && data.f.constructor.name === 'Object') {
        request.options.fields = data.f;
    }

    return request;
}

function convertToObjectId (request) {
    try {
        // convert _id to MongoDB's ObjectId
        if (request.query._id) {

            // convert with operators
            if (typeof request.query._id === 'object') {
                for (var op in request.query._id) {
                    for (var i = 0, l = request.query._id[op].length; i < l; ++i) {
                        request.query._id[op][i] = ObjectId(equest.query._id[op][i]);
                    }
                }
            } else {
                request.query._id = ObjectId(request.query._id);
            }
        }

        // convert _tp to MongoDB's ObjectId
        if (request.query['_tp']) {
            request.query['_tp'] = ObjectId(request.query['_tp']);
        }

        // convert _ln._id to MongoDB's ObjectId
        if (request.query['_ln._id']) {
            request.query['_ln._id'] = ObjectId(request.query['_ln._id']);
        }

        // convert _ln._tp to MongoDB's ObjectId
        if (request.query['_ln._tp']) {
            request.query['_ln._tp'] = ObjectId(request.query['_ln._id']);
        }

        return request;
    } catch (err) {
        return;
    }
}

module.exports = function (method, link) {

    if (!io[method]) {
        return link.send(501, 'Method not implemented');
    }

    // check parameters
    var request = createRequest(method, link);

    if (!request) {
        return link.send(400, 'Bad request.');
    }

    // get template (cache)
    templates.getTemplate(request, function (err, request) {

        if (err) {
            return link.send(err.statusCode || 500, err.message);
        }

        request = convertToObjectId(request);

        if (!request) {
            return link.send(400, 'Incorrect ObjectId format');
        }

        // do input/output
        io[method](link, request);
    });
};
<commit_msg>Convert elemMatch keys into objectid.<commit_after>var io = require('./io');
var templates = require('./templates');
var ObjectId = require('modm').ObjectId;

function createRequest (method, link) {

    var data = link.data || {};

    // template id is mandatory
    if (!data.t) {
        return;
    }

    var request = {
        role: link.session._rid,
        options: {},
        templateId: data.t
    };

    // query
    request.query = data.q || {};
    request.query._tp = data.t;

    // update
    if (data.d && data.d.constructor.name === 'Object') {

        // set type
        if (method === 'insert') {
            data.d._tp = data.t;
        }

        request.data = data.d;
    }

    // options
    if (data.o && data.o.constructor.name === 'Object') {
        request.options = data.o;
    }

    // fields
    if (data.f && data.f.constructor.name === 'Object') {
        request.options.fields = data.f;
    }

    return request;
}

function convertToObjectId (request) {
    try {
        // convert _id to MongoDB's ObjectId
        if (request.query._id) {

            // convert with operators
            if (typeof request.query._id === 'object') {
                for (var op in request.query._id) {
                    for (var i = 0, l = request.query._id[op].length; i < l; ++i) {
                        request.query._id[op][i] = ObjectId(equest.query._id[op][i]);
                    }
                }
            } else {
                request.query._id = ObjectId(request.query._id);
            }
        }

        // convert _tp to MongoDB's ObjectId
        if (request.query['_tp']) {
            request.query['_tp'] = ObjectId(request.query['_tp']);
        }

        // convert _ln._id to MongoDB's ObjectId
        if (request.query['_ln._id']) {
            request.query['_ln._id'] = ObjectId(request.query['_ln._id']);
        }

        // convert _ln._tp to MongoDB's ObjectId
        if (request.query['_ln._tp']) {
            var tp = request.query['_ln._tp'];
            for (var i in tp) {
                tp[i] = ObjectId(tp[i]);
            }
        }

        // convert _ln.$elemMatch keys to MongoDB's ObjectId
        if (request.query['_ln']['$elemMatch']) {
            var elemMatch = request.query['_ln']['$elemMatch'];
            for (var i in elemMatch) {
                if (['_id', '_tp'].indexOf(i) > -1) {
                    elemMatch[i] = ObjectId(elemMatch[i]);
                }
            }
        }

        return request;
    } catch (err) {
        return;
    }
}

module.exports = function (method, link) {

    if (!io[method]) {
        return link.send(501, 'Method not implemented');
    }

    // check parameters
    var request = createRequest(method, link);

    if (!request) {
        return link.send(400, 'Bad request.');
    }

    // get template (cache)
    templates.getTemplate(request, function (err, request) {

        if (err) {
            return link.send(err.statusCode || 500, err.message);
        }

        request = convertToObjectId(request);

        if (!request) {
            return link.send(400, 'Incorrect ObjectId format');
        }

        // do input/output
        io[method](link, request);
    });
};
<|endoftext|>


<commit_before>/**
 * Hey guys! I'm going to write kind of a template here that you guys
 * can fill out however you like. If you want to change function names
 * or given variable names, let me know and I will update the other
 * files to reflect the changes!
 */

/**
 * The Model is a container for a set of bodies. You'll be able to
 * manipulate them through here!
 */

var Sector = function(){
	this.bodies = [];
	// this.planets = [];
	// this.stars = []
	this.running = true;
	this.k = 5;
}

Sector.prototype.addBody = function(x, y, t, vx, vy, r) { // Remind Kevin to edit values
	vx *= this.k;
	vy *= this.k;
	if (t > 2)
	{
		var new_body = new Star(x, y, t, vx, vy); // Remind Kevin to put stars.
		this.bodies.push(new_body);
		//this.stars.push(new_body)
	}
	else if (t < 1)
	{
		var new_body = new Moon(x, y, t, vx, vy, r);
		this.bodies.push(new_body);
	}
	else
	{
		var new_body = new Planet(x, y, t, vx, vy, r);
		this.bodies.push(new_body);
		//this.planets.push(new_body);
	}
}
Sector.prototype.update = function(dt) {
	if (this.running) {
		for (var idx in this.bodies) {
			this.bodies[idx].move(dt);

			if (this.bodies.length > 1) {

				var accel_vector = this.neighbor(this.bodies[idx]);
				var x_accel = accel_vector[0];
				var y_accel = accel_vector[1];

				this.bodies[idx].accelerate(x_accel, y_accel, dt);
			}
			this.collision_update();
		}
	}
}
Sector.prototype.get_bodies = function() {
	return this.bodies;
}

Sector.prototype.collision_update = function(){
	var col_array = [[],[]]; // collision
	for (var idx = 0; idx < this.bodies.length - 1; idx++) {
		for (var idx2 = idx + 1; idx2 < this.bodies.length; idx2++) {
			if (idx != idx2) {
				var x1 = this.bodies[idx].x_position;
				var x2 = this.bodies[idx2].x_position;
				var y1 = this.bodies[idx].y_position;
				var y2 = this.bodies[idx2].y_position; // Remind Kevin to add index.
				var x_dist = (x1-x2);
				var y_dist = (y1-y2);
				var t_dist = AstroMath.distance(x_dist, y_dist, 0, 0);
				var required_space = this.bodies[idx].radius + this.bodies[idx2].radius;
				if (required_space > t_dist){
					console.log("COLLISION DETECTED", required_space, t_dist)
					if (Star.prototype.isPrototypeOf(this.bodies[idx])) {
						this.bodies.splice(idx2, 1);
						var coor = (x2,y2);
						col_array[0].push(coor);
					}
					else if (Star.prototype.isPrototypeOf(this.bodies[idx2])){
						this.bodies.splice(idx, 1);
						var coor = (x1,y1);
						col_array[0].push(coor);
					}
					else {
						console.log(this.bodies, idx);
						console.log("Removing!", this.bodies[idx]);
						this.bodies.splice(idx, 1);
						this.bodies.splice(idx2 - 1, 1);
						var x_coor = (x1+x2)/2;
						var y_coor = (y1+y2)/2;
						var coor = (x_coor,y_coor);
						col_array[1].push(coor);
					}
				}
			}
		}
	}
	return col_array;
}

Sector.prototype.neighbor = function(body) {

	var dist_array = [];
	var neighbor_array = [];

	var orbitables = [];

	if (Moon.prototype.isPrototypeOf(body)) {
		for (var idx in this.bodies) {
			if (Planet.prototype.isPrototypeOf(this.bodies[idx])) {
				orbitables.push(this.bodies[idx]);
			}
		}
	}
	else if (Planet.prototype.isPrototypeOf(body)) {
		for (var idx in this.bodies) {
			if (Star.prototype.isPrototypeOf(this.bodies[idx])) {
				orbitables.push(this.bodies[idx]);
			}
		}
	}

	if (orbitables.length != 0) {
		for (var idx in orbitables) {
			var dist = AstroMath.distance(body.x_position, body.y_position, orbitables[idx].x_position, orbitables[idx].y_position);
			if (dist != 0){
				dist_array.push(dist);
				neighbor_array.push(orbitables[idx]);
			}
		}

		var min_dist = dist_array[0];
		for (var i = 1; i < dist_array.length; i++) {
			if (dist_array[i] < min_dist) {
				min_dist = dist_array[i];
			}
		}
		var n_idx = dist_array.indexOf(min_dist);
		var best_neighbor = neighbor_array[n_idx];
		// console.log(body, best_neighbor, neighbor_array, n_idx);
		return this.acceleration(body, best_neighbor);
	} else {
		return [0, 0];
	}
}

// calculates the acceleration for body1 (body2 is unaffected by this acceleration)
Sector.prototype.acceleration = function(body1, body2) {

	var dx = body2.x_position - body1.x_position;
	var dy = body2.y_position - body1.y_position;
	var dist = AstroMath.distance(0, 0, dx, dy);
	if (dist == 0) {
		return [0, 0];
	}
	var magnitude = body2.mass / Math.pow(dist, 2);
	// console.log(magnitude);

	var cos = dx / dist;
	var sin = dy / dist;
	return [magnitude*cos, magnitude*sin]
}
<commit_msg>slightly altered output to collision_update<commit_after>/**
 * Hey guys! I'm going to write kind of a template here that you guys
 * can fill out however you like. If you want to change function names
 * or given variable names, let me know and I will update the other
 * files to reflect the changes!
 */

/**
 * The Model is a container for a set of bodies. You'll be able to
 * manipulate them through here!
 */

var Sector = function(){
	this.bodies = [];
	// this.planets = [];
	// this.stars = []
	this.running = true;
	this.k = 5;
}

Sector.prototype.addBody = function(x, y, t, vx, vy, r) { // Remind Kevin to edit values
	vx *= this.k;
	vy *= this.k;
	if (t > 2)
	{
		var new_body = new Star(x, y, t, vx, vy); // Remind Kevin to put stars.
		this.bodies.push(new_body);
		//this.stars.push(new_body)
	}
	else if (t < 1)
	{
		var new_body = new Moon(x, y, t, vx, vy, r);
		this.bodies.push(new_body);
	}
	else
	{
		var new_body = new Planet(x, y, t, vx, vy, r);
		this.bodies.push(new_body);
		//this.planets.push(new_body);
	}
}
Sector.prototype.update = function(dt) {
	if (this.running) {
		for (var idx in this.bodies) {
			this.bodies[idx].move(dt);

			if (this.bodies.length > 1) {

				var accel_vector = this.neighbor(this.bodies[idx]);
				var x_accel = accel_vector[0];
				var y_accel = accel_vector[1];

				this.bodies[idx].accelerate(x_accel, y_accel, dt);
			}
			this.collision_update();
		}
	}
}
Sector.prototype.get_bodies = function() {
	return this.bodies;
}

Sector.prototype.collision_update = function(){
	var col_array = []; // collision
	for (var idx = 0; idx < this.bodies.length - 1; idx++) {
		for (var idx2 = idx + 1; idx2 < this.bodies.length; idx2++) {
			if (idx != idx2) {
				var x1 = this.bodies[idx].x_position;
				var x2 = this.bodies[idx2].x_position;
				var y1 = this.bodies[idx].y_position;
				var y2 = this.bodies[idx2].y_position; // Remind Kevin to add index.
				var x_dist = (x1-x2);
				var y_dist = (y1-y2);
				var t_dist = AstroMath.distance(x_dist, y_dist, 0, 0);
				var required_space = this.bodies[idx].radius + this.bodies[idx2].radius;
				if (required_space > t_dist){
					console.log("COLLISION DETECTED", required_space, t_dist)
					if (Star.prototype.isPrototypeOf(this.bodies[idx])) {
						this.bodies.splice(idx2, 1);
						var coor = (x2,y2);
						col_array.push(coor);
					}
					else if (Star.prototype.isPrototypeOf(this.bodies[idx2])){
						this.bodies.splice(idx, 1);
						var coor = (x1,y1);
						col_array.push(coor);
					}
					else {
						console.log(this.bodies, idx);
						console.log("Removing!", this.bodies[idx]);
						this.bodies.splice(idx, 1);
						this.bodies.splice(idx2 - 1, 1);
						var x_coor = (x1+x2)/2;
						var y_coor = (y1+y2)/2;
						var coor = (x_coor,y_coor);
						col_array.push(coor);
					}
				}
			}
		}
	}
	return col_array;
}

Sector.prototype.neighbor = function(body) {

	var dist_array = [];
	var neighbor_array = [];

	var orbitables = [];

	if (Moon.prototype.isPrototypeOf(body)) {
		for (var idx in this.bodies) {
			if (Planet.prototype.isPrototypeOf(this.bodies[idx])) {
				orbitables.push(this.bodies[idx]);
			}
		}
	}
	else if (Planet.prototype.isPrototypeOf(body)) {
		for (var idx in this.bodies) {
			if (Star.prototype.isPrototypeOf(this.bodies[idx])) {
				orbitables.push(this.bodies[idx]);
			}
		}
	}

	if (orbitables.length != 0) {
		for (var idx in orbitables) {
			var dist = AstroMath.distance(body.x_position, body.y_position, orbitables[idx].x_position, orbitables[idx].y_position);
			if (dist != 0){
				dist_array.push(dist);
				neighbor_array.push(orbitables[idx]);
			}
		}

		var min_dist = dist_array[0];
		for (var i = 1; i < dist_array.length; i++) {
			if (dist_array[i] < min_dist) {
				min_dist = dist_array[i];
			}
		}
		var n_idx = dist_array.indexOf(min_dist);
		var best_neighbor = neighbor_array[n_idx];
		// console.log(body, best_neighbor, neighbor_array, n_idx);
		return this.acceleration(body, best_neighbor);
	} else {
		return [0, 0];
	}
}

// calculates the acceleration for body1 (body2 is unaffected by this acceleration)
Sector.prototype.acceleration = function(body1, body2) {

	var dx = body2.x_position - body1.x_position;
	var dy = body2.y_position - body1.y_position;
	var dist = AstroMath.distance(0, 0, dx, dy);
	if (dist == 0) {
		return [0, 0];
	}
	var magnitude = body2.mass / Math.pow(dist, 2);
	// console.log(magnitude);

	var cos = dx / dist;
	var sin = dy / dist;
	return [magnitude*cos, magnitude*sin]
}
<|endoftext|>


<commit_before>/**
 * AccessibleBlockly
 *
 * Copyright 2016 Google Inc.
 * https://developers.google.com/blockly/
 *
 * 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.
 */

/**
 * @fileoverview Angular2 Component that details how Blockly.Block's are
 * rendered in the workspace in AccessibleBlockly. Also handles any
 * interactions with the blocks.
 * @author madeeha@google.com (Madeeha Ghori)
 */

blocklyApp.WorkspaceTreeComponent = ng.core
  .Component({
    selector: 'blockly-workspace-tree',
    template: `

      {{block.toString()}}

          {{'BLOCK_ACTION_LIST'|translate}}

              <button [id]="idMap[buttonInfo.baseIdKey + 'Button']" (click)="buttonInfo.action()"
                      [disabled]="buttonInfo.isDisabled()">
                {{buttonInfo.translationIdForText|translate}}
              </button>

          <blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field" [level]="level + 1"></blockly-field>
          <blockly-workspace-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
                                  [block]="inputBlock.connection.targetBlock()" [level]="level + 1"
                                  [tree]="tree">
          </blockly-workspace-tree>

             {{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed:

                <button [id]="idMap[fieldButtonInfo.baseIdKey + 'Button']"
                        (click)="fieldButtonInfo.action(inputBlock.connection)"
                        [disabled]="fieldButtonInfo.isDisabled(inputBlock.connection)">
                  {{fieldButtonInfo.translationIdForText|translate}}
                </button>

    <blockly-workspace-tree *ngIf= "block.nextConnection && block.nextConnection.targetBlock()"
                            [block]="block.nextConnection.targetBlock()"
                            [level]="level" [tree]="tree">
    </blockly-workspace-tree>
    `,
    directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() {
      return blocklyApp.WorkspaceTreeComponent;
    })],
    inputs: ['block', 'level', 'tree', 'isTopLevel'],
    pipes: [blocklyApp.TranslatePipe]
  })
  .Class({
    constructor: [
        blocklyApp.ClipboardService, blocklyApp.TreeService,
        blocklyApp.UtilsService,
        function(_clipboardService, _treeService, _utilsService) {
      this.infoBlocks = Object.create(null);
      this.clipboardService = _clipboardService;
      this.treeService = _treeService;
      this.utilsService = _utilsService;
    }],
    isIsolatedTopLevelBlock_: function(block) {
      // Returns whether the given block is at the top level, and has no
      // siblings.
      var blockIsAtTopLevel = !block.getParent();
      var blockHasNoSiblings = (
          (!block.nextConnection ||
           !block.nextConnection.targetConnection) &&
          (!block.previousConnection ||
           !block.previousConnection.targetConnection));
      return blockIsAtTopLevel && blockHasNoSiblings;
    },
    removeBlockAndSetFocus_: function(block, deleteBlockFunc) {
      // This method runs the given function and then does one of two things:
      // - If the block is an isolated top-level block, it shifts the tree
      //   focus.
      // - Otherwise, it sets the correct new active desc for the current tree.
      if (this.isIsolatedTopLevelBlock_(block)) {
        var nextNodeToFocusOn =
            this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id);

        this.treeService.clearActiveDesc(this.tree.id);
        deleteBlockFunc();
        // Invoke a digest cycle, so that the DOM settles.
        setTimeout(function() {
          nextNodeToFocusOn.focus();
        });
      } else {
        var blockRootNode = document.getElementById(this.idMap['blockRoot']);
        var nextActiveDesc =
            this.treeService.getNextActiveDescWhenBlockIsDeleted(
                blockRootNode);
        this.treeService.runWhilePreservingFocus(
            deleteBlockFunc, this.tree.id, nextActiveDesc.id);
      }
    },
    cutBlock_: function() {
      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.clipboardService.cut(that.block);
      });
    },
    deleteBlock_: function() {
      var blockDescription = this.block.toString();

      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.block.dispose(true);
      });

      alert('Block deleted: ' + blockDescription);
    },
    pasteToConnection_: function(connection) {
      // This involves two steps:
      // - Put the block on the destination tree.
      // - Change the current tree-level focus to the destination tree, and the
      // screenreader focus for the destination tree to the block just moved.
      var newBlockId = null;

      // If the connection is a 'previousConnection' and that connection is
      // already joined to something, use the 'nextConnection' of the
      // previous block instead in order to do an insertion.
      if (connection.type == Blockly.PREVIOUS_STATEMENT &&
          connection.isConnected()) {
        newBlockId = this.clipboardService.pasteFromClipboard(
            connection.targetConnection);
      } else {
        newBlockId = this.clipboardService.pasteFromClipboard(connection);
      }

      // Invoke a digest cycle, so that the DOM settles.
      var that = this;
      setTimeout(function() {
        // Move the screenreader focus to the newly-pasted block.
        that.treeService.clearActiveDesc(that.tree.id);
        that.treeService.setActiveDesc(newBlockId + 'blockRoot', that.tree.id);
      });
    },
    moveToMarkedSpot_: function() {
      // This involves three steps:
      // - Put the block on the destination tree.
      // - Remove the block from the source tree, while preserving the
      // screenreader focus for that tree.
      // - Change the current tree-level focus to the destination tree, and the
      // screenreader focus for the destination tree to the block just moved.
      var blockDescription = this.block.toString();

      var newBlockId = this.clipboardService.pasteToMarkedConnection(
          this.block);

      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.block.dispose(true);
      });

      // Invoke a digest cycle, so that the DOM settles.
      setTimeout(function() {
        var destinationTreeId = that.treeService.getTreeIdForBlock(newBlockId);
        that.treeService.clearActiveDesc(destinationTreeId);

        document.getElementById(destinationTreeId).focus();
        that.treeService.setActiveDesc(
            newBlockId + 'blockRoot', destinationTreeId);

        alert('Block moved to marked spot: ' + blockDescription);
      });
    },
    ngOnInit: function() {
      var that = this;

      // Generate a list of action buttons.
      this.actionButtonsInfo = [{
        baseIdKey: 'cut',
        translationIdForText: 'CUT_BLOCK',
        action: that.cutBlock_.bind(that),
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'copy',
        translationIdForText: 'COPY_BLOCK',
        action: that.clipboardService.copy.bind(
            that.clipboardService, that.block, true),
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'pasteBelow',
        translationIdForText: 'PASTE_BELOW',
        action: that.pasteToConnection_.bind(that, that.block.nextConnection),
        isDisabled: function() {
          return Boolean(
              !that.block.nextConnection ||
              !that.isCompatibleWithClipboard(that.block.nextConnection));
        }
      }, {
        baseIdKey: 'pasteAbove',
        translationIdForText: 'PASTE_ABOVE',
        action: that.pasteToConnection_.bind(
            that, that.block.previousConnection),
        isDisabled: function() {
          return Boolean(
              !that.block.previousConnection ||
              !that.isCompatibleWithClipboard(that.block.previousConnection));
        }
      }, {
        baseIdKey: 'markBelow',
        translationIdForText: 'MARK_SPOT_BELOW',
        action: that.clipboardService.markConnection.bind(
            that.clipboardService, that.block.nextConnection),
        isDisabled: function() {
          return !that.block.nextConnection;
        }
      }, {
        baseIdKey: 'markAbove',
        translationIdForText: 'MARK_SPOT_ABOVE',
        action: that.clipboardService.markConnection.bind(
            that.clipboardService, that.block.previousConnection),
        isDisabled: function() {
          return !that.block.previousConnection;
        }
      }, {
        baseIdKey: 'moveToMarkedSpot',
        translationIdForText: 'MOVE_TO_MARKED_SPOT',
        action: that.moveToMarkedSpot_.bind(that),
        isDisabled: function() {
          return !that.clipboardService.isMovableToMarkedConnection(
              that.block);
        }
      }, {
        baseIdKey: 'delete',
        translationIdForText: 'DELETE',
        action: that.deleteBlock_.bind(that),
        isDisabled: function() {
          return false;
        }
      }];

      // Generate a list of action buttons.
      this.fieldButtonsInfo = [{
        baseIdKey: 'markSpot',
        translationIdForText: 'MARK_THIS_SPOT',
        action: function(connection) {
          that.clipboardService.markConnection(connection);
        },
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'paste',
        translationIdForText: 'PASTE',
        action: function(connection) {
          that.pasteToConnection_(connection);
        },
        isDisabled: function(connection) {
          return !that.isCompatibleWithClipboard(connection);
        }
      }];

      // Make a list of all the id keys.
      this.idKeys = ['blockRoot', 'blockSummary', 'listItem', 'label'];
      this.actionButtonsInfo.forEach(function(buttonInfo) {
        that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button');
      });
      this.fieldButtonsInfo.forEach(function(buttonInfo) {
        that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button');
      });
      for (var i = 0; i < this.block.inputList.length; i++) {
        var inputBlock = this.block.inputList[i];
        if (inputBlock.connection && !inputBlock.connection.targetBlock()) {
          that.idKeys.push(
              'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i,
              'markSpotButton' + i, 'paste' + i, 'pasteButton' + i);
        }
      }
    },
    ngDoCheck: function() {
      // Generate a unique id for each id key. This needs to be done every time
      // changes happen, but after the first ng-init, in order to force the
      // element ids to change in cases where, e.g., a block is inserted in the
      // middle of a sequence of blocks.
      this.idMap = {};
      for (var i = 0; i < this.idKeys.length; i++) {
        this.idMap[this.idKeys[i]] = this.block.id + this.idKeys[i];
      }
    },
    ngAfterViewInit: function() {
      // If this is a top-level tree in the workspace, set its id and active
      // descendant. (Note that a timeout is needed here in order to trigger
      // Angular change detection.)
      var that = this;
      setTimeout(function() {
        if (that.tree && that.isTopLevel && !that.tree.id) {
          that.tree.id = that.utilsService.generateUniqueId();
        }
        if (that.tree && that.isTopLevel &&
            !that.treeService.getActiveDescId(that.tree.id)) {
          that.treeService.setActiveDesc(that.idMap['blockRoot'], that.tree.id);
        }
      });
    },
    generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
      return this.utilsService.generateAriaLabelledByAttr(
          mainLabel, secondLabel, isDisabled);
    },
    isCompatibleWithClipboard: function(connection) {
      return this.clipboardService.isCompatibleWithClipboard(connection);
    }
  });
<commit_msg>Always remove screenreader focus from block before pasting to one of its connections.<commit_after>/**
 * AccessibleBlockly
 *
 * Copyright 2016 Google Inc.
 * https://developers.google.com/blockly/
 *
 * 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.
 */

/**
 * @fileoverview Angular2 Component that details how Blockly.Block's are
 * rendered in the workspace in AccessibleBlockly. Also handles any
 * interactions with the blocks.
 * @author madeeha@google.com (Madeeha Ghori)
 */

blocklyApp.WorkspaceTreeComponent = ng.core
  .Component({
    selector: 'blockly-workspace-tree',
    template: `

      {{block.toString()}}

          {{'BLOCK_ACTION_LIST'|translate}}

              <button [id]="idMap[buttonInfo.baseIdKey + 'Button']" (click)="buttonInfo.action()"
                      [disabled]="buttonInfo.isDisabled()">
                {{buttonInfo.translationIdForText|translate}}
              </button>

          <blockly-field *ngFor="#field of inputBlock.fieldRow" [field]="field" [level]="level + 1"></blockly-field>
          <blockly-workspace-tree *ngIf="inputBlock.connection && inputBlock.connection.targetBlock()"
                                  [block]="inputBlock.connection.targetBlock()" [level]="level + 1"
                                  [tree]="tree">
          </blockly-workspace-tree>

             {{utilsService.getInputTypeLabel(inputBlock.connection)}} {{utilsService.getBlockTypeLabel(inputBlock)}} needed:

                <button [id]="idMap[fieldButtonInfo.baseIdKey + 'Button']"
                        (click)="fieldButtonInfo.action(inputBlock.connection)"
                        [disabled]="fieldButtonInfo.isDisabled(inputBlock.connection)">
                  {{fieldButtonInfo.translationIdForText|translate}}
                </button>

    <blockly-workspace-tree *ngIf= "block.nextConnection && block.nextConnection.targetBlock()"
                            [block]="block.nextConnection.targetBlock()"
                            [level]="level" [tree]="tree">
    </blockly-workspace-tree>
    `,
    directives: [blocklyApp.FieldComponent, ng.core.forwardRef(function() {
      return blocklyApp.WorkspaceTreeComponent;
    })],
    inputs: ['block', 'level', 'tree', 'isTopLevel'],
    pipes: [blocklyApp.TranslatePipe]
  })
  .Class({
    constructor: [
        blocklyApp.ClipboardService, blocklyApp.TreeService,
        blocklyApp.UtilsService,
        function(_clipboardService, _treeService, _utilsService) {
      this.infoBlocks = Object.create(null);
      this.clipboardService = _clipboardService;
      this.treeService = _treeService;
      this.utilsService = _utilsService;
    }],
    isIsolatedTopLevelBlock_: function(block) {
      // Returns whether the given block is at the top level, and has no
      // siblings.
      var blockIsAtTopLevel = !block.getParent();
      var blockHasNoSiblings = (
          (!block.nextConnection ||
           !block.nextConnection.targetConnection) &&
          (!block.previousConnection ||
           !block.previousConnection.targetConnection));
      return blockIsAtTopLevel && blockHasNoSiblings;
    },
    removeBlockAndSetFocus_: function(block, deleteBlockFunc) {
      // This method runs the given function and then does one of two things:
      // - If the block is an isolated top-level block, it shifts the tree
      //   focus.
      // - Otherwise, it sets the correct new active desc for the current tree.
      if (this.isIsolatedTopLevelBlock_(block)) {
        var nextNodeToFocusOn =
            this.treeService.getNodeToFocusOnWhenTreeIsDeleted(this.tree.id);

        this.treeService.clearActiveDesc(this.tree.id);
        deleteBlockFunc();
        // Invoke a digest cycle, so that the DOM settles.
        setTimeout(function() {
          nextNodeToFocusOn.focus();
        });
      } else {
        var blockRootNode = document.getElementById(this.idMap['blockRoot']);
        var nextActiveDesc =
            this.treeService.getNextActiveDescWhenBlockIsDeleted(
                blockRootNode);
        this.treeService.runWhilePreservingFocus(
            deleteBlockFunc, this.tree.id, nextActiveDesc.id);
      }
    },
    cutBlock_: function() {
      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.clipboardService.cut(that.block);
      });
    },
    deleteBlock_: function() {
      var blockDescription = this.block.toString();

      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.block.dispose(true);
      });

      alert('Block deleted: ' + blockDescription);
    },
    pasteToConnection_: function(connection) {
      // This involves two steps:
      // - Put the block on the destination tree.
      // - Change the current tree-level focus to the destination tree, and the
      // screenreader focus for the destination tree to the block just moved.
      var newBlockId = null;

      this.treeService.clearActiveDesc(this.tree.id);

      // If the connection is a 'previousConnection' and that connection is
      // already joined to something, use the 'nextConnection' of the
      // previous block instead in order to do an insertion.
      if (connection.type == Blockly.PREVIOUS_STATEMENT &&
          connection.isConnected()) {
        newBlockId = this.clipboardService.pasteFromClipboard(
            connection.targetConnection);
      } else {
        newBlockId = this.clipboardService.pasteFromClipboard(connection);
      }

      // Invoke a digest cycle, so that the DOM settles.
      var that = this;
      setTimeout(function() {
        // Move the focus to the current tree.
        document.getElementById(that.tree.id).focus();
        // Move the screenreader focus to the newly-pasted block.
        that.treeService.setActiveDesc(newBlockId + 'blockRoot', that.tree.id);
      });
    },
    moveToMarkedSpot_: function() {
      // This involves three steps:
      // - Put the block on the destination tree.
      // - Remove the block from the source tree, while preserving the
      // screenreader focus for that tree.
      // - Change the current tree-level focus to the destination tree, and the
      // screenreader focus for the destination tree to the block just moved.
      var blockDescription = this.block.toString();

      var newBlockId = this.clipboardService.pasteToMarkedConnection(
          this.block);

      var that = this;
      this.removeBlockAndSetFocus_(this.block, function() {
        that.block.dispose(true);
      });

      // Invoke a digest cycle, so that the DOM settles.
      setTimeout(function() {
        var destinationTreeId = that.treeService.getTreeIdForBlock(newBlockId);
        that.treeService.clearActiveDesc(destinationTreeId);

        document.getElementById(destinationTreeId).focus();
        that.treeService.setActiveDesc(
            newBlockId + 'blockRoot', destinationTreeId);

        alert('Block moved to marked spot: ' + blockDescription);
      });
    },
    ngOnInit: function() {
      var that = this;

      // Generate a list of action buttons.
      this.actionButtonsInfo = [{
        baseIdKey: 'cut',
        translationIdForText: 'CUT_BLOCK',
        action: that.cutBlock_.bind(that),
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'copy',
        translationIdForText: 'COPY_BLOCK',
        action: that.clipboardService.copy.bind(
            that.clipboardService, that.block, true),
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'pasteBelow',
        translationIdForText: 'PASTE_BELOW',
        action: that.pasteToConnection_.bind(that, that.block.nextConnection),
        isDisabled: function() {
          return Boolean(
              !that.block.nextConnection ||
              !that.isCompatibleWithClipboard(that.block.nextConnection));
        }
      }, {
        baseIdKey: 'pasteAbove',
        translationIdForText: 'PASTE_ABOVE',
        action: that.pasteToConnection_.bind(
            that, that.block.previousConnection),
        isDisabled: function() {
          return Boolean(
              !that.block.previousConnection ||
              !that.isCompatibleWithClipboard(that.block.previousConnection));
        }
      }, {
        baseIdKey: 'markBelow',
        translationIdForText: 'MARK_SPOT_BELOW',
        action: that.clipboardService.markConnection.bind(
            that.clipboardService, that.block.nextConnection),
        isDisabled: function() {
          return !that.block.nextConnection;
        }
      }, {
        baseIdKey: 'markAbove',
        translationIdForText: 'MARK_SPOT_ABOVE',
        action: that.clipboardService.markConnection.bind(
            that.clipboardService, that.block.previousConnection),
        isDisabled: function() {
          return !that.block.previousConnection;
        }
      }, {
        baseIdKey: 'moveToMarkedSpot',
        translationIdForText: 'MOVE_TO_MARKED_SPOT',
        action: that.moveToMarkedSpot_.bind(that),
        isDisabled: function() {
          return !that.clipboardService.isMovableToMarkedConnection(
              that.block);
        }
      }, {
        baseIdKey: 'delete',
        translationIdForText: 'DELETE',
        action: that.deleteBlock_.bind(that),
        isDisabled: function() {
          return false;
        }
      }];

      // Generate a list of action buttons.
      this.fieldButtonsInfo = [{
        baseIdKey: 'markSpot',
        translationIdForText: 'MARK_THIS_SPOT',
        action: function(connection) {
          that.clipboardService.markConnection(connection);
        },
        isDisabled: function() {
          return false;
        }
      }, {
        baseIdKey: 'paste',
        translationIdForText: 'PASTE',
        action: function(connection) {
          that.pasteToConnection_(connection);
        },
        isDisabled: function(connection) {
          return !that.isCompatibleWithClipboard(connection);
        }
      }];

      // Make a list of all the id keys.
      this.idKeys = ['blockRoot', 'blockSummary', 'listItem', 'label'];
      this.actionButtonsInfo.forEach(function(buttonInfo) {
        that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button');
      });
      this.fieldButtonsInfo.forEach(function(buttonInfo) {
        that.idKeys.push(buttonInfo.baseIdKey, buttonInfo.baseIdKey + 'Button');
      });
      for (var i = 0; i < this.block.inputList.length; i++) {
        var inputBlock = this.block.inputList[i];
        if (inputBlock.connection && !inputBlock.connection.targetBlock()) {
          that.idKeys.push(
              'inputList' + i, 'inputMenuLabel' + i, 'markSpot' + i,
              'markSpotButton' + i, 'paste' + i, 'pasteButton' + i);
        }
      }
    },
    ngDoCheck: function() {
      // Generate a unique id for each id key. This needs to be done every time
      // changes happen, but after the first ng-init, in order to force the
      // element ids to change in cases where, e.g., a block is inserted in the
      // middle of a sequence of blocks.
      this.idMap = {};
      for (var i = 0; i < this.idKeys.length; i++) {
        this.idMap[this.idKeys[i]] = this.block.id + this.idKeys[i];
      }
    },
    ngAfterViewInit: function() {
      // If this is a top-level tree in the workspace, set its id and active
      // descendant. (Note that a timeout is needed here in order to trigger
      // Angular change detection.)
      var that = this;
      setTimeout(function() {
        if (that.tree && that.isTopLevel && !that.tree.id) {
          that.tree.id = that.utilsService.generateUniqueId();
        }
        if (that.tree && that.isTopLevel &&
            !that.treeService.getActiveDescId(that.tree.id)) {
          that.treeService.setActiveDesc(that.idMap['blockRoot'], that.tree.id);
        }
      });
    },
    generateAriaLabelledByAttr: function(mainLabel, secondLabel, isDisabled) {
      return this.utilsService.generateAriaLabelledByAttr(
          mainLabel, secondLabel, isDisabled);
    },
    isCompatibleWithClipboard: function(connection) {
      return this.clipboardService.isCompatibleWithClipboard(connection);
    }
  });
<|endoftext|>


<commit_before>'use strict';

var $ = require('jquery');
var bootbox = require('bootbox');
var Raven = require('raven-js');
var ko = require('knockout');

var ProjectSettings = require('js/projectSettings.js');

var $osf = require('js/osfHelpers');
require('css/addonsettings.css');

var ctx = window.contextVars;

// Initialize treebeard grid for notifications
var ProjectNotifications = require('js/notificationsTreebeard.js');
var $notificationsMsg = $('#configureNotificationsMessage');
var notificationsURL = ctx.node.urls.api  + 'subscriptions/';
// Need check because notifications settings don't exist on registration's settings page
if ($('#grid').length) {
    $.ajax({
        url: notificationsURL,
        type: 'GET',
        dataType: 'json'
    }).done(function(response) {
        new ProjectNotifications(response);
    }).fail(function(xhr, status, error) {
        $notificationsMsg.addClass('text-danger');
        $notificationsMsg.text('Could not retrieve notification settings.');
        Raven.captureMessage('Could not GET notification settings.', {
            url: notificationsURL, status: status, error: error
        });
    });
}

//Initialize treebeard grid for wiki
var ProjectWiki = require('js/wikiSettingsTreebeard.js');
var wikiSettingsURL = ctx.node.urls.api  + 'wiki/settings/';
var $wikiMsg = $('#configureWikiMessage');

if ($('#wgrid').length) {
    $.ajax({
        url: wikiSettingsURL,
        type: 'GET',
        dataType: 'json'
    }).done(function(response) {
        new ProjectWiki(response);
    }).fail(function(xhr, status, error) {
        $wikiMsg.addClass('text-danger');
        $wikiMsg.text('Could not retrieve wiki settings.');
        Raven.captureMessage('Could not GET wiki settings.', {
            url: wikiSettingsURL, status: status, error: error
        });
    });
}

$(document).ready(function() {

    // Apply KO bindings for Project Settings
    var categoryOptions = [];
    var keys = Object.keys(window.contextVars.nodeCategories);
    for (var i = 0; i < keys.length; i++) {
        categoryOptions.push({
            label: window.contextVars.nodeCategories[keys[i]],
            value: keys[i]
        });
    }
    var disableCategory = !window.contextVars.node.parentExists;
    // need check because node category doesn't exist for registrations
    if ($('#projectSettings').length) {
        var projectSettingsVM = new ProjectSettings.ProjectSettings( {
            currentTitle: ctx.node.title,
            currentDescription: ctx.node.description,
            category: ctx.node.category,
            categoryOptions: categoryOptions,
            node_id: ctx.node.id,
            updateUrl:  $osf.apiV2Url('nodes/' + ctx.node.id + '/'),
            disabled: disableCategory
        });
        ko.applyBindings(projectSettingsVM, $('#projectSettings')[0]);
    }

    $('#deleteNode').on('click', function() {
        ProjectSettings.getConfirmationCode(ctx.node.nodeType);
    });

    // TODO: Knockout-ify me
    $('#commentSettings').on('submit', function() {
        var $commentMsg = $('#configureCommentingMessage');

        var $this = $(this);
        var commentLevel = $this.find('input[name="commentLevel"]:checked').val();

        $osf.postJSON(
            ctx.node.urls.api + 'settings/comments/',
            {commentLevel: commentLevel}
        ).done(function() {
            $commentMsg.text('Successfully updated settings.');
            $commentMsg.addClass('text-success');
            if($osf.isSafari()){
                //Safari can't update jquery style change before reloading. So delay is applied here
                setTimeout(function(){window.location.reload();}, 100);
            } else {
                window.location.reload();
            }

        }).fail(function() {
            bootbox.alert({
                message: 'Could not set commenting configuration. Please try again.',
                buttons:{
                    ok:{
                        label:'Close',
                        className:'btn-default'
                    }
                }
            });
        });

        return false;

    });

    var checkedOnLoad = $('#selectAddonsForm input:checked');
    var uncheckedOnLoad = $('#selectAddonsForm input:not(:checked)');

    // Set up submission for addon selection form
    $('#selectAddonsForm').on('submit', function() {

        var formData = {};
        $('#selectAddonsForm').find('input').each(function(idx, elm) {
            var $elm = $(elm);
            formData[$elm.attr('name')] = $elm.is(':checked');
        });

        var unchecked = checkedOnLoad.filter('#selectAddonsForm input:not(:checked)');
        var checked = uncheckedOnLoad.filter('#selectAddonsForm input:checked');
        var msgElm = $(this).find('.addon-settings-message');

        var submit = function() {
            var request = $osf.postJSON(ctx.node.urls.api + 'settings/addons/', formData);
            return request;
        };

        function successMessage() {
            msgElm.text('Settings updated');
            setTimeout(
                function() {
                    msgElm.text('');
                },
                5000
            );
        }
        function successfulAddonUpdate() {
            successMessage();
            checkedOnLoad = $('#selectAddonsForm input:checked');
            uncheckedOnLoad = $('#selectAddonsForm input:not(:checked)');
            if($osf.isSafari()) {
                        //Safari can't update jquery style change before reloading. So delay is applied here
                        setTimeout(function(){window.location.reload();}, 100);
            } else {
                        window.location.reload();
            }
        }
        function failedAddonUpdate() {
            var msg = 'Sorry, we had trouble saving your settings. If this persists please contact support@osf.io';
            bootbox.alert({
                title: 'Request failed',
                message: msg,
                buttons: {
                    ok: {
                        label: 'Close',
                        className: 'btn-default'
                    }
                }
            });
        }
        // some addons disabled (unchecked warning adopted from profile-settings-addons-page.js)
        if(unchecked.length > 0) {
            var uncheckedText = $.map(unchecked, function(el){
                return ['
', $(el).closest('label').text().trim(), '
'].join('');
            }).join('');
            uncheckedText = ['
', uncheckedText, '
'].join('');
            bootbox.confirm({
                title: 'Are you sure you want to remove the add-ons you have deselected? ',
                message: uncheckedText,
                callback: function(result) {
                    if (result) {
                        var request = submit();
                        request.done(successfulAddonUpdate);
                        request.fail(failedAddonUpdate);
                    } else{
                        unchecked.each(function(i, el){ $(el).prop('checked', true); });
                    }
                },
                buttons: {
                    confirm: {
                        label: 'Remove',
                        className: 'btn-danger'
                    }
                }
            });
        }
        //no addons disabled but some addons enabled
        else if(checked.length>0) {
            var request = submit();
            request.done(successfulAddonUpdate);
            request.fail(failedAddonUpdate);
        }
        // no changes to the state of the addons
        else {
            successMessage();
        }

        return false;

    });

    /* Before closing the page, Check whether the newly checked addon are updated or not */
    $(window).on('beforeunload',function() {
      //new checked items but not updated
      var checked = uncheckedOnLoad.filter('#selectAddonsForm input:checked');
      //new unchecked items but not updated
      var unchecked = checkedOnLoad.filter('#selectAddonsForm input:not(:checked)');

      if(unchecked.length > 0 || checked.length > 0) {
        return 'The changes on addon setting are not submitted!';
      }
    });

    // Show capabilities modal on selecting an addon; unselect if user
    // rejects terms
    $('.addon-select').on('change', function() {
        var that = this,
            $that = $(that);
        if ($that.is(':checked')) {
            var name = $that.attr('name');
            var capabilities = $('#capabilities-' + name).html();
            if (capabilities) {
                bootbox.confirm({
                    message: capabilities,
                    callback: function(result) {
                        if (!result) {
                            $(that).attr('checked', false);
                        }
                    },
                    buttons:{
                        confirm:{
                            label:'Confirm'
                        }
                    }
               });
            }
        }
    });
});

var WikiSettingsViewModel = {
    enabled: ko.observable(ctx.wiki.isEnabled), // <- this would get set in the mako template, as usual
    wikiMessage: ko.observable('')
};

var self = this;
self.viewModel = WikiSettingsViewModel;

self.viewModel.enabled.subscribe(function(newValue) {
    var self = this;
    $osf.postJSON(ctx.node.urls.api + 'settings/addons/', {wiki: newValue}
    ).done(function(response) {
        if (newValue) {
            self.wikiMessage('Wiki Enabled');
        }
        else {
            self.wikiMessage('Wiki Disabled');
        }
        //Give user time to see message before reload.
        setTimeout(function(){window.location.reload();}, 1500);
    }).fail(function(xhr, status, error) {
        $osf.growl('Error', 'Unable to update wiki');
        Raven.captureMessage('Could not update wiki.', {
            url: ctx.node.urls.api + 'settings/addons/', status: status, error: error
        });
        setTimeout(function(){window.location.reload();}, 1500);
    });
    return true;
}, self.viewModel);

$osf.applyBindings(self.viewModel, '#selectWikiForm');
<commit_msg>remove self and just use video model directly<commit_after>'use strict';

var $ = require('jquery');
var bootbox = require('bootbox');
var Raven = require('raven-js');
var ko = require('knockout');

var ProjectSettings = require('js/projectSettings.js');

var $osf = require('js/osfHelpers');
require('css/addonsettings.css');

var ctx = window.contextVars;

// Initialize treebeard grid for notifications
var ProjectNotifications = require('js/notificationsTreebeard.js');
var $notificationsMsg = $('#configureNotificationsMessage');
var notificationsURL = ctx.node.urls.api  + 'subscriptions/';
// Need check because notifications settings don't exist on registration's settings page
if ($('#grid').length) {
    $.ajax({
        url: notificationsURL,
        type: 'GET',
        dataType: 'json'
    }).done(function(response) {
        new ProjectNotifications(response);
    }).fail(function(xhr, status, error) {
        $notificationsMsg.addClass('text-danger');
        $notificationsMsg.text('Could not retrieve notification settings.');
        Raven.captureMessage('Could not GET notification settings.', {
            url: notificationsURL, status: status, error: error
        });
    });
}

//Initialize treebeard grid for wiki
var ProjectWiki = require('js/wikiSettingsTreebeard.js');
var wikiSettingsURL = ctx.node.urls.api  + 'wiki/settings/';
var $wikiMsg = $('#configureWikiMessage');

if ($('#wgrid').length) {
    $.ajax({
        url: wikiSettingsURL,
        type: 'GET',
        dataType: 'json'
    }).done(function(response) {
        new ProjectWiki(response);
    }).fail(function(xhr, status, error) {
        $wikiMsg.addClass('text-danger');
        $wikiMsg.text('Could not retrieve wiki settings.');
        Raven.captureMessage('Could not GET wiki settings.', {
            url: wikiSettingsURL, status: status, error: error
        });
    });
}

$(document).ready(function() {

    // Apply KO bindings for Project Settings
    var categoryOptions = [];
    var keys = Object.keys(window.contextVars.nodeCategories);
    for (var i = 0; i < keys.length; i++) {
        categoryOptions.push({
            label: window.contextVars.nodeCategories[keys[i]],
            value: keys[i]
        });
    }
    var disableCategory = !window.contextVars.node.parentExists;
    // need check because node category doesn't exist for registrations
    if ($('#projectSettings').length) {
        var projectSettingsVM = new ProjectSettings.ProjectSettings( {
            currentTitle: ctx.node.title,
            currentDescription: ctx.node.description,
            category: ctx.node.category,
            categoryOptions: categoryOptions,
            node_id: ctx.node.id,
            updateUrl:  $osf.apiV2Url('nodes/' + ctx.node.id + '/'),
            disabled: disableCategory
        });
        ko.applyBindings(projectSettingsVM, $('#projectSettings')[0]);
    }

    $('#deleteNode').on('click', function() {
        ProjectSettings.getConfirmationCode(ctx.node.nodeType);
    });

    // TODO: Knockout-ify me
    $('#commentSettings').on('submit', function() {
        var $commentMsg = $('#configureCommentingMessage');

        var $this = $(this);
        var commentLevel = $this.find('input[name="commentLevel"]:checked').val();

        $osf.postJSON(
            ctx.node.urls.api + 'settings/comments/',
            {commentLevel: commentLevel}
        ).done(function() {
            $commentMsg.text('Successfully updated settings.');
            $commentMsg.addClass('text-success');
            if($osf.isSafari()){
                //Safari can't update jquery style change before reloading. So delay is applied here
                setTimeout(function(){window.location.reload();}, 100);
            } else {
                window.location.reload();
            }

        }).fail(function() {
            bootbox.alert({
                message: 'Could not set commenting configuration. Please try again.',
                buttons:{
                    ok:{
                        label:'Close',
                        className:'btn-default'
                    }
                }
            });
        });

        return false;

    });

    var checkedOnLoad = $('#selectAddonsForm input:checked');
    var uncheckedOnLoad = $('#selectAddonsForm input:not(:checked)');

    // Set up submission for addon selection form
    $('#selectAddonsForm').on('submit', function() {

        var formData = {};
        $('#selectAddonsForm').find('input').each(function(idx, elm) {
            var $elm = $(elm);
            formData[$elm.attr('name')] = $elm.is(':checked');
        });

        var unchecked = checkedOnLoad.filter('#selectAddonsForm input:not(:checked)');
        var checked = uncheckedOnLoad.filter('#selectAddonsForm input:checked');
        var msgElm = $(this).find('.addon-settings-message');

        var submit = function() {
            var request = $osf.postJSON(ctx.node.urls.api + 'settings/addons/', formData);
            return request;
        };

        function successMessage() {
            msgElm.text('Settings updated');
            setTimeout(
                function() {
                    msgElm.text('');
                },
                5000
            );
        }
        function successfulAddonUpdate() {
            successMessage();
            checkedOnLoad = $('#selectAddonsForm input:checked');
            uncheckedOnLoad = $('#selectAddonsForm input:not(:checked)');
            if($osf.isSafari()) {
                        //Safari can't update jquery style change before reloading. So delay is applied here
                        setTimeout(function(){window.location.reload();}, 100);
            } else {
                        window.location.reload();
            }
        }
        function failedAddonUpdate() {
            var msg = 'Sorry, we had trouble saving your settings. If this persists please contact support@osf.io';
            bootbox.alert({
                title: 'Request failed',
                message: msg,
                buttons: {
                    ok: {
                        label: 'Close',
                        className: 'btn-default'
                    }
                }
            });
        }
        // some addons disabled (unchecked warning adopted from profile-settings-addons-page.js)
        if(unchecked.length > 0) {
            var uncheckedText = $.map(unchecked, function(el){
                return ['
', $(el).closest('label').text().trim(), '
'].join('');
            }).join('');
            uncheckedText = ['
', uncheckedText, '
'].join('');
            bootbox.confirm({
                title: 'Are you sure you want to remove the add-ons you have deselected? ',
                message: uncheckedText,
                callback: function(result) {
                    if (result) {
                        var request = submit();
                        request.done(successfulAddonUpdate);
                        request.fail(failedAddonUpdate);
                    } else{
                        unchecked.each(function(i, el){ $(el).prop('checked', true); });
                    }
                },
                buttons: {
                    confirm: {
                        label: 'Remove',
                        className: 'btn-danger'
                    }
                }
            });
        }
        //no addons disabled but some addons enabled
        else if(checked.length>0) {
            var request = submit();
            request.done(successfulAddonUpdate);
            request.fail(failedAddonUpdate);
        }
        // no changes to the state of the addons
        else {
            successMessage();
        }

        return false;

    });

    /* Before closing the page, Check whether the newly checked addon are updated or not */
    $(window).on('beforeunload',function() {
      //new checked items but not updated
      var checked = uncheckedOnLoad.filter('#selectAddonsForm input:checked');
      //new unchecked items but not updated
      var unchecked = checkedOnLoad.filter('#selectAddonsForm input:not(:checked)');

      if(unchecked.length > 0 || checked.length > 0) {
        return 'The changes on addon setting are not submitted!';
      }
    });

    // Show capabilities modal on selecting an addon; unselect if user
    // rejects terms
    $('.addon-select').on('change', function() {
        var that = this,
            $that = $(that);
        if ($that.is(':checked')) {
            var name = $that.attr('name');
            var capabilities = $('#capabilities-' + name).html();
            if (capabilities) {
                bootbox.confirm({
                    message: capabilities,
                    callback: function(result) {
                        if (!result) {
                            $(that).attr('checked', false);
                        }
                    },
                    buttons:{
                        confirm:{
                            label:'Confirm'
                        }
                    }
               });
            }
        }
    });
});

var WikiSettingsViewModel = {
    enabled: ko.observable(ctx.wiki.isEnabled), // <- this would get set in the mako template, as usual
    wikiMessage: ko.observable('')
};

WikiSettingsViewModel.enabled.subscribe(function(newValue) {
    var self = this;
    $osf.postJSON(ctx.node.urls.api + 'settings/addons/', {wiki: newValue}
    ).done(function(response) {
        if (newValue) {
            self.wikiMessage('Wiki Enabled');
        }
        else {
            self.wikiMessage('Wiki Disabled');
        }
        //Give user time to see message before reload.
        setTimeout(function(){window.location.reload();}, 1500);
    }).fail(function(xhr, status, error) {
        $osf.growl('Error', 'Unable to update wiki');
        Raven.captureMessage('Could not update wiki.', {
            url: ctx.node.urls.api + 'settings/addons/', status: status, error: error
        });
        setTimeout(function(){window.location.reload();}, 1500);
    });
    return true;
}, WikiSettingsViewModel);

$osf.applyBindings(WikiSettingsViewModel, '#selectWikiForm');
<|endoftext|>