<commit_before>module.exports = function(grunt) {

	grunt.initConfig({
		clean: {
			dist: {
				src: ['dist']
			}
		},
		watch: {
			scripts: {
					files: ['src/kopf/**/*.*','src/kopf/*.*'],
					tasks: ['build'],
					options: {
					spawn: false
				}
			}
		},
		copy: {
			main: {
				files: [
					{expand: true, flatten: true, src: ['src/lib/ace/mode-json.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/lib/ace/worker-json.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/theme-kopf.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/css/dark_style.css'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/css/light_style.css'], dest: './dist/'}
				]
			}
		},
		concat: {
			vendorjs: {
				src: [
					'src/lib/jquery/jquery-1.10.2.min.js',
					'src/lib/angularjs/angular.min.js',
					'src/lib/ace/ace.js',
					'src/lib/jsontree/jsontree.min.js',
					'src/lib/bootstrap/js/bootstrap.js'
				],
				dest: 'dist/lib.js'
			},
			vendorcss: {
				src: [
					'src/lib/bootstrap/css/bootstrap.css'
				],
				dest: 'dist/lib.css'
			},
			appjs: {
				src: [
					'src/kopf/elastic/alias.js',
					'src/kopf/elastic/cluster_changes.js',
					'src/kopf/elastic/cluster_health.js',
					'src/kopf/elastic/cluster_settings.js',
					'src/kopf/elastic/cluster.js',
					'src/kopf/elastic/elastic_client.js',
					'src/kopf/elastic/es_connection.js',
					'src/kopf/elastic/index.js',
					'src/kopf/elastic/editable_index_settings.js',
					'src/kopf/elastic/node.js',
					'src/kopf/elastic/shard.js',
					'src/kopf/elastic/token.js',
					'src/kopf/elastic/repository.js',
					'src/kopf/elastic/snapshot.js',
                    'src/kopf/elastic/warmer.js',
                    'src/kopf/elastic/percolator.js',
                    'src/kopf/elastic/index_metadata.js',
					// CONTROLLERS
					'src/kopf/kopf.js',
					'src/kopf/controllers/aliases.js',
					'src/kopf/controllers/analysis.js',
					'src/kopf/controllers/cluster_health.js',
					'src/kopf/controllers/cluster_overview.js',
					'src/kopf/controllers/cluster_settings.js',
					'src/kopf/controllers/create_index.js',
					'src/kopf/controllers/global.js',
					'src/kopf/controllers/index_settings.js',
					'src/kopf/controllers/navbar.js',
					'src/kopf/controllers/rest.js',
					'src/kopf/controllers/percolator.js',
					'src/kopf/controllers/repository.js',
					'src/kopf/controllers/confirm_dialog.js',
					'src/kopf/controllers/warmup.js',
					'src/kopf/controllers/benchmark.js',
					// SERVICES
					'src/kopf/services/alerts.js',
					'src/kopf/services/settings.js',
					'src/kopf/services/aceeditor.js',
					'src/kopf/services/theme.js',
                    'src/kopf/services/elastic.js',
                    'src/kopf/services/host_history.js',
                    'src/kopf/services/external_settings.js',
					// MODELS
					'src/kopf/models/ace_editor.js',
					'src/kopf/models/gist.js',
					'src/kopf/models/benchmark.js',
                    'src/kopf/models/request.js',
                    'src/kopf/models/cluster_navigation.js',
                    'src/kopf/models/modal_controls.js',
                    'src/kopf/models/paginator.js',
                    'src/kopf/models/alias_filter.js',
                    'src/kopf/models/snapshot_filter.js',
                    'src/kopf/models/warmer_filter.js',
                    'src/kopf/models/index_filter.js',
                    'src/kopf/models/node_filter.js',
					// UTIL
					'src/kopf/util.js'
				],
				dest: 'dist/kopf.js'
			},
			appcss: {
				src: [
					'src/kopf/kopf.css',
					'src/kopf/css/percolator.css',
					'src/kopf/css/common.css',
					'src/kopf/css/aliases.css',
					'src/kopf/css/analysis.css',
					'src/kopf/css/cluster_health.css',
					'src/kopf/css/cluster_overview.css',
					'src/kopf/css/gist_share.css',
					'src/kopf/css/json_tree.css',
					'src/kopf/css/navbar.css',
					'src/kopf/css/rest_client.css',
					'src/kopf/css/warmup.css',
					'src/kopf/css/repository.css'
				],
				dest: 'dist/kopf.css'
			}

		},
		connect: {
			server: {
				options: {
					port: 9000,
					base: '.',
					keepalive: true
				}
			}
		},
		jshint: {
			kopf: {
				src: [
					'src/kopf/elastic/alias.js',
					'src/kopf/elastic/cluster_changes.js',
					'src/kopf/elastic/cluster_health.js',
					'src/kopf/elastic/cluster_settings.js',
					'src/kopf/elastic/cluster.js',
					'src/kopf/elastic/elastic_client.js',
					'src/kopf/elastic/es_connection.js',
					'src/kopf/elastic/index.js',
					'src/kopf/elastic/editable_index_settings.js',
					'src/kopf/elastic/node.js',
					'src/kopf/elastic/shard.js',
					'src/kopf/elastic/token.js',
					'src/kopf/elastic/repository.js',
					'src/kopf/elastic/snapshot.js',
                    'src/kopf/elastic/warmer.js',
                    'src/kopf/elastic/percolator.js',
                    'src/kopf/elastic/index_metadata.js',
					// CONTROLLERS
					'src/kopf/kopf.js',
					'src/kopf/controllers/aliases.js',
					'src/kopf/controllers/analysis.js',
					'src/kopf/controllers/cluster_health.js',
					'src/kopf/controllers/cluster_overview.js',
					'src/kopf/controllers/cluster_settings.js',
					'src/kopf/controllers/create_index.js',
					'src/kopf/controllers/global.js',
					'src/kopf/controllers/index_settings.js',
					'src/kopf/controllers/navbar.js',
					'src/kopf/controllers/rest.js',
					'src/kopf/controllers/percolator.js',
					'src/kopf/controllers/repository.js',
					'src/kopf/controllers/confirm_dialog.js',
					'src/kopf/controllers/warmup.js',
					'src/kopf/controllers/benchmark.js',
					// SERVICES
					'src/kopf/services/alerts.js',
					'src/kopf/services/settings.js',
					'src/kopf/services/aceeditor.js',
					'src/kopf/services/theme.js',
                    'src/kopf/services/elastic.js',
                    'src/kopf/services/host_history.js',
                    'src/kopf/services/external_settings.js',
					// MODELS
					'src/kopf/models/ace_editor.js',
					'src/kopf/models/gist.js',
					'src/kopf/models/benchmark.js',
                    'src/kopf/models/request.js',
                    'src/kopf/models/cluster_navigation.js',
                    'src/kopf/models/modal_controls.js',
                    'src/kopf/models/paginator.js',
                    'src/kopf/models/alias_filter.js',
                    'src/kopf/models/snapshot_filter.js',
                    'src/kopf/models/warmer_filter.js',
                    'src/kopf/models/index_filter.js',
                    'src/kopf/models/node_filter.js',
					// UTIL
					'src/kopf/util.js'
				]
			}
		},
		qunit: {
			all: ['tests/all.html']
		},
		karma: {
			unit: { configFile: 'tests/karma.config.js', keepalive: true }
		}
	});
	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-connect');
	grunt.loadNpmTasks('grunt-contrib-copy');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-qunit');
	grunt.loadNpmTasks('grunt-karma');
	grunt.registerTask('dev', ['karma', 'watch'])
	grunt.registerTask('test', ['karma'])
	grunt.registerTask('build', ['clean', 'jshint', 'qunit', 'copy', 'concat']);
	grunt.registerTask('server', ['clean', 'jshint', 'qunit', 'copy', 'concat','connect:server']);
};
<commit_msg>added google javascript style check<commit_after>module.exports = function(grunt) {

	grunt.initConfig({
		clean: {
			dist: {
				src: ['dist']
			}
		},
		watch: {
			scripts: {
					files: ['src/kopf/**/*.*','src/kopf/*.*'],
					tasks: ['build'],
					options: {
					spawn: false
				}
			}
		},
		copy: {
			main: {
				files: [
					{expand: true, flatten: true, src: ['src/lib/ace/mode-json.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/lib/ace/worker-json.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/theme-kopf.js'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/css/dark_style.css'], dest: './dist/'},
					{expand: true, flatten: true, src: ['src/kopf/css/light_style.css'], dest: './dist/'}
				]
			}
		},
		concat: {
			vendorjs: {
				src: [
					'src/lib/jquery/jquery-1.10.2.min.js',
					'src/lib/angularjs/angular.min.js',
					'src/lib/ace/ace.js',
					'src/lib/jsontree/jsontree.min.js',
					'src/lib/bootstrap/js/bootstrap.js'
				],
				dest: 'dist/lib.js'
			},
			vendorcss: {
				src: [
					'src/lib/bootstrap/css/bootstrap.css'
				],
				dest: 'dist/lib.css'
			},
			appjs: {
				src: [
					'src/kopf/elastic/alias.js',
					'src/kopf/elastic/cluster_changes.js',
					'src/kopf/elastic/cluster_health.js',
					'src/kopf/elastic/cluster_settings.js',
					'src/kopf/elastic/cluster.js',
					'src/kopf/elastic/elastic_client.js',
					'src/kopf/elastic/es_connection.js',
					'src/kopf/elastic/index.js',
					'src/kopf/elastic/editable_index_settings.js',
					'src/kopf/elastic/node.js',
					'src/kopf/elastic/shard.js',
					'src/kopf/elastic/token.js',
					'src/kopf/elastic/repository.js',
					'src/kopf/elastic/snapshot.js',
                    'src/kopf/elastic/warmer.js',
                    'src/kopf/elastic/percolator.js',
                    'src/kopf/elastic/index_metadata.js',
					// CONTROLLERS
					'src/kopf/kopf.js',
					'src/kopf/controllers/aliases.js',
					'src/kopf/controllers/analysis.js',
					'src/kopf/controllers/cluster_health.js',
					'src/kopf/controllers/cluster_overview.js',
					'src/kopf/controllers/cluster_settings.js',
					'src/kopf/controllers/create_index.js',
					'src/kopf/controllers/global.js',
					'src/kopf/controllers/index_settings.js',
					'src/kopf/controllers/navbar.js',
					'src/kopf/controllers/rest.js',
					'src/kopf/controllers/percolator.js',
					'src/kopf/controllers/repository.js',
					'src/kopf/controllers/confirm_dialog.js',
					'src/kopf/controllers/warmup.js',
					'src/kopf/controllers/benchmark.js',
					// SERVICES
					'src/kopf/services/alerts.js',
					'src/kopf/services/settings.js',
					'src/kopf/services/aceeditor.js',
					'src/kopf/services/theme.js',
                    'src/kopf/services/elastic.js',
                    'src/kopf/services/host_history.js',
                    'src/kopf/services/external_settings.js',
					// MODELS
					'src/kopf/models/ace_editor.js',
					'src/kopf/models/gist.js',
					'src/kopf/models/benchmark.js',
                    'src/kopf/models/request.js',
                    'src/kopf/models/cluster_navigation.js',
                    'src/kopf/models/modal_controls.js',
                    'src/kopf/models/paginator.js',
                    'src/kopf/models/alias_filter.js',
                    'src/kopf/models/snapshot_filter.js',
                    'src/kopf/models/warmer_filter.js',
                    'src/kopf/models/index_filter.js',
                    'src/kopf/models/node_filter.js',
					// UTIL
					'src/kopf/util.js'
				],
				dest: 'dist/kopf.js'
			},
			appcss: {
				src: [
					'src/kopf/kopf.css',
					'src/kopf/css/percolator.css',
					'src/kopf/css/common.css',
					'src/kopf/css/aliases.css',
					'src/kopf/css/analysis.css',
					'src/kopf/css/cluster_health.css',
					'src/kopf/css/cluster_overview.css',
					'src/kopf/css/gist_share.css',
					'src/kopf/css/json_tree.css',
					'src/kopf/css/navbar.css',
					'src/kopf/css/rest_client.css',
					'src/kopf/css/warmup.css',
					'src/kopf/css/repository.css'
				],
				dest: 'dist/kopf.css'
			}

		},
		connect: {
			server: {
				options: {
					port: 9000,
					base: '.',
					keepalive: true
				}
			}
		},
		jshint: {
			kopf: {
				src: [
					'src/kopf/elastic/alias.js',
					'src/kopf/elastic/cluster_changes.js',
					'src/kopf/elastic/cluster_health.js',
					'src/kopf/elastic/cluster_settings.js',
					'src/kopf/elastic/cluster.js',
					'src/kopf/elastic/elastic_client.js',
					'src/kopf/elastic/es_connection.js',
					'src/kopf/elastic/index.js',
					'src/kopf/elastic/editable_index_settings.js',
					'src/kopf/elastic/node.js',
					'src/kopf/elastic/shard.js',
					'src/kopf/elastic/token.js',
					'src/kopf/elastic/repository.js',
					'src/kopf/elastic/snapshot.js',
                    'src/kopf/elastic/warmer.js',
                    'src/kopf/elastic/percolator.js',
                    'src/kopf/elastic/index_metadata.js',
					// CONTROLLERS
					'src/kopf/kopf.js',
					'src/kopf/controllers/aliases.js',
					'src/kopf/controllers/analysis.js',
					'src/kopf/controllers/cluster_health.js',
					'src/kopf/controllers/cluster_overview.js',
					'src/kopf/controllers/cluster_settings.js',
					'src/kopf/controllers/create_index.js',
					'src/kopf/controllers/global.js',
					'src/kopf/controllers/index_settings.js',
					'src/kopf/controllers/navbar.js',
					'src/kopf/controllers/rest.js',
					'src/kopf/controllers/percolator.js',
					'src/kopf/controllers/repository.js',
					'src/kopf/controllers/confirm_dialog.js',
					'src/kopf/controllers/warmup.js',
					'src/kopf/controllers/benchmark.js',
					// SERVICES
					'src/kopf/services/alerts.js',
					'src/kopf/services/settings.js',
					'src/kopf/services/aceeditor.js',
					'src/kopf/services/theme.js',
                    'src/kopf/services/elastic.js',
                    'src/kopf/services/host_history.js',
                    'src/kopf/services/external_settings.js',
					// MODELS
					'src/kopf/models/ace_editor.js',
					'src/kopf/models/gist.js',
					'src/kopf/models/benchmark.js',
                    'src/kopf/models/request.js',
                    'src/kopf/models/cluster_navigation.js',
                    'src/kopf/models/modal_controls.js',
                    'src/kopf/models/paginator.js',
                    'src/kopf/models/alias_filter.js',
                    'src/kopf/models/snapshot_filter.js',
                    'src/kopf/models/warmer_filter.js',
                    'src/kopf/models/index_filter.js',
                    'src/kopf/models/node_filter.js',
					// UTIL
					'src/kopf/util.js'
				]
			}
		},
		qunit: {
			all: ['tests/all.html']
		},
		karma: {
			unit: { configFile: 'tests/karma.config.js', keepalive: true }
		},
        jscs: {
            main: [ 'src/kopf/**/*.js' ],
            except: [ 'src/kopf/theme-kopf.js' ],
            options: {
                preset: 'google'
            }
        }
	});
	grunt.loadNpmTasks('grunt-contrib-clean');
	grunt.loadNpmTasks('grunt-contrib-concat');
	grunt.loadNpmTasks('grunt-contrib-connect');
	grunt.loadNpmTasks('grunt-contrib-copy');
	grunt.loadNpmTasks('grunt-contrib-watch');
	grunt.loadNpmTasks('grunt-contrib-jshint');
	grunt.loadNpmTasks('grunt-contrib-qunit');
	grunt.loadNpmTasks('grunt-karma');
    grunt.loadNpmTasks("grunt-jscs");
	grunt.registerTask('dev', ['karma', 'watch'])
	grunt.registerTask('test', ['karma'])
	grunt.registerTask('build', ['clean', 'jshint', 'qunit', 'copy', 'concat', 'jscs']);
	grunt.registerTask('server', ['clean', 'jshint', 'qunit', 'copy', 'concat','connect:server']);
};
<|endoftext|>


<commit_before>import config from 'src/config'
import {STAT_DESCRIPTORS} from 'src/common/models/stat'
import {connect} from 'src/db'
import {replace as replacePlayer} from 'src/server/db/player'
import {replace as replaceModerator} from 'src/server/db/moderator'
import {addUserToTeam} from 'src/server/services/gitHubService'
import {getLatestCycleForChapter} from 'src/server/db/cycle'
import {addPlayerIdsToPool, getPoolsForCycleWithPlayerCount} from 'src/server/db/pool'
import {GOAL_SELECTION} from 'src/common/models/cycle'
import {LEVELS, computePlayerLevel} from 'src/server/util/stats'

const r = connect()

const {
  ELO,
  ESTIMATION_ACCURACY,
  LEVEL,
} = STAT_DESCRIPTORS

// we want new players to start on level 1, but not act as
// if they have a higher estimation accuracy than current
// level 1 players
const newPlayerEstimationAccuracy = LEVELS[1].requirements[ESTIMATION_ACCURACY] + 0.01
const DEFAULT_PLAYER_STATS = {
  stats: {
    [ELO]: {rating: 1000},
    weightedAverages: {
      [ESTIMATION_ACCURACY]: newPlayerEstimationAccuracy,
    },
  },
}
DEFAULT_PLAYER_STATS.stats[LEVEL] = computePlayerLevel(DEFAULT_PLAYER_STATS)

const upsertToDatabase = {
  // we use .replace() instead of .insert() in case we get duplicates in the queue
  moderator: gameUser => replaceModerator(gameUser, {returnChanges: 'always'}),
  player: gameUser => replacePlayer({...gameUser, ...DEFAULT_PLAYER_STATS}, {returnChanges: 'always'}),
}

export function start() {
  const jobService = require('src/server/services/jobService')
  jobService.processJobs('userCreated', processUserCreated)
}

export async function processUserCreated(user) {
  const gameUser = await addUserToDatabase(user)
  await addUserToChapterGitHubTeam(user, gameUser)
  await notifyCRMSystemOfPlayerSignUp(user)

  const cycle = await getLatestCycleForChapter(gameUser.chapterId)
  if (cycle.state === GOAL_SELECTION) {
    await addNewPlayerToPool(gameUser, cycle)
  }
}

async function addNewPlayerToPool(gameUser, cycle) {
  const poolsWithCount = await getPoolsForCycleWithPlayerCount(cycle.id)
    .filter({level: gameUser.stats.level})

  poolsWithCount.sort((previousPool, currentPool) => previousPool.count - currentPool.count)
  await addPlayerIdsToPool(poolsWithCount[0].id, [gameUser.id])
}

async function addUserToDatabase(user) {
  if (!user.inviteCode) {
    throw new Error(`user with id ${user.id} has no inviteCode, unable to determine chapter assignment`)
  }
  const chapters = await r.table('chapters').getAll(user.inviteCode, {index: 'inviteCodes'}).run()
  if (chapters.length === 0) {
    throw new Error(`no chapter found for inviteCode ${user.inviteCode} on user with id ${user.id}`)
  }
  const chapter = chapters[0]
  const now = r.now()
  const gameUser = {
    id: user.id,
    chapterId: chapter.id,
    createdAt: now,
    updatedAt: now,
  }
  const gameRoles = ['player', 'moderator']
  const dbInsertPromises = []
  gameRoles.forEach(role => {
    if (_userHasRole(user, role)) {
      dbInsertPromises.push(upsertToDatabase[role](gameUser))
    }
  })
  const upsertedUsers = await Promise.all(dbInsertPromises)
    .then(results => results.map(result => result.changes[0].new_val))
    .catch(err => {
      throw new Error(`Unable to insert game user(s): ${err}`)
    })
  return upsertedUsers[0]
}

async function addUserToChapterGitHubTeam(user, gameUser) {
  const chapter = await r.table('chapters').get(gameUser.chapterId).run()
  console.log(`Adding ${user.handle} to GitHub team ${chapter.channelName} (${chapter.githubTeamId})`)

  return addUserToTeam(user.handle, chapter.githubTeamId)
}

function notifyCRMSystemOfPlayerSignUp(user) {
  const crmService = require('src/server/services/crmService')

  if (config.server.crm.enabled !== true) {
    return Promise.resolve()
  }
  if (!_userHasRole(user, 'player')) {
    return Promise.resolve()
  }

  return crmService.notifyContactSignedUp(user.email)
}

function _userHasRole(user, role) {
  if (!user.roles || !Array.isArray(user.roles)) {
    return false
  }
  return user.roles.indexOf(role) >= 0
}
<commit_msg>add in baseline stats for new users<commit_after>import config from 'src/config'
import {STAT_DESCRIPTORS, PRO_PLAYER_STATS_BASELINE} from 'src/common/models/stat'
import {connect} from 'src/db'
import {replace as replacePlayer} from 'src/server/db/player'
import {replace as replaceModerator} from 'src/server/db/moderator'
import {addUserToTeam} from 'src/server/services/gitHubService'
import {getLatestCycleForChapter} from 'src/server/db/cycle'
import {addPlayerIdsToPool, getPoolsForCycleWithPlayerCount} from 'src/server/db/pool'
import {GOAL_SELECTION} from 'src/common/models/cycle'
import {LEVELS, computePlayerLevel} from 'src/server/util/stats'

const r = connect()

const {
  ELO,
  ESTIMATION_ACCURACY,
  LEVEL,
} = STAT_DESCRIPTORS

// we want new players to start on level 1, but not act as
// if they have a higher estimation accuracy than current
// level 1 players
const newPlayerEstimationAccuracy = LEVELS[1].requirements[ESTIMATION_ACCURACY] + 0.01
const DEFAULT_PLAYER_STATS = {
  [ELO]: {rating: 1000},
  weightedAverages: {
    [ESTIMATION_ACCURACY]: newPlayerEstimationAccuracy,
  },
}
DEFAULT_PLAYER_STATS.stats[LEVEL] = computePlayerLevel(DEFAULT_PLAYER_STATS)

const upsertToDatabase = {
  // we use .replace() instead of .insert() in case we get duplicates in the queue
  moderator: gameUser => replaceModerator(gameUser, {returnChanges: 'always'}),
  player: (gameUser, idmUser) => {
    const statsBaseline = _userHasRole(idmUser, 'sep') ? PRO_PLAYER_STATS_BASELINE : {}
    return replacePlayer({
      ...gameUser,
      stats: {
        ...DEFAULT_PLAYER_STATS,
        ...statsBaseline,
      },
      statsBaseline,
    }, {returnChanges: 'always'})
  },
}

export function start() {
  const jobService = require('src/server/services/jobService')
  jobService.processJobs('userCreated', processUserCreated)
}

export async function processUserCreated(user) {
  const gameUser = await addUserToDatabase(user)
  await addUserToChapterGitHubTeam(user, gameUser)
  await notifyCRMSystemOfPlayerSignUp(user)

  const cycle = await getLatestCycleForChapter(gameUser.chapterId)
  if (cycle.state === GOAL_SELECTION) {
    await addNewPlayerToPool(gameUser, cycle)
  }
}

async function addNewPlayerToPool(gameUser, cycle) {
  const poolsWithCount = await getPoolsForCycleWithPlayerCount(cycle.id)
    .filter({level: gameUser.stats.level})

  poolsWithCount.sort((previousPool, currentPool) => previousPool.count - currentPool.count)
  await addPlayerIdsToPool(poolsWithCount[0].id, [gameUser.id])
}

async function addUserToDatabase(user) {
  if (!user.inviteCode) {
    throw new Error(`user with id ${user.id} has no inviteCode, unable to determine chapter assignment`)
  }
  const chapters = await r.table('chapters').getAll(user.inviteCode, {index: 'inviteCodes'}).run()
  if (chapters.length === 0) {
    throw new Error(`no chapter found for inviteCode ${user.inviteCode} on user with id ${user.id}`)
  }
  const chapter = chapters[0]
  const now = r.now()
  const gameUser = {
    id: user.id,
    chapterId: chapter.id,
    createdAt: now,
    updatedAt: now,
  }
  const gameRoles = ['player', 'moderator']
  const dbInsertPromises = []
  gameRoles.forEach(role => {
    if (_userHasRole(user, role)) {
      dbInsertPromises.push(upsertToDatabase[role](gameUser, user))
    }
  })
  const upsertedUsers = await Promise.all(dbInsertPromises)
    .then(results => results.map(result => result.changes[0].new_val))
    .catch(err => {
      throw new Error(`Unable to insert game user(s): ${err}`)
    })
  return upsertedUsers[0]
}

async function addUserToChapterGitHubTeam(user, gameUser) {
  const chapter = await r.table('chapters').get(gameUser.chapterId).run()
  console.log(`Adding ${user.handle} to GitHub team ${chapter.channelName} (${chapter.githubTeamId})`)

  return addUserToTeam(user.handle, chapter.githubTeamId)
}

function notifyCRMSystemOfPlayerSignUp(user) {
  const crmService = require('src/server/services/crmService')

  if (config.server.crm.enabled !== true) {
    return Promise.resolve()
  }
  if (!_userHasRole(user, 'player')) {
    return Promise.resolve()
  }

  return crmService.notifyContactSignedUp(user.email)
}

function _userHasRole(user, role) {
  if (!user.roles || !Array.isArray(user.roles)) {
    return false
  }
  return user.roles.indexOf(role) >= 0
}
<|endoftext|>


<commit_before>/*global describe,it,element,by,takeScreenshot,expect,browser*/

describe("sap.m.ComboBoxClearIcon", function() {
	"use strict";

	it("Should load the ClearButton test page",function() {
		element(by.id("customCssButton")).click();
		expect(takeScreenshot()).toLookAs("clearIcon-initial");
	});

	it("ComboBox - entering value should show the clear icon", function() {
		var comboBox = element(by.id("__box3"));
		comboBox.click();
		browser.actions().sendKeys("B").perform();
		expect(takeScreenshot()).toLookAs("clearIcon-shown");
	});

	it("ComboBox - deleting the value should hide the clear icon", function() {
		var comboBoxClearIcon = element(by.css("#__box3-content > div > span:nth-child(1)"));
		comboBoxClearIcon.click();
		expect(takeScreenshot()).toLookAs("clearIcon-hidden");
	});

	it("MultiComboBox - entering value should show the clear icon", function() {
		var comboBox = element(by.id("__box9"));
		comboBox.click();
		browser.actions().sendKeys("D").perform();
		expect(takeScreenshot()).toLookAs("clearIcon-shown-mcbox");
	});

	it("MultiComboBox - focus out should hide the clear icon", function() {
		element(by.id("customCssButton")).click();
		expect(takeScreenshot()).toLookAs("mcbClearIcon-hidden-focusout");
	});

	it("MultiComboBox - Selecting value should hide the clear icon", function() {
		var multiComboBox = element(by.id("__box9"));
		multiComboBox.click();
		browser.actions().sendKeys("D").perform();
		element.all(by.css('.sapMComboBoxBasePicker[style*="display: block"] li.sapMLIB')).get(0).click();
		expect(takeScreenshot()).toLookAs("mcbClearIcon-hidden-selection");
	});

});
<commit_msg>[INTERNAL] sap.m.ComboBox: Clear icon visual test update<commit_after>/*global describe,it,element,by,takeScreenshot,expect,browser,protractor*/

describe("sap.m.ComboBoxClearIcon", function() {
	"use strict";

	browser.testrunner.currentSuite.meta.controlName = 'sap.m.ComboBox';

	it("Should load the ClearButton test page",function() {
		element(by.id("customCssButton")).click();
		expect(takeScreenshot()).toLookAs("clearIcon-initial");
	});

	it("ComboBox - entering value should show the clear icon", function() {
		var comboBox = element(by.id("__box3"));
		comboBox.click();
		browser.actions().sendKeys("B").perform();
		expect(takeScreenshot()).toLookAs("clearIcon-shown");
	});

	it("ComboBox - deleting the value should hide the clear icon", function() {
		var comboBoxClearIcon = element(by.css("#__box3-content > div > span:nth-child(1)"));
		comboBoxClearIcon.click();
		expect(takeScreenshot()).toLookAs("clearIcon-hidden");
	});

	it("MultiComboBox - entering value should show the clear icon", function() {
		var comboBox = element(by.id("__box9"));
		comboBox.click();
		browser.actions().sendKeys("D").perform();
		expect(takeScreenshot()).toLookAs("clearIcon-shown-mcbox");
	});

	it("MultiComboBox - focus out should hide the clear icon", function() {
		browser.actions().sendKeys(protractor.Key.TAB).perform();
		expect(takeScreenshot()).toLookAs("mcbClearIcon-hidden-focusout");
	});

	it("MultiComboBox - Selecting value should hide the clear icon", function() {
		var multiComboBox = element(by.id("__box9"));
		multiComboBox.click();
		browser.actions().sendKeys("D").perform();
		element.all(by.css('.sapMComboBoxBasePicker[style*="display: block"] li.sapMLIB')).get(0).click();
		expect(takeScreenshot()).toLookAs("mcbClearIcon-hidden-selection");
	});

});
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */
sap.ui.define([
		'jquery.sap.global',
		'sap/ui/thirdparty/URI',
		'sap/ui/Device',
		'sap/ui/test/_LogCollector'
	], function ($, URI, Device, _LogCollector) {
	"use strict";

	/*global CollectGarbage */

	var oFrameWindow = null,
		$Frame = null,
		oFramePlugin = null,
		oFrameUtils = null,
		oFrameJQuery = null,
		bRegisteredToUI5Init = false,
		bUi5Loaded = false,
		oAutoWaiter = null,
		FrameHashChanger = null,
		sOpaLogLevel;

	/*
	 * INTERNALS
	 */
	function handleFrameLoad () {
		oFrameWindow = $Frame[0].contentWindow;

		registerOnError();

		//immediately check for UI5 to be loaded, to intercept when the hash changes
		checkForUI5ScriptLoaded();
	}

	function setFrameSize(sWidth, sHeight) {
		// by default the frame is scaled down from 100% of the page in both dimensions
		// user-defined dimensions should not be scaled
		if (sWidth) {
			$Frame.css("width", sWidth);
		} else {
			$Frame.addClass("default-scale-x");
		}
		if (sHeight) {
			$Frame.css("height", sHeight);
		} else {
			$Frame.addClass("default-scale-y");
		}
		if (!sWidth && !sHeight) {
			$Frame.addClass("default-scale-both");
		}
	}

	function registerOnError () {
		var fnFrameOnError = oFrameWindow.onerror;

		oFrameWindow.onerror = function (sErrorMsg, sUrl, iLine, iColumn, oError) {
			var vReturnValue = false;

			if (fnFrameOnError) {
				// save the return value if the original returns true - the error is supressed
				vReturnValue = fnFrameOnError.apply(this, arguments);
			}

			// a global exception in the outer window's scope should be fired. but since this onerror
			// function is wrapped in QUnits onerror function the exception needs to be thrown in a setTimeout
			// to make sure the QUnit onerror can run to the end
			setTimeout(function () {
				// column number and error object may be missing in older browsers. Currently, Edge doesn't provide the oError object
				var sColumn = iColumn ? "\ncolumn: " + iColumn : "";
				var sIFrameError = oError && "\niFrame error: " + (oError.stack ? oError.message + "\n" + oError.stack : oError) || "";
				throw new Error("Error in launched application iFrame: " + sErrorMsg + "\nurl: " + sUrl + "\nline: " + iLine + sColumn + sIFrameError);
			}, 0);
			return vReturnValue;
		};

	}

	function checkForUI5ScriptLoaded () {
		if (bUi5Loaded) {
			return true;
		}

		if (oFrameWindow && oFrameWindow.sap && oFrameWindow.sap.ui && oFrameWindow.sap.ui.getCore) {
			if (!bRegisteredToUI5Init) {
				oFrameWindow.sap.ui.getCore().attachInit(handleUi5Loaded);
			}

			bRegisteredToUI5Init = true;
		}

		return bUi5Loaded;
	}

	/**
	 * Firefox only function - load sinon as often as needed until it is defined.
	 * @param {function} fnDone executed when sinon is loaded
	 */
	function loadSinon(fnDone) {
		oFrameWindow.sap.ui.require(["sap/ui/thirdparty/sinon"], function (sinon) {
			if (!sinon) {
				setTimeout(function () {
					loadSinon(fnDone);
				}, 50);
			} else {
				fnDone();
			}
		});
	}

	function handleUi5Loaded () {
		registerFrameResourcePaths();

		if (Device.browser.firefox) {
			// In Firefox there is a bug when the app loads sinon and OPA loads it from outside.
			// sinon might be undefined in a module requiring it. So the workaround comes here:
			// trigger the load of sinon - wait until it is defined. Only when it is defined continue loading other modules
			loadSinon(loadFrameModules);
		} else {
			// no workaround - directly load all other modules
			loadFrameModules();
		}
	}

	function afterModulesLoaded () {
		// forward OPA log messages from the inner iframe to the Log listener of the outer frame
		oFrameJQuery.sap.log.addLogListener(_LogCollector.getInstance()._oListener);

		bUi5Loaded = true;
	}

	function registerFrameResourcePaths () {
		oFrameJQuery = oFrameWindow.jQuery;
		//All Opa related resources in the iframe should be the same version
		//that is running in the test and not the (evtl. not available) version of Opa of the running App.
		registerAbsoluteResourcePathInIframe("sap/ui/test");
		registerAbsoluteResourcePathInIframe("sap/ui/qunit");
		registerAbsoluteResourcePathInIframe("sap/ui/thirdparty");
	}

	/**
	 * Disables most of the navigation in an IFrame, only setHash has an effect on the real IFrame history after running this function.
	 * Reason: replace hash does not work in an IFrame so it may not be called at all.
	 * This makes it necessary to hook into all navigation methods
	 * @private
	 */
	function modifyIFrameNavigation (hasher, History, HashChanger) {

		var oHashChanger = new HashChanger(),
			oHistory = new History(oHashChanger),
			fnOriginalSetHash = hasher.setHash,
			fnOriginalGetHash = hasher.getHash,
			sCurrentHash,
			bKnownHashChange = false,
			fnOriginalGo = oFrameWindow.history.go;

		// replace hash is only allowed if it is triggered within the inner window. Even if you trigger an event from the outer test, it will not work.
		// Therefore we have mock the behavior of replace hash. If an application uses the dom api to change the hash window.location.hash, this workaround will fail.
		hasher.replaceHash = function (sHash) {
			bKnownHashChange = true;
			var sOldHash = this.getHash();
			sCurrentHash = sHash;
			// fire the secret events for the local history so the recording is correct.
			// The hash changer is not the global singleton it is a local one only used in this scope for the history.
			oHashChanger.fireEvent("hashReplaced",{ sHash : sHash });
			this.changed.dispatch(sHash, sOldHash);
		};

		hasher.setHash = function (sHash) {
			bKnownHashChange = true;
			var sRealCurrentHash = fnOriginalGetHash.call(this);
			sCurrentHash = sHash;
			// fire the secret events for the local history so the recording is correct.
			// The hash changer is not the global singleton it is a local one only used in this scope for the history.
			oHashChanger.fireEvent("hashSet", { sHash : sHash });
			fnOriginalSetHash.apply(this, arguments);

			// Happens when setHash("a") back setHash("a") is called.
			// Then dispatch the previous hash as new one because hasher does not dispatch if the real hash stays the same
			if (sRealCurrentHash === this.getHash()) {
				// always dispatch the current position of the history, since this can only happen in the backwards / forwards direction
				this.changed.dispatch(sRealCurrentHash, oHistory.aHistory[oHistory.iHistoryPosition]);
			}

		};

		// This function also needs to be manipulated since hasher does not know about our intercepted replace
		hasher.getHash = function() {
			//initial hash
			if (sCurrentHash === undefined) {
				return fnOriginalGetHash.apply(this, arguments);
			}

			return sCurrentHash;
		};

		// when a link is clicked or the hash is directly set we only get a changed event.
		hasher.changed.add(function (sNewHash) {
			// only if the change does not come from the other known places it is likely to be a pressed link
			if (!bKnownHashChange) {
				// fire the secret events for the local history so the recording is correct.
				// The hash changer is not the global singleton it is a local one only used in this scope for the history.
				oHashChanger.fireEvent("hashSet", { sHash : sNewHash });
				sCurrentHash = sNewHash;
			}
			bKnownHashChange = false;
		});

		oHashChanger.init();

		function goBack () {
			bKnownHashChange = true;
			var sNewPreviousHash = oHistory.aHistory[oHistory.iHistoryPosition],
				sNewCurrentHash = oHistory.getPreviousHash();

			sCurrentHash = sNewCurrentHash;
			hasher.changed.dispatch(sNewCurrentHash, sNewPreviousHash);
		}

		function goForward () {
			bKnownHashChange = true;
			var sNewCurrentHash = oHistory.aHistory[oHistory.iHistoryPosition + 1],
				sNewPreviousHash = oHistory.aHistory[oHistory.iHistoryPosition];

			if (sNewCurrentHash === undefined) {
				$.sap.log.error("Could not navigate forwards, there is no history entry in the forwards direction", this);
				return;
			}

			sCurrentHash = sNewCurrentHash;
			hasher.changed.dispatch(sNewCurrentHash, sNewPreviousHash);
		}

		oFrameWindow.history.back = goBack;
		oFrameWindow.history.forward = goForward;

		oFrameWindow.history.go = function (iSteps) {
			if (iSteps === -1) {
				goBack();
				return;
			} else if (iSteps === 1) {
				goForward();
				return;
			}

			$.sap.log.error("Using history.go with a number greater than 1 is not supported by OPA5", this);
			return fnOriginalGo.apply(oFrameWindow.history, arguments);
		};
	}

	function loadFrameModules() {
		oFrameWindow.sap.ui.require([
			"sap/ui/test/OpaPlugin",
			"sap/ui/test/autowaiter/_autoWaiter",
			"sap/ui/test/_OpaLogger",
			"sap/ui/qunit/QUnitUtils",
			"sap/ui/thirdparty/hasher",
			"sap/ui/core/routing/History",
			"sap/ui/core/routing/HashChanger"
		], function (
			OpaPlugin,
			_autoWaiter,
			_OpaLogger,
			QUnitUtils,
			hasher,
			History,
			HashChanger
		) {
			_OpaLogger.setLevel(sOpaLogLevel);
			oFramePlugin = new OpaPlugin();
			oAutoWaiter = _autoWaiter;
			oFrameUtils = QUnitUtils;
			modifyIFrameNavigation(hasher, History, HashChanger);
			FrameHashChanger = HashChanger;
			afterModulesLoaded();
		});
	}

	function registerAbsoluteResourcePathInIframe(sResource) {
		var sOpaLocation = sap.ui.require.toUrl(sResource);
		var sAbsoluteOpaPath = new URI(sOpaLocation).absoluteTo(document.baseURI).search("").toString();
		var fnConfig = oFrameWindow.sap.ui._ui5loader || oFrameWindow.sap.ui.loader.config;
		if (fnConfig) {
			var paths = {};
			paths[sResource] = sAbsoluteOpaPath;
			fnConfig({
				paths: paths
			});
		} else if (oFrameJQuery && oFrameJQuery.sap && oFrameJQuery.sap.registerResourcePath) {
			oFrameJQuery.sap.registerResourcePath(sResource, sAbsoluteOpaPath);
		} else {
			throw new Error("iFrameLauncher.js: UI5 module system not found.");
		}
	}

	function destroyFrame () {
		if (!oFrameWindow) {
			throw new Error("sap.ui.test.launchers.iFrameLauncher: Teardown was called before launch. No iFrame was loaded.");
		}
		// Workaround for IE - there are errors even after removing the frame so setting the onerror to noop again seems to be fine
		oFrameWindow.onerror = $.noop;
		for (var i = 0; i < $Frame.length; i++) {
			$Frame[0].src = "about:blank";
			$Frame[0].contentWindow.document.write('');
			$Frame[0].contentWindow.close();
		}
		if ( typeof CollectGarbage == "function") {
			CollectGarbage(); // eslint-disable-line
		}
		$Frame.remove();
		oFrameJQuery = null;
		oFramePlugin = null;
		oFrameUtils = null;
		oFrameWindow = null;
		bUi5Loaded = false;
		bRegisteredToUI5Init = false;
		oAutoWaiter = null;
		FrameHashChanger = null;
	}

	/**
	 * Contains the logic to place an iFrame to the dom and overwrites the navigation inside of it.
	 * Every launch call needs a corresponding teardown call, or errors will be logged and the calls will be ignored.
	 * Please have a look at {@link sap.ui.test.Opa5#iStartMyAppInAFrame} and {@link sap.ui.test.Opa5#iTeardownMyAppFrame}for the public functions using this class.
	 * @private
	 */
	return {
		launch: function (options) {
			if (oFrameWindow) {
				throw new Error("sap.ui.test.launchers.iFrameLauncher: Launch was called twice without teardown. Only one iFrame may be loaded at a time.");
			}

			//invalidate the cache
			$Frame = $("#" + options.frameId);

			if (!$Frame.length) {
				if (!options.source) {
					$.sap.log.error("No source was given to launch the IFrame", this);
				}
				//invalidate other caches
				$Frame = $('<IFrame id="' + options.frameId + '" class="opaFrame" src="' + options.source + '"></IFrame>');
				$("body").append($Frame);
				setFrameSize(options.width, options.height);
			}

			if ($Frame[0].contentDocument && $Frame[0].contentDocument.readyState === "complete") {
				handleFrameLoad();
			} else {
				$Frame.on("load", handleFrameLoad);
			}
			sOpaLogLevel = options.opaLogLevel;
			return checkForUI5ScriptLoaded();
		},
		hasLaunched: function () {
			checkForUI5ScriptLoaded();
			return bUi5Loaded;
		},
		teardown: function () {
			destroyFrame();
		},
		getHashChanger: function () {
			if (!FrameHashChanger) {
				return null;
			}

			return FrameHashChanger.getInstance();
		},
		getPlugin: function () {
			return oFramePlugin;
		},
		getJQuery: function () {
			return oFrameJQuery;
		},
		getUtils: function () {
			return oFrameUtils;
		},
		getWindow: function () {
			return oFrameWindow;
		},
		_getAutoWaiter: function () {
			return oAutoWaiter;
		}
	};
}, /* export= */ true);
<commit_msg>[FIX] test/launchers/iFrameLauncher: fixed loader config access<commit_after>/*!
 * ${copyright}
 */
sap.ui.define([
		'jquery.sap.global',
		'sap/ui/thirdparty/URI',
		'sap/ui/Device',
		'sap/ui/test/_LogCollector',
		'sap/base/util/ObjectPath'
	], function ($, URI, Device, _LogCollector, ObjectPath) {
	"use strict";

	/*global CollectGarbage */

	var oFrameWindow = null,
		$Frame = null,
		oFramePlugin = null,
		oFrameUtils = null,
		oFrameJQuery = null,
		bRegisteredToUI5Init = false,
		bUi5Loaded = false,
		oAutoWaiter = null,
		FrameHashChanger = null,
		sOpaLogLevel;

	/*
	 * INTERNALS
	 */
	function handleFrameLoad () {
		oFrameWindow = $Frame[0].contentWindow;

		registerOnError();

		//immediately check for UI5 to be loaded, to intercept when the hash changes
		checkForUI5ScriptLoaded();
	}

	function setFrameSize(sWidth, sHeight) {
		// by default the frame is scaled down from 100% of the page in both dimensions
		// user-defined dimensions should not be scaled
		if (sWidth) {
			$Frame.css("width", sWidth);
		} else {
			$Frame.addClass("default-scale-x");
		}
		if (sHeight) {
			$Frame.css("height", sHeight);
		} else {
			$Frame.addClass("default-scale-y");
		}
		if (!sWidth && !sHeight) {
			$Frame.addClass("default-scale-both");
		}
	}

	function registerOnError () {
		var fnFrameOnError = oFrameWindow.onerror;

		oFrameWindow.onerror = function (sErrorMsg, sUrl, iLine, iColumn, oError) {
			var vReturnValue = false;

			if (fnFrameOnError) {
				// save the return value if the original returns true - the error is supressed
				vReturnValue = fnFrameOnError.apply(this, arguments);
			}

			// a global exception in the outer window's scope should be fired. but since this onerror
			// function is wrapped in QUnits onerror function the exception needs to be thrown in a setTimeout
			// to make sure the QUnit onerror can run to the end
			setTimeout(function () {
				// column number and error object may be missing in older browsers. Currently, Edge doesn't provide the oError object
				var sColumn = iColumn ? "\ncolumn: " + iColumn : "";
				var sIFrameError = oError && "\niFrame error: " + (oError.stack ? oError.message + "\n" + oError.stack : oError) || "";
				throw new Error("Error in launched application iFrame: " + sErrorMsg + "\nurl: " + sUrl + "\nline: " + iLine + sColumn + sIFrameError);
			}, 0);
			return vReturnValue;
		};

	}

	function checkForUI5ScriptLoaded () {
		if (bUi5Loaded) {
			return true;
		}

		if (oFrameWindow && oFrameWindow.sap && oFrameWindow.sap.ui && oFrameWindow.sap.ui.getCore) {
			if (!bRegisteredToUI5Init) {
				oFrameWindow.sap.ui.getCore().attachInit(handleUi5Loaded);
			}

			bRegisteredToUI5Init = true;
		}

		return bUi5Loaded;
	}

	/**
	 * Firefox only function - load sinon as often as needed until it is defined.
	 * @param {function} fnDone executed when sinon is loaded
	 */
	function loadSinon(fnDone) {
		oFrameWindow.sap.ui.require(["sap/ui/thirdparty/sinon"], function (sinon) {
			if (!sinon) {
				setTimeout(function () {
					loadSinon(fnDone);
				}, 50);
			} else {
				fnDone();
			}
		});
	}

	function handleUi5Loaded () {
		registerFrameResourcePaths();

		if (Device.browser.firefox) {
			// In Firefox there is a bug when the app loads sinon and OPA loads it from outside.
			// sinon might be undefined in a module requiring it. So the workaround comes here:
			// trigger the load of sinon - wait until it is defined. Only when it is defined continue loading other modules
			loadSinon(loadFrameModules);
		} else {
			// no workaround - directly load all other modules
			loadFrameModules();
		}
	}

	function afterModulesLoaded () {
		// forward OPA log messages from the inner iframe to the Log listener of the outer frame
		oFrameJQuery.sap.log.addLogListener(_LogCollector.getInstance()._oListener);

		bUi5Loaded = true;
	}

	function registerFrameResourcePaths () {
		oFrameJQuery = oFrameWindow.jQuery;
		//All Opa related resources in the iframe should be the same version
		//that is running in the test and not the (evtl. not available) version of Opa of the running App.
		registerAbsoluteResourcePathInIframe("sap/ui/test");
		registerAbsoluteResourcePathInIframe("sap/ui/qunit");
		registerAbsoluteResourcePathInIframe("sap/ui/thirdparty");
	}

	/**
	 * Disables most of the navigation in an IFrame, only setHash has an effect on the real IFrame history after running this function.
	 * Reason: replace hash does not work in an IFrame so it may not be called at all.
	 * This makes it necessary to hook into all navigation methods
	 * @private
	 */
	function modifyIFrameNavigation (hasher, History, HashChanger) {

		var oHashChanger = new HashChanger(),
			oHistory = new History(oHashChanger),
			fnOriginalSetHash = hasher.setHash,
			fnOriginalGetHash = hasher.getHash,
			sCurrentHash,
			bKnownHashChange = false,
			fnOriginalGo = oFrameWindow.history.go;

		// replace hash is only allowed if it is triggered within the inner window. Even if you trigger an event from the outer test, it will not work.
		// Therefore we have mock the behavior of replace hash. If an application uses the dom api to change the hash window.location.hash, this workaround will fail.
		hasher.replaceHash = function (sHash) {
			bKnownHashChange = true;
			var sOldHash = this.getHash();
			sCurrentHash = sHash;
			// fire the secret events for the local history so the recording is correct.
			// The hash changer is not the global singleton it is a local one only used in this scope for the history.
			oHashChanger.fireEvent("hashReplaced",{ sHash : sHash });
			this.changed.dispatch(sHash, sOldHash);
		};

		hasher.setHash = function (sHash) {
			bKnownHashChange = true;
			var sRealCurrentHash = fnOriginalGetHash.call(this);
			sCurrentHash = sHash;
			// fire the secret events for the local history so the recording is correct.
			// The hash changer is not the global singleton it is a local one only used in this scope for the history.
			oHashChanger.fireEvent("hashSet", { sHash : sHash });
			fnOriginalSetHash.apply(this, arguments);

			// Happens when setHash("a") back setHash("a") is called.
			// Then dispatch the previous hash as new one because hasher does not dispatch if the real hash stays the same
			if (sRealCurrentHash === this.getHash()) {
				// always dispatch the current position of the history, since this can only happen in the backwards / forwards direction
				this.changed.dispatch(sRealCurrentHash, oHistory.aHistory[oHistory.iHistoryPosition]);
			}

		};

		// This function also needs to be manipulated since hasher does not know about our intercepted replace
		hasher.getHash = function() {
			//initial hash
			if (sCurrentHash === undefined) {
				return fnOriginalGetHash.apply(this, arguments);
			}

			return sCurrentHash;
		};

		// when a link is clicked or the hash is directly set we only get a changed event.
		hasher.changed.add(function (sNewHash) {
			// only if the change does not come from the other known places it is likely to be a pressed link
			if (!bKnownHashChange) {
				// fire the secret events for the local history so the recording is correct.
				// The hash changer is not the global singleton it is a local one only used in this scope for the history.
				oHashChanger.fireEvent("hashSet", { sHash : sNewHash });
				sCurrentHash = sNewHash;
			}
			bKnownHashChange = false;
		});

		oHashChanger.init();

		function goBack () {
			bKnownHashChange = true;
			var sNewPreviousHash = oHistory.aHistory[oHistory.iHistoryPosition],
				sNewCurrentHash = oHistory.getPreviousHash();

			sCurrentHash = sNewCurrentHash;
			hasher.changed.dispatch(sNewCurrentHash, sNewPreviousHash);
		}

		function goForward () {
			bKnownHashChange = true;
			var sNewCurrentHash = oHistory.aHistory[oHistory.iHistoryPosition + 1],
				sNewPreviousHash = oHistory.aHistory[oHistory.iHistoryPosition];

			if (sNewCurrentHash === undefined) {
				$.sap.log.error("Could not navigate forwards, there is no history entry in the forwards direction", this);
				return;
			}

			sCurrentHash = sNewCurrentHash;
			hasher.changed.dispatch(sNewCurrentHash, sNewPreviousHash);
		}

		oFrameWindow.history.back = goBack;
		oFrameWindow.history.forward = goForward;

		oFrameWindow.history.go = function (iSteps) {
			if (iSteps === -1) {
				goBack();
				return;
			} else if (iSteps === 1) {
				goForward();
				return;
			}

			$.sap.log.error("Using history.go with a number greater than 1 is not supported by OPA5", this);
			return fnOriginalGo.apply(oFrameWindow.history, arguments);
		};
	}

	function loadFrameModules() {
		oFrameWindow.sap.ui.require([
			"sap/ui/test/OpaPlugin",
			"sap/ui/test/autowaiter/_autoWaiter",
			"sap/ui/test/_OpaLogger",
			"sap/ui/qunit/QUnitUtils",
			"sap/ui/thirdparty/hasher",
			"sap/ui/core/routing/History",
			"sap/ui/core/routing/HashChanger"
		], function (
			OpaPlugin,
			_autoWaiter,
			_OpaLogger,
			QUnitUtils,
			hasher,
			History,
			HashChanger
		) {
			_OpaLogger.setLevel(sOpaLogLevel);
			oFramePlugin = new OpaPlugin();
			oAutoWaiter = _autoWaiter;
			oFrameUtils = QUnitUtils;
			modifyIFrameNavigation(hasher, History, HashChanger);
			FrameHashChanger = HashChanger;
			afterModulesLoaded();
		});
	}

	function registerAbsoluteResourcePathInIframe(sResource) {
		var sOpaLocation = sap.ui.require.toUrl(sResource);
		var sAbsoluteOpaPath = new URI(sOpaLocation).absoluteTo(document.baseURI).search("").toString();
		var fnConfig = ObjectPath.get("sap.ui._ui5loader.config", oFrameWindow) || ObjectPath.get("sap.ui.loader.config", oFrameWindow);
		if (fnConfig) {
			var paths = {};
			paths[sResource] = sAbsoluteOpaPath;
			fnConfig({
				paths: paths
			});
		} else if (oFrameJQuery && oFrameJQuery.sap && oFrameJQuery.sap.registerResourcePath) {
			oFrameJQuery.sap.registerResourcePath(sResource, sAbsoluteOpaPath);
		} else {
			throw new Error("iFrameLauncher.js: UI5 module system not found.");
		}
	}

	function destroyFrame () {
		if (!oFrameWindow) {
			throw new Error("sap.ui.test.launchers.iFrameLauncher: Teardown was called before launch. No iFrame was loaded.");
		}
		// Workaround for IE - there are errors even after removing the frame so setting the onerror to noop again seems to be fine
		oFrameWindow.onerror = $.noop;
		for (var i = 0; i < $Frame.length; i++) {
			$Frame[0].src = "about:blank";
			$Frame[0].contentWindow.document.write('');
			$Frame[0].contentWindow.close();
		}
		if ( typeof CollectGarbage == "function") {
			CollectGarbage(); // eslint-disable-line
		}
		$Frame.remove();
		oFrameJQuery = null;
		oFramePlugin = null;
		oFrameUtils = null;
		oFrameWindow = null;
		bUi5Loaded = false;
		bRegisteredToUI5Init = false;
		oAutoWaiter = null;
		FrameHashChanger = null;
	}

	/**
	 * Contains the logic to place an iFrame to the dom and overwrites the navigation inside of it.
	 * Every launch call needs a corresponding teardown call, or errors will be logged and the calls will be ignored.
	 * Please have a look at {@link sap.ui.test.Opa5#iStartMyAppInAFrame} and {@link sap.ui.test.Opa5#iTeardownMyAppFrame}for the public functions using this class.
	 * @private
	 */
	return {
		launch: function (options) {
			if (oFrameWindow) {
				throw new Error("sap.ui.test.launchers.iFrameLauncher: Launch was called twice without teardown. Only one iFrame may be loaded at a time.");
			}

			//invalidate the cache
			$Frame = $("#" + options.frameId);

			if (!$Frame.length) {
				if (!options.source) {
					$.sap.log.error("No source was given to launch the IFrame", this);
				}
				//invalidate other caches
				$Frame = $('<IFrame id="' + options.frameId + '" class="opaFrame" src="' + options.source + '"></IFrame>');
				$("body").append($Frame);
				setFrameSize(options.width, options.height);
			}

			if ($Frame[0].contentDocument && $Frame[0].contentDocument.readyState === "complete") {
				handleFrameLoad();
			} else {
				$Frame.on("load", handleFrameLoad);
			}
			sOpaLogLevel = options.opaLogLevel;
			return checkForUI5ScriptLoaded();
		},
		hasLaunched: function () {
			checkForUI5ScriptLoaded();
			return bUi5Loaded;
		},
		teardown: function () {
			destroyFrame();
		},
		getHashChanger: function () {
			if (!FrameHashChanger) {
				return null;
			}

			return FrameHashChanger.getInstance();
		},
		getPlugin: function () {
			return oFramePlugin;
		},
		getJQuery: function () {
			return oFrameJQuery;
		},
		getUtils: function () {
			return oFrameUtils;
		},
		getWindow: function () {
			return oFrameWindow;
		},
		_getAutoWaiter: function () {
			return oAutoWaiter;
		}
	};
}, /* export= */ true);
<|endoftext|>


<commit_before>import FitLM from './fit_lm'
import {
  extend
} from '../graph.util'

import aggregator from './data_aggregator'

// http://stackoverflow.com/questions/26965171/fast-nearest-power-of-2-in-javascript
function pow2ceil( v ) {
  v--;
  var p = 2;
  while ( v >>= 1 ) {
    p <<= 1;
  }
  return p;
}

function binarySearch( target, haystack, reverse ) {

  let seedA = 0,
    length = haystack.length,
    seedB = ( length - 1 ),
    seedInt,
    i = 0,
    nanDirection = 1;

  if ( haystack[ seedA ] == target ) {
    return seedA;
  }

  if ( haystack[ seedB ] == target ) {
    return seedB;
  }

  while ( true ) {
    i++;
    if ( i > 100 ) {
      throw "Error loop";
    }

    seedInt = Math.floor( ( seedA + seedB ) / 2 );
    //  seedInt -= seedInt % 2; // Always looks for an x.

    while ( isNaN( haystack[ seedInt ] ) ) {
      seedInt += nanDirection;
    }

    if ( seedInt == seedA || haystack[ seedInt ] == target || seedInt == seedB ) {
      return seedInt;
    }

    //    console.log(seedA, seedB, seedInt, haystack[seedInt]);
    if ( haystack[ seedInt ] <= target ) {
      if ( reverse ) {
        seedB = seedInt;
      } else {
        seedA = seedInt;
      }
    } else if ( haystack[ seedInt ] > target ) {
      if ( reverse ) {
        seedA = seedInt;
      } else {
        seedB = seedInt;
      }
    }

    nanDirection *= -1;
  }
}

class Waveform {

  constructor() {}

  /** [ [ x1, y1 ], [ x2, y2 ] ] */
  setDataXY( data ) {

    let newData = [ [], [] ];
    data.map( ( el, index ) => {
      newData[ 0 ][ index ] = el[ 0 ];
      newData[ 1 ][ index ] = el[ 1 ];
    } );

    this._dataUpdated( data );
    return this;
  }

  setDataY( data ) {

    let newData = [ [], [] ];
    data.map( ( el, index ) => {
      newData[ index ][ 1 ] = el;
      newData[ index ][ 0 ] = index;
    } );

    this._dataUpdated( newData );
    return this;
  }

  flipXY() {
    let temp;
    this.data = data.map( ( el ) => {
      temp = el[ 0 ];
      el[ 0 ] = el[ 1 ];
      el[ 1 ] = temp;
      return el;
    } );
    this.dataUpdated();
  }

  dataUpdated() {
    const l = this.data.length;
    let i = 0,
      monoDir = this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ],
      minX = this.data[ 0 ][ 0 ],
      maxX = this.data[ 0 ][ 0 ],
      minY = this.data[ 0 ][ 1 ],
      maxY = this.data[ 0 ][ 1 ];

    this._monotoneous = true;

    for ( ; i < l; i++ ) {
      if ( monoDir !== ( this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ] ) ) {
        this._monotoneous = false;
      }

      minX = Math.min( this.data[ i ][ 0 ], minX );
      maxX = Math.max( this.data[ i ][ 0 ], maxX );
      minY = Math.min( this.data[ i ][ 1 ], minY );
      maxY = Math.max( this.data[ i ][ 1 ], maxY );
    }

    this.minX = minX;
    this.maxX = maxX;
    this.minY = minY;
    this.maxY = maxY;
  }

  _getMin( dim, from, to ) {

    let min = this.data[ from ][ dim ];
    for ( var i = from + 1; i <= to; i++ ) {
      min = this.data[ i ][ dim ] < min ? this.data[ i ][ dim ] : min;
    }
    return min;
  }

  _getMax( dim, from, to ) {
      let max = this.data[ from ][ dim ];
      for ( var i = from + 1; i <= to; i++ ) {
        max = this.data[ i ][ dim ] > max ? this.data[ i ][ dim ] : max;
      }
      return max;
    }
    /*
      getXMin( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMin( 0, subsetFromIndex, subsetToIndex );
      }

      getXMax( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMax( 0, subsetFromIndex, subsetToIndex );
      }

      getMin( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMin( 1, subsetFromIndex, subsetToIndex );
      }

      getMax( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMax( 1, subsetFromIndex, subsetToIndex );
      }
      */

  getXMin() {
    return this.minX;
  }

  getXMax() {
    return this.maxX;
  }

  getYMin() {
    return this.minY;
  }

  getYMax() {
    return this.maxY;
  }

  getMin() {
    return this.minY;
  }

  getMax() {
    return this.maxY;
  }

  getMinX() {
    return this.minX;
  }

  getMaxX() {
    return this.maxX;
  }

  getMinY() {
    return this.minY;
  }

  getMaxY() {
    return this.maxY;
  }

  getLength() {
    return this.data.length;
  }

  getDataY() {

    return this.data.map( ( el ) => el[ 1 ] );
  }

  getDataX() {
    return this.data.map( ( el ) => el[ 0 ] );
  }

  getDataToUseFlat() {

    let dataToUse = this.dataInUse ? this.dataInUse : this.data;

    let arr = new Array( dataToUse.length * 2 ).fill( 0 );
    let j = 0;
    for ( var i = 0, l = dataToUse.length; i < l; i += 1 ) {
      arr[ j ] = dataToUse[ i ][ 0 ];
      arr[ j + 1 ] = dataToUse[ i ][ 1 ];
      j += 2;
    }

    return arr;
  }

  fit( options ) {

    var fit = new FitLM( extend( {}, {
      dataY: this
    }, options ) );
    fit.init();
    fit.fit();
    return this;
  }

  _integrateP( from = 0, to = this.getLength() - 1 ) {

    from = Math.round( from );
    to = Math.round( to );

    var l = to - from + 1;
    var sum = 0,
      delta;

    let deltaTot = 0;
    var arrX = this.getDataX();
    var arrY = this.getDataY();

    for ( ; from <= to; from++ ) {

      if ( arrX.length - 1 > from ) {

        deltaTot += arrX[ from + 1 ] - arrX[ from ]
        sum += arrY[ from ] * ( arrX[ from + 1 ] - arrX[ from ] );
      }
    }
    return [ sum, l, deltaTot ];
  }

  integrateP( from, to ) {
    var val = this._integrateP( from, to );
    return val[ 0 ];
  }

  average( p0 = 0, p1 = this.getLength() - 1 ) {
    return this.getAverageP( p0, p1 );
  }

  mean() {
    return this.average();
  }

  getAverageP( from, to ) {
    var sum = this._integrateP( from, to );
    return sum[ 0 ] / sum[ 2 ];
  }

  getAverageX( from, to ) {
    var sum = this._integrateX( from, to );
    return sum[ 0 ] / sum[ 2 ];
  }

  checkMonotonicity() {

    let i = 0;
    const l = this.data.length;
    let dir = this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ];

    for ( ; i < l; i++ ) {
      if ( dir !== ( this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ] ) ) {
        return this._monotoneous = false;
      }
    }

    return this._monotoneous = true;
  }

  requireMonotonicity() {
    if ( !this.isMonotoneous() ) {
      throw "The x wave must be monotonic";
    }
  }

  isMonotoneous() {
    return !!this._monotoneous;
  }

  invert( data ) {
    let d = data || this.data;
    d.reverse();
    return d;
  }

  resampleForDisplay( options ) { // Serie redrawing

    let i = 0;

    this.requireMonotonicity();

    let inverting = false;

    if ( this.data[ 1 ][ 0 ] < this.data[ 0 ][ 0 ] ) {
      this.invert();
      inverting = true;
    }

    const l = this.getLength();

    let data = [],
      dataMinMax = [],
      resampleSum, resampleMin, resampleMax, resampleNum, resample_x_start, resample_x_px_start,
      x_px,
      dataY = this.getDataY(),
      dataX = this.getDataX(),
      doing_mean = false,
      firstPointIndex = 0;

    if ( !options.xPosition ) {
      throw "No position calculation method provided";
    }

    if ( !options.resampleToPx ) {
      throw "No \"resampleToPx\" method was provided. Unit: px per point";
    }

    for ( ; i < l; i++ ) {

      if ( options.minX > dataX[ i ] ) {

        firstPointIndex = i;
        continue;
      }

      x_px = options.xPosition( dataX[ i ] );

      if ( !doing_mean ) {

        if ( !firstPointIndex ) {

          firstPointIndex = i;
        }

        while ( isNaN( dataY[ i ] ) ) {
          i++;
        }

        resampleSum = resampleMin = resampleMax = dataY[ firstPointIndex ];
        resampleNum = 1;
        resample_x_px_start = x_px;
        resample_x_start = dataX[ i ];
        firstPointIndex = 0;

        doing_mean = true;

        continue;
      }

      if ( Math.abs( x_px - resample_x_px_start ) > options.resampleToPx || i == l || isNaN( dataY[ i ] ) ) {

        let xpos = ( resample_x_start + dataX[ i ] ) / 2;

        data.push( [
          xpos,
          resampleSum / resampleNum
        ] );

        dataMinMax.push( xpos, resampleMin, resampleMax );

        if ( options.maxX !== undefined && dataX > options.maxX ) {
          return;
        }

        doing_mean = false;

        continue;
      }

      resampleSum += dataY[ i ];
      resampleNum++;

      resampleMin = Math.min( resampleMin, dataY[ i ] );
      resampleMax = Math.max( resampleMax, dataY[ i ] );
    }

    if ( inverting ) {
      this.dataInUse = this.invert( data );
      inverting = true;
      return this.invert( dataMinMax );
    }

    this.dataInUse = data;
    return dataMinMax;
  }

  multiply( numberOrWave ) {

    if ( numberOrWave instanceof Waveform ) {
      return this._multiplyByWave( numberOrWave );
    } else if ( typeof numberOrWave == 'number' ) {
      return this._multiplyByNumber( numberOrWave );
    }
  }

  _multiplyByNumber( num ) {

    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] *= num;
    }
    return this;
  }

  _multiplyByWave( wave ) {

    let y = wave.getDataY();
    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] *= y[ i ];
    }
    return this;
  }

  interpolate( x ) {

    let xData = this.getDataX(),
      yData = this.getDataY(),
      xIndex = binarySearch( x, xData, !this.getMonotoneousDirection() );

    return ( x - xData[ xIndex ] ) / ( xData[ xIndex + 1 ] - xData[ xIndex ] ) * ( yData[ xIndex + 1 ] - yData[ xIndex ] ) + yData[ xIndex ];
  }

  divide( numberOrWave ) {

    if ( numberOrWave instanceof Waveform ) {
      return this._multiplyByWave( numberOrWave );
    } else if ( typeof numberOrWave == 'number' ) {
      return this._multiplyByNumber( numberOrWave );
    }
  }

  _divideByNumber( num ) {

    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] /= num;
    }
    return this;
  }

  _divideByWave( wave ) {

    let yDataThis = this.getDataY(),
      xDataThis = this.getDataX(),
      i = 0;

    const l = this.getLength();

    for ( ; i < l; i++ ) {

      yDataThis[ i ] /= wave.interpolate( xDataThis[ x ] );
    }

    return this;
  }

  aggregate() {

    const levels = 10;

    let level = 128; // 128 points

    let i = 0;

    this._dataAggregating = {};
    this._dataAggregated = {};

    for ( ; i < levels; i++ ) {

      this._dataAggregating[ level ] = aggregator( {

        min: this.getMinX(),
        max: this.getMaxX(),
        data: this.data,
        numPoints: level

      } ).then( ( data ) => {

        this._dataAggregated[ data.numPoints ] = data.data;
      } );

      if ( level > this.getLength() ) {
        break;
      }

      level *= 2;
    }
  }

  hasAggregation() {
    return !!this._dataAggregated;
  }

  getAggregatedData( pxWidth ) {

    var level = pow2ceil( pxWidth );
    if ( this._dataAggregated[ level ] ) {
      this.dataInUse = this._dataAggregated[ level ];
      return;
    } else if ( this._dataAggregating[ level ] ) {
      return this._dataAggregating[ level ];
    }

    this.dataInUse = this.data;
  }

};

export default Waveform<commit_msg>WIP waveform<commit_after>import FitLM from './fit_lm'
import {
  extend
} from '../graph.util'

import aggregator from './data_aggregator'

// http://stackoverflow.com/questions/26965171/fast-nearest-power-of-2-in-javascript
function pow2ceil( v ) {
  v--;
  var p = 2;
  while ( v >>= 1 ) {
    p <<= 1;
  }
  return p;
}

function binarySearch( target, haystack, reverse ) {

  let seedA = 0,
    length = haystack.length,
    seedB = ( length - 1 ),
    seedInt,
    i = 0,
    nanDirection = 1;

  if ( haystack[ seedA ] == target ) {
    return seedA;
  }

  if ( haystack[ seedB ] == target ) {
    return seedB;
  }

  while ( true ) {
    i++;
    if ( i > 100 ) {
      throw "Error loop";
    }

    seedInt = Math.floor( ( seedA + seedB ) / 2 );
    //  seedInt -= seedInt % 2; // Always looks for an x.

    while ( isNaN( haystack[ seedInt ] ) ) {
      seedInt += nanDirection;
    }

    if ( seedInt == seedA || haystack[ seedInt ] == target || seedInt == seedB ) {
      return seedInt;
    }

    //    console.log(seedA, seedB, seedInt, haystack[seedInt]);
    if ( haystack[ seedInt ] <= target ) {
      if ( reverse ) {
        seedB = seedInt;
      } else {
        seedA = seedInt;
      }
    } else if ( haystack[ seedInt ] > target ) {
      if ( reverse ) {
        seedA = seedInt;
      } else {
        seedB = seedInt;
      }
    }

    nanDirection *= -1;
  }
}

class Waveform {

  constructor() {}

  /** [ [ x1, y1 ], [ x2, y2 ] ] */
  setDataXY( data ) {

    let newData = [ [], [] ];
    data.map( ( el, index ) => {
      newData[ 0 ][ index ] = el[ 0 ];
      newData[ 1 ][ index ] = el[ 1 ];
    } );

    this._dataUpdated( data );
    return this;
  }

  setDataY( data ) {

    let newData = [ [], [] ];
    data.map( ( el, index ) => {
      newData[ index ][ 1 ] = el;
      newData[ index ][ 0 ] = index;
    } );

    this._dataUpdated( newData );
    return this;
  }

  flipXY() {
    let temp;
    temp = this.data.x;
    this.data.x = this.data.y;
    this.data.y = temp;

    this.dataUpdated( [ this.data.x, this.data.y ] );
  }

  dataUpdated() {
    const l = this.data.length;
    let i = 0,
      monoDir = this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ],
      minX = this.data[ 0 ][ 0 ],
      maxX = this.data[ 0 ][ 0 ],
      minY = this.data[ 0 ][ 1 ],
      maxY = this.data[ 0 ][ 1 ];

    this._monotoneous = true;

    for ( ; i < l; i++ ) {
      if ( monoDir !== ( this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ] ) ) {
        this._monotoneous = false;
      }

      minX = Math.min( this.data[ i ][ 0 ], minX );
      maxX = Math.max( this.data[ i ][ 0 ], maxX );
      minY = Math.min( this.data[ i ][ 1 ], minY );
      maxY = Math.max( this.data[ i ][ 1 ], maxY );
    }

    this.minX = minX;
    this.maxX = maxX;
    this.minY = minY;
    this.maxY = maxY;
  }

  _getMin( dim, from, to ) {

    let min = this.data[ from ][ dim ];
    for ( var i = from + 1; i <= to; i++ ) {
      min = this.data[ i ][ dim ] < min ? this.data[ i ][ dim ] : min;
    }
    return min;
  }

  _getMax( dim, from, to ) {
      let max = this.data[ from ][ dim ];
      for ( var i = from + 1; i <= to; i++ ) {
        max = this.data[ i ][ dim ] > max ? this.data[ i ][ dim ] : max;
      }
      return max;
    }
    /*
      getXMin( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMin( 0, subsetFromIndex, subsetToIndex );
      }

      getXMax( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMax( 0, subsetFromIndex, subsetToIndex );
      }

      getMin( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMin( 1, subsetFromIndex, subsetToIndex );
      }

      getMax( subsetFromIndex = 0, subsetToIndex = this.data.length - 1 ) {
        return this._getMax( 1, subsetFromIndex, subsetToIndex );
      }
      */

  getXMin() {
    return this.minX;
  }

  getXMax() {
    return this.maxX;
  }

  getYMin() {
    return this.minY;
  }

  getYMax() {
    return this.maxY;
  }

  getMin() {
    return this.minY;
  }

  getMax() {
    return this.maxY;
  }

  getMinX() {
    return this.minX;
  }

  getMaxX() {
    return this.maxX;
  }

  getMinY() {
    return this.minY;
  }

  getMaxY() {
    return this.maxY;
  }

  getLength() {
    return this.data.length;
  }

  getDataY() {

    return this.data.map( ( el ) => el[ 1 ] );
  }

  getDataX() {
    return this.data.map( ( el ) => el[ 0 ] );
  }

  getDataToUseFlat() {

    let dataToUse = this.dataInUse ? this.dataInUse : this.data;

    let arr = new Array( dataToUse.length * 2 ).fill( 0 );
    let j = 0;
    for ( var i = 0, l = dataToUse.length; i < l; i += 1 ) {
      arr[ j ] = dataToUse[ i ][ 0 ];
      arr[ j + 1 ] = dataToUse[ i ][ 1 ];
      j += 2;
    }

    return arr;
  }

  fit( options ) {

    var fit = new FitLM( extend( {}, {
      dataY: this
    }, options ) );
    fit.init();
    fit.fit();
    return this;
  }

  _integrateP( from = 0, to = this.getLength() - 1 ) {

    from = Math.round( from );
    to = Math.round( to );

    var l = to - from + 1;
    var sum = 0,
      delta;

    let deltaTot = 0;
    var arrX = this.getDataX();
    var arrY = this.getDataY();

    for ( ; from <= to; from++ ) {

      if ( arrX.length - 1 > from ) {

        deltaTot += arrX[ from + 1 ] - arrX[ from ]
        sum += arrY[ from ] * ( arrX[ from + 1 ] - arrX[ from ] );
      }
    }
    return [ sum, l, deltaTot ];
  }

  integrateP( from, to ) {
    var val = this._integrateP( from, to );
    return val[ 0 ];
  }

  average( p0 = 0, p1 = this.getLength() - 1 ) {
    return this.getAverageP( p0, p1 );
  }

  mean() {
    return this.average();
  }

  getAverageP( from, to ) {
    var sum = this._integrateP( from, to );
    return sum[ 0 ] / sum[ 2 ];
  }

  getAverageX( from, to ) {
    var sum = this._integrateX( from, to );
    return sum[ 0 ] / sum[ 2 ];
  }

  checkMonotonicity() {

    let i = 0;
    const l = this.data.length;
    let dir = this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ];

    for ( ; i < l; i++ ) {
      if ( dir !== ( this.data[ 1 ][ 0 ] > this.data[ 0 ][ 0 ] ) ) {
        return this._monotoneous = false;
      }
    }

    return this._monotoneous = true;
  }

  requireMonotonicity() {
    if ( !this.isMonotoneous() ) {
      throw "The x wave must be monotonic";
    }
  }

  isMonotoneous() {
    return !!this._monotoneous;
  }

  invert( data ) {
    let d = data || this.data;
    d.reverse();
    return d;
  }

  resampleForDisplay( options ) { // Serie redrawing

    let i = 0;

    this.requireMonotonicity();

    let inverting = false;

    if ( this.data[ 1 ][ 0 ] < this.data[ 0 ][ 0 ] ) {
      this.invert();
      inverting = true;
    }

    const l = this.getLength();

    let data = [],
      dataMinMax = [],
      resampleSum, resampleMin, resampleMax, resampleNum, resample_x_start, resample_x_px_start,
      x_px,
      dataY = this.getDataY(),
      dataX = this.getDataX(),
      doing_mean = false,
      firstPointIndex = 0;

    if ( !options.xPosition ) {
      throw "No position calculation method provided";
    }

    if ( !options.resampleToPx ) {
      throw "No \"resampleToPx\" method was provided. Unit: px per point";
    }

    for ( ; i < l; i++ ) {

      if ( options.minX > dataX[ i ] ) {

        firstPointIndex = i;
        continue;
      }

      x_px = options.xPosition( dataX[ i ] );

      if ( !doing_mean ) {

        if ( !firstPointIndex ) {

          firstPointIndex = i;
        }

        while ( isNaN( dataY[ i ] ) ) {
          i++;
        }

        resampleSum = resampleMin = resampleMax = dataY[ firstPointIndex ];
        resampleNum = 1;
        resample_x_px_start = x_px;
        resample_x_start = dataX[ i ];
        firstPointIndex = 0;

        doing_mean = true;

        continue;
      }

      if ( Math.abs( x_px - resample_x_px_start ) > options.resampleToPx || i == l || isNaN( dataY[ i ] ) ) {

        let xpos = ( resample_x_start + dataX[ i ] ) / 2;

        data.push( [
          xpos,
          resampleSum / resampleNum
        ] );

        dataMinMax.push( xpos, resampleMin, resampleMax );

        if ( options.maxX !== undefined && dataX > options.maxX ) {
          return;
        }

        doing_mean = false;

        continue;
      }

      resampleSum += dataY[ i ];
      resampleNum++;

      resampleMin = Math.min( resampleMin, dataY[ i ] );
      resampleMax = Math.max( resampleMax, dataY[ i ] );
    }

    if ( inverting ) {
      this.dataInUse = this.invert( data );
      inverting = true;
      return this.invert( dataMinMax );
    }

    this.dataInUse = data;
    return dataMinMax;
  }

  multiply( numberOrWave ) {

    if ( numberOrWave instanceof Waveform ) {
      return this._multiplyByWave( numberOrWave );
    } else if ( typeof numberOrWave == 'number' ) {
      return this._multiplyByNumber( numberOrWave );
    }
  }

  _multiplyByNumber( num ) {

    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] *= num;
    }
    return this;
  }

  _multiplyByWave( wave ) {

    let y = wave.getDataY();
    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] *= y[ i ];
    }
    return this;
  }

  interpolate( x ) {

    let xData = this.getDataX(),
      yData = this.getDataY(),
      xIndex = binarySearch( x, xData, !this.getMonotoneousDirection() );

    return ( x - xData[ xIndex ] ) / ( xData[ xIndex + 1 ] - xData[ xIndex ] ) * ( yData[ xIndex + 1 ] - yData[ xIndex ] ) + yData[ xIndex ];
  }

  divide( numberOrWave ) {

    if ( numberOrWave instanceof Waveform ) {
      return this._multiplyByWave( numberOrWave );
    } else if ( typeof numberOrWave == 'number' ) {
      return this._multiplyByNumber( numberOrWave );
    }
  }

  _divideByNumber( num ) {

    let i = 0,
      l = this.getLength();

    for ( ; i < l; i++ ) {
      this.data[ i ][ 0 ] /= num;
    }
    return this;
  }

  _divideByWave( wave ) {

    let yDataThis = this.getDataY(),
      xDataThis = this.getDataX(),
      i = 0;

    const l = this.getLength();

    for ( ; i < l; i++ ) {

      yDataThis[ i ] /= wave.interpolate( xDataThis[ x ] );
    }

    return this;
  }

  aggregate() {

    const levels = 10;

    let level = 128; // 128 points

    let i = 0;

    this._dataAggregating = {};
    this._dataAggregated = {};

    for ( ; i < levels; i++ ) {

      this._dataAggregating[ level ] = aggregator( {

        min: this.getMinX(),
        max: this.getMaxX(),
        data: this.data,
        numPoints: level

      } ).then( ( data ) => {

        this._dataAggregated[ data.numPoints ] = data.data;
      } );

      if ( level > this.getLength() ) {
        break;
      }

      level *= 2;
    }
  }

  hasAggregation() {
    return !!this._dataAggregated;
  }

  getAggregatedData( pxWidth ) {

    var level = pow2ceil( pxWidth );
    if ( this._dataAggregated[ level ] ) {
      this.dataInUse = this._dataAggregated[ level ];
      return;
    } else if ( this._dataAggregating[ level ] ) {
      return this._dataAggregating[ level ];
    }

    this.dataInUse = this.data;
  }

};

export default Waveform<|endoftext|>


<commit_before>/* Class: mop.cms.CMS */

mop.modules.CMS = new Class({

	/* Constructor: initialize */
	Extends: mop.modules.Module,
	Interfaces: mop.modules.navigation.NavigationDataSource,
	Implements: mop.util.Broadcaster,

	rootObjectId: null,
  currentObjectId: null,
	pageContent: null,
	pageIdToLoad: null,
	scriptsLoaded: null,
	titleText: "",
	titleElement: null,
	slugIPE: null,
	loadedCSS: [],
	loadedJS: [],
	stringIdentifier: "[ object, mop.modules.CMS ]",
	options: {},

	/* Section: Getters & Setters */

	getRemoveObjectRequestURL: function( parentId ){
		return mop.util.getBaseURL() + "ajax/compound/cms/removeObject/" + parentId;
	},

	getRequestPageURL: function( nodeId ){
		return mop.util.getBaseURL() + "ajax/html/cms/getPage/" + nodeId;
	},

	getRequestTierURL: function( parentId, deepLink ){
		var deepLinkAppend = ( deepLink )? "/" + deepLink : '';
		var url = mop.util.getBaseURL() + "ajax/compound/navigation/getTier/" + parentId + deepLinkAppend;
		return url;
	},

	getAddObjectRequestURL: function( parentId, templateId ){
		return mop.util.getBaseURL() + "ajax/compound/cms/addObject/" + parentId + "/" + templateId;
	},

	getTogglePublishedStatusRequestURL: function( nodeId ){
		return mop.util.getBaseURL() + "ajax/data/cms/togglePublish/"+ nodeId;
	},

	getSaveFieldURL: function(){
		return  mop.util.getBaseURL() + "ajax/data/cms/savefield/"+ this.getObjectId();
	},

	getSubmitSortOrderURL: function(){
	    return mop.util.getBaseURL() + "ajax/data/cms/saveSortOrder/" + this.getObjectId();
	},

	getRootNodeId: function(){
		return this.options.rootObjectId;
	},

	getObjectId: function(){
		return this.currentObjectId;
	},

	setObjectId: function( objectId ){
		this.currentObjectId = objectId;
	},

	/* Section: Constructor */
	initialize: function( anElement, options ){
    this.parent( anElement, null, options );
//    console.log( this.options, this.elementClass, this.options.rootObjectId );
    this.rootNodeId = this.options.rootObjectId;
    $$( "script" ).each( function( aScriptTag ){
        this.loadedJS.push( aScriptTag.get("src") );
    }, this );

    $$( "link[rel=stylesheet]" ).each(
        function( aStyleSheetTag ){this.loadedCSS.push(  aStyleSheetTag );
    }, this );
	},

	/* Section: Methods */
	toString: function(){return this.stringIdentifier},

	build: function(){
//		console.log( "build", this.element );
		this.pageContent = $("nodeContent");
		this.initModules( this.element );
	},

	populate: function( html ){
		$("nodeContent").unspin();
		this.pageContent.set( 'html', html );
		this.UIFields = this.initUI( this.pageContent );
//		console.log( "populate", this.toString(), this.pageContent );
		this.initModules( this.pageContent );
		this.titleElement = this.element.getElement( ".objectTitle" );
		if( this.titleElement ){
			var titleIPE = this.titleElement.getElement('.field-title');
			this.titleText = titleIPE.retrieve('Class').getValue();
   		this.slugIPE = this.titleElement.getElement( ".field-slug" );
			// var titleIPE = this.titleElement.getElement( ".field-title" ).retrieve("Class");
			if( titleIPE ){
				titleIPE.addListener( this );
				this.addEvent( 'uifieldsaveresponse', this.onUIFieldSaved.bind( this ) );
			}
		}
	},

	clearPage: function(){
//		console.log( "clearPage" );
		this.destroyChildModules( this.pageContent );
		this.destroyUIFields();
		this.pageContent.empty();
	},

	onUIFieldSaved: function( fieldName, response ){
		console.log( "onUIFieldSaved", fieldName, response );
		switch( fieldName ){
			case 'title':
				this.onTitleEdited( response );
			break;
		}
	},

/*
    Section: Event Handlers
*/
	onTitleEdited: function( response ){
		this.broadcastMessage( 'objectnamechanged', [ this.getObjectId(), response.value ] );
    if( this.slugIPE ) this.slugIPE.retrieve( "Class" ).setValue( response.slug );
	},

	onJSLoaded: function( html, jsLoadCount ){
		// keeps any callbacks from previous pageloads from registering
		this.scriptsLoaded++;
//		console.log( this, "onJSLoaded", html, this.scriptsLoaded, this.loadedJS.length );
		if( this.loadedJS.length == this.loadedJS.length ){
			this.populate( html );
		}
	},

/*
    Section: Server Requests
*/

    /*
    	Function: requestPage
    	Requests pageData and calls requestPageResponse on callback
    	Arguments: nodeId MoPObject Id of a page object.
    */
	requestPage: function( nodeId ){
			this.setObjectId( nodeId );
			return new Request.JSON( {url: this.getRequestPageURL( nodeId ), onSuccess: this.requestPageResponse.bind( this )} ).send();
	},

	/*
		Function: requestPageResponse
		Callback to requestPage, loops through supplied JSON object and attached css, html, js, in that order then looks through #pageContent and initialize modules therein....
		Arguments:
			json - Object : { css: [ "pathToCSSFile", "pathToCSSFile", ... ], js: [ "pathToJSFile", "pathToJSFile", "pathToJSFile", ... ], html: "String" }
	*/
   requestPageResponse: function( json ){
      if( !json.returnValue ) throw json.response.error;
      json.response.css.each( function( styleSheetURL, index ){
         styleSheetURL = mop.util.getBaseURL() + styleSheetURL;
         if( !this.loadedCSS.contains( styleSheetURL ) ) mop.util.loadStyleSheet( styleSheetURL );
         this.loadedCSS.push( styleSheetURL );
      }, this );
      this.scriptsLoaded = 0;
      var noneLoaded = true;
      if( json.response.js.length && json.response.js.length > 0){
         json.response.js.each( function( urlString, i ){
            urlString = mop.util.getBaseURL() + urlString;
            //                console.log( ":::: ", urlString, this.loadedJS.indexOf( urlString ) );
            if( this.loadedJS.indexOf( urlString ) == -1 ){
               noneLoaded = false;
               //                   console.log( "::::::", urlString, "not in loadedJSArray" );
               this.loadedJS.push( urlString );
               mop.util.loadJS( urlString, {
                  type: "text/javascript",
                  onload: this.onJSLoaded.bind( this, [ json.response.html ] )
               } );
            }
         }, this );
         if( noneLoaded ) this.populate( json.response.html );
      }else{
         this.populate( json.response.html );
      }
   },

/*
	Section: mop.modules.navigation.NavigtionDelegate Interface Requests and Response
*/

	onNodeSelected: function( nodeId ){
		console.log( this.toString(), "onNodeSelected", nodeId );
		this.clearPage();
		if( this.pageRequest ) this.pageRequest.cancel();
		this.pageRequest = this.requestPage( nodeId );
	},

/*
	Section: mop.modules.navigation.NavigationDataSource Interface Requests and Response
*/

	requestTier: function( parentId, deepLink, callback ){
		this.currentObjectId = parentId;
		return new Request.JSON( {
			url: this.getRequestTierURL( parentId, deepLink ),
			onSuccess: function( json ){
				this.requestTierResponse( json );
				callback( json );
			}.bind( this )
		}).send();
	},

	requestTierResponse: function( json ){
		if( !json.returnValue ) console.log( this.toString(), "requestTier error:", json.response.error );
	},

	saveTierSortRequest: function( newOrder ){
		return new Request.JSON( { url: this.getSubmitSortOrderURL(), onComplete: this.saveSortResponse.bind( this ) } ).post( { sortOrder: newOrder } );
	},

	saveSortResponse: function( json ){
		if( !json.returnValue ) console.log( this.toString(), "saveSortRequest error:", json.response.error );
	},

   addObjectRequest: function( parentId, templateId, nodeProperties, callback ){
      console.log( "addObjectRequest", parentId, templateId, nodeProperties, callback );
      return new Request.JSON({
         url: this.getAddObjectRequestURL( parentId, templateId ),
         onSuccess: function( json  ){
            this.addObjectResponse( json );
            callback( json );
         }.bind( this )
      }).post( nodeProperties );
   },

    addObjectResponse: function( json ){
        console.log( "addObjectResponse", json );
        if( !json.returnValue ) console.log( this.toString(), "addObjectRequest error:", json.response.error );
    },

    removeObjectRequest: function( parentId, callback ){
        return new Request.JSON({
            url: this.getRemoveObjectRequestURL( parentId ),
            onSuccess: function( json ){this.removeObjectResponse( json );callback();}.bind( this )
        }).send();
    },

    removeObjectResponse: function( json ){
        console.log( this.toString(), "removeObjectResponse", json, Array.from( arguments ) );
        if( !json.returnValue ) console.log( this.toString(), "removeObjectRequest error:", json.response.error );
    },

    /*
    Function: togglePublishedStatus
    Sends page publish toggle ajax call
    Argument: pageId {Number}
    Callback: onTogglePublish
    */
    togglePublishedStatusRequest: function( nodeId, callback ){
        console.log( "::::", this.getTogglePublishedStatusRequestURL( nodeId ) );
        return new Request.JSON({
            url: this.getTogglePublishedStatusRequestURL( nodeId ),
            onSuccess: function( json ){
                this.togglePublishedStatusResponse( json );
                if( callback ) callback( json );
            }.bind( this )
        }).send();
    },

    togglePublishedStatusResponse: function( json ){
        if( !json.returnValue ) console.log( this.toString(), "togglePublishedStatusRequest error:", json.response.error );
    }

});

if( !mop.util.hasDOMReadyFired() ){
	window.addEvent( "domready", function(){
		mop.util.DOMReadyHasFired();
		mop.historyManager = new mop.util.HistoryManager().instance();
		mop.historyManager.init();
		mop.ModalManager = new mop.ui.ModalManager();
		mop.DepthManager = new mop.util.DepthManager();
		var doAuthTimeout = mop.util.getValueFromClassName( 'loginTimeout', $(document).getElement("body").get("class") );
		if( window.location.href.indexOf( "auth" ) == -1 && doAuthTimeout && doAuthTimeout != "0" ) mop.loginMonitor = new mop.util.LoginMonitor();
		mop.util.EventManager.broadcastMessage( "resize" );
		mop.CMS = new mop.modules.CMS( "cms" );
	});
}
<commit_msg>moved request cancelling to navigationDelegate from navigation<commit_after>/* Class: mop.cms.CMS */

mop.modules.CMS = new Class({

	/* Constructor: initialize */
	Extends: mop.modules.Module,
	Interfaces: mop.modules.navigation.NavigationDataSource,
	Implements: mop.util.Broadcaster,

	rootObjectId: null,
  currentObjectId: null,
	pageContent: null,
	pageIdToLoad: null,
	scriptsLoaded: null,
	titleText: "",
	titleElement: null,
	slugIPE: null,
	loadedCSS: [],
	loadedJS: [],
	stringIdentifier: "[ object, mop.modules.CMS ]",
	options: {},

	/* Section: Getters & Setters */

	getRemoveObjectRequestURL: function( parentId ){
		return mop.util.getBaseURL() + "ajax/compound/cms/removeObject/" + parentId;
	},

	getRequestPageURL: function( nodeId ){
		return mop.util.getBaseURL() + "ajax/html/cms/getPage/" + nodeId;
	},

	getRequestTierURL: function( parentId, deepLink ){
		var deepLinkAppend = ( deepLink )? "/" + deepLink : '';
		var url = mop.util.getBaseURL() + "ajax/compound/navigation/getTier/" + parentId + deepLinkAppend;
		return url;
	},

	getAddObjectRequestURL: function( parentId, templateId ){
		return mop.util.getBaseURL() + "ajax/compound/cms/addObject/" + parentId + "/" + templateId;
	},

	getTogglePublishedStatusRequestURL: function( nodeId ){
		return mop.util.getBaseURL() + "ajax/data/cms/togglePublish/"+ nodeId;
	},

	getSaveFieldURL: function(){
		return  mop.util.getBaseURL() + "ajax/data/cms/savefield/"+ this.getObjectId();
	},

	getSubmitSortOrderURL: function(){
	    return mop.util.getBaseURL() + "ajax/data/cms/saveSortOrder/" + this.getObjectId();
	},

	getRootNodeId: function(){
		return this.options.rootObjectId;
	},

	getObjectId: function(){
		return this.currentObjectId;
	},

	setObjectId: function( objectId ){
		this.currentObjectId = objectId;
	},

	/* Section: Constructor */
	initialize: function( anElement, options ){
    this.parent( anElement, null, options );
//    console.log( this.options, this.elementClass, this.options.rootObjectId );
    this.rootNodeId = this.options.rootObjectId;
    $$( "script" ).each( function( aScriptTag ){
        this.loadedJS.push( aScriptTag.get("src") );
    }, this );

    $$( "link[rel=stylesheet]" ).each(
        function( aStyleSheetTag ){this.loadedCSS.push(  aStyleSheetTag );
    }, this );
	},

	/* Section: Methods */
	toString: function(){return this.stringIdentifier},

	build: function(){
//		console.log( "build", this.element );
		this.pageContent = $("nodeContent");
		this.initModules( this.element );
	},

	populate: function( html ){
		$("nodeContent").unspin();
		this.pageContent.set( 'html', html );
		this.UIFields = this.initUI( this.pageContent );
//		console.log( "populate", this.toString(), this.pageContent );
		this.initModules( this.pageContent );
		this.titleElement = this.element.getElement( ".objectTitle" );
		if( this.titleElement ){
			var titleIPE = this.titleElement.getElement('.field-title');
			this.titleText = titleIPE.retrieve('Class').getValue();
   		this.slugIPE = this.titleElement.getElement( ".field-slug" );
			// var titleIPE = this.titleElement.getElement( ".field-title" ).retrieve("Class");
			if( titleIPE ){
				titleIPE.addListener( this );
				this.addEvent( 'uifieldsaveresponse', this.onUIFieldSaved.bind( this ) );
			}
		}
	},

	clearPage: function(){
//		console.log( "clearPage" );
		this.destroyChildModules( this.pageContent );
		this.destroyUIFields();
		this.pageContent.empty();
	},

	onUIFieldSaved: function( fieldName, response ){
		console.log( "onUIFieldSaved", fieldName, response );
		switch( fieldName ){
			case 'title':
				this.onTitleEdited( response );
			break;
		}
	},

/*
    Section: Event Handlers
*/
	onTitleEdited: function( response ){
		this.broadcastMessage( 'objectnamechanged', [ this.getObjectId(), response.value ] );
    if( this.slugIPE ) this.slugIPE.retrieve( "Class" ).setValue( response.slug );
	},

	onJSLoaded: function( html, jsLoadCount ){
		// keeps any callbacks from previous pageloads from registering
		this.scriptsLoaded++;
//		console.log( this, "onJSLoaded", html, this.scriptsLoaded, this.loadedJS.length );
		if( this.loadedJS.length == this.loadedJS.length ){
			this.populate( html );
		}
	},

/*
    Section: Server Requests
*/

    /*
    	Function: requestPage
    	Requests pageData and calls requestPageResponse on callback
    	Arguments: nodeId MoPObject Id of a page object.
    */
	requestPage: function( nodeId ){
			this.setObjectId( nodeId );
			return new Request.JSON( {url: this.getRequestPageURL( nodeId ), onSuccess: this.requestPageResponse.bind( this )} ).send();
	},

	/*
		Function: requestPageResponse
		Callback to requestPage, loops through supplied JSON object and attached css, html, js, in that order then looks through #pageContent and initialize modules therein....
		Arguments:
			json - Object : { css: [ "pathToCSSFile", "pathToCSSFile", ... ], js: [ "pathToJSFile", "pathToJSFile", "pathToJSFile", ... ], html: "String" }
	*/
   requestPageResponse: function( json ){
      if( !json.returnValue ) throw json.response.error;
      json.response.css.each( function( styleSheetURL, index ){
         styleSheetURL = mop.util.getBaseURL() + styleSheetURL;
         if( !this.loadedCSS.contains( styleSheetURL ) ) mop.util.loadStyleSheet( styleSheetURL );
         this.loadedCSS.push( styleSheetURL );
      }, this );
      this.scriptsLoaded = 0;
      var noneLoaded = true;
      if( json.response.js.length && json.response.js.length > 0){
         json.response.js.each( function( urlString, i ){
            urlString = mop.util.getBaseURL() + urlString;
            //                console.log( ":::: ", urlString, this.loadedJS.indexOf( urlString ) );
            if( this.loadedJS.indexOf( urlString ) == -1 ){
               noneLoaded = false;
               //                   console.log( "::::::", urlString, "not in loadedJSArray" );
               this.loadedJS.push( urlString );
               mop.util.loadJS( urlString, {
                  type: "text/javascript",
                  onload: this.onJSLoaded.bind( this, [ json.response.html ] )
               } );
            }
         }, this );
         if( noneLoaded ) this.populate( json.response.html );
      }else{
         this.populate( json.response.html );
      }
   },

/*
	Section: mop.modules.navigation.NavigtionDelegate Interface Requests and Response
*/

	onNodeSelected: function( nodeId ){
		console.log( this.toString(), "onNodeSelected", nodeId );
		this.clearPage();
		if( this.pageRequest ) this.pageRequest.cancel();
		this.pageRequest = this.requestPage( nodeId );
	},

/*
	Section: mop.modules.navigation.NavigationDataSource Interface Requests and Response
*/

	requestTier: function( parentId, deepLink, callback ){
		this.currentObjectId = parentId;
		if( this.currentTierRequest ) this.currentTierRequest.cancel();
		this.currentTierRequest = new Request.JSON( {
			url: this.getRequestTierURL( parentId, deepLink ),
			onSuccess: function( json ){
				this.requestTierResponse( json );
				callback( json );
			}.bind( this )
		}).send();
		return this.currentTierRequest;
	},

	requestTierResponse: function( json ){
		if( !json.returnValue ) console.log( this.toString(), "requestTier error:", json.response.error );
	},

	saveTierSortRequest: function( newOrder ){
		return new Request.JSON( { url: this.getSubmitSortOrderURL(), onComplete: this.saveSortResponse.bind( this ) } ).post( { sortOrder: newOrder } );
	},

	saveSortResponse: function( json ){
		if( !json.returnValue ) console.log( this.toString(), "saveSortRequest error:", json.response.error );
	},

   addObjectRequest: function( parentId, templateId, nodeProperties, callback ){
      console.log( "addObjectRequest", parentId, templateId, nodeProperties, callback );
      return new Request.JSON({
         url: this.getAddObjectRequestURL( parentId, templateId ),
         onSuccess: function( json  ){
            this.addObjectResponse( json );
            callback( json );
         }.bind( this )
      }).post( nodeProperties );
   },

    addObjectResponse: function( json ){
        console.log( "addObjectResponse", json );
        if( !json.returnValue ) console.log( this.toString(), "addObjectRequest error:", json.response.error );
    },

    removeObjectRequest: function( parentId, callback ){
        return new Request.JSON({
            url: this.getRemoveObjectRequestURL( parentId ),
            onSuccess: function( json ){this.removeObjectResponse( json );callback();}.bind( this )
        }).send();
    },

    removeObjectResponse: function( json ){
        console.log( this.toString(), "removeObjectResponse", json, Array.from( arguments ) );
        if( !json.returnValue ) console.log( this.toString(), "removeObjectRequest error:", json.response.error );
    },

    /*
    Function: togglePublishedStatus
    Sends page publish toggle ajax call
    Argument: pageId {Number}
    Callback: onTogglePublish
    */
    togglePublishedStatusRequest: function( nodeId, callback ){
        console.log( "::::", this.getTogglePublishedStatusRequestURL( nodeId ) );
        return new Request.JSON({
            url: this.getTogglePublishedStatusRequestURL( nodeId ),
            onSuccess: function( json ){
                this.togglePublishedStatusResponse( json );
                if( callback ) callback( json );
            }.bind( this )
        }).send();
    },

    togglePublishedStatusResponse: function( json ){
        if( !json.returnValue ) console.log( this.toString(), "togglePublishedStatusRequest error:", json.response.error );
    }

});

if( !mop.util.hasDOMReadyFired() ){
	window.addEvent( "domready", function(){
		mop.util.DOMReadyHasFired();
		mop.historyManager = new mop.util.HistoryManager().instance();
		mop.historyManager.init();
		mop.ModalManager = new mop.ui.ModalManager();
		var doAuthTimeout = mop.util.getValueFromClassName( 'loginTimeout', $(document).getElement("body").get("class") );
		if( window.location.href.indexOf( "auth" ) == -1 && doAuthTimeout && doAuthTimeout != "0" ) mop.loginMonitor = new mop.util.LoginMonitor();
		mop.util.EventManager.broadcastMessage( "resize" );
		mop.CMS = new mop.modules.CMS( "cms" );
	});
}
<|endoftext|>