Best JavaScript code snippet using karma
file-list.spec.js
Source:file-list.spec.js
...82 it('returns a unique set', () => {83 list = new List(patterns('/a.*', '*.txt'), [], emitter, preprocess)84 return list.refresh().then(() => {85 expect(list.files.served).to.have.length(3)86 expect(pathsFrom(list.files.served)).to.contain('/a.txt', '/b.txt', '/c.txt')87 })88 })89 it('returns only served files', () => {90 var files = [91 new config.Pattern('/a.*', true), // served: true92 new config.Pattern('/some/*.js', false) // served: false93 ]94 list = new List(files, [], emitter, preprocess)95 return list.refresh().then(() => {96 expect(pathsFrom(list.files.served)).to.eql(['/a.txt'])97 })98 })99 it('marks no cache files', () => {100 var files = [101 new config.Pattern('/a.*'), // nocach: false102 new config.Pattern('/some/*.js', true, true, true, true) // nocache: true103 ]104 list = new List(files, [], emitter, preprocess)105 return list.refresh().then(() => {106 expect(pathsFrom(list.files.served)).to.deep.equal([107 '/a.txt',108 '/some/a.js',109 '/some/b.js'110 ])111 expect(preprocess).to.have.been.calledOnce112 expect(list.files.served[0].doNotCache).to.be.false113 expect(list.files.served[1].doNotCache).to.be.true114 expect(list.files.served[2].doNotCache).to.be.true115 })116 })117 it('returns a flat array of included files', () => {118 var files = [119 new config.Pattern('/a.*', true, false), // included: false120 new config.Pattern('/some/*.js') // included: true121 ]122 list = new List(files, [], emitter, preprocess)123 return list.refresh().then(() => {124 expect(pathsFrom(list.files.included)).not.to.contain('/a.txt')125 expect(pathsFrom(list.files.included)).to.deep.equal([126 '/some/a.js',127 '/some/b.js'128 ])129 })130 })131 })132 describe('_isExcluded', () => {133 beforeEach(() => {134 preprocess = sinon.spy((file, done) => process.nextTick(done))135 emitter = new EventEmitter()136 })137 it('returns undefined when no match is found', () => {138 list = new List([], ['hello.js', 'world.js'], emitter, preprocess)139 expect(list._isExcluded('hello.txt')).to.be.undefined140 expect(list._isExcluded('/hello/world/i.js')).to.be.undefined141 })142 it('returns the first match if it finds one', () => {143 list = new List([], ['*.js', '**/*.js'], emitter, preprocess)144 expect(list._isExcluded('world.js')).to.be.eql('*.js')145 expect(list._isExcluded('/hello/world/i.js')).to.be.eql('**/*.js')146 })147 })148 describe('_isIncluded', () => {149 beforeEach(() => {150 preprocess = sinon.spy((file, done) => process.nextTick(done))151 emitter = new EventEmitter()152 })153 it('returns undefined when no match is found', () => {154 list = new List(patterns('*.js'), [], emitter, preprocess)155 expect(list._isIncluded('hello.txt')).to.be.undefined156 expect(list._isIncluded('/hello/world/i.js')).to.be.undefined157 })158 it('returns the first match if it finds one', () => {159 list = new List(patterns('*.js', '**/*.js'), [], emitter, preprocess)160 expect(list._isIncluded('world.js').pattern).to.be.eql('*.js')161 expect(list._isIncluded('/hello/world/i.js').pattern).to.be.eql('**/*.js')162 })163 })164 describe('_exists', () => {165 beforeEach(() => {166 patternList = _.cloneDeep(PATTERN_LIST)167 mg = _.cloneDeep(MG)168 preprocess = sinon.spy((file, done) => process.nextTick(done))169 emitter = new EventEmitter()170 glob = {171 Glob: (pattern, opts) => ({172 found: patternList[pattern],173 statCache: mg.statCache174 })175 }176 List = proxyquire('../../lib/file-list', {177 helper: helper,178 glob: glob,179 path: pathLib.posix,180 'graceful-fs': mockFs181 })182 list = new List(patterns('/some/*.js', '*.txt'), [], emitter, preprocess)183 return list.refresh()184 })185 it('returns false when no match is found', () => {186 expect(list._exists('/some/s.js')).to.be.false187 expect(list._exists('/hello/world.ex')).to.be.false188 })189 it('returns true when a match is found', () => {190 expect(list._exists('/some/a.js')).to.be.true191 expect(list._exists('/some/b.js')).to.be.true192 })193 })194 describe('refresh', () => {195 beforeEach(() => {196 patternList = _.cloneDeep(PATTERN_LIST)197 mg = _.cloneDeep(MG)198 preprocess = sinon.spy((file, done) => process.nextTick(done))199 emitter = new EventEmitter()200 glob = {201 Glob: (pattern, opts) => ({202 found: patternList[pattern],203 statCache: mg.statCache204 })205 }206 List = proxyquire('../../lib/file-list', {207 helper: helper,208 glob: glob,209 path: pathLib.posix,210 'graceful-fs': mockFs211 })212 list = new List(patterns('/some/*.js', '*.txt'), [], emitter, preprocess, 100)213 })214 it('resolves patterns', () => {215 return list.refresh().then((files) => {216 expect(list.buckets.size).to.equal(2)217 var first = pathsFrom(list.buckets.get('/some/*.js'))218 var second = pathsFrom(list.buckets.get('*.txt'))219 expect(first).to.contain('/some/a.js', '/some/b.js')220 expect(second).to.contain('/a.txt', '/b.txt', '/c.txt')221 })222 })223 it('uses the file from the first matcher if two matchers match the same file', () => {224 list = new List(patterns('/a.*', '*.txt'), [], emitter, preprocess, 100)225 return list.refresh().then(() => {226 var first = pathsFrom(list.buckets.get('/a.*'))227 var second = pathsFrom(list.buckets.get('*.txt'))228 expect(first).to.contain('/a.txt')229 expect(second).not.to.contain('/a.txt')230 })231 })232 it('cancels refreshs', () => {233 var checkResult = (files) => {234 expect(_.map(files.served, 'path')).to.contain('/some/a.js', '/some/b.js', '/some/c.js')235 }236 var p1 = list.refresh().then(checkResult)237 patternList['/some/*.js'].push('/some/c.js')238 mg.statCache['/some/c.js'] = {mtime: new Date(Date.now() + 5000)}239 var p2 = list.refresh().then(checkResult)240 var called = false241 var callback = (data) => {242 expect(called).to.be.false243 expect(data.served[0].mtime.toString()).to.not.equal(data.served[2].mtime.toString())244 expect(data.served[0].mtime.toString()).to.equal(data.served[1].mtime.toString())245 called = true246 }247 list._emitter.on('file_list_modified', callback)248 return Promise.all([p1, p2]).then(() => {249 list._emitter.removeListener('file_list_modified', callback)250 })251 })252 it('sets the mtime for all files', () => {253 return list.refresh().then((files) => {254 var bucket = list.buckets.get('/some/*.js')255 var file1 = findFile('/some/a.js', bucket)256 var file2 = findFile('/some/b.js', bucket)257 expect(file1.mtime).to.be.eql(mg.statCache['/some/a.js'].mtime)258 expect(file2.mtime).to.be.eql(mg.statCache['/some/b.js'].mtime)259 })260 })261 it('sets the mtime for relative patterns', () => {262 list = new List(patterns('/some/world/../*.js', '*.txt'), [], emitter, preprocess)263 return list.refresh().then((files) => {264 var bucket = list.buckets.get('/some/world/../*.js')265 var file1 = findFile('/some/a.js', bucket)266 var file2 = findFile('/some/b.js', bucket)267 expect(file1.mtime).to.be.eql(mg.statCache['/some/a.js'].mtime)268 expect(file2.mtime).to.be.eql(mg.statCache['/some/b.js'].mtime)269 })270 })271 it('should sort files within buckets and keep order of patterns (buckets)', () => {272 // /(a.* ) => /a.txt [MATCH in *.txt as well]273 // /some/*.(js) => /some/a.js, /some/b.js [/some/b.js EXCLUDED]274 // *.(txt ) => /c.txt, a.txt, b.txt [UNSORTED]275 list = new List(patterns('/a.*', '/some/*.js', '*.txt'), ['**/b.js'], emitter, preprocess)276 return list.refresh().then((files) => {277 expect(pathsFrom(files.served)).to.deep.equal([278 '/a.txt',279 '/some/a.js',280 '/b.txt',281 '/c.txt'282 ])283 })284 })285 it('ingores excluded files', () => {286 list = new List(patterns('*.txt'), ['/a.*', '**/b.txt'], emitter, preprocess)287 return list.refresh().then((files) => {288 var bucket = pathsFrom(list.buckets.get('*.txt'))289 expect(bucket).to.contain('/c.txt')290 expect(bucket).not.to.contain('/a.txt')291 expect(bucket).not.to.contain('/b.txt')292 })293 })294 it('does not glob urls and sets the isUrl flag', () => {295 list = new List(patterns('http://some.com'), [], emitter, preprocess)296 return list.refresh()297 .then((files) => {298 var bucket = list.buckets.get('http://some.com')299 var file = findFile('http://some.com', bucket)300 expect(file).to.have.property('isUrl', true)301 }302 )303 })304 it('preprocesses all files', () => {305 return list.refresh().then((files) => {306 expect(preprocess.callCount).to.be.eql(5)307 })308 })309 it('fails when a preprocessor fails', () => {310 preprocess = sinon.spy((file, next) => {311 next(new Error('failing'), null)312 })313 list = new List(patterns('/some/*.js'), [], emitter, preprocess)314 return list.refresh().catch((err) => {315 expect(err.message).to.be.eql('failing')316 })317 })318 it('fires modified before resolving promise after subsequent calls', () => {319 var modified = sinon.stub()320 emitter.on('file_list_modified', modified)321 return list.refresh().then(() => {322 expect(modified).to.have.been.calledOnce323 })324 .then(() => {325 list.refresh().then(() => {326 expect(modified).to.have.been.calledTwice327 })328 })329 })330 })331 describe('reload', () => {332 beforeEach(() => {333 preprocess = sinon.spy((file, done) => process.nextTick(done))334 emitter = new EventEmitter()335 list = new List(patterns('/some/*.js', '*.txt'), [], emitter, preprocess)336 })337 it('refreshes, even when a refresh is already happening', () => {338 sinon.spy(list, '_refresh')339 return Promise.all([340 list.refresh(),341 list.reload(patterns('*.txt'), [])342 ])343 .then(() => {344 expect(list._refresh).to.have.been.calledTwice345 }346 )347 })348 })349 describe('addFile', () => {350 var clock = null351 beforeEach(() => {352 patternList = PATTERN_LIST353 mg = MG354 preprocess = sinon.spy((file, done) => process.nextTick(done))355 emitter = new EventEmitter()356 glob = {357 Glob: (pattern, opts) => ({358 found: patternList[pattern],359 statCache: mg.statCache360 })361 }362 clock = sinon.useFakeTimers()363 // This hack is needed to ensure lodash is using the fake timers364 // from sinon365 List = proxyquire('../../lib/file-list', {366 lodash: _.runInContext(),367 helper: helper,368 glob: glob,369 'graceful-fs': mockFs,370 path: pathLib.posix,371 bluebird: Promise372 })373 list = new List(patterns('/some/*.js', '*.txt'), ['/secret/*.txt'], emitter, preprocess, 100)374 })375 afterEach(() => {376 clock.restore()377 Promise.setScheduler((fn) => process.nextTick(fn))378 })379 it('does not add excluded files', () => {380 return list.refresh().then((before) => {381 return list.addFile('/secret/hello.txt').then((files) => {382 expect(files.served).to.be.eql(before.served)383 })384 })385 })386 it('does not add already existing files', () => {387 return list.refresh().then((before) => {388 return list.addFile('/some/a.js').then((files) => {389 expect(files.served).to.be.eql(before.served)390 })391 })392 })393 it('does not add unmatching files', () => {394 return list.refresh().then((before) => {395 return list.addFile('/some/a.ex').then((files) => {396 expect(files.served).to.be.eql(before.served)397 })398 })399 })400 it('adds the file to the correct bucket', () => {401 return list.refresh().then((before) => {402 return list.addFile('/some/d.js').then((files) => {403 expect(pathsFrom(files.served)).to.contain('/some/d.js')404 var bucket = list.buckets.get('/some/*.js')405 expect(pathsFrom(bucket)).to.contain('/some/d.js')406 })407 })408 })409 it('fires "file_list_modified"', () => {410 var modified = sinon.stub()411 emitter.on('file_list_modified', modified)412 return list.refresh().then(() => {413 modified.reset()414 return list.addFile('/some/d.js').then(() => {415 clock.tick(101)416 expect(modified).to.have.been.calledOnce417 })418 })419 })420 it('ignores quick double "add"', () => {421 // On linux fs.watch (chokidar with usePolling: false) fires "add" event twice.422 // This checks that we only stat and preprocess the file once.423 return list.refresh().then(() => {424 preprocess.reset()425 sinon.spy(mockFs, 'stat')426 return Promise.all([427 list.addFile('/some/d.js'),428 list.addFile('/some/d.js')429 ]).then(() => {430 expect(preprocess).to.have.been.calledOnce431 expect(mockFs.stat).to.have.been.calledOnce432 })433 })434 })435 it('sets the proper mtime of the new file', () => {436 list = new List(patterns('/a.*'), [], emitter, preprocess)437 return list.refresh().then(() => {438 return list.addFile('/a.js').then((files) => {439 expect(findFile('/a.js', files.served).mtime).to.eql(new Date('2012-01-01'))440 })441 })442 })443 it('preprocesses the added file', () => {444 // MATCH: /a.txt445 list = new List(patterns('/a.*'), [], emitter, preprocess)446 return list.refresh().then((files) => {447 preprocess.reset()448 return list.addFile('/a.js').then(() => {449 expect(preprocess).to.have.been.calledOnce450 expect(preprocess.args[0][0].originalPath).to.eql('/a.js')451 })452 })453 })454 })455 describe('changeFile', () => {456 var clock = null457 beforeEach(() => {458 patternList = PATTERN_LIST459 mg = MG460 Promise.setScheduler((fn) => fn())461 preprocess = sinon.spy((file, done) => process.nextTick(done))462 emitter = new EventEmitter()463 glob = {464 Glob: (pattern, opts) => ({465 found: patternList[pattern],466 statCache: mg.statCache467 })468 }469 clock = sinon.useFakeTimers()470 // This hack is needed to ensure lodash is using the fake timers471 // from sinon472 List = proxyquire('../../lib/file-list', {473 lodash: _.runInContext(),474 helper: helper,475 glob: glob,476 'graceful-fs': mockFs,477 path: pathLib.posix,478 bluebird: Promise479 })480 mockFs._touchFile('/some/a.js', '2012-04-04')481 mockFs._touchFile('/some/b.js', '2012-05-05')482 })483 afterEach(() => {484 clock.restore()485 Promise.setScheduler((fn) => process.nextTick(fn))486 })487 it('updates mtime and fires "file_list_modified"', () => {488 // MATCH: /some/a.js, /some/b.js489 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess, 100)490 var modified = sinon.stub()491 emitter.on('file_list_modified', modified)492 return list.refresh().then((files) => {493 mockFs._touchFile('/some/b.js', '2020-01-01')494 modified.reset()495 return list.changeFile('/some/b.js').then((files) => {496 clock.tick(101)497 expect(modified).to.have.been.calledOnce498 expect(findFile('/some/b.js', files.served).mtime).to.be.eql(new Date('2020-01-01'))499 })500 })501 })502 it('does not fire "file_list_modified" if no matching file is found', () => {503 // MATCH: /some/a.js504 list = new List(patterns('/some/*.js', '/a.*'), ['/some/b.js'], emitter, preprocess)505 var modified = sinon.stub()506 emitter.on('file_list_modified', modified)507 return list.refresh().then((files) => {508 mockFs._touchFile('/some/b.js', '2020-01-01')509 modified.reset()510 return list.changeFile('/some/b.js').then(() => {511 expect(modified).to.not.have.been.called512 })513 })514 })515 it('does not fire "file_list_modified" if mtime has not changed', () => {516 // chokidar on fucking windows sometimes fires event multiple times517 // MATCH: /some/a.js, /some/b.js, /a.txt518 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess)519 var modified = sinon.stub()520 emitter.on('file_list_modified', modified)521 return list.refresh().then((files) => {522 // not touching the file, stat will return still the same523 modified.reset()524 return list.changeFile('/some/b.js').then(() => {525 expect(modified).not.to.have.been.called526 })527 })528 })529 it('preprocesses the changed file', () => {530 // MATCH: /some/a.js, /some/b.js531 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess)532 return list.refresh().then((files) => {533 preprocess.reset()534 mockFs._touchFile('/some/a.js', '2020-01-01')535 return list.changeFile('/some/a.js').then(() => {536 expect(preprocess).to.have.been.called537 expect(preprocess.lastCall.args[0]).to.have.property('path', '/some/a.js')538 })539 })540 })541 })542 describe('removeFile', () => {543 var clock = null544 beforeEach(() => {545 patternList = PATTERN_LIST546 mg = MG547 Promise.setScheduler((fn) => fn())548 preprocess = sinon.spy((file, done) => process.nextTick(done))549 emitter = new EventEmitter()550 glob = {551 Glob: (pattern, opts) => ({552 found: patternList[pattern],553 statCache: mg.statCache554 })555 }556 clock = sinon.useFakeTimers()557 // This hack is needed to ensure lodash is using the fake timers558 // from sinon559 List = proxyquire('../../lib/file-list', {560 lodash: _.runInContext(),561 helper: helper,562 glob: glob,563 'graceful-fs': mockFs,564 path: pathLib.posix,565 bluebird: Promise566 })567 modified = sinon.stub()568 emitter.on('file_list_modified', modified)569 })570 afterEach(() => {571 clock.restore()572 Promise.setScheduler((fn) => process.nextTick(fn))573 })574 it('removes the file from the list and fires "file_list_modified"', () => {575 // MATCH: /some/a.js, /some/b.js, /a.txt576 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess, 100)577 var modified = sinon.stub()578 emitter.on('file_list_modified', modified)579 return list.refresh().then((files) => {580 modified.reset()581 return list.removeFile('/some/a.js')582 }).then((files) => {583 expect(pathsFrom(files.served)).to.be.eql([584 '/some/b.js',585 '/a.txt'586 ])587 clock.tick(101)588 expect(modified).to.have.been.calledOnce589 })590 })591 it('does not fire "file_list_modified" if the file is not in the list', () => {592 // MATCH: /some/a.js, /some/b.js, /a.txt593 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess)594 return list.refresh().then((files) => {595 modified.reset()596 return list.removeFile('/a.js').then(() => {597 expect(modified).to.not.have.been.called598 })599 })600 })601 })602 describe('batch interval', () => {603 // IMPORTANT: When writing tests for debouncing behaviour, you must wait for the promise604 // returned by list.changeFile or list.addFile. list.removeFile calls self._emitModified()605 // in a different manner and doesn't *need* to be waited on. If you use this behaviour606 // in your tests it can can lead to very confusing results when they are modified or607 // extended.608 //609 // Rule of thumb: Always wait on the promises returned by list.addFile, list.changeFile,610 // and list.removeFile.611 var clock = null612 beforeEach(() => {613 patternList = PATTERN_LIST614 mg = MG615 Promise.setScheduler((fn) => fn())616 preprocess = sinon.spy((file, done) => process.nextTick(done))617 emitter = new EventEmitter()618 glob = {619 Glob: (pattern, opts) => ({620 found: patternList[pattern],621 statCache: mg.statCache622 })623 }624 modified = sinon.stub()625 emitter.on('file_list_modified', modified)626 clock = sinon.useFakeTimers()627 // This hack is needed to ensure lodash is using the fake timers628 // from sinon629 List = proxyquire('../../lib/file-list', {630 lodash: _.runInContext(),631 helper: helper,632 glob: glob,633 'graceful-fs': mockFs,634 path: pathLib.posix,635 bluebird: Promise636 })637 })638 afterEach(() => {639 clock.restore()640 Promise.setScheduler((fn) => process.nextTick(fn))641 })642 it('debounces calls to emitModified', () => {643 list = new List(patterns(), [], emitter, preprocess, 100)644 return list.refresh().then(() => {645 modified.reset()646 list._emitModified()647 clock.tick(99)648 expect(modified).to.not.have.been.called649 list._emitModified()650 clock.tick(2)651 expect(modified).to.not.have.been.called652 clock.tick(97)653 expect(modified).to.not.have.been.called654 clock.tick(2)655 expect(modified).to.have.been.calledOnce656 clock.tick(1000)657 expect(modified).to.have.been.calledOnce658 list._emitModified()659 clock.tick(99)660 expect(modified).to.have.been.calledOnce661 clock.tick(2)662 expect(modified).to.have.been.calledTwice663 })664 })665 it('debounces a single file change', () => {666 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess, 100)667 return list.refresh().then((files) => {668 modified.reset()669 // Even with no changes, all these files are served670 list.addFile('/some/0.js').then(() => {671 clock.tick(99)672 expect(modified).to.not.have.been.called673 clock.tick(2)674 expect(modified).to.have.been.calledOnce675 files = modified.lastCall.args[0]676 expect(pathsFrom(files.served)).to.be.eql([677 '/some/0.js',678 '/some/a.js',679 '/some/b.js',680 '/a.txt'681 ])682 })683 })684 })685 it('debounces several changes to a file', () => {686 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess, 100)687 return list.refresh().then((files) => {688 modified.reset()689 list.addFile('/some/0.js').then(() => {690 clock.tick(99)691 expect(modified).to.not.have.been.called692 // Modify file, must change mtime too, or change is ignored693 mockFs._touchFile('/some/0.js', '2020-01-01')694 list.changeFile('/some/0.js').then(() => {695 // Ensure that the debounce timer was reset696 clock.tick(2)697 expect(modified).to.not.have.been.called698 // Ensure that debounce timer fires after 100ms699 clock.tick(99)700 expect(modified).to.have.been.calledOnce701 // Make sure there aren't any lingering debounce calls702 clock.tick(1000)703 // Modify file (one hour later mtime)704 expect(modified).to.have.been.calledOnce705 mockFs._touchFile('/some/0.js', '2020-01-02')706 list.changeFile('/some/0.js').then(() => {707 clock.tick(99)708 expect(modified).to.have.been.calledOnce709 clock.tick(2)710 expect(modified).to.have.been.calledTwice711 // Make sure there aren't any lingering calls712 clock.tick(1000)713 expect(modified).to.have.been.calledTwice714 })715 })716 })717 })718 })719 it('debounces multiple changes until there is quiescence', () => {720 // MATCH: /some/a.js, /some/b.js, /a.txt721 list = new List(patterns('/some/*.js', '/a.*'), [], emitter, preprocess, 100)722 return list.refresh().then((files) => {723 modified.reset()724 mockFs._touchFile('/some/b.js', '2020-01-01')725 list.changeFile('/some/b.js')726 list.removeFile('/some/a.js') // /some/b.js, /a.txt727 list.removeFile('/a.txt') // /some/b.js728 list.addFile('/a.txt') // /some/b.js, /a.txt729 list.addFile('/some/0.js').then(() => { // /some/0.js, /some/b.js, /a.txt730 clock.tick(99)731 expect(modified).to.not.have.been.called732 mockFs._touchFile('/a.txt', '2020-01-01')733 list.changeFile('/a.txt').then(() => {734 clock.tick(2)735 expect(modified).to.not.have.been.called736 clock.tick(100)737 expect(modified).to.have.been.calledOnce738 clock.tick(1000)739 expect(modified).to.have.been.calledOnce740 files = modified.lastCall.args[0]741 expect(pathsFrom(files.served)).to.be.eql([742 '/some/0.js',743 '/some/b.js',744 '/a.txt'745 ])746 })747 })748 })749 })750 it('waits while file preprocessing, if the file was deleted and immediately added', () => {751 list = new List(patterns('/a.*'), [], emitter, preprocess, 100)752 list.refresh().then((files) => {753 preprocess.reset()754 modified.reset()755 // Remove and then immediately add file to the bucket...
bibleNodes-cMenuFunctions.js
Source:bibleNodes-cMenuFunctions.js
1//connecting nodes2var firstNode = null;3var secondNode;4var makeClickedNodeSecondNode = 0;5var deletedNodeIdsArray = [];67//Function to make mouseDown nodeDiv the endNode8function endNodeAssigner(elm2makeEndNode) {9 elm2makeEndNode.addEventListener('mousedown', function (e) {10 if (makeClickedNodeSecondNode == 1) {11 secondNode = this;12 //stop it from linking to itself13 if (firstNode != secondNode) {14 setConnect2Attribute(firstNode, secondNode);15 drawSVGConnectingLine(firstNode, secondNode);16 }17 secondNode = null;18 firstNode = null;1920 makeClickedNodeSecondNode = 0;21 }22 });23}2425addEventListenersToDivNodesOnPageLoad(); //This function is here becasue it needs the endNodeAssigner() function to have been defined2627//The ConnectTo and the ConnectFrom attributes of a nodeDiv show what divs it is connected to and its relationship to it28function setConnect2Attribute(first, second) {29 if (first != second) {30 var connect2Array = [];31 if (first.hasAttribute('connectTo')) {32 var abc = (first.getAttribute('connectTo')).trim().split(' ');33 connect2Array = connect2Array.concat(abc);34 }35 var secondNodeId = second.getAttribute('nodeId');36 if (connect2Array.indexOf(secondNodeId) == -1) {37 connect2Array.push(secondNodeId);38 }39 first.setAttribute('connectTo', connect2Array.join(" "))40 }41}4243//This is the function the 'ConnectTo' button on the rightClick menu triggers44function nodeToConnectCurrentNodeTo() {45 // startNendNodesArrayOfDeletedSVGlines = [];46 firstNode = currentNode; //the right-click event is also a mousedown event, therefore, it makes the right-clicked nodeDiv the currentNode47 makeClickedNodeSecondNode = 1; //This condition determines if the node clicked after this would be made the endNode4849 hideContextMenu()50}5152//This is the function the 'Clone Node' button on the rightClick menu triggers53function createNewNode(type) {54 var newDivNode = document.createElement('DIV');55 //Assign new nodeId class from cloned divNode56 var newNodeID;57 newDivNode.classList.add('divNode');58 if (deletedNodeIdsArray.length == 0) {59 newNodeID = divNodes.length;60 } else {61 newNodeID = deletedNodeIdsArray[0] - 1;62 deletedNodeIdsArray.shift();63 }64 assignNodeID(newDivNode, newNodeID);65 //Remove border indicating node was selected66 newDivNode.style.border = '';67 //Create the nodeDiv at the mouse's coordinate68 newDivNode.style.top = rClick_Y + 'px';69 newDivNode.style.left = rClick_X + 'px';70 newDivNode.setAttribute('tabindex', 1);71 //Make the nodeId the textContent of the new nodeDiv72 newDivNode.textContent = 'node' + (newNodeID + 1);73 //Assing eventListners to nodeDiv74 newDivNode.addEventListener('mousedown', nodeCanvasMouseDownFunction);75 //Append new nodeDiv76 nodeCanvas.appendChild(newDivNode);77 endNodeAssigner(newDivNode);78 //For creating set nodes79 if (type == 'set') {80 newDivNode.classList.add('set_item');81 newDivNode.setAttribute('setclass', null);82 createSet();83 }8485 hideContextMenu()86}8788//Make divNode editable89var editableDiv = null;90var editablePathLabel = null;91nodeCanvas.addEventListener('dblclick', function (ev) {92 ev.preventDefault();93 //Get the clicked element94 ev = ev || window.event;95 var target = ev.target || ev.srcElement;9697 if (target.classList.contains('divNode')) {98 makeNodeDivEditable();99 }100 if (target.classList.contains('pathLabel')) {101 editablePathLabel = target;102 makePathLabelEditable();103 }104 if (target.tagName == 'svg') {105 createNewNode();106 }107108 //prevent doubleClick109 return false;110}, false);111112function makeNodeDivEditable() {113 editableDiv = currentNode;114 currentNode.contentEditable = 'true';115116 hideContextMenu()117}118119function makePathLabelEditable() {120 editablePathLabel.contentEditable = 'true';121122 hideContextMenu()123}124125function deleteNodeDiv() {126 //Delete all svg paths connected to the nodeDiv to be deleted 127 var pathClass = currentNode.getAttribute('nodeId');128 var deletedNodeId = pathClass;129 var pathsToRemove = nodeCanvas.querySelectorAll('path.' + pathClass);130 for (p = 0; p < pathsToRemove.length; p++) {131 //check for the node at the other end (either connectedTo or connectedFrom)132 var linkFrom = pathsToRemove[p].getAttribute('connectedfrom');133 var linkTo = pathsToRemove[p].getAttribute('connectedto');134 if (linkTo == pathClass) {135 var nodeOnOtherEnd = nodeCanvas.querySelector('.divNode.' + linkFrom);136 var toArray = [];137 toArray = toArray.concat((nodeOnOtherEnd.getAttribute('connectTo')).split(' '));138 if (toArray.length > 1) {139 var pathClassIndex = toArray.indexOf(pathClass);140 toArray.splice(pathClassIndex, 1);141 var newLinkToValue = toArray.join(' ');142 nodeOnOtherEnd.setAttribute('connectTo', newLinkToValue)143 } else {144 nodeOnOtherEnd.removeAttribute('connectTo')145 }146 }147 //remove path148 pathsToRemove[p].remove();149 }150151 //Delete the nodeDiv152 currentNode.remove();153154 //Save the nodeId of the deleted divNode to be used when creating a new divNode155 // deletedNodeIdsArray.push(deletedNodeId);156 deletedNodeIdsArray.push(Number(deletedNodeId.replace('node', ''))); //remove 'node' from the string so that only the number par to the string is left and convert the number sting into an actual number to be sorted157 deletedNodeIdsArray = [...new Float64Array(deletedNodeIdsArray).sort()];158159 hideContextMenu()160}161162//THIS HIDES OR SHOWS ALL DESCENDANTS OF SELECTED NODE163164//FUNCTIONS TO DETERMINE IF ALL DESCENDANTS OR JUST THE NEXT GENERATION SHOULD BE SHOWN/HIDDEN165function descendants2Toggle(allOrNextOnly) {166 if (allOrNextOnly == 'all') {167 firstGenRadio.checked = false;168 allNfirstGenRadio.checked = false;169 } else if (allOrNextOnly == 'firstGeneration') {170 allGenRadio.checked = false;171 allNfirstGenRadio.checked = false;172 } else if (allOrNextOnly == 'hideAllShowFirst') {173 firstGenRadio.checked = false;174 allGenRadio.checked = false;175 }176}177178//TO SHOW AND HIDE THE ITERACTIVITY CONTROLS179var interactivebuttons = document.getElementsByClassName('interactivebutton');180var makeInteractive = document.getElementById('makeInteractive');181182function interactivity() {183 if (makeInteractive.classList.contains('noninteractive')) {184 for (i = 0; i < interactivebuttons.length; i++) {185 interactivebuttons[i].style.display = '';186 }187 makeInteractive.classList.remove('noninteractive');188 nodeCanvas.addEventListener('mousedown', toggleDescendants);189 makeInteractive.classList.add('coloron');190 } else {191 for (i = 0; i < interactivebuttons.length; i++) {192 interactivebuttons[i].style.display = 'none';193 interactivebuttons[i].querySelector('input').checked = false;194 }195 makeInteractive.classList.add('noninteractive');196 makeInteractive.classList.remove('coloron');197 }198}199//To determine Which Function to Call200function toggleDescendants(e) {201 if ((currentNode) && (aNodeHasBeenClicked == 1)) {202 if (allGenRadio.checked) {203 toggleAllDescendants();204 } else if (firstGenRadio.checked) {205 toggleFirstGeneration()206 } else if (allNfirstGenRadio.checked) {207 toggleAllnFirstGeneration()208 }209 }210}211var arrayOfAllDescendants = [];212213function toggleAllDescendants(thisNode, showORhide) {214 if (!thisNode) {215 thisNode = currentNode216 }217 if ((thisNode.hasAttribute('connectTo')) && ((thisNode.getAttribute('connectTo')).trim().split(' ') != '')) {218 var connect2Array = [];219 var abc = (thisNode.getAttribute('connectTo')).trim().split(' ');220 connect2Array = connect2Array.concat(abc);221222 //SHOW ALL DESCENDANTS AND CONNECTING PATHS OF SELECTED NODE IF THEY HAVE BEEN HIDDEN223 if (((!showORhide) || (showORhide == 'show')) && (thisNode.classList.contains('descendantshidden'))) {224 connect2Array.forEach(descendant => {225 var currentDescendant = nodeCanvas.querySelector('[nodeid=' + descendant + ']')226 fadeInShow2(currentDescendant, 300);227 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');228 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');229 for (i = 0; i < pathsFrom.length; i++) {230 fadeInShow2(pathsFrom[i], 300);231 }232 for (i = 0; i < pathsTo.length; i++) {233 //Show path if node to which it is connected TO is not Hidden 234 if (nodeCanvas.querySelector('.divNode.' + pathsTo[i].getAttribute('connectedfrom')).style.display != 'none') {235 fadeInShow2(pathsTo[i], 400);236 //show path's label if any237 if (pathLabeToToggle = nodeCanvas.querySelector('.pathLabel[labelfor="' + pathsTo[i].id + '"]')) {238 fadeInShow2(pathLabeToToggle, 400);239 }240 }241 }242 toggleAllDescendants(currentDescendant, 'show');243 });244 thisNode.classList.remove('descendantshidden');245 }246 //HIDE ALL DESCENDANTS AND CONNECTING PATHS OF SELECTED NODE247 else if ((!showORhide) || (showORhide == 'hide') && (thisNode.classList.contains('descendantshidden') == false)) {248 thisNode.classList.add('descendantshidden');249 connect2Array.forEach(descendant => {250 var currentDescendant;251 if (nodeCanvas.querySelector('[nodeid=' + descendant + ']').classList.contains('descendantshidden') == false) {252 currentDescendant = nodeCanvas.querySelector('[nodeid=' + descendant + ']')253 fadeOutHide(currentDescendant, 400);254 // if(arrayOfAllDescendants.indexOf(currentDescendant) === -1){255 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');256 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');257 for (i = 0; i < pathsFrom.length; i++) {258 fadeOutHide(pathsFrom[i], 400);259 }260 for (i = 0; i < pathsTo.length; i++) {261 fadeOutHide(pathsTo[i], 400);262 }263 //Hide descendants of current descendant264 if (currentDescendant.classList.contains('descendantshidden') == false) {265 toggleAllDescendants(currentDescendant, 'hide');266 }267 }268 });269 }270 }271272 hideContextMenu()273}274275function toggleFirstGeneration() {276 if ((currentNode.hasAttribute('connectTo')) && ((currentNode.getAttribute('connectTo')).trim().split(' ') != '')) {277 var connect2Array = [];278 var abc = ((currentNode.getAttribute('connectTo'))).trim().split(' ');279 connect2Array = connect2Array.concat(abc);280281 //SHOWS NEXT GENERATION NODES AND PATHS282 if (currentNode.classList.contains('descendantshidden')) {283 connect2Array.forEach(descendant => {284 fadeInShow(nodeCanvas.querySelector('[nodeid=' + descendant + ']'), 800);285 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');286 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');287 //Show all paths connected FROM the node288 for (i = 0; i < pathsFrom.length; i++) {289 //Show path if node to which it is connected FROM is not Hidden 290 if (nodeCanvas.querySelector('.divNode.' + pathsFrom[i].getAttribute('connectedto')).style.display != 'none') {291 fadeInShow(pathsFrom[i], 800);292 //show path's label if any293 if (pathLabeToToggle = nodeCanvas.querySelector('.pathLabel[labelfor="' + pathsFrom[i].id + '"]')) {294 fadeInShow(pathLabeToToggle, 800);295 }296 }297 }298 //Show all paths connected TO the node299 for (i = 0; i < pathsTo.length; i++) {300 //Show path if node to which it is connected TO is not Hidden 301 if (nodeCanvas.querySelector('.divNode.' + pathsTo[i].getAttribute('connectedfrom')).style.display != 'none') {302 fadeInShow(pathsTo[i], 800);303 //show path's label if any304 if (pathLabeToToggle = nodeCanvas.querySelector('.pathLabel[labelfor="' + pathsTo[i].id + '"]')) {305 fadeInShow(pathLabeToToggle, 800);306 }307 }308 }309 });310 currentNode.classList.remove('descendantshidden');311 }312 //HIDES NEXT GENERATION NODES AND ALL PATHS CONNECTED TO CURRENT NODE313 else {314 connect2Array.forEach(descendant => {315 fadeOutHide(nodeCanvas.querySelector('[nodeid=' + descendant + ']'), 800);316 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');317 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');318 for (i = 0; i < pathsFrom.length; i++) {319 fadeOutHide(pathsFrom[i], 800);320 }321 for (i = 0; i < pathsTo.length; i++) {322 fadeOutHide(pathsTo[i], 800);323 //show path's label if any324 if (pathLabeToToggle = nodeCanvas.querySelector('.pathLabel[labelfor="' + pathsTo[i].id + '"]')) {325 fadeOutHide(pathLabeToToggle, 800);326 }327 }328 });329 currentNode.classList.add('descendantshidden');330 }331 }332333 hideContextMenu()334}335336function toggleAllnFirstGeneration(thisNode, showORhide) {337 if (!thisNode) {338 thisNode = currentNode339 }340 if ((thisNode.hasAttribute('connectTo')) && ((thisNode.getAttribute('connectTo')).trim().split(' ') != '')) {341 var connect2Array = [];342 var abc = (thisNode.getAttribute('connectTo')).trim().split(' ');343 connect2Array = connect2Array.concat(abc);344345 //SHOW NEXT GENERATION AND CONNECTING PATHS ONLY346 if (currentNode.classList.contains('descendantshidden')) {347 connect2Array.forEach(descendant => {348 fadeInShow(nodeCanvas.querySelector('[nodeid=' + descendant + ']'), 800);349 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');350 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');351 //Show all paths connected FROM the node352 for (i = 0; i < pathsFrom.length; i++) {353 if (nodeCanvas.querySelector('.divNode.' + pathsFrom[i].getAttribute('connectedto')).style.display != 'none') {354 fadeInShow(pathsFrom[i], 800);355 }356 }357 //Show all paths connected TO the node358 for (i = 0; i < pathsTo.length; i++) {359 //Show path if node to which it is connected TO is not Hidden 360 if (nodeCanvas.querySelector('.divNode.' + pathsTo[i].getAttribute('connectedfrom')).style.display != 'none') {361 fadeInShow(pathsTo[i], 800);362 //show path's label if any363 if (pathLabeToToggle = nodeCanvas.querySelector('.pathLabel[labelfor="' + pathsTo[i].id + '"]')) {364 fadeInShow(pathLabeToToggle, 800);365 }366 }367 }368 });369 currentNode.classList.remove('descendantshidden');370 }371 //HIDE ALL DESCENDANTS AND CONNECTING PATHS OF SELECTED NODE372 else if ((!showORhide) || (showORhide == 'hide')) {373 connect2Array.forEach(descendant => {374 var currentDescendant = nodeCanvas.querySelector('[nodeid=' + descendant + ']')375 fadeOutHide(currentDescendant, 400);376 var pathsFrom = svg.querySelectorAll('[connectedfrom=' + descendant + ']');377 var pathsTo = svg.querySelectorAll('[connectedto=' + descendant + ']');378 for (i = 0; i < pathsFrom.length; i++) {379 fadeOutHide(pathsFrom[i], 400);380 }381 for (i = 0; i < pathsTo.length; i++) {382 fadeOutHide(pathsTo[i], 400);383 }384 toggleAllDescendants(currentDescendant, 'hide');385 });386 thisNode.classList.add('descendantshidden');387 }388 }389390 hideContextMenu()391}392393//FUNCTION TO DELETE SELECTED PATH394function deletePath() {395 //get nodeId of startNode396 var startNodeId = selectedPath.getAttribute('connectedfrom');397 var pathConnectedTo = selectedPath.getAttribute('connectedto');398 //find divNode with the nodeId equal to the connectedFrom of the selected path399 var startNode = nodeCanvas.querySelector('[nodeid=' + startNodeId + ']');400 var startNodeConnectTo = startNode.getAttribute('connectTo');401402 // if (startNodeConnectTo === pathConnectedTo) {403 // startNode.removeAttribute('connectTo');404 // }405 // else {406 var connect2Array = [];407 var abc = startNodeConnectTo.split(' ');408 connect2Array = connect2Array.concat(abc);409 connect2Array.splice(connect2Array.indexOf(pathConnectedTo), 1);410 startNode.setAttribute('connectTo', connect2Array.join(" "));411 // }412 //Remove paths label if it has one413 if (labelToRemove = nodeCanvas.querySelector('[labelFor=' + selectedPath.id + ']')) {414 labelToRemove.remove();415 }416 //Remove the selectd path417 selectedPath.remove();418419 hideContextMenu();420}421422function addLabelToPath() {423 //if there is no node attached to the path already424 if (!nodeCanvas.querySelector('[labelFor=' + selectedPath.id + ']')) {425 // get Center of svg Path426 var bbox = selectedPath.getBBox();427 var pathXmid = Math.floor(bbox.x + bbox.width / 2.0);428 var pathYmid = Math.floor(bbox.y + bbox.height / 2.0);429430 //create pathLabel431 var svgLabel = document.createElement('DIV');432 svgLabel.classList.add('pathLabel');433 nodeCanvas.appendChild(svgLabel);434 //Position the pathLabel435 svgLabel.style.position = 'absolute';436 svgLabel.style.left = pathXmid + 'px';437 svgLabel.style.top = pathYmid + 'px';438 svgLabel.setAttribute('labelFor', selectedPath.id)439 svgLabel.textContent = 'edit label';440 svgLabel.contentEditable = 'true';441 editablePathLabel = svgLabel;442 }443444 hideContextMenu();445}446447function realignPathLabel() {448 for (l = 0; l < labelsForAttr.length; l++) {449 //find svg path with the id450 // get Center of svg Path451 var redrawnPath = nodeCanvas.querySelector("#" + labelsForAttr[l]);452 var bbox = redrawnPath.getBBox();453 var pathXmid = Math.floor(bbox.x + bbox.width / 2.0);454 var pathYmid = Math.floor(bbox.y + bbox.height / 2.0);455456 //find label with the for attribute457 var tempSvglabel = nodeCanvas.querySelector('[labelfor=' + labelsForAttr[l] + ']');458459 if (tempSvglabel) {460 tempSvglabel.style.left = pathXmid + 'px';461 tempSvglabel.style.top = pathYmid + 'px';462 }463 }464}465466// NOTES' FUNCTION467var notesCount = 0;468var highestNoteCount;469var notesCountArray = [];470var missingNotesIndexArr = [];471//check if there are any notes on the page already and...472//get their values, add them to the notesCountArray, and sort the notesCountArray473var allNotes = connectionDetails.querySelectorAll('[note]');474for (n = 0; n < allNotes.length; n++) {475 notesCountArray.push(allNotes[n].getAttribute('note'));476 notesCountArray = [...new Float64Array(notesCountArray).sort()];477 //addEventListener to notes on pageLoad478 allNotes[n].addEventListener('dblclick', noteDblClick);479}480// var notesMax = Math.max(...notesCountArray);481// var notesMin = Math.min(...notesCountArray);482483if (allNotes.length == 0) {484 notesCount = 0;485} else if (allNotes.length == 1) {486 if (allNotes[0] > 1) {487 var mNum = 0;488 var mDiff = allNotes[0] - 1;489 while (mNum < mDiff) {490 missingNotesIndexArr.push(++mNum)491 }492 } else {493 notesCount = 1;494 } //if the only note is 1495} else if (allNotes.length > 1) {496 for (n = 0; n < allNotes.length; n++) {497 //for the first number in the array, check if the number is greater than 1498 if ((n == 0) && (allNotes[0] > 1)) {499 var mNum = 0;500 var mDiff = allNotes[0] - 1;501 while (mNum < mDiff) {502 missingNotesIndexArr.push(++mNum)503 }504 }505 //if the difference btw the present index and the next one is greater than 1506 if ((allNotes[n + 1] - allNotes[n]) > 1) {507 var mNum = allNotes[n];508 var mDiff = allNotes[n + 1];509 while (mNum < (allNotes[n + 1] - 1)) {510 missingNotesIndexArr.push(++mNum)511 }512 }513 if (n == allNotes.length - 1) {514 highestNoteCount = allNotes[n];515 missingNotesIndexArr.push(++allNotes[n])516 //This is really not a missing number517 //This number is greater than the last number in the array so that the last number of the array will not be duplicated when the last noteCount is increamented518 }519 }520521}522523function addNote() {524 //first check if element already has a note attached to it525 if (elementToCommentOn.getAttribute('note') == null) {526 //first check if there are notesIndexes that are missing527 if (missingNotesIndexArr.length == 0) {528 ++notesCount;529 notesCountArray.push(notesCount);530 } else {531 notesCount = missingNotesIndexArr.shift(); //Removes the first element from an array and returns it.532 notesCountArray.push(notesCount);533 notesCountArray = [...new Float64Array(notesCountArray).sort()];534 }535 elementToCommentOn.setAttribute('note', notesCount);536537 var noteDiv = document.createElement('DIV');538 //The note Div will have the same 'note' attribute value as the element it is a comment to539 noteDiv.classList.add('notes');540 noteDiv.setAttribute('note', notesCount);541542 noteDiv.setAttribute('tabindex', 1);543 noteDiv.innerHTML = '<hr>' + 'note ' + notesCount;544 if (elementToCommentOn.classList.contains('divNode')) {545 noteDiv.innerHTML = '<hr><h2>' + elementToCommentOn.textContent.replace(/(<br>)/g, "") + '</h2>';546 }547 if (elementToCommentOn.classList.contains('svg-connectors')) {548 noteDiv.innerHTML = '<hr><h2>' + elementToCommentOn.getAttribute('connectedfrom') + '<em> : </em>' + elementToCommentOn.getAttribute('connectedto') + '</h2>';549 }550 noteDiv.contentEditable = 'true';551 //Assing eventListners to nodeDiv552 noteDiv.addEventListener('dblclick', noteDblClick);553 //Append new nodeDiv554 connectionDetails.appendChild(noteDiv);555 //highest noteCount556 highestNoteCount = notesCount;557 }558559 hideContextMenu();560}561562//function to make note editable on doubleClick563function noteDblClick() {564 //first, if any editable note before this, make it uneditable565 if ((prevEditableNote = connectionDetails.querySelector('[contenteditable="true"]')) && (prevEditableNote != this)) {566 prevEditableNote.contentEditable = 'false'567 }568 //Make note editable on doubleClick569 this.contentEditable = 'true'570}571572var toggleCheck = 0;573574function toggleConnectionDetails() {575 if (toggleCheck == '0') {576 connectionDetails.style.right = '-' + connectionDetails.offsetWidth + 'px';577 toggleCheck = 1;578 } else {579 connectionDetails.style.right = '';580 toggleCheck = 0;581 }582}583toggleConnectionDetails()584585function deleteNote() {586 highestNoteCount = notesCountArray[notesCountArray.length - 1];587 notesCustomContextMenu.style.display = 'none'; //hide notes ContextMenu588 var noteIndex = Number(noteToDelete.getAttribute('note'));589 nodeCanvas.querySelector('[note="' + noteIndex + '"]').removeAttribute('note'); //remove the note attribute from the div or path to which the note to be deleted belongs590 notesCountArray.splice(notesCountArray.indexOf(noteIndex), 1)591 missingNotesIndexArr.push(Number(noteIndex)); //Add the noteIndex of the deleted note to the missingNotesIndexArr592 missingNotesIndexArr = [...new Float64Array(missingNotesIndexArr).sort()]; //Sort the array593 if (missingNotesIndexArr.indexOf((highestNoteCount + 1)) == -1) {594 missingNotesIndexArr.push(highestNoteCount + 1)595 missingNotesIndexArr = [...new Float64Array(missingNotesIndexArr).sort()];596 } else if (noteIndex == highestNoteCount) {597 missingNotesIndexArr = [...new Float64Array(missingNotesIndexArr).sort()];598 }599 noteToDelete.remove();600 noteToDelete = null;
...
object-observer.js
Source:object-observer.js
1const2 INSERT = 'insert',3 UPDATE = 'update',4 DELETE = 'delete',5 REVERSE = 'reverse',6 SHUFFLE = 'shuffle',7 oMetaKey = Symbol('observable-meta-key'),8 validObservableOptionKeys = { async: 1 },9 validObserverOptionKeys = { path: 1, pathsOf: 1, pathsFrom: 1 },10 processObserveOptions = function processObserveOptions(options) {11 const result = {};12 if (options.path !== undefined) {13 if (typeof options.path !== 'string' || options.path === '') {14 throw new Error('"path" option, if/when provided, MUST be a non-empty string');15 }16 result.path = options.path;17 }18 if (options.pathsOf !== undefined) {19 if (options.path) {20 throw new Error('"pathsOf" option MAY NOT be specified together with "path" option');21 }22 if (typeof options.pathsOf !== 'string') {23 throw new Error('"pathsOf" option, if/when provided, MUST be a string (MAY be empty)');24 }25 result.pathsOf = options.pathsOf.split('.').filter(n => n);26 }27 if (options.pathsFrom !== undefined) {28 if (options.path || options.pathsOf) {29 throw new Error('"pathsFrom" option MAY NOT be specified together with "path"/"pathsOf" option/s');30 }31 if (typeof options.pathsFrom !== 'string' || options.pathsFrom === '') {32 throw new Error('"pathsFrom" option, if/when provided, MUST be a non-empty string');33 }34 result.pathsFrom = options.pathsFrom;35 }36 const invalidOptions = Object.keys(options).filter(option => !validObserverOptionKeys.hasOwnProperty(option));37 if (invalidOptions.length) {38 throw new Error(`'${invalidOptions.join(', ')}' is/are not a valid observer option/s`);39 }40 return result;41 },42 observe = function observe(observer, options) {43 if (typeof observer !== 'function') {44 throw new Error(`observer MUST be a function, got '${observer}'`);45 }46 const47 oMeta = this[oMetaKey],48 observers = oMeta.observers;49 if (!observers.some(o => o[0] === observer)) {50 let opts;51 if (options) {52 opts = processObserveOptions(options);53 } else {54 opts = {};55 }56 observers.push([observer, opts]);57 } else {58 console.warn('observer may be bound to an observable only once; will NOT rebind');59 }60 },61 unobserve = function unobserve() {62 const oMeta = this[oMetaKey];63 const observers = oMeta.observers;64 let ol = observers.length;65 if (ol) {66 let al = arguments.length;67 if (al) {68 while (al--) {69 let i = ol;70 while (i--) {71 if (observers[i][0] === arguments[al]) {72 observers.splice(i, 1);73 ol--;74 }75 }76 }77 } else {78 observers.splice(0);79 }80 }81 },82 propertiesBluePrint = { [oMetaKey]: { value: null }, observe: { value: observe }, unobserve: { value: unobserve } },83 prepareObject = function prepareObject(source, oMeta) {84 propertiesBluePrint[oMetaKey].value = oMeta;85 const target = Object.defineProperties({}, propertiesBluePrint);86 for (const key of Object.keys(source)) {87 target[key] = getObservedOf(source[key], key, oMeta);88 }89 return target;90 },91 prepareArray = function prepareArray(source, oMeta) {92 let i = 0, l = source.length;93 propertiesBluePrint[oMetaKey].value = oMeta;94 const target = Object.defineProperties(new Array(l), propertiesBluePrint);95 for (; i < l; i++) {96 target[i] = getObservedOf(source[i], i, oMeta);97 }98 return target;99 },100 prepareTypedArray = function prepareTypedArray(source, oMeta) {101 propertiesBluePrint[oMetaKey].value = oMeta;102 Object.defineProperties(source, propertiesBluePrint);103 return source;104 },105 filterChanges = function filterChanges(options, changes) {106 let result = changes;107 if (options.path) {108 const oPath = options.path;109 result = changes.filter(change =>110 change.path.join('.') === oPath111 );112 } else if (options.pathsOf) {113 const oPathsOf = options.pathsOf;114 result = changes.filter(change =>115 change.path.length === oPathsOf.length + 1 ||116 (change.path.length === oPathsOf.length && (change.type === REVERSE || change.type === SHUFFLE))117 );118 } else if (options.pathsFrom) {119 const oPathsFrom = options.pathsFrom;120 result = changes.filter(change =>121 change.path.join('.').startsWith(oPathsFrom)122 );123 }124 return result;125 },126 callObserverSafe = function callObserverSafe(listener, changes) {127 try {128 listener(changes);129 } catch (e) {130 console.error(`failed to notify listener ${listener} with ${changes}`, e);131 }132 },133 callObserversFromMT = function callObserversFromMT() {134 const batches = this.batches;135 this.batches = null;136 for (const [listener, options] of batches) {137 callObserverSafe(listener, options);138 }139 },140 callObservers = function callObservers(oMeta, changes) {141 let currentObservable = oMeta;142 let observers, target, options, relevantChanges, i, newPath, tmp;143 const l = changes.length;144 do {145 observers = currentObservable.observers;146 i = observers.length;147 while (i--) {148 [target, options] = observers[i];149 relevantChanges = filterChanges(options, changes);150 if (relevantChanges.length) {151 if (currentObservable.options.async) {152 // this is the async dispatch handling153 if (!currentObservable.batches) {154 currentObservable.batches = [];155 queueMicrotask(callObserversFromMT.bind(currentObservable));156 }157 let rb = currentObservable.batches.find(b => b[0] === target);158 if (!rb) {159 rb = [target, []];160 currentObservable.batches.push(rb);161 }162 Array.prototype.push.apply(rb[1], relevantChanges);163 } else {164 // this is the naive straight forward synchronous dispatch165 callObserverSafe(target, relevantChanges);166 }167 }168 }169 let tmpa;170 if (currentObservable.parent) {171 tmpa = new Array(l);172 for (let i = 0; i < l; i++) {173 tmp = changes[i];174 newPath = [currentObservable.ownKey, ...tmp.path];175 tmpa[i] = {176 type: tmp.type,177 path: newPath,178 value: tmp.value,179 oldValue: tmp.oldValue,180 object: tmp.object181 };182 }183 changes = tmpa;184 currentObservable = currentObservable.parent;185 } else {186 currentObservable = null;187 }188 } while (currentObservable);189 },190 getObservedOf = function getObservedOf(item, key, parent) {191 if (!item || typeof item !== 'object') {192 return item;193 } else if (Array.isArray(item)) {194 return new ArrayOMeta({ target: item, ownKey: key, parent: parent }).proxy;195 } else if (ArrayBuffer.isView(item)) {196 return new TypedArrayOMeta({ target: item, ownKey: key, parent: parent }).proxy;197 } else if (item instanceof Date || item instanceof Error) {198 return item;199 } else {200 return new ObjectOMeta({ target: item, ownKey: key, parent: parent }).proxy;201 }202 },203 proxiedPop = function proxiedPop() {204 const oMeta = this[oMetaKey],205 target = oMeta.target,206 poppedIndex = target.length - 1;207 let popResult = target.pop();208 if (popResult && typeof popResult === 'object') {209 const tmpObserved = popResult[oMetaKey];210 if (tmpObserved) {211 popResult = tmpObserved.detach();212 }213 }214 const changes = [{ type: DELETE, path: [poppedIndex], oldValue: popResult, object: this }];215 callObservers(oMeta, changes);216 return popResult;217 },218 proxiedPush = function proxiedPush() {219 const220 oMeta = this[oMetaKey],221 target = oMeta.target,222 l = arguments.length,223 pushContent = new Array(l),224 initialLength = target.length;225 for (let i = 0; i < l; i++) {226 pushContent[i] = getObservedOf(arguments[i], initialLength + i, oMeta);227 }228 const pushResult = Reflect.apply(target.push, target, pushContent);229 const changes = [];230 for (let i = initialLength, l = target.length; i < l; i++) {231 changes[i - initialLength] = { type: INSERT, path: [i], value: target[i], object: this };232 }233 callObservers(oMeta, changes);234 return pushResult;235 },236 proxiedShift = function proxiedShift() {237 const238 oMeta = this[oMetaKey],239 target = oMeta.target;240 let shiftResult, i, l, item, tmpObserved;241 shiftResult = target.shift();242 if (shiftResult && typeof shiftResult === 'object') {243 tmpObserved = shiftResult[oMetaKey];244 if (tmpObserved) {245 shiftResult = tmpObserved.detach();246 }247 }248 // update indices of the remaining items249 for (i = 0, l = target.length; i < l; i++) {250 item = target[i];251 if (item && typeof item === 'object') {252 tmpObserved = item[oMetaKey];253 if (tmpObserved) {254 tmpObserved.ownKey = i;255 }256 }257 }258 const changes = [{ type: DELETE, path: [0], oldValue: shiftResult, object: this }];259 callObservers(oMeta, changes);260 return shiftResult;261 },262 proxiedUnshift = function proxiedUnshift() {263 const264 oMeta = this[oMetaKey],265 target = oMeta.target,266 al = arguments.length,267 unshiftContent = new Array(al);268 for (let i = 0; i < al; i++) {269 unshiftContent[i] = getObservedOf(arguments[i], i, oMeta);270 }271 const unshiftResult = Reflect.apply(target.unshift, target, unshiftContent);272 for (let i = 0, l = target.length, item; i < l; i++) {273 item = target[i];274 if (item && typeof item === 'object') {275 const tmpObserved = item[oMetaKey];276 if (tmpObserved) {277 tmpObserved.ownKey = i;278 }279 }280 }281 // publish changes282 const l = unshiftContent.length;283 const changes = new Array(l);284 for (let i = 0; i < l; i++) {285 changes[i] = { type: INSERT, path: [i], value: target[i], object: this };286 }287 callObservers(oMeta, changes);288 return unshiftResult;289 },290 proxiedReverse = function proxiedReverse() {291 const292 oMeta = this[oMetaKey],293 target = oMeta.target;294 let i, l, item;295 target.reverse();296 for (i = 0, l = target.length; i < l; i++) {297 item = target[i];298 if (item && typeof item === 'object') {299 const tmpObserved = item[oMetaKey];300 if (tmpObserved) {301 tmpObserved.ownKey = i;302 }303 }304 }305 const changes = [{ type: REVERSE, path: [], object: this }];306 callObservers(oMeta, changes);307 return this;308 },309 proxiedSort = function proxiedSort(comparator) {310 const311 oMeta = this[oMetaKey],312 target = oMeta.target;313 let i, l, item;314 target.sort(comparator);315 for (i = 0, l = target.length; i < l; i++) {316 item = target[i];317 if (item && typeof item === 'object') {318 const tmpObserved = item[oMetaKey];319 if (tmpObserved) {320 tmpObserved.ownKey = i;321 }322 }323 }324 const changes = [{ type: SHUFFLE, path: [], object: this }];325 callObservers(oMeta, changes);326 return this;327 },328 proxiedFill = function proxiedFill(filVal, start, end) {329 const330 oMeta = this[oMetaKey],331 target = oMeta.target,332 changes = [],333 tarLen = target.length,334 prev = target.slice(0);335 start = start === undefined ? 0 : (start < 0 ? Math.max(tarLen + start, 0) : Math.min(start, tarLen));336 end = end === undefined ? tarLen : (end < 0 ? Math.max(tarLen + end, 0) : Math.min(end, tarLen));337 if (start < tarLen && end > start) {338 target.fill(filVal, start, end);339 let tmpObserved;340 for (let i = start, item, tmpTarget; i < end; i++) {341 item = target[i];342 target[i] = getObservedOf(item, i, oMeta);343 if (prev.hasOwnProperty(i)) {344 tmpTarget = prev[i];345 if (tmpTarget && typeof tmpTarget === 'object') {346 tmpObserved = tmpTarget[oMetaKey];347 if (tmpObserved) {348 tmpTarget = tmpObserved.detach();349 }350 }351 changes.push({ type: UPDATE, path: [i], value: target[i], oldValue: tmpTarget, object: this });352 } else {353 changes.push({ type: INSERT, path: [i], value: target[i], object: this });354 }355 }356 callObservers(oMeta, changes);357 }358 return this;359 },360 proxiedCopyWithin = function proxiedCopyWithin(dest, start, end) {361 const362 oMeta = this[oMetaKey],363 target = oMeta.target,364 tarLen = target.length;365 dest = dest < 0 ? Math.max(tarLen + dest, 0) : dest;366 start = start === undefined ? 0 : (start < 0 ? Math.max(tarLen + start, 0) : Math.min(start, tarLen));367 end = end === undefined ? tarLen : (end < 0 ? Math.max(tarLen + end, 0) : Math.min(end, tarLen));368 const len = Math.min(end - start, tarLen - dest);369 if (dest < tarLen && dest !== start && len > 0) {370 const371 prev = target.slice(0),372 changes = [];373 target.copyWithin(dest, start, end);374 for (let i = dest, nItem, oItem, tmpObserved; i < dest + len; i++) {375 // update newly placed observables, if any376 nItem = target[i];377 if (nItem && typeof nItem === 'object') {378 nItem = getObservedOf(nItem, i, oMeta);379 target[i] = nItem;380 }381 // detach overridden observables, if any382 oItem = prev[i];383 if (oItem && typeof oItem === 'object') {384 tmpObserved = oItem[oMetaKey];385 if (tmpObserved) {386 oItem = tmpObserved.detach();387 }388 }389 if (typeof nItem !== 'object' && nItem === oItem) {390 continue;391 }392 changes.push({ type: UPDATE, path: [i], value: nItem, oldValue: oItem, object: this });393 }394 callObservers(oMeta, changes);395 }396 return this;397 },398 proxiedSplice = function proxiedSplice() {399 const400 oMeta = this[oMetaKey],401 target = oMeta.target,402 splLen = arguments.length,403 spliceContent = new Array(splLen),404 tarLen = target.length;405 // observify the newcomers406 for (let i = 0; i < splLen; i++) {407 spliceContent[i] = getObservedOf(arguments[i], i, oMeta);408 }409 // calculate pointers410 const411 startIndex = splLen === 0 ? 0 : (spliceContent[0] < 0 ? tarLen + spliceContent[0] : spliceContent[0]),412 removed = splLen < 2 ? tarLen - startIndex : spliceContent[1],413 inserted = Math.max(splLen - 2, 0),414 spliceResult = Reflect.apply(target.splice, target, spliceContent),415 newTarLen = target.length;416 // reindex the paths417 let tmpObserved;418 for (let i = 0, item; i < newTarLen; i++) {419 item = target[i];420 if (item && typeof item === 'object') {421 tmpObserved = item[oMetaKey];422 if (tmpObserved) {423 tmpObserved.ownKey = i;424 }425 }426 }427 // detach removed objects428 let i, l, item;429 for (i = 0, l = spliceResult.length; i < l; i++) {430 item = spliceResult[i];431 if (item && typeof item === 'object') {432 tmpObserved = item[oMetaKey];433 if (tmpObserved) {434 spliceResult[i] = tmpObserved.detach();435 }436 }437 }438 const changes = [];439 let index;440 for (index = 0; index < removed; index++) {441 if (index < inserted) {442 changes.push({ type: UPDATE, path: [startIndex + index], value: target[startIndex + index], oldValue: spliceResult[index], object: this });443 } else {444 changes.push({ type: DELETE, path: [startIndex + index], oldValue: spliceResult[index], object: this });445 }446 }447 for (; index < inserted; index++) {448 changes.push({ type: INSERT, path: [startIndex + index], value: target[startIndex + index], object: this });449 }450 callObservers(oMeta, changes);451 return spliceResult;452 },453 proxiedTypedArraySet = function proxiedTypedArraySet(source, offset) {454 const455 oMeta = this[oMetaKey],456 target = oMeta.target,457 souLen = source.length,458 prev = target.slice(0);459 offset = offset || 0;460 target.set(source, offset);461 const changes = new Array(souLen);462 for (let i = offset; i < (souLen + offset); i++) {463 changes[i - offset] = { type: UPDATE, path: [i], value: target[i], oldValue: prev[i], object: this };464 }465 callObservers(oMeta, changes);466 },467 proxiedArrayMethods = {468 pop: proxiedPop,469 push: proxiedPush,470 shift: proxiedShift,471 unshift: proxiedUnshift,472 reverse: proxiedReverse,473 sort: proxiedSort,474 fill: proxiedFill,475 copyWithin: proxiedCopyWithin,476 splice: proxiedSplice477 },478 proxiedTypedArrayMethods = {479 reverse: proxiedReverse,480 sort: proxiedSort,481 fill: proxiedFill,482 copyWithin: proxiedCopyWithin,483 set: proxiedTypedArraySet484 };485class OMetaBase {486 constructor(properties, cloningFunction) {487 const { target, parent, ownKey } = properties;488 if (parent && ownKey !== undefined) {489 this.parent = parent;490 this.ownKey = ownKey;491 } else {492 this.parent = null;493 this.ownKey = null;494 }495 const targetClone = cloningFunction(target, this);496 this.observers = [];497 this.revocable = Proxy.revocable(targetClone, this);498 this.proxy = this.revocable.proxy;499 this.target = targetClone;500 this.options = this.processOptions(properties.options);501 }502 processOptions(options) {503 if (options) {504 if (typeof options !== 'object') {505 throw new Error(`Observable options if/when provided, MAY only be an object, got '${options}'`);506 }507 const invalidOptions = Object.keys(options).filter(option => !validObservableOptionKeys.hasOwnProperty(option));508 if (invalidOptions.length) {509 throw new Error(`'${invalidOptions.join(', ')}' is/are not a valid Observable option/s`);510 }511 return Object.assign({}, options);512 } else {513 return {};514 }515 }516 detach() {517 this.parent = null;518 return this.target;519 }520 set(target, key, value) {521 let oldValue = target[key];522 if (value !== oldValue) {523 const newValue = getObservedOf(value, key, this);524 target[key] = newValue;525 if (oldValue && typeof oldValue === 'object') {526 const tmpObserved = oldValue[oMetaKey];527 if (tmpObserved) {528 oldValue = tmpObserved.detach();529 }530 }531 const changes = oldValue === undefined532 ? [{ type: INSERT, path: [key], value: newValue, object: this.proxy }]533 : [{ type: UPDATE, path: [key], value: newValue, oldValue: oldValue, object: this.proxy }];534 callObservers(this, changes);535 }536 return true;537 }538 deleteProperty(target, key) {539 let oldValue = target[key];540 delete target[key];541 if (oldValue && typeof oldValue === 'object') {542 const tmpObserved = oldValue[oMetaKey];543 if (tmpObserved) {544 oldValue = tmpObserved.detach();545 }546 }547 const changes = [{ type: DELETE, path: [key], oldValue: oldValue, object: this.proxy }];548 callObservers(this, changes);549 return true;550 }551}552class ObjectOMeta extends OMetaBase {553 constructor(properties) {554 super(properties, prepareObject);555 }556}557class ArrayOMeta extends OMetaBase {558 constructor(properties) {559 super(properties, prepareArray);560 }561 get(target, key) {562 if (proxiedArrayMethods.hasOwnProperty(key)) {563 return proxiedArrayMethods[key];564 } else {565 return target[key];566 }567 }568}569class TypedArrayOMeta extends OMetaBase {570 constructor(properties) {571 super(properties, prepareTypedArray);572 }573 get(target, key) {574 if (proxiedTypedArrayMethods.hasOwnProperty(key)) {575 return proxiedTypedArrayMethods[key];576 } else {577 return target[key];578 }579 }580}581class Observable {582 constructor() {583 throw new Error('Observable MAY NOT be created via constructor, see "Observable.from" API');584 }585 static from(target, options) {586 if (!target || typeof target !== 'object') {587 throw new Error('observable MAY ONLY be created from a non-null object');588 } else if (target[oMetaKey]) {589 return target;590 } else if (Array.isArray(target)) {591 return new ArrayOMeta({ target: target, ownKey: null, parent: null, options: options }).proxy;592 } else if (ArrayBuffer.isView(target)) {593 return new TypedArrayOMeta({ target: target, ownKey: null, parent: null, options: options }).proxy;594 } else if (target instanceof Date || target instanceof Error) {595 throw new Error(`${target} found to be one of a on-observable types`);596 } else {597 return new ObjectOMeta({ target: target, ownKey: null, parent: null, options: options }).proxy;598 }599 }600 static isObservable(input) {601 return !!(input && input[oMetaKey]);602 }603}604Object.freeze(Observable);...
pathFinder.js
Source:pathFinder.js
1import { Graph } from "./graph.js";2import { Vertex } from "./vertex.js";3export class PathFinder {4 constructor(graph) {5 this._graph = graph;6 this._distances = new Map();7 this._visited = new Map();8 this._prev = new Map();9 }10 get prev() {11 return this._prev;12 }13 get visited() {14 return this._visited;15 }16 get graph() {17 return this._graph;18 }19 get distances() {20 return this._distances;21 }22 isCompleted() {23 return [...this.visited.values()].every((value) => value);24 }25 findMinMark() {26 let min = Infinity;27 let minKey = null;28 this.distances.forEach((value, key) => {29 if (value < min && !this.visited.get(key)) {30 minKey = key;31 min = value;32 }33 });34 return minKey;35 }36 findPath(from) {37 if (this.graph instanceof Graph && from instanceof Vertex) {38 const vertices = this.graph.vertices;39 const matrix = this.graph.matrix;40 const distances = this.distances;41 const visited = this.visited;42 const prev = this.prev;43 for (let vertex of vertices) {44 distances.set(vertex, Infinity);45 visited.set(vertex, false);46 prev.set(vertex, from);47 }48 distances.set(from, 0);49 while (!this.isCompleted()) {50 const minMarkVertex = this.findMinMark();51 const children = matrix.get(minMarkVertex);52 const minMark = distances.get(minMarkVertex);53 if (children) {54 children.forEach((length, adjacent) => {55 const currentMark = distances.get(adjacent);56 if (currentMark > minMark + length) {57 distances.set(adjacent, minMark + length);58 prev.set(adjacent, minMarkVertex);59 }60 });61 }62 visited.set(minMarkVertex, true);63 }64 const pathTo = (to) => {65 if (to instanceof Vertex && from !== to) {66 const path = [to];67 let currentVertex = to;68 while (currentVertex !== from) {69 const parent = prev.get(currentVertex);70 path.unshift(parent);71 currentVertex = parent;72 }73 return path;74 }75 return [];76 };77 return {78 distances,79 pathTo,80 };81 }82 return null;83 }84 findAllPaths() {85 const paths = new Map();86 for (let firstVertex of this.graph.vertices) {87 const pathsFrom = paths.get(firstVertex) || new Map();88 const { pathTo } = this.findPath(firstVertex);89 for (let secondVertex of this.graph.vertices) {90 const pathsTo = paths.get(secondVertex) || new Map();91 const path = pathsFrom.get(secondVertex) || [];92 if (firstVertex !== secondVertex && !pathsFrom.get(secondVertex)) {93 const foundPath = pathTo(secondVertex);94 path.push(...foundPath);95 pathsTo.set(firstVertex, path.slice().reverse());96 }97 paths.set(secondVertex, pathsTo);98 pathsFrom.set(secondVertex, path);99 }100 paths.set(firstVertex, pathsFrom);101 }102 return paths;103 }...
Task12.js
Source:Task12.js
1function isLargeCave(cave) {2 return cave == cave.toUpperCase();3}4function hasTwoSmallCaves(path) {5 let smallCaves = new Array();6 for (let i = 0; i < path.length; i++) {7 if (!isLargeCave(path[i]) && smallCaves.includes(path[i])) {8 return true;9 }10 smallCaves.push(path[i]);11 }12 return false;13}14function traverse(pathsFrom, pathsTo, ends, currentPath, visitASmallCaveTwice) {15 let foundPaths = 0;16 let currentCave = currentPath[currentPath.length-1];17 for (let i = 0; i < pathsFrom.length; i++) {18 if (pathsFrom[i] == currentCave && (isLargeCave(pathsTo[i]) || !currentPath.includes(pathsTo[i]) || (visitASmallCaveTwice && !hasTwoSmallCaves(currentPath)))) {19 let pathCopy = currentPath.slice();20 pathCopy.push(pathsTo[i]);21 foundPaths += traverse(pathsFrom, pathsTo, ends, pathCopy, visitASmallCaveTwice);22 }23 }24 if (ends.includes(currentCave)) {25 foundPaths++;26 }27 return foundPaths;28}29function task(lines) {30 let pathsFrom = new Array();31 let pathsTo = new Array();32 let beginnings = new Array();33 let ends = new Array();34 for (let i = 0; i < lines.length; i++) {35 let parts = lines[i].split('-');36 if (parts[0] == 'start') {37 beginnings.push(parts[1]);38 } else if (parts[1] == 'start') {39 beginnings.push(parts[0]);40 } else if(parts[0] == 'end') {41 ends.push(parts[1]);42 } else if(parts[1] == 'end') {43 ends.push(parts[0]);44 } else {45 pathsFrom.push(parts[0]);46 pathsTo.push(parts[1]);47 pathsFrom.push(parts[1]);48 pathsTo.push(parts[0]);49 }50 }51 let totalPaths = 0;52 for (let i = 0; i < beginnings.length; i++) {53 totalPaths += traverse(pathsFrom, pathsTo, ends, [beginnings[i]], false);54 }55 let totalPaths2 = 0;56 for (let i = 0; i < beginnings.length; i++) {57 totalPaths2 += traverse(pathsFrom, pathsTo, ends, [beginnings[i]], true);58 }59 return new Array(totalPaths, totalPaths2);60}61function runTask() {62 let input = document.getElementById("taskinput").value;63 let allLines = input.split('\n');64 let result = task(allLines);65 document.getElementById("result1").innerHTML = result[0];66 document.getElementById("result2").innerHTML = result[1];...
10.js
Source:10.js
1const map = {};2function pathsFrom(joltages, current) {3 if (joltages[current] === undefined) throw new Error(`I don't have that adapter (${current})`);4 if (map[current]) return map[current];5 let paths = 0;6 for (let i = current + 1; i < joltages[joltages.length - 1] && joltages[i] - joltages[current] <= 3; i++) {7 if (joltages[i]) { paths += pathsFrom(joltages, i); }8 }9 paths = paths || 1;10 map[current] = paths;11 return paths;12}13module.exports = function (data) {14 let joltages = data.replace(/\r/g, '').split(/\n/).map(Number).sort((a, b) => a - b);15 joltages.unshift(0);16 joltages.push(joltages[joltages.length - 1] + 3);17 let ones = 0;18 let threes = 0;19 for (let i = 1; i < joltages.length; i++) {20 if (joltages[i] - joltages[i - 1] === 1) ones++;21 else if (joltages[i] - joltages[i - 1] === 3) threes++;22 }23 console.log(ones * threes);24 console.log(pathsFrom(joltages, 0));...
paths.js
Source:paths.js
...5 verify(grid);6 var results = [];7 grid.forEach(function (row, r) {8 row.forEach(function (item, c) {9 var str = pathsFrom(grid, r, c, opts);10 results = results.concat(str);11 });12 })13 return results;...
index.js
Source:index.js
1module.exports = {2 paths: require('./src/paths').paths,3 pathsFrom: require('./src/pathsFrom').pathsFrom...
Using AI Code Generation
1var allTestFiles = [];2var TEST_REGEXP = /(spec|test)\.js$/i;3var pathToModule = function(path) {4};5Object.keys(window.__karma__.files).forEach(function(file) {6 if (TEST_REGEXP.test(file)) {7 allTestFiles.push(pathToModule(file));8 }9});10require.config({11 paths: {
Using AI Code Generation
1var pathsFrom = window.__karma__.pathsFrom;2requirejs.config({3 baseUrl: pathsFrom('base'),4 deps: Object.keys(window.__karma__.files).filter(isTestFile),5});6function isTestFile(path) {7 return /\.spec\.js$/.test(path);8}9requirejs.config({10 baseUrl: pathsFrom('base'),11 deps: Object.keys(window.__karma__.files).filter(isTestFile),12});13function isTestFile(path) {14 return /\.spec\.js$/.test(path);15}16var pathsFrom = window.__karma__.pathsFrom;17requirejs.config({18 baseUrl: pathsFrom('base'),19 deps: Object.keys(window.__karma__.files).filter(isTestFile),20});21function isTestFile(path) {22 return /\.spec\.js$/.test(path);23}24var pathsFrom = window.__karma__.pathsFrom;25requirejs.config({26 baseUrl: pathsFrom('base'),27 deps: Object.keys(window.__karma__.files).filter(isTestFile),28});29function isTestFile(path) {30 return /\.spec\.js$/.test(path);
Using AI Code Generation
1var pathsFrom = require('karma').config.pathsFrom;2var pathsFrom = require('karma').config.pathsFrom;3module.exports = function(config) {4 config.set({5 pathsFrom('app', [6 pathsFrom('test', [7 });8};
Using AI Code Generation
1var paths = __karma__.config.args[0];2requirejs.config({3 deps: Object.keys(paths),4});5requirejs(['test/test1', 'test/test2'], function () {6});
Using AI Code Generation
1var pathsFrom = require('karma').config.PathsFrom;2module.exports = function(config) {3 config.set({4 pathsFrom('src/**/*.spec.js'),5 pathsFrom('src/**/*.test.js')6 });7};
Using AI Code Generation
1const pathsFrom = require('karma/lib/utils').pathsFrom;2const webpackConfig = require('./webpack.config.js');3const webpack = require('webpack');4module.exports = function(config) {5 config.set({6 preprocessors: {7 },8 webpack: {9 },10 webpackMiddleware: {11 },12 })13}14const webpack = require('webpack');15module.exports = {
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!