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!!
