<commit_before>"use strict";

class gsuiMixer {
	constructor() {
		const root = gsuiMixer.template.cloneNode( true ),
			addBtn = root.querySelector( ".gsuiMixer-addChan" ),
			dnd = new gsuiReorder(),
			gsdata = new gsdataMixer( {
				addChan: this._addChan.bind( this ),
				removeChan: this._removeChan.bind( this ),
				redirectChan: this._updateChanConnections.bind( this ),
				toggleChan: ( id, b ) => this._chans[ id ].root.classList.toggle( "gsuiMixerChannel-muted", !b ),
				changePanChan: ( id, val ) => this._chans[ id ].pan.setValue( val ),
				changeGainChan: ( id, val ) => this._chans[ id ].gain.setValue( val ),
				changeOrderChan: ( id, val ) => this._chans[ id ].root.dataset.order = val,
				renameChan: ( id, val ) => {
					this._chans[ id ].name.textContent = val;
					this.onupdateChan( id, "name", val );
				},
			} );

		this.rootElement = root;
		this.gsdata = gsdata;
		this._call = ( ...args ) => this.gsdata.callAction( this.onchange, ...args );
		this._pmain = root.querySelector( ".gsuiMixer-panMain" );
		this._pchans = root.querySelector( ".gsuiMixer-panChannels" );
		this._chans = {};
		this._chanSelected = null;
		this.oninput =
		this.onchange =
		this.onaddChan =
		this.ondeleteChan =
		this.onupdateChan =
		this.onselectChan = gsdata.noop;
		this._analyserH = 10;
		this._attached = false;
		Object.seal( this );

		addBtn.onclick = this._call.bind( null, "addChan" );
		dnd.setDirection( "h" );
		dnd.setRootElement( root );
		dnd.setSelectors( {
			item: ".gsuiMixerChannel",
			handle: ".gsuiMixerChannel-grip",
			parent: ".gsuiMixer-panChannels",
		} );
		dnd.onchange = () => {
			const obj = gsuiReorder.listComputeOrderChange( this._pchans, {} );

			this.onchange( obj );
		};
		this.empty();
		this.selectChan( "main" );
	}

	attached() {
		const pan = this._pchans;

		this._attached = true;
		pan.style.bottom = `${ pan.clientHeight - pan.offsetHeight }px`;
		Object.entries( this.gsdata.data ).forEach( ( [ id, obj ] ) => {
			const html = this._chans[ id ];

			html.pan.attached();
			html.gain.attached();
			this.onaddChan( id, obj );
		} );
		this.resized();
	}
	resized() {
		const chans = Object.values( this._chans ),
			h = chans[ 0 ].analyser.rootElement.getBoundingClientRect().height;

		this._analyserH = h;
		chans.forEach( html => html.analyser.setResolution( h ) );
	}
	updateAudioData( id, ldata, rdata ) {
		this._chans[ id ].analyser.draw( ldata, rdata );
	}
	empty() {
		this.gsdata.clear();
		this.gsdata.change( {
			main: {
				toggle: true,
				name: "main",
				gain: 1,
				pan: 0,
			},
		} );
		this.selectChan( "main" );
	}
	change( obj ) {
		this.gsdata.change( obj );
		gsuiReorder.listReorder( this._pchans, obj );
	}
	selectChan( id ) {
		if ( id in this.gsdata.data ) {
			const chan = this._chans[ id ].root,
				pchan = this._chans[ this._chanSelected ];

			pchan && pchan.root.classList.remove( "gsuiMixer-selected" );
			chan.classList.add( "gsuiMixer-selected" );
			this._chanSelected = id;
			this._updateChanConnections();
			this.onselectChan( id );
		}
	}

	// private:
	// .........................................................................
	_getNextChan( el, dir ) {
		const sibling = el[ dir ];

		return sibling && "id" in sibling.dataset ? sibling : null;
	}
	_updateChanConnections() {
		const selId = this._chanSelected;

		if ( selId ) {
			const chanDest = this.gsdata.data[ selId ].dest,
				html = this._chans[ selId ];
			let bOnce = false;

			Object.entries( this.gsdata.data ).forEach( ( [ id, chan ] ) => {
				const html = this._chans[ id ],
					a = id === chanDest,
					b = chan.dest === selId;

				html.connectA.dataset.icon = a ? "caret-up" : "";
				html.connectB.dataset.icon = b ? "caret-down" : "";
				bOnce = bOnce || b;
			} );
			html.connectA.dataset.icon = selId !== "main" ? "caret-down" : "";
			html.connectB.dataset.icon = bOnce ? "caret-up" : "";
		}
	}
	_addChan( id, chan ) {
		const root = gsuiMixer.channelTemplate.cloneNode( true ),
			qs = n => root.querySelector( `.gsuiMixerChannel-${ n }` ),
			pan = new gsuiSlider(),
			gain = new gsuiSlider(),
			canvas = qs( "analyser" ),
			html = { root, pan, gain,
				name: qs( "name" ),
				connectA: qs( "connectA" ),
				connectB: qs( "connectB" ),
				analyser: new gsuiAnalyser(),
			};

		this._chans[ id ] = html;
		root.dataset.id = id;
		qs( "pan" ).append( pan.rootElement );
		qs( "gain" ).append( gain.rootElement );
		html.analyser.setCanvas( canvas );
		pan.options( { min: -1, max: 1, step: .001, type: "circular", strokeWidth: 3 } );
		gain.options( { min: 0, max: 1, step: .001, type: "linear-y" } );
		pan.oninput = this.oninput.bind( null, id, "pan" );
		gain.oninput = this.oninput.bind( null, id, "gain" );
		pan.onchange = this._call.bind( this, "updateChanProp", id, "pan" );
		gain.onchange = this._call.bind( this, "updateChanProp", id, "gain" );
		canvas.onclick =
		qs( "nameWrap" ).onclick = this.selectChan.bind( this, id );
		qs( "toggle" ).onclick = this._call.bind( null, "toggleChan", id );
		qs( "delete" ).onclick = this._call.bind( null, "removeChan", id );
		qs( "connect" ).onclick = () => this._call( "redirectChan", this._chanSelected, id );
		( id === "main" ? this._pmain : this._pchans ).append( root );
		if ( this._attached ) {
			pan.attached();
			gain.attached();
			html.analyser.setResolution( this._analyserH );
		}
		this.onaddChan( id, chan );
		this._updateChanConnections();
	}
	_removeChan( id ) {
		const el = this._chans[ id ].root;

		if ( id === this._chanSelected ) {
			const next = this._getNextChan( el, "nextElementSibling" );

			if ( next ) {
				this.selectChan( next.dataset.id );
			} else {
				const prev = this._getNextChan( el, "previousElementSibling" );

				this.selectChan( prev ? prev.dataset.id : "main" );
			}
		}
		this.ondeleteChan( id );
		delete this._chans[ id ];
		el.remove();
	}
}

gsuiMixer.template = document.querySelector( "#gsuiMixer-template" );
gsuiMixer.template.remove();
gsuiMixer.template.removeAttribute( "id" );

gsuiMixer.channelTemplate = document.querySelector( "#gsuiMixerChannel-template" );
gsuiMixer.channelTemplate.remove();
gsuiMixer.channelTemplate.removeAttribute( "id" );
<commit_msg>gsuiMixer: dragndrop.onchange, send a message with the diff object<commit_after>"use strict";

class gsuiMixer {
	constructor() {
		const root = gsuiMixer.template.cloneNode( true ),
			addBtn = root.querySelector( ".gsuiMixer-addChan" ),
			dnd = new gsuiReorder(),
			gsdata = new gsdataMixer( {
				addChan: this._addChan.bind( this ),
				removeChan: this._removeChan.bind( this ),
				redirectChan: this._updateChanConnections.bind( this ),
				toggleChan: ( id, b ) => this._chans[ id ].root.classList.toggle( "gsuiMixerChannel-muted", !b ),
				changePanChan: ( id, val ) => this._chans[ id ].pan.setValue( val ),
				changeGainChan: ( id, val ) => this._chans[ id ].gain.setValue( val ),
				changeOrderChan: ( id, val ) => this._chans[ id ].root.dataset.order = val,
				renameChan: ( id, val ) => {
					this._chans[ id ].name.textContent = val;
					this.onupdateChan( id, "name", val );
				},
			} );

		this.rootElement = root;
		this.gsdata = gsdata;
		this._call = ( ...args ) => this.gsdata.callAction( this.onchange, ...args );
		this._pmain = root.querySelector( ".gsuiMixer-panMain" );
		this._pchans = root.querySelector( ".gsuiMixer-panChannels" );
		this._chans = {};
		this._chanSelected = null;
		this.oninput =
		this.onchange =
		this.onaddChan =
		this.ondeleteChan =
		this.onupdateChan =
		this.onselectChan = gsdata.noop;
		this._analyserH = 10;
		this._attached = false;
		Object.seal( this );

		addBtn.onclick = this._call.bind( null, "addChan" );
		dnd.setDirection( "h" );
		dnd.setRootElement( root );
		dnd.setSelectors( {
			item: ".gsuiMixerChannel",
			handle: ".gsuiMixerChannel-grip",
			parent: ".gsuiMixer-panChannels",
		} );
		dnd.onchange = elChan => {
			const obj = gsuiReorder.listComputeOrderChange( this._pchans, {} ),
				chanName = this.gsdata.data[ elChan.dataset.id ].name;

			this.onchange( obj, [ "mixer", "reorderChan", chanName ] );
		};
		this.empty();
		this.selectChan( "main" );
	}

	attached() {
		const pan = this._pchans;

		this._attached = true;
		pan.style.bottom = `${ pan.clientHeight - pan.offsetHeight }px`;
		Object.entries( this.gsdata.data ).forEach( ( [ id, obj ] ) => {
			const html = this._chans[ id ];

			html.pan.attached();
			html.gain.attached();
			this.onaddChan( id, obj );
		} );
		this.resized();
	}
	resized() {
		const chans = Object.values( this._chans ),
			h = chans[ 0 ].analyser.rootElement.getBoundingClientRect().height;

		this._analyserH = h;
		chans.forEach( html => html.analyser.setResolution( h ) );
	}
	updateAudioData( id, ldata, rdata ) {
		this._chans[ id ].analyser.draw( ldata, rdata );
	}
	empty() {
		this.gsdata.clear();
		this.gsdata.change( {
			main: {
				toggle: true,
				name: "main",
				gain: 1,
				pan: 0,
			},
		} );
		this.selectChan( "main" );
	}
	change( obj ) {
		this.gsdata.change( obj );
		gsuiReorder.listReorder( this._pchans, obj );
	}
	selectChan( id ) {
		if ( id in this.gsdata.data ) {
			const chan = this._chans[ id ].root,
				pchan = this._chans[ this._chanSelected ];

			pchan && pchan.root.classList.remove( "gsuiMixer-selected" );
			chan.classList.add( "gsuiMixer-selected" );
			this._chanSelected = id;
			this._updateChanConnections();
			this.onselectChan( id );
		}
	}

	// private:
	// .........................................................................
	_getNextChan( el, dir ) {
		const sibling = el[ dir ];

		return sibling && "id" in sibling.dataset ? sibling : null;
	}
	_updateChanConnections() {
		const selId = this._chanSelected;

		if ( selId ) {
			const chanDest = this.gsdata.data[ selId ].dest,
				html = this._chans[ selId ];
			let bOnce = false;

			Object.entries( this.gsdata.data ).forEach( ( [ id, chan ] ) => {
				const html = this._chans[ id ],
					a = id === chanDest,
					b = chan.dest === selId;

				html.connectA.dataset.icon = a ? "caret-up" : "";
				html.connectB.dataset.icon = b ? "caret-down" : "";
				bOnce = bOnce || b;
			} );
			html.connectA.dataset.icon = selId !== "main" ? "caret-down" : "";
			html.connectB.dataset.icon = bOnce ? "caret-up" : "";
		}
	}
	_addChan( id, chan ) {
		const root = gsuiMixer.channelTemplate.cloneNode( true ),
			qs = n => root.querySelector( `.gsuiMixerChannel-${ n }` ),
			pan = new gsuiSlider(),
			gain = new gsuiSlider(),
			canvas = qs( "analyser" ),
			html = { root, pan, gain,
				name: qs( "name" ),
				connectA: qs( "connectA" ),
				connectB: qs( "connectB" ),
				analyser: new gsuiAnalyser(),
			};

		this._chans[ id ] = html;
		root.dataset.id = id;
		qs( "pan" ).append( pan.rootElement );
		qs( "gain" ).append( gain.rootElement );
		html.analyser.setCanvas( canvas );
		pan.options( { min: -1, max: 1, step: .001, type: "circular", strokeWidth: 3 } );
		gain.options( { min: 0, max: 1, step: .001, type: "linear-y" } );
		pan.oninput = this.oninput.bind( null, id, "pan" );
		gain.oninput = this.oninput.bind( null, id, "gain" );
		pan.onchange = this._call.bind( this, "updateChanProp", id, "pan" );
		gain.onchange = this._call.bind( this, "updateChanProp", id, "gain" );
		canvas.onclick =
		qs( "nameWrap" ).onclick = this.selectChan.bind( this, id );
		qs( "toggle" ).onclick = this._call.bind( null, "toggleChan", id );
		qs( "delete" ).onclick = this._call.bind( null, "removeChan", id );
		qs( "connect" ).onclick = () => this._call( "redirectChan", this._chanSelected, id );
		( id === "main" ? this._pmain : this._pchans ).append( root );
		if ( this._attached ) {
			pan.attached();
			gain.attached();
			html.analyser.setResolution( this._analyserH );
		}
		this.onaddChan( id, chan );
		this._updateChanConnections();
	}
	_removeChan( id ) {
		const el = this._chans[ id ].root;

		if ( id === this._chanSelected ) {
			const next = this._getNextChan( el, "nextElementSibling" );

			if ( next ) {
				this.selectChan( next.dataset.id );
			} else {
				const prev = this._getNextChan( el, "previousElementSibling" );

				this.selectChan( prev ? prev.dataset.id : "main" );
			}
		}
		this.ondeleteChan( id );
		delete this._chans[ id ];
		el.remove();
	}
}

gsuiMixer.template = document.querySelector( "#gsuiMixer-template" );
gsuiMixer.template.remove();
gsuiMixer.template.removeAttribute( "id" );

gsuiMixer.channelTemplate = document.querySelector( "#gsuiMixerChannel-template" );
gsuiMixer.channelTemplate.remove();
gsuiMixer.channelTemplate.removeAttribute( "id" );
<|endoftext|>


<commit_before>module.exports = {
  title: 'Science Museum Group Collection',
  titlePage: 'Science Museum Group Collection',
  strap: 'Search our collection',
  intro:
    'Explore over 350,000 objects and archives from the Science Museum, Science and Industry Museum, National Science and Media Museum, National Railway Museum and Locomotion.',
  smg__description:
    'As the world’s leading group of science museums, we share our unparalleled collection spanning science, technology, engineering, mathematics and medicine with over five million visitors each year.',
  headerImages: [
    '_D819175-ret.jpg',
    '1970-0025_0007.jpg',
    '_D818504.jpg',
    'textile-sign.jpg',
    'switches.jpg',
    '_D817130-Edit2.jpg',
    '1878-0005_0004_A163_0002.jpg',
    'cd0634_030_100924_1988_242.jpg'
  ],
  collectionGroup: [
    {
      title: 'See what’s on display',
      collection: [
        {
          type: 'collection',
          title: 'Science Museum',
          description:
            'The home of human ingenuity, exploring stories of scientific achievement',
          figure: '/assets/img/home/collections/sciencemuseum.jpg',
          columns: 'small-6 medium-3',
          link: '/search/museum/science-museum'
        },
        {
          type: 'collection',
          title: 'Science and Industry Museum',
          figure: '/assets/img/home/collections/msi.jpg',
          description:
            'The story of Manchester’s scientific and industrial past, present and future',
          columns: 'small-6 medium-3',
          link: '/search/museum/museum-of-science-and-industry'
        },
        {
          type: 'collection',
          title: 'National Science and Media Museum',
          figure: '/assets/img/home/collections/nmm.jpg',
          description:
            'The science and culture of light and sound technologies, and their impact on our lives',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-media-museum'
        },
        {
          type: 'collection',
          title: 'National Railway Museum',
          figure: '/assets/img/home/collections/nrm.jpg',
          description:
            'Home to the largest collection of railway objects in the world',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-railway-museum'
        }
      ]
    }
  ],
  collectionThemes: [
    {
      title: 'Themes from the collection',
      theme: [
        {
          title: 'Medicine',
          description: 'Explore global medical developments and advances over time',
          figure: '/assets/img/home/collections/medicine.jpg',
          links: [
            {
              title: 'Pharmacology',
              link: '/search/categories/materia-medica-&-pharmacology'
            },
            {
              title: 'Surgery',
              link: '/search/categories/surgery'
            },
            {
              title: 'Asian medicine',
              link: '/search/categories/asian-medicine'
            },
            {
              title: 'Therapeutics',
              link: '/search/categories/therapeutics'
            },
            {
              title: 'Orthopaedics',
              link: '/search/categories/orthopaedics'
            }
          ]
        },
        {
          title: 'Railways',
          description: 'The development of railways from goods transport to commuter travel',
          figure: '/assets/img/home/collections/railway.jpg',
          links: [
            {
              title: 'Posters and handbills',
              link: '/search/categories/railway-posters,-notices-&-handbills'
            },
            {
              title: 'Passenger comforts',
              link: '/search/categories/passenger-comforts'
            },
            {
              title: 'Railway photographic collection',
              link: '/search/categories/photographic-collections-(railway)/images'
            },
            {
              title: 'Infrastructure',
              link: '/search/categories/railway-infrastructure'
            }
          ]
        },
        {
          title: 'Art',
          description: 'From innovative processes to depicting our connection with science',
          figure: '/assets/img/home/collections/art-theme.jpg',
          links: [
            {
              title: 'Artworks',
              link: '/search/categories/art'
            },
            {
              title: 'Posters',
              link: '/search/objects/object_type/poster'
            },
            {
              title: 'Photographs',
              link:
                '/search/categories/photographs'
            },
            {
              title: 'Daily Herald photographic archive',
              link: '/objects/co205344/the-photographic-library-of-the-daily-herald-newspaper-1911-1965'
            }
          ]
        },
        {
          title: 'Industrial Revolution',
          description: 'The machines and industries that powered the Industrial Revolution',
          figure: '/assets/img/home/collections/industrial-revolution.jpg',
          links: [
            {
              title: 'Textile industry',
              link: '/search/categories/textile-industry'
            },
            {
              title: 'Textiles machinery',
              link: '/search/categories/textiles-machinery'
            },
            {
              title: 'Motive power',
              link: '/search/categories/motive-power'
            },
            {
              title: 'Coal mining',
              link: '/search/categories/coal-mining'
            }
          ]
        }
      ]
    }
  ],
  collectionCarousel: [
    {
      title: 'Highlights',
      collection: [
        {
          type: 'collection',
          title: 'Never been seen',
          figure: '/assets/img/home/collections/never-been-seen.jpg',
          columns: '',
          link: 'https://thesciencemuseum.github.io/never-been-seen/'
        },
        {
          type: 'collection',
          title: 'What the Machine Saw',
          figure: '/assets/img/home/collections/what-the-machine-saw.jpg',
          columns: '',
          link: 'https://johnstack.github.io/what-the-machine-saw/'
        },
        {
          type: 'collection',
          title: 'Random object generator',
          figure: '/assets/img/home/collections/random-object-generator.jpg',
          columns: '',
          link: 'https://collection.sciencemuseumgroup.org.uk/search/slideshow'
        },
        {
          type: 'collection',
          title: 'Rotational photography',
          figure: '/assets/img/home/collections/rotational.jpg',
          columns: '',
          link: '/search/objects/rotational'
        },
        {
          type: 'collection',
          title: 'The Babbage Papers',
          figure: '/assets/img/home/collections/babbage.jpg',
          columns: '',
          link: '/documents/aa110000003/the-babbage-papers'
        },
        {
          type: 'collection',
          title: 'Toys & games',
          figure: '/assets/img/home/collections/toys.jpg',
          columns: '',
          link: '/search?q=toy'
        },
        {
          type: 'collection',
          title: 'Information Age',
          figure: '/assets/img/home/collections/informationage.jpg',
          columns: '',
          link: '/search/gallery/information-age-gallery%3A-web'
        },
        {
          type: 'collection',
          title: 'Mathematics',
          figure: '/assets/img/home/collections/mathematics-highlights.jpg',
          columns: '',
          link: '/search/categories/mathematics'
        },
        {
          type: 'collection',
          title: 'Cinematography',
          figure: '/assets/img/home/collections/cinematography.jpg',
          columns: '',
          link: '/search/categories/cinematography/images'
        },
        {
          type: 'collection',
          title: 'Psychometric Tests',
          figure: '/assets/img/home/collections/psychometric-tests.jpg',
          columns: '',
          link: '/search/objects/object_type/psychometric-test'
        },
        {
          type: 'collection',
          title: 'Television',
          figure: '/assets/img/home/collections/television.jpg',
          columns: '',
          link: '/search/categories/television'
        },
        {
          type: 'collection',
          title: 'Radiomedicine',
          figure: '/assets/img/home/collections/radiomedicine.jpg',
          columns: '',
          link: '/search/categories/radiomedicine'
        },
        {
          type: 'collection',
          title: 'Creative industries',
          figure: '/assets/img/home/collections/creative-industries.jpg',
          columns: '',
          link: '/search/categories/creative-industries'
        },
        {
          type: 'collection',
          title: 'Astronomy',
          figure: '/assets/img/home/collections/astronomy.jpg',
          columns: '',
          link: '/search/categories/astronomy'
        },
        {
          type: 'collection',
          title: 'Computing and Data processing',
          figure: '/assets/img/home/collections/computing.jpg',
          columns: '',
          link: '/search/categories/computing-&-data-processing'
        },
        {
          type: 'collection',
          title: 'Time measurement',
          figure: '/assets/img/home/collections/time-measurement.jpg',
          columns: '',
          link: '/search/categories/time-measurement'
        },
        {
          type: 'collection',
          title: 'Radio communication',
          figure: '/assets/img/home/collections/radio-communication.jpg',
          columns: '',
          link: '/search/categories/radio-communication'
        }
      ]
    }
  ]
};
<commit_msg>uppercase Never Been Seen promo box text<commit_after>module.exports = {
  title: 'Science Museum Group Collection',
  titlePage: 'Science Museum Group Collection',
  strap: 'Search our collection',
  intro:
    'Explore over 350,000 objects and archives from the Science Museum, Science and Industry Museum, National Science and Media Museum, National Railway Museum and Locomotion.',
  smg__description:
    'As the world’s leading group of science museums, we share our unparalleled collection spanning science, technology, engineering, mathematics and medicine with over five million visitors each year.',
  headerImages: [
    '_D819175-ret.jpg',
    '1970-0025_0007.jpg',
    '_D818504.jpg',
    'textile-sign.jpg',
    'switches.jpg',
    '_D817130-Edit2.jpg',
    '1878-0005_0004_A163_0002.jpg',
    'cd0634_030_100924_1988_242.jpg'
  ],
  collectionGroup: [
    {
      title: 'See what’s on display',
      collection: [
        {
          type: 'collection',
          title: 'Science Museum',
          description:
            'The home of human ingenuity, exploring stories of scientific achievement',
          figure: '/assets/img/home/collections/sciencemuseum.jpg',
          columns: 'small-6 medium-3',
          link: '/search/museum/science-museum'
        },
        {
          type: 'collection',
          title: 'Science and Industry Museum',
          figure: '/assets/img/home/collections/msi.jpg',
          description:
            'The story of Manchester’s scientific and industrial past, present and future',
          columns: 'small-6 medium-3',
          link: '/search/museum/museum-of-science-and-industry'
        },
        {
          type: 'collection',
          title: 'National Science and Media Museum',
          figure: '/assets/img/home/collections/nmm.jpg',
          description:
            'The science and culture of light and sound technologies, and their impact on our lives',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-media-museum'
        },
        {
          type: 'collection',
          title: 'National Railway Museum',
          figure: '/assets/img/home/collections/nrm.jpg',
          description:
            'Home to the largest collection of railway objects in the world',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-railway-museum'
        }
      ]
    }
  ],
  collectionThemes: [
    {
      title: 'Themes from the collection',
      theme: [
        {
          title: 'Medicine',
          description: 'Explore global medical developments and advances over time',
          figure: '/assets/img/home/collections/medicine.jpg',
          links: [
            {
              title: 'Pharmacology',
              link: '/search/categories/materia-medica-&-pharmacology'
            },
            {
              title: 'Surgery',
              link: '/search/categories/surgery'
            },
            {
              title: 'Asian medicine',
              link: '/search/categories/asian-medicine'
            },
            {
              title: 'Therapeutics',
              link: '/search/categories/therapeutics'
            },
            {
              title: 'Orthopaedics',
              link: '/search/categories/orthopaedics'
            }
          ]
        },
        {
          title: 'Railways',
          description: 'The development of railways from goods transport to commuter travel',
          figure: '/assets/img/home/collections/railway.jpg',
          links: [
            {
              title: 'Posters and handbills',
              link: '/search/categories/railway-posters,-notices-&-handbills'
            },
            {
              title: 'Passenger comforts',
              link: '/search/categories/passenger-comforts'
            },
            {
              title: 'Railway photographic collection',
              link: '/search/categories/photographic-collections-(railway)/images'
            },
            {
              title: 'Infrastructure',
              link: '/search/categories/railway-infrastructure'
            }
          ]
        },
        {
          title: 'Art',
          description: 'From innovative processes to depicting our connection with science',
          figure: '/assets/img/home/collections/art-theme.jpg',
          links: [
            {
              title: 'Artworks',
              link: '/search/categories/art'
            },
            {
              title: 'Posters',
              link: '/search/objects/object_type/poster'
            },
            {
              title: 'Photographs',
              link:
                '/search/categories/photographs'
            },
            {
              title: 'Daily Herald photographic archive',
              link: '/objects/co205344/the-photographic-library-of-the-daily-herald-newspaper-1911-1965'
            }
          ]
        },
        {
          title: 'Industrial Revolution',
          description: 'The machines and industries that powered the Industrial Revolution',
          figure: '/assets/img/home/collections/industrial-revolution.jpg',
          links: [
            {
              title: 'Textile industry',
              link: '/search/categories/textile-industry'
            },
            {
              title: 'Textiles machinery',
              link: '/search/categories/textiles-machinery'
            },
            {
              title: 'Motive power',
              link: '/search/categories/motive-power'
            },
            {
              title: 'Coal mining',
              link: '/search/categories/coal-mining'
            }
          ]
        }
      ]
    }
  ],
  collectionCarousel: [
    {
      title: 'Highlights',
      collection: [
        {
          type: 'collection',
          title: 'Never Been Seen',
          figure: '/assets/img/home/collections/never-been-seen.jpg',
          columns: '',
          link: 'https://thesciencemuseum.github.io/never-been-seen/'
        },
        {
          type: 'collection',
          title: 'What the Machine Saw',
          figure: '/assets/img/home/collections/what-the-machine-saw.jpg',
          columns: '',
          link: 'https://johnstack.github.io/what-the-machine-saw/'
        },
        {
          type: 'collection',
          title: 'Random object generator',
          figure: '/assets/img/home/collections/random-object-generator.jpg',
          columns: '',
          link: 'https://collection.sciencemuseumgroup.org.uk/search/slideshow'
        },
        {
          type: 'collection',
          title: 'Rotational photography',
          figure: '/assets/img/home/collections/rotational.jpg',
          columns: '',
          link: '/search/objects/rotational'
        },
        {
          type: 'collection',
          title: 'The Babbage Papers',
          figure: '/assets/img/home/collections/babbage.jpg',
          columns: '',
          link: '/documents/aa110000003/the-babbage-papers'
        },
        {
          type: 'collection',
          title: 'Toys & games',
          figure: '/assets/img/home/collections/toys.jpg',
          columns: '',
          link: '/search?q=toy'
        },
        {
          type: 'collection',
          title: 'Information Age',
          figure: '/assets/img/home/collections/informationage.jpg',
          columns: '',
          link: '/search/gallery/information-age-gallery%3A-web'
        },
        {
          type: 'collection',
          title: 'Mathematics',
          figure: '/assets/img/home/collections/mathematics-highlights.jpg',
          columns: '',
          link: '/search/categories/mathematics'
        },
        {
          type: 'collection',
          title: 'Cinematography',
          figure: '/assets/img/home/collections/cinematography.jpg',
          columns: '',
          link: '/search/categories/cinematography/images'
        },
        {
          type: 'collection',
          title: 'Psychometric Tests',
          figure: '/assets/img/home/collections/psychometric-tests.jpg',
          columns: '',
          link: '/search/objects/object_type/psychometric-test'
        },
        {
          type: 'collection',
          title: 'Television',
          figure: '/assets/img/home/collections/television.jpg',
          columns: '',
          link: '/search/categories/television'
        },
        {
          type: 'collection',
          title: 'Radiomedicine',
          figure: '/assets/img/home/collections/radiomedicine.jpg',
          columns: '',
          link: '/search/categories/radiomedicine'
        },
        {
          type: 'collection',
          title: 'Creative industries',
          figure: '/assets/img/home/collections/creative-industries.jpg',
          columns: '',
          link: '/search/categories/creative-industries'
        },
        {
          type: 'collection',
          title: 'Astronomy',
          figure: '/assets/img/home/collections/astronomy.jpg',
          columns: '',
          link: '/search/categories/astronomy'
        },
        {
          type: 'collection',
          title: 'Computing and Data processing',
          figure: '/assets/img/home/collections/computing.jpg',
          columns: '',
          link: '/search/categories/computing-&-data-processing'
        },
        {
          type: 'collection',
          title: 'Time measurement',
          figure: '/assets/img/home/collections/time-measurement.jpg',
          columns: '',
          link: '/search/categories/time-measurement'
        },
        {
          type: 'collection',
          title: 'Radio communication',
          figure: '/assets/img/home/collections/radio-communication.jpg',
          columns: '',
          link: '/search/categories/radio-communication'
        }
      ]
    }
  ]
};
<|endoftext|>


<commit_before>module.exports = {
  title: 'Science Museum Group Collection',
  titlePage: 'Science Museum Group Collection',
  strap: 'Search our collection',
  intro:
    'Explore over 250,000 objects and archives from the Science Museum, Science and Industry Museum, National Science and Media Museum and National Railway Museum.',
  smg__description:
    'As the world’s leading group of science museums, we share our unparalleled collection spanning science, technology, engineering, mathematics and medicine with over five million visitors each year.',
  headerImages: [
    '_D819175-ret.jpg',
    '1970-0025_0007.jpg',
    '_D818504.jpg',
    'textile-sign.jpg',
    'switches.jpg',
    '_D817130-Edit2.jpg',
    '1878-0005_0004_A163_0002.jpg',
    'cd0634_030_100924_1988_242.jpg'
  ],
  collectionGroup: [
    {
      title: 'See what’s on display',
      collection: [
        {
          type: 'collection',
          title: 'Science Museum',
          description:
            'The home of human ingenuity, exploring stories of scientific achievement',
          figure: '/assets/img/home/collections/sciencemuseum.jpg',
          columns: 'small-6 medium-3',
          link: '/search/museum/science-museum'
        },
        {
          type: 'collection',
          title: 'Science and Industry Museum',
          figure: '/assets/img/home/collections/msi.jpg',
          description:
            'The story of Manchester’s scientific and industrial past, present and future',
          columns: 'small-6 medium-3',
          link: '/search/museum/museum-of-science-and-industry'
        },
        {
          type: 'collection',
          title: 'National Science and Media Museum',
          figure: '/assets/img/home/collections/nmm.jpg',
          description:
            'The science and culture of light and sound technologies, and their impact on our lives',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-media-museum'
        },
        {
          type: 'collection',
          title: 'National Railway Museum',
          figure: '/assets/img/home/collections/nrm.jpg',
          description:
            'Home to the largest collection of railway objects in the world',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-railway-museum'
        }
      ]
    }
  ],
  collectionThemes: [
    {
      title: 'Themes from the collection',
      theme: [
        {
          title: 'Medicine',
          description: 'Explore global medical advances over time',
          figure: '/assets/img/home/collections/medicine.jpg',
          links: [
            {
              title: 'Pharmacology',
              link: '/search/categories/materia-medica-&-pharmacology'
            },
            {
              title: 'Surgery',
              link: '/search/categories/surgery'
            },
            {
              title: 'Oriental medicine',
              link: '/search/categories/oriental-medicine'
            },
            {
              title: 'Therapeutics',
              link: '/search/categories/therapeutics'
            },
            {
              title: 'Orthopaedics',
              link: '/search/categories/orthopaedics'
            }
          ]
        },
        {
          title: 'Railways',
          description: 'The changing use of the railways from engineering advances to seaside posters',
          figure: '/assets/img/home/collections/railway.jpg',
          links: [
            {
              title: 'Posters and handbills',
              link: '/search/categories/railway-posters,-notices-&-handbills'
            },
            {
              title: 'Passenger comforts',
              link: '/search/categories/passenger-comforts'
            },
            {
              title: 'Railway photographic collection',
              link: '/search/categories/photographic-collections-(railway)/images'
            },
            {
              title: 'Infrastructure',
              link: '/search/categories/railway-infrastructure'
            }
          ]
        },
        {
          title: 'Art',
          description: 'From innovative processes to depicting our connection with science',
          figure: '/assets/img/home/collections/art-theme.jpg',
          links: [
            {
              title: 'Artworks',
              link: '/search/categories/art'
            },
            {
              title: 'Posters',
              link: '/search/objects/object_type/poster'
            },
            {
              title: 'Photographs',
              link:
                '/search/categories/photographs'
            },
            {
              title: 'Daily Herald photographic archive',
              link: '/objects/co205344/the-photographic-library-of-the-daily-herald-newspaper-1911-1965'
            }
          ]
        },
        {
          title: 'Industrial Revolution',
          description: 'The machines and industries that powered the Industrial Revolution',
          figure: '/assets/img/home/collections/industrial-revolution.jpg',
          links: [
            {
              title: 'Textile industry',
              link: '/search/categories/textile-industry'
            },
            {
              title: 'Textiles machinery',
              link: '/search/categories/textiles-machinery'
            },
            {
              title: 'Motive power',
              link: '/search/categories/motive-power'
            },
            {
              title: 'Coal mining',
              link: '/search/categories/coal-mining'
            }
          ]
        }
      ]
    }
  ],
  collectionCarousel: [
    {
      title: 'Highlights',
      collection: [
        {
          type: 'collection',
          title: 'The Babbage Papers',
          description: 'The drawings and papers of mathematician and computer pioneer Charles Babbage',
          figure: '/assets/img/home/collections/babbage.jpg',
          columns: '',
          link: '/documents/aa110000003/the-babbage-papers'
        },
        {
          type: 'collection',
          title: 'Toys & games',
          figure: '/assets/img/home/collections/toys.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search?q=toy'
        },
        {
          type: 'collection',
          title: 'Information Age',
          figure: '/assets/img/home/collections/informationage.jpg',
          description: 'Search across a snapshot of objects representing the development of the internet',
          columns: '',
          link: '/search/gallery/information-age-gallery%3A-web'
        },
        {
          type: 'collection',
          title: 'Mathematics',
          figure: '/assets/img/home/collections/mathematics-highlights.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/mathematics'
        },
        {
          type: 'collection',
          title: 'Cinematography',
          figure: '/assets/img/home/collections/cinematography.jpg',
          description: 'Objects representing the history of cinema: from magic lanterns to digital technologies',
          columns: '',
          link: '/search/categories/cinematography/images'
        },
        {
          type: 'collection',
          title: 'Smoking',
          figure: '/assets/img/home/collections/smoking.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/smoking'
        },
        {
          type: 'collection',
          title: 'Television',
          figure: '/assets/img/home/collections/television.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/television'
        },
        {
          type: 'collection',
          title: 'Radiomedicine',
          figure: '/assets/img/home/collections/radiomedicine.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/radiomedicine'
        },
        {
          type: 'collection',
          title: 'Creative industries',
          figure: '/assets/img/home/collections/creative-industries.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/creative-industries'
        },
        {
          type: 'collection',
          title: 'Astronomy',
          figure: '/assets/img/home/collections/astronomy.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/astronomy'
        },
        {
          type: 'collection',
          title: 'Computing and Data processing',
          figure: '/assets/img/home/collections/computing.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/computing-&-data-processing'
        },
        {
          type: 'collection',
          title: 'Time measurement',
          figure: '/assets/img/home/collections/time-measurement.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/time-measurement'
        },
        {
          type: 'collection',
          title: 'Radio communication',
          figure: '/assets/img/home/collections/radio-communication.jpg',
          description: 'Intro paragraph',
          columns: '',
          link: '/search/categories/radio-communication'
        }
      ]
    }
  ]
};
<commit_msg>Tidy up highlights text<commit_after>module.exports = {
  title: 'Science Museum Group Collection',
  titlePage: 'Science Museum Group Collection',
  strap: 'Search our collection',
  intro:
    'Explore over 250,000 objects and archives from the Science Museum, Science and Industry Museum, National Science and Media Museum and National Railway Museum.',
  smg__description:
    'As the world’s leading group of science museums, we share our unparalleled collection spanning science, technology, engineering, mathematics and medicine with over five million visitors each year.',
  headerImages: [
    '_D819175-ret.jpg',
    '1970-0025_0007.jpg',
    '_D818504.jpg',
    'textile-sign.jpg',
    'switches.jpg',
    '_D817130-Edit2.jpg',
    '1878-0005_0004_A163_0002.jpg',
    'cd0634_030_100924_1988_242.jpg'
  ],
  collectionGroup: [
    {
      title: 'See what’s on display',
      collection: [
        {
          type: 'collection',
          title: 'Science Museum',
          description:
            'The home of human ingenuity, exploring stories of scientific achievement',
          figure: '/assets/img/home/collections/sciencemuseum.jpg',
          columns: 'small-6 medium-3',
          link: '/search/museum/science-museum'
        },
        {
          type: 'collection',
          title: 'Science and Industry Museum',
          figure: '/assets/img/home/collections/msi.jpg',
          description:
            'The story of Manchester’s scientific and industrial past, present and future',
          columns: 'small-6 medium-3',
          link: '/search/museum/museum-of-science-and-industry'
        },
        {
          type: 'collection',
          title: 'National Science and Media Museum',
          figure: '/assets/img/home/collections/nmm.jpg',
          description:
            'The science and culture of light and sound technologies, and their impact on our lives',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-media-museum'
        },
        {
          type: 'collection',
          title: 'National Railway Museum',
          figure: '/assets/img/home/collections/nrm.jpg',
          description:
            'Home to the largest collection of railway objects in the world',
          columns: 'small-6 medium-3',
          link: '/search/museum/national-railway-museum'
        }
      ]
    }
  ],
  collectionThemes: [
    {
      title: 'Themes from the collection',
      theme: [
        {
          title: 'Medicine',
          description: 'Explore global medical advances over time',
          figure: '/assets/img/home/collections/medicine.jpg',
          links: [
            {
              title: 'Pharmacology',
              link: '/search/categories/materia-medica-&-pharmacology'
            },
            {
              title: 'Surgery',
              link: '/search/categories/surgery'
            },
            {
              title: 'Oriental medicine',
              link: '/search/categories/oriental-medicine'
            },
            {
              title: 'Therapeutics',
              link: '/search/categories/therapeutics'
            },
            {
              title: 'Orthopaedics',
              link: '/search/categories/orthopaedics'
            }
          ]
        },
        {
          title: 'Railways',
          description: 'The changing use of the railways from engineering advances to seaside posters',
          figure: '/assets/img/home/collections/railway.jpg',
          links: [
            {
              title: 'Posters and handbills',
              link: '/search/categories/railway-posters,-notices-&-handbills'
            },
            {
              title: 'Passenger comforts',
              link: '/search/categories/passenger-comforts'
            },
            {
              title: 'Railway photographic collection',
              link: '/search/categories/photographic-collections-(railway)/images'
            },
            {
              title: 'Infrastructure',
              link: '/search/categories/railway-infrastructure'
            }
          ]
        },
        {
          title: 'Art',
          description: 'From innovative processes to depicting our connection with science',
          figure: '/assets/img/home/collections/art-theme.jpg',
          links: [
            {
              title: 'Artworks',
              link: '/search/categories/art'
            },
            {
              title: 'Posters',
              link: '/search/objects/object_type/poster'
            },
            {
              title: 'Photographs',
              link:
                '/search/categories/photographs'
            },
            {
              title: 'Daily Herald photographic archive',
              link: '/objects/co205344/the-photographic-library-of-the-daily-herald-newspaper-1911-1965'
            }
          ]
        },
        {
          title: 'Industrial Revolution',
          description: 'The machines and industries that powered the Industrial Revolution',
          figure: '/assets/img/home/collections/industrial-revolution.jpg',
          links: [
            {
              title: 'Textile industry',
              link: '/search/categories/textile-industry'
            },
            {
              title: 'Textiles machinery',
              link: '/search/categories/textiles-machinery'
            },
            {
              title: 'Motive power',
              link: '/search/categories/motive-power'
            },
            {
              title: 'Coal mining',
              link: '/search/categories/coal-mining'
            }
          ]
        }
      ]
    }
  ],
  collectionCarousel: [
    {
      title: 'Highlights',
      collection: [
        {
          type: 'collection',
          title: 'The Babbage Papers',
          description: 'The drawings and papers of mathematician and computer pioneer Charles Babbage',
          figure: '/assets/img/home/collections/babbage.jpg',
          columns: '',
          link: '/documents/aa110000003/the-babbage-papers'
        },
        {
          type: 'collection',
          title: 'Toys & games',
          figure: '/assets/img/home/collections/toys.jpg',
          columns: '',
          link: '/search?q=toy'
        },
        {
          type: 'collection',
          title: 'Information Age',
          figure: '/assets/img/home/collections/informationage.jpg',
          description: 'Search across a snapshot of objects representing the development of the internet',
          columns: '',
          link: '/search/gallery/information-age-gallery%3A-web'
        },
        {
          type: 'collection',
          title: 'Mathematics',
          figure: '/assets/img/home/collections/mathematics-highlights.jpg',
          columns: '',
          link: '/search/categories/mathematics'
        },
        {
          type: 'collection',
          title: 'Cinematography',
          figure: '/assets/img/home/collections/cinematography.jpg',
          description: 'Objects representing the history of cinema: from magic lanterns to digital technologies',
          columns: '',
          link: '/search/categories/cinematography/images'
        },
        {
          type: 'collection',
          title: 'Smoking',
          figure: '/assets/img/home/collections/smoking.jpg',
          columns: '',
          link: '/search/categories/smoking'
        },
        {
          type: 'collection',
          title: 'Television',
          figure: '/assets/img/home/collections/television.jpg',
          columns: '',
          link: '/search/categories/television'
        },
        {
          type: 'collection',
          title: 'Radiomedicine',
          figure: '/assets/img/home/collections/radiomedicine.jpg',
          columns: '',
          link: '/search/categories/radiomedicine'
        },
        {
          type: 'collection',
          title: 'Creative industries',
          figure: '/assets/img/home/collections/creative-industries.jpg',
          columns: '',
          link: '/search/categories/creative-industries'
        },
        {
          type: 'collection',
          title: 'Astronomy',
          figure: '/assets/img/home/collections/astronomy.jpg',
          columns: '',
          link: '/search/categories/astronomy'
        },
        {
          type: 'collection',
          title: 'Computing and Data processing',
          figure: '/assets/img/home/collections/computing.jpg',
          columns: '',
          link: '/search/categories/computing-&-data-processing'
        },
        {
          type: 'collection',
          title: 'Time measurement',
          figure: '/assets/img/home/collections/time-measurement.jpg',
          columns: '',
          link: '/search/categories/time-measurement'
        },
        {
          type: 'collection',
          title: 'Radio communication',
          figure: '/assets/img/home/collections/radio-communication.jpg',
          columns: '',
          link: '/search/categories/radio-communication'
        }
      ]
    }
  ]
};
<|endoftext|>


<commit_before>/*jslint node: true, nomen: true, sloppy: true*/

var cp = require('child_process'),
    fs = require('fs'),
    tests,
    command,
    exitCode = 0;

// Execute requirejs optimizer
cp.exec('node ' + __dirname + '/../vendor/r.js/dist/r.js -o ' + __dirname + '/Classify.build.js', function (error, stdout, stderr) {

    // Print success or error
    if (error !== null) {
        console.error(stderr);
        exitCode = 1;
    } else {
        console.log(stdout);
    }

    // Remove other files
    var files = fs.readdirSync(__dirname + '/../dist'),
        distDir = __dirname + '/../dist/';

    files.forEach(function (file) {

        var code;

        if (file.substr(file.length - 3) === ".js" && file !== "Classify.js") {
            console.log("Deleting " + file);
            code = fs.unlinkSync(distDir + file);

            if (code === 0) {
                exitCode = 1;
            }
        }
    });

    // Exit if something went badly
    if (exitCode) {
        process.exit(exitCode);
    }

    // Run tests
    command = 'mocha -R list ' + __dirname + '/../test/Classify.test.js';

    if (process.platform === 'win32') {
        tests = cp.spawn('cmd', ['/s', '/c', command], { customFds: [0, 1, 2] });
    } else {
        tests = cp.spawn('sh', ['-c', command], { customFds: [0, 1, 2] });
    }
    tests.on('exit', function (code) {

        if (code !== 0) {
            process.exit(1);
        } else {
            process.exit(0);
        }
    });
});<commit_msg>Add error message and fix CS.<commit_after>/*jslint node: true, nomen: true, sloppy: true*/

var cp = require('child_process'),
    fs = require('fs'),
    tests,
    command,
    exitCode = 0;

// Execute requirejs optimizer
cp.exec('node ' + __dirname + '/../vendor/r.js/dist/r.js -o ' + __dirname + '/Classify.build.js', function (error, stdout, stderr) {

    // Print success or error
    if (error !== null) {
        console.error(stderr);
        exitCode = 1;
    } else {
        console.log(stdout);
    }

    // Remove other files
    var files = fs.readdirSync(__dirname + '/../dist'),
        distDir = __dirname + '/../dist/';

    files.forEach(function (file) {

        var code;

        if (file.substr(file.length - 3) === '.js' && file !== 'Classify.js') {
            console.log('Deleting ' + file);
            code = fs.unlinkSync(distDir + file);

            if (code === 0) {
                console.error('Unable to delete file.');
                exitCode = 1;
            }
        }
    });

    // Exit if something went badly
    if (exitCode) {
        process.exit(exitCode);
    }

    // Run tests
    command = 'mocha -R list ' + __dirname + '/../test/Classify.test.js';

    if (process.platform === 'win32') {
        tests = cp.spawn('cmd', ['/s', '/c', command], { customFds: [0, 1, 2] });
    } else {
        tests = cp.spawn('sh', ['-c', command], { customFds: [0, 1, 2] });
    }
    tests.on('exit', function (code) {

        if (code !== 0) {
            process.exit(1);
        } else {
            process.exit(0);
        }
    });
});<|endoftext|>


<commit_before>/// write to file
set fso = CreateObject("Scripting.FileSystemObject");
set s = fso.CreateTextFile("C:\test.txt", True);
s.writeline("HI");
s.writeline("Bye");
s.writeline("-----------------------------");
s.Close();
<commit_msg>Update index.js<commit_after>/// write to file
//set fso = CreateObject("Scripting.FileSystemObject");
set s = fso.CreateTextFile("C:\test.txt", True);
s.writeline("HI");
s.writeline("Bye");
s.writeline("-----------------------------");
s.Close();
<|endoftext|>


<commit_before>const moment = require('moment');

// Import modules
const LogUtil = require('../libs/LogUtil');
const BotLibs = require('../libs/Botkit');
const InstagramDriver = require('../libs/InstagramDriver');
const QueryUtil = require('../libs/QueryUtil');

const app = require('../app');

// Get node environment
const slackChannelID = process.env.SLACK_CHANNEL;
const isProd = process.env.NODE_ENV === 'production';

// Destructure functions
const getMediaById = InstagramDriver.getMediaById;

if (isProd) {
  // If app is running in production mode
  LogUtil.winston.log('info', 'Production environment detected. Starting Slackbot.');

  // Initialize the bot
  BotLibs.init();

  const botInstance = BotLibs.instance;
  const botController = BotLibs.controller;

  // Start RTM function
  const startRTM = () => {
    botInstance.startRTM((err) => {
      if (err) {
        LogUtil.winston.log('error', err);
      }
    });
  };

  // Start the real-time messaging
  startRTM();

  // Start the real-time messaging if it is closed
  botController.on('rtm_close', () => {
    LogUtil.winston.log('info', 'RTM closed. Restarting RTM now!');

    startRTM();
  });

  // On route hit
  app.post('/callback-sub', (req, res) => {
    LogUtil.winston.log('info', 'Got POST request from Instagram Subscriptions: ', req.body);

    // JSON Object of POST data
    const mediaID = req.body['0'].data.media_id;

    const callback = (json) => {
      const data = json.data;
      LogUtil.winston.log('info', JSON.stringify(json));
      app.locals.mongoDriver.db.collection('postedmedias').insertOne(data);
      res.send();

      botInstance.say({
        text: `Ada post baru nih di Instagram! ${data.link}\n\n${data.caption.text}`,
        channel: slackChannelID,
      });
    };

    getMediaById(mediaID, callback);
  });

  // List events
  const ambient = 'ambient';

  // Helpers functions
  const isDateValid = string => moment(string, 'DD-MM-YYYY').isValid();

  const setParamsFromMessage = (message) => {
    // Set default if not defined to start of and end of week
    const defaultStartDate = moment()
      .hour(0)
      .minute(0)
      .second(0)
      .startOf('week');
    const defaultEndDate = moment()
      .hour(0)
      .minute(0)
      .second(0)
      .endOf('week');

    const [,
      startDate = defaultStartDate,
      endDate = defaultEndDate,
      sort,
    ] = message.text.split(' ');

    return { startDate, endDate, sort };
  };

  const formatDatetime = momentObject => momentObject.format('dddd, Do MMMM YYYY');

  // On receive events
  // Help
  botController.hears(['!help'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const textArrays = [
      '*Format Query:* _[command]_ _[t1]_ _[t2]_ _[sort]_',
      '  • _[command]_ *(wajib)*: adalah perintah yang diberikan kepada bot.',
      '     1. *!help*: Memunculkan list _command_ yang ada,',
      '     2. *!review*: Memunculkan review post-post sejak _[t1]_ hingga _[t2]_,',
      '     3. *!count*: Menghitung jumlah likes dari post-post sejak _[t1]_ hingga _[t2]_,',
      '     4. *!mostlikes*: Mencari post-post yang memiliki jumlah likes paling banyak sejak _[t1]_ hingga _[t2]_.',
      '  • _[t1]_ *(opsional)*:, waktu awal dengan format _DD-MM-YYYY_. *Default*: hari Senin pada minggu ini.',
      '  • _[t2]_ *(opsional)*:, waktu akhir dengan format _DD-MM-YYYY_. *Default*: hari Minggu pada minggu ini.',
      '  • _[sort]_ *(opsional)*:, mengurutkan hasil query berdasarkan atribut dengan format _[field]-[order]_. *Default*: unsorted.',
      '     1. *[field]*: pilihan field yang dapat diurutkan diantaranya *time*, *likes*, *comments*, *tags*,',
      '     2. *[order]*: pilihan order, yaitu *asc* (kecil ke besar) dan *desc* (besar ke kecil).\n',
      '*Contoh Query:* !review 07-06-2017 15-06-2017 sort:likes-asc',
    ];
    const text = textArrays.join('\n');

    bot.reply(message, text);
  });

  // Week review
  botController.hears(['!review'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      // If the dates are valid or if review is not defined; it is current week
      const callback = (err, posts, momentProps) => {
        if (!err) {
          const {
            startDateMoment: start,
            endDateMoment: end,
          } = momentProps;
          const length = posts.length;
          let botMsg = '';

          if (length) {
            botMsg = `Review dari ${formatDatetime(start)} hingga ${formatDatetime(end)}:\n`;

            posts.forEach((post, i) => {
              const {
                link,
                created_time: date,
                likes,
                caption,
              } = post;
              const createdAt = formatDatetime(moment.unix(date));

              // Manually concat for each post
              botMsg += `${i + 1}. ${link} (${createdAt}) - ${likes.count} likes\n` +
                      `${caption.text}`;

              // Add newline if it is not the last element
              botMsg += (i + 1 < length) ? '\n' : '';
            });
          } else {
            botMsg = `Tidak ada post dari ${formatDatetime(start)} hingga ${formatDatetime(end)}`;
          }

          bot.reply(message, botMsg);
        }
      };

      const query = {
        likes: 1,
        created_time: 1,
        'caption.text': 1,
        link: 1,
      };

      QueryUtil.getMediasByTimerange(app.locals.mongoDriver.db, params, query, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get total likes of posts in a timerange
  botController.hears(['!count'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const {
            startDate: start,
            endDate: end,
            totalLikes,
          } = json.data;

          bot.reply(
            message,
            `Total post likes count dari ${start} hingga ${end} ada ${totalLikes}.`
          );
        }
      };

      QueryUtil.getTotalLikesInPeriod(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get post(s) with the most likes in a timerange
  botController.hears(['!mostlikes'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const {
            startDate: start,
            endDate: end,
            posts,
          } = json.data;

          const length = posts.length;
          let botMsg = '';

          if (length) {
            posts.forEach((post, i) => {
              const { link, date, likes, text } = post;
              // Manually concat for each post
              botMsg += `${i + 1}. ${link} (${date}) - ${likes} likes\n` +
                      `${text}`;

              // Add newline if it is not the last element
              botMsg += (i + 1 < length) ? '\n' : '';
            });
          } else {
            botMsg = `Tidak ada post dari ${start} hingga ${end}`;
          }

          bot.reply(message, botMsg);
        }
      };

      QueryUtil.getMostLikedPosts(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get post(s) with the most likes in a timerange
  botController.hears(['!followers'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const data = json.data;
          const length = data.length;
          let botMsg = '';

          if (length) {
            const start = formatDatetime(moment.unix(data[0].time));
            const end = formatDatetime(moment.unix(data[length - 1].time));

            botMsg = `Jumlah followers dari *${start}* hingga *${end}*:\n`;

            data.forEach((followersDay, i) => {
              const { time, followers_count: followersCount } = followersDay;
              const timeFormat = formatDatetime(moment.unix(time));

              botMsg += `${i + 1}. *${timeFormat}*: *${followersCount}* akun.\n`;
            });
          } else {
            botMsg = 'Query tidak dapat menemukan data yang diminta. Silahkan coba lagi.';
          }

          bot.reply(message, botMsg);
        }
      };

      QueryUtil.getFollowersCountSince(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });
} else {
  // Local/development mode
  LogUtil.winston.log('info', 'No production environment is detected. Slackbot is not running.');
}
<commit_msg>fix(callback subs): fix callback sub v3<commit_after>const moment = require('moment');

// Import modules
const LogUtil = require('../libs/LogUtil');
const BotLibs = require('../libs/Botkit');
const InstagramDriver = require('../libs/InstagramDriver');
const QueryUtil = require('../libs/QueryUtil');

const app = require('../app');

// Get node environment
const slackChannelID = process.env.SLACK_CHANNEL;
const isProd = process.env.NODE_ENV === 'production';

// Destructure functions
const getMediaById = InstagramDriver.getMediaById;

if (isProd) {
  // If app is running in production mode
  LogUtil.winston.log('info', 'Production environment detected. Starting Slackbot.');

  // Initialize the bot
  BotLibs.init();

  const botInstance = BotLibs.instance;
  const botController = BotLibs.controller;

  // Start RTM function
  const startRTM = () => {
    botInstance.startRTM((err) => {
      if (err) {
        LogUtil.winston.log('error', err);
      }
    });
  };

  // Start the real-time messaging
  startRTM();

  // Start the real-time messaging if it is closed
  botController.on('rtm_close', () => {
    LogUtil.winston.log('info', 'RTM closed. Restarting RTM now!');

    startRTM();
  });

  // On route hit
  app.post('/callback-sub', (req, res) => {
    LogUtil.winston.log('info', 'Got POST request from Instagram Subscriptions: ', req.body);

    // JSON Object of POST data
    const mediaID = req.body['0'].data.media_id;

    const callback = (json) => {
      const data = json.data;
      LogUtil.winston.log('info', JSON.stringify(data.link), JSON.stringify(data.caption.text));
      app.locals.mongoDriver.db.collection('postedmedias').insertOne(data);
      res.send();

      botInstance.say({
        text: `Ada post baru nih di Instagram! ${data.link}\n\n${data.caption.text}`,
        channel: slackChannelID,
      });
    };

    getMediaById(mediaID, callback);
  });

  // List events
  const ambient = 'ambient';

  // Helpers functions
  const isDateValid = string => moment(string, 'DD-MM-YYYY').isValid();

  const setParamsFromMessage = (message) => {
    // Set default if not defined to start of and end of week
    const defaultStartDate = moment()
      .hour(0)
      .minute(0)
      .second(0)
      .startOf('week');
    const defaultEndDate = moment()
      .hour(0)
      .minute(0)
      .second(0)
      .endOf('week');

    const [,
      startDate = defaultStartDate,
      endDate = defaultEndDate,
      sort,
    ] = message.text.split(' ');

    return { startDate, endDate, sort };
  };

  const formatDatetime = momentObject => momentObject.format('dddd, Do MMMM YYYY');

  // On receive events
  // Help
  botController.hears(['!help'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const textArrays = [
      '*Format Query:* _[command]_ _[t1]_ _[t2]_ _[sort]_',
      '  • _[command]_ *(wajib)*: adalah perintah yang diberikan kepada bot.',
      '     1. *!help*: Memunculkan list _command_ yang ada,',
      '     2. *!review*: Memunculkan review post-post sejak _[t1]_ hingga _[t2]_,',
      '     3. *!count*: Menghitung jumlah likes dari post-post sejak _[t1]_ hingga _[t2]_,',
      '     4. *!mostlikes*: Mencari post-post yang memiliki jumlah likes paling banyak sejak _[t1]_ hingga _[t2]_.',
      '  • _[t1]_ *(opsional)*:, waktu awal dengan format _DD-MM-YYYY_. *Default*: hari Senin pada minggu ini.',
      '  • _[t2]_ *(opsional)*:, waktu akhir dengan format _DD-MM-YYYY_. *Default*: hari Minggu pada minggu ini.',
      '  • _[sort]_ *(opsional)*:, mengurutkan hasil query berdasarkan atribut dengan format _[field]-[order]_. *Default*: unsorted.',
      '     1. *[field]*: pilihan field yang dapat diurutkan diantaranya *time*, *likes*, *comments*, *tags*,',
      '     2. *[order]*: pilihan order, yaitu *asc* (kecil ke besar) dan *desc* (besar ke kecil).\n',
      '*Contoh Query:* !review 07-06-2017 15-06-2017 sort:likes-asc',
    ];
    const text = textArrays.join('\n');

    bot.reply(message, text);
  });

  // Week review
  botController.hears(['!review'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      // If the dates are valid or if review is not defined; it is current week
      const callback = (err, posts, momentProps) => {
        if (!err) {
          const {
            startDateMoment: start,
            endDateMoment: end,
          } = momentProps;
          const length = posts.length;
          let botMsg = '';

          if (length) {
            botMsg = `Review dari ${formatDatetime(start)} hingga ${formatDatetime(end)}:\n`;

            posts.forEach((post, i) => {
              const {
                link,
                created_time: date,
                likes,
                caption,
              } = post;
              const createdAt = formatDatetime(moment.unix(date));

              // Manually concat for each post
              botMsg += `${i + 1}. ${link} (${createdAt}) - ${likes.count} likes\n` +
                      `${caption.text}`;

              // Add newline if it is not the last element
              botMsg += (i + 1 < length) ? '\n' : '';
            });
          } else {
            botMsg = `Tidak ada post dari ${formatDatetime(start)} hingga ${formatDatetime(end)}`;
          }

          bot.reply(message, botMsg);
        }
      };

      const query = {
        likes: 1,
        created_time: 1,
        'caption.text': 1,
        link: 1,
      };

      QueryUtil.getMediasByTimerange(app.locals.mongoDriver.db, params, query, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get total likes of posts in a timerange
  botController.hears(['!count'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const {
            startDate: start,
            endDate: end,
            totalLikes,
          } = json.data;

          bot.reply(
            message,
            `Total post likes count dari ${start} hingga ${end} ada ${totalLikes}.`
          );
        }
      };

      QueryUtil.getTotalLikesInPeriod(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get post(s) with the most likes in a timerange
  botController.hears(['!mostlikes'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const {
            startDate: start,
            endDate: end,
            posts,
          } = json.data;

          const length = posts.length;
          let botMsg = '';

          if (length) {
            posts.forEach((post, i) => {
              const { link, date, likes, text } = post;
              // Manually concat for each post
              botMsg += `${i + 1}. ${link} (${date}) - ${likes} likes\n` +
                      `${text}`;

              // Add newline if it is not the last element
              botMsg += (i + 1 < length) ? '\n' : '';
            });
          } else {
            botMsg = `Tidak ada post dari ${start} hingga ${end}`;
          }

          bot.reply(message, botMsg);
        }
      };

      QueryUtil.getMostLikedPosts(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });

  // Get post(s) with the most likes in a timerange
  botController.hears(['!followers'], [ambient], (bot, message) => {
    LogUtil.winston.log('info', `Message: ${JSON.stringify(message)}`);

    const params = setParamsFromMessage(message);

    if (isDateValid(params.startDate) && isDateValid(params.endDate)) {
      const callback = (json) => {
        if (json.success) {
          const data = json.data;
          const length = data.length;
          let botMsg = '';

          if (length) {
            const start = formatDatetime(moment.unix(data[0].time));
            const end = formatDatetime(moment.unix(data[length - 1].time));

            botMsg = `Jumlah followers dari *${start}* hingga *${end}*:\n`;

            data.forEach((followersDay, i) => {
              const { time, followers_count: followersCount } = followersDay;
              const timeFormat = formatDatetime(moment.unix(time));

              botMsg += `${i + 1}. *${timeFormat}*: *${followersCount}* akun.\n`;
            });
          } else {
            botMsg = 'Query tidak dapat menemukan data yang diminta. Silahkan coba lagi.';
          }

          bot.reply(message, botMsg);
        }
      };

      QueryUtil.getFollowersCountSince(app.locals.mongoDriver.db, params, callback);
    } else {
      bot.reply(message, 'Tanggal input tidak valid!');
    }
  });
} else {
  // Local/development mode
  LogUtil.winston.log('info', 'No production environment is detected. Slackbot is not running.');
}
<|endoftext|>


<commit_before>'use strict';

var handlers = require('../handlers/journals')
  , routes
  ;

routes = [
  {
    method: 'GET',
    path: '/journals',
    handler: handlers.getJournals
  },
  {
    method: 'GET',
    path: '/journals/collection/{saSeknr}',
    handler: handlers.getJournalsCollection
  },
  {
    method: 'GET',
    path: '/journals/date/distinct',
    handler: handlers.getJournalsDatesDistinct
  },
  {
    method: 'GET',
    path: '/journals/date/{date}',
    handler: handlers.getJournalsByDate
  },
  {
    method: 'GET',
    path: '/journals/latest',
    handler: handlers.getLatestJournals
  },
  {
    method: 'GET',
    path: '/journal/{jpSeknr}',
    handler: handlers.getJournal
  }
]

module.exports = routes;<commit_msg>Adds route /jounals/department/{department}<commit_after>'use strict';

var handlers = require('../handlers/journals')
  , routes
  ;

routes = [
  {
    method: 'GET',
    path: '/journals',
    handler: handlers.getJournals
  },
  {
    method: 'GET',
    path: '/journals/collection/{saSeknr}',
    handler: handlers.getJournalsCollection
  },
  {
    method: 'GET',
    path: '/journals/date/distinct',
    handler: handlers.getJournalsDatesDistinct
  },
  {
    method: 'GET',
    path: '/journals/date/{date}',
    handler: handlers.getJournalsByDate
  },
  {
    method: 'GET',
    path: '/journals/department/{department}',
    handler: handlers.getJournalsByDepartment
  },
  {
    method: 'GET',
    path: '/journals/latest',
    handler: handlers.getLatestJournals
  },
  {
    method: 'GET',
    path: '/journal/{jpSeknr}',
    handler: handlers.getJournal
  }
]

module.exports = routes;<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var redis = require('redis');

client = redis.createClient();

/* GET users listing. */
router.post('/', function(req, res, next) {
  client.get(req.body.id, function(err, reply){
    if(err){throw err;}
    else if(!reply){
      var json = {
        name: req.body.name,
        mail: req.body.mail,
        tell: req.body.tell,
        twitter: req.body.twitter,
        image: req.body.image
      };
      if(!json.mail || !json.name || !req.body.id || !req.body.image){res.send("luck params");return;}
      client.set(req.body.id, JSON.stringify(json), function(err, keys_replies){
        if(err){throw err;}
        else{res.send(req.body.name + " is registered.");return;}
        return;
      });
    }
    else{res.send("already registered.");return;}
  });
});

module.exports = router;
<commit_msg>add comment<commit_after>var express = require('express');
var router = express.Router();
var redis = require('redis');

client = redis.createClient();

/* GET users listing. */
router.post('/', function(req, res, next) {
  client.get(req.body.id, function(err, reply){
    if(err){throw err;}
    else if(!reply){
      var json = {
        name: req.body.name,
        mail: req.body.mail,
        tell: req.body.tell,
        twitter: req.body.twitter,
        image: req.body.image,
        comment: req.body.comment
      };
      if(!json.mail || !json.name || !req.body.id || !req.body.image){res.send("luck params");return;}
      client.set(req.body.id, JSON.stringify(json), function(err, keys_replies){
        if(err){throw err;}
        else{res.send(req.body.name + " is registered.");return;}
        return;
      });
    }
    else{res.send("already registered.");return;}
  });
});

module.exports = router;
<|endoftext|>


<commit_before>var response = require('response');
var JSONStream = require('JSONStream');
var formBody = require('body/form');
var redirect = require('../lib/redirect');

exports.install = function (server, prefix) {
  var prefix = prefix || '/sessions';

  /*
  * Create a session
  */

  server.route(prefix, function (req, res) {
    if (req.method === 'POST') {
      formBody(req, res, function (err, body) {

        server.accountdown.verify('basic', body, function (err, ok, id) {
          if (err) {
            console.error(err);
          } else if (!ok) {
            // TODO: notify user about incorrect password, and offer reset password option (issue #47)
          } else {
            server.auth.login(res, { username: id }, function (loginerr, data) {
              if (loginerr) console.error(loginerr);
            });
          }
          redirect(res, '/');
        });
      });
    }
  });

  /*
  * Destroy a session
  */

  server.route(prefix + '/destroy', function (req, res) {
    server.auth.delete(req, function () {
      server.auth.cookie.destroy(res);
      redirect(res, '/');
    });
  });
}
<commit_msg>Trim username whitespace before submitting<commit_after>var response = require('response');
var JSONStream = require('JSONStream');
var formBody = require('body/form');
var redirect = require('../lib/redirect');

exports.install = function (server, prefix) {
  var prefix = prefix || '/sessions';

  /*
  * Create a session
  */

  server.route(prefix, function (req, res) {
    if (req.method === 'POST') {
      formBody(req, res, function (err, body) {
        if (body.username) {
          body.username = body.username.trim();
        }
        server.accountdown.verify('basic', body, function (err, ok, id) {
          if (err) {
            console.error(err);
          } else if (!ok) {
            // TODO: notify user about incorrect password, and offer reset password option (issue #47)
          } else {
            server.auth.login(res, { username: id }, function (loginerr, data) {
              if (loginerr) console.error(loginerr);
            });
          }
          redirect(res, '/');
        });
      });
    }
  });

  /*
  * Destroy a session
  */

  server.route(prefix + '/destroy', function (req, res) {
    server.auth.delete(req, function () {
      server.auth.cookie.destroy(res);
      redirect(res, '/');
    });
  });
}
<|endoftext|>


<commit_before>// Copyright (c) 2013, Benjamin J. Kelly ("Author")
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'use strict';

module.exports = NetbiosNameService;

var EventEmitter = require('events').EventEmitter;
var dgram = require('dgram');
var net = require('net');
var util = require('util');

var Cache = require('./cache');
var Stream = require('./stream');
var pack = require('./pack');
var unpack = require('./unpack');

util.inherits(NetbiosNameService, EventEmitter);

function NetbiosNameService(options) {
  var self = this instanceof NetbiosNameService
           ? this
           : Object.create(NetbiosNameService.prototype);

  EventEmitter.call(self);

  options = options || Object.create(null);

  self._tcpDisable = options.tcpDisable;
  if (!self._tcpDisable) {
    self._tcpPort = options.tcpPort || 137;
    self._tcpAddress = options.tcpAddress;
    self._tcpServer = options.tcpServer;
  }

  self._udpDisable = options.udpDisable;
  if (!self._udpDisable) {
    self._udpPort = options.udpPort || 137;
    self._udpAddress = options.udpAddress;
    self._udpSocket = options.udpSocket;
  }

  self._localType = options.localType || 'broadcast';
  self._localAddress = options.localAddress || '127.0.0.1';

  self._cache = new Cache();
  self._localNames = new Cache({enableTimeouts: false});
  self._responseHandlers = Object.create(null);

  return self;
}

// Events:
//  - register
//  - unregister

NetbiosNameService.prototype.start = function(callback) {
  var self = this;
  self._startTcp(function() {
    self._startUdp(function() {
      if (typeof callback === 'function') {
        callback();
      }
    });
  });
};

NetbiosNameService.prototype._startTcp = function(callback) {
  if (!this._tcpDisable) {
    var needListen = false;
    if (!this._tcpServer) {
      this._tcpServer = net.createServer();
      needListen = true;
    }

    this._tcpServer.on('error', this.emit.bind(this, 'error'));
    this._tcpServer.on('connection', this._onTcpConnect.bind(this));

    if (needListen) {
      this._tcpServer.listen(this._tcpPort, this._tcpAddress, callback);
      return;
    }
  }

  callback();
};

NetbiosNameService.prototype._startUdp = function(callback) {
  if (!this._udpDisable) {
    var needBind = false;
    if (!this._udpSocket) {
      this._udpSocket = dgram.createSocket('udp4');
      needBind = true;
    }

    this._udpSocket.on('error', this.emit.bind(this, 'error'));
    this._udpSocket.on('message', this._onUdpMsg.bind(this));

    if (needBind) {
      this._udpSocket.on('listening', callback);
      this._udpSocket.bind(this._udpPort, this._udpAddress);
      return;
    }
  }

  callback();
};

NetbiosNameService.prototype.stop = function(callback) {
  var self = this;
  self._stopTcp(function() {
    self._stopUdp(function() {
      if (typeof callback === 'function') {
        callback();
      }
    });
  });
};

NetbiosNameService.prototype._stopTcp = function(callback) {
  var self = this;
  if (self._tcpServer) {
    self._tcpServer.close(function() {
      self._tcpServer = null;
      callback();
    });
    return;
  }
  callback();
};

NetbiosNameService.prototype._stopUdp = function(callback) {
  var self = this;
  if (self._udpSocket) {
    self._udpSocket.on('close', function() {
      self._udpSocket = null;
      callback();
    });
    self._udpSocket.close();
    return;
  }
  callback();
};

NetbiosNameService.prototype.register = function(name, suffix, address, callback) {
  // TODO: implement register

  // if already in name cache

    // ignore or flag as conflict

  // if not in name cache

    // send register request

    // save state for response
};

NetbiosNameService.prototype.unregister = function(name, suffix, callback) {
  // TODO: implement unregister

  // if registered by us

    // send unregister request
};

NetbiosNameService.prototype.query = function(name, suffix, callback) {
  // TODO: implement query

  // if in name cache

    // send status request

    // save state for response

  // if not in name cache

    // send query request

    // save state for response
};

NetbiosNameService.prototype._onUdpMsg = function(msg, rinfo) {
  var self = this;
  unpack(msg, 0, function(error, len, nbmsg) {
    if (error) {
      self.emit('error', error);
      return;
    }

    self._onNetbiosMsg(nbmsg, self._sendUdpMsg.bind(self, rinfo.address,
                                                    rinfo.port));
  });
};

NetbiosNameService.prototype._sendUdpMsg = function(address, port, msg) {
  var self = this;

  // create a maximum sized buffer
  //  - 576 for recommended MTU minus IP/UDP header space
  var maxSize = 576 - 20 - 8;
  var buf = new Buffer(maxSize);

  pack(buf, msg, function(error, len) {
    if (error) {
      self.emit('error', error);
      return;
    }

    self._udpSocket.send(buf, 0, len, port, address);
  });
};

NetbiosNameService.prototype._onTcpConnect = function(socket) {
  var self = this;
  var stream = new Stream(socket);

  // TODO: How do we handle socket teardown here?  Do we have to cleanup
  //       anything to avoid memory leaks due to stale objects?

  stream.on('error', self.emit.bind(self, 'error'));
  stream.on('message', function(msg) {
    self._onNetbiosMsg(msg, stream.write.bind(stream));
  });
};

NetbiosNameService.prototype._onNetbiosMsg = function(msg, sendFunc) {
  if (msg.response) {
    _onResponse(msg, sendFunc);
  } else {
    _onRequest(msg, sendFunc);
  }
};

NetbiosNameService.prototype._onResponse = function(msg, sendFunc) {
  // If we are expecting this response, then process it appropriately.  Ignore
  // spurious responses we are not expecting.
  var handler = this._responseHandlers[msg.transactionId];
  if (typeof handler === 'function') {
    handler(msg, sendFunc);
  }
};

NetbiosNameService.prototype._onRequest = function(msg, sendFunc) {
  switch (msg.op) {
    case 'query':
      this._onQuery(msg, sendFunc);
      break;
    case 'registration':
      this._onRegistration(msg, sendFunc);
      break;
    case 'release':
      this._onRelease(msg, sendFunc);
      break;
    case 'wack':
      // do nothing
      break;
    case 'refresh':
      this._onRefresh(msg, sendFunc);
      break;
    default:
      // do nothing
      break;
  }
};

NetbiosNameService.prototype._onQuery = function(msg, sendFunc) {
  var q = msg.questions[0];

  var answer = null;
  if (q.type === 'nb') {
    answer = this._localNames.getNb(q.name, q.suffix);
  } else if (q.type === 'nbstat') {
    answer = this._localNames.getNbstats(q.name, q.suffix);
  }

  if (answer) {
    var response = {
      transactionId: msg.transactionId,
      response: true,
      op: msg.op,
      authoritative: true,
      answerRecords: [answer]
    };
    sendFunc(response);
  }
};

NetbiosNameService.prototype._onRegistration = function(msg, sendFunc) {
  var rec = msg.additionalRecords[0];

  // Check to see if we have this name claimed.  If both the local and remote
  // names are registered as a group, then there is no conflict.
  var localRec = this._localNames.getNb(rec.name, rec.suffix);
  if (localRec &&
      (!localRec.nb.entries[0].group || !rec.nb.entries[0].group)) {

    // Send a conflict response
    var response = {
      transactionId: msg.transactionId,
      response: true,
      op: msg.op,
      authoritative: true,
      error: 'active',
      answerRecords: [localRec]
    };
    sendFunc(response);
  }
};

NetbiosNameService.prototype._onRelease = function(msg, sendFunc) {
  var rec = msg.additionalRecords[0];
  this._cache.remove(rec.name, rec.suffix);
};

NetbiosNameService.prototype._onRefresh = function(msg, sendFunc) {
  var rec = msg.additionalRecords[0];

  // To be safe, ignore refresh requests for names we think we own.  This
  // shouldn't happen in theory.
  if (!this._localNames.contains(rec.name, rec.suffix)) {
    this._cache.update(rec);
  }
};
<commit_msg>Fix _onNetbiosMsg to call methods on this.  Also, make handlers more resilient to malformed messages.<commit_after>// Copyright (c) 2013, Benjamin J. Kelly ("Author")
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

'use strict';

module.exports = NetbiosNameService;

var EventEmitter = require('events').EventEmitter;
var dgram = require('dgram');
var net = require('net');
var util = require('util');

var Cache = require('./cache');
var Stream = require('./stream');
var pack = require('./pack');
var unpack = require('./unpack');

util.inherits(NetbiosNameService, EventEmitter);

function NetbiosNameService(options) {
  var self = this instanceof NetbiosNameService
           ? this
           : Object.create(NetbiosNameService.prototype);

  EventEmitter.call(self);

  options = options || Object.create(null);

  self._tcpDisable = options.tcpDisable;
  if (!self._tcpDisable) {
    self._tcpPort = options.tcpPort || 137;
    self._tcpAddress = options.tcpAddress;
    self._tcpServer = options.tcpServer;
  }

  self._udpDisable = options.udpDisable;
  if (!self._udpDisable) {
    self._udpPort = options.udpPort || 137;
    self._udpAddress = options.udpAddress;
    self._udpSocket = options.udpSocket;
  }

  self._localType = options.localType || 'broadcast';
  self._localAddress = options.localAddress || '127.0.0.1';

  self._cache = new Cache();
  self._localNames = new Cache({enableTimeouts: false});
  self._responseHandlers = Object.create(null);

  return self;
}

// Events:
//  - register
//  - unregister

NetbiosNameService.prototype.start = function(callback) {
  var self = this;
  self._startTcp(function() {
    self._startUdp(function() {
      if (typeof callback === 'function') {
        callback();
      }
    });
  });
};

NetbiosNameService.prototype._startTcp = function(callback) {
  if (!this._tcpDisable) {
    var needListen = false;
    if (!this._tcpServer) {
      this._tcpServer = net.createServer();
      needListen = true;
    }

    this._tcpServer.on('error', this.emit.bind(this, 'error'));
    this._tcpServer.on('connection', this._onTcpConnect.bind(this));

    if (needListen) {
      this._tcpServer.listen(this._tcpPort, this._tcpAddress, callback);
      return;
    }
  }

  callback();
};

NetbiosNameService.prototype._startUdp = function(callback) {
  if (!this._udpDisable) {
    var needBind = false;
    if (!this._udpSocket) {
      this._udpSocket = dgram.createSocket('udp4');
      needBind = true;
    }

    this._udpSocket.on('error', this.emit.bind(this, 'error'));
    this._udpSocket.on('message', this._onUdpMsg.bind(this));

    if (needBind) {
      this._udpSocket.on('listening', callback);
      this._udpSocket.bind(this._udpPort, this._udpAddress);
      return;
    }
  }

  callback();
};

NetbiosNameService.prototype.stop = function(callback) {
  var self = this;
  self._stopTcp(function() {
    self._stopUdp(function() {
      if (typeof callback === 'function') {
        callback();
      }
    });
  });
};

NetbiosNameService.prototype._stopTcp = function(callback) {
  var self = this;
  if (self._tcpServer) {
    self._tcpServer.close(function() {
      self._tcpServer = null;
      callback();
    });
    return;
  }
  callback();
};

NetbiosNameService.prototype._stopUdp = function(callback) {
  var self = this;
  if (self._udpSocket) {
    self._udpSocket.on('close', function() {
      self._udpSocket = null;
      callback();
    });
    self._udpSocket.close();
    return;
  }
  callback();
};

NetbiosNameService.prototype.register = function(name, suffix, address, callback) {
  // TODO: implement register

  // if already in name cache

    // ignore or flag as conflict

  // if not in name cache

    // send register request

    // save state for response
};

NetbiosNameService.prototype.unregister = function(name, suffix, callback) {
  // TODO: implement unregister

  // if registered by us

    // send unregister request
};

NetbiosNameService.prototype.query = function(name, suffix, callback) {
  // TODO: implement query

  // if in name cache

    // send status request

    // save state for response

  // if not in name cache

    // send query request

    // save state for response
};

NetbiosNameService.prototype._onUdpMsg = function(msg, rinfo) {
  var self = this;
  unpack(msg, 0, function(error, len, nbmsg) {
    if (error) {
      self.emit('error', error);
      return;
    }

    self._onNetbiosMsg(nbmsg, self._sendUdpMsg.bind(self, rinfo.address,
                                                    rinfo.port));
  });
};

NetbiosNameService.prototype._sendUdpMsg = function(address, port, msg) {
  var self = this;

  // create a maximum sized buffer
  //  - 576 for recommended MTU minus IP/UDP header space
  var maxSize = 576 - 20 - 8;
  var buf = new Buffer(maxSize);

  pack(buf, msg, function(error, len) {
    if (error) {
      self.emit('error', error);
      return;
    }

    self._udpSocket.send(buf, 0, len, port, address);
  });
};

NetbiosNameService.prototype._onTcpConnect = function(socket) {
  var self = this;
  var stream = new Stream(socket);

  // TODO: How do we handle socket teardown here?  Do we have to cleanup
  //       anything to avoid memory leaks due to stale objects?

  stream.on('error', self.emit.bind(self, 'error'));
  stream.on('message', function(msg) {
    self._onNetbiosMsg(msg, stream.write.bind(stream));
  });
};

NetbiosNameService.prototype._onNetbiosMsg = function(msg, sendFunc) {
  if (msg.response) {
    this._onResponse(msg, sendFunc);
  } else {
    this._onRequest(msg, sendFunc);
  }
};

NetbiosNameService.prototype._onResponse = function(msg, sendFunc) {
  // If we are expecting this response, then process it appropriately.  Ignore
  // spurious responses we are not expecting.
  var handler = this._responseHandlers[msg.transactionId];
  if (typeof handler === 'function') {
    handler(msg, sendFunc);
  }
};

NetbiosNameService.prototype._onRequest = function(msg, sendFunc) {
  switch (msg.op) {
    case 'query':
      this._onQuery(msg, sendFunc);
      break;
    case 'registration':
      this._onRegistration(msg, sendFunc);
      break;
    case 'release':
      this._onRelease(msg, sendFunc);
      break;
    case 'wack':
      // do nothing
      break;
    case 'refresh':
      this._onRefresh(msg, sendFunc);
      break;
    default:
      // do nothing
      break;
  }
};

NetbiosNameService.prototype._onQuery = function(msg, sendFunc) {
  var q = msg.questions ? msg.questions[0] : null;
  if (!q) {
    return;
  }

  var answer = null;
  if (q.type === 'nb') {
    answer = this._localNames.getNb(q.name, q.suffix);
  } else if (q.type === 'nbstat') {
    answer = this._localNames.getNbstats(q.name, q.suffix);
  }

  if (answer) {
    var response = {
      transactionId: msg.transactionId,
      response: true,
      op: msg.op,
      authoritative: true,
      answerRecords: [answer]
    };
    sendFunc(response);
  }
};

NetbiosNameService.prototype._onRegistration = function(msg, sendFunc) {
  var rec = msg.additionalRecords ? msg.additionalRecords[0] : null;
  if (!rec) {
    return;
  }

  // Check to see if we have this name claimed.  If both the local and remote
  // names are registered as a group, then there is no conflict.
  var localRec = this._localNames.getNb(rec.name, rec.suffix);
  if (localRec &&
      (!localRec.nb.entries[0].group || !rec.nb.entries[0].group)) {

    // Send a conflict response
    var response = {
      transactionId: msg.transactionId,
      response: true,
      op: msg.op,
      authoritative: true,
      error: 'active',
      answerRecords: [localRec]
    };
    sendFunc(response);
  }
};

NetbiosNameService.prototype._onRelease = function(msg, sendFunc) {
  var rec = msg.additionalRecords[0];
  this._cache.remove(rec.name, rec.suffix);
};

NetbiosNameService.prototype._onRefresh = function(msg, sendFunc) {
  var rec = msg.additionalRecords[0];

  // To be safe, ignore refresh requests for names we think we own.  This
  // shouldn't happen in theory.
  if (!this._localNames.contains(rec.name, rec.suffix)) {
    this._cache.update(rec);
  }
};
<|endoftext|>


<commit_before>const create = require('./index')

describe('rpc/create', () => {
  it('should create a new user', async () => {
    const email = 'test@test.com'
    const id = '12345'
    const insertResult = {
      insertedId: id
    }
    const db = {
      insertOne: jest.fn(() => Promise.resolve(insertResult))
    }
    const result = await create({email}, {db})
    expect(db.insertOne)
      .toBeCalledWith({
        email,
        verified: false
      })
    expect(result)
      .toEqual({
        id
      })
  })

  it('show throw an error if email is missing', async () => {
    try {
      await create({}, {})
    } catch (err) {
      expect(err.message)
        .toBe('Missing Input Parameter')
    }
  })
})
<commit_msg>Make sure error is a handled error<commit_after>const create = require('./index')

describe('rpc/create', () => {
  it('should create a new user', async () => {
    const email = 'test@test.com'
    const id = '12345'
    const insertResult = {
      insertedId: id
    }
    const db = {
      insertOne: jest.fn(() => Promise.resolve(insertResult))
    }
    const result = await create({email}, {db})
    expect(db.insertOne)
      .toBeCalledWith({
        email,
        verified: false
      })
    expect(result)
      .toEqual({
        id
      })
  })

  it('show throw an error if email is missing', async () => {
    try {
      await create({}, {})
    } catch (err) {
      expect(err.message)
        .toBe('Missing Input Parameter')
      expect(err.handled)
        .toBe(true)
    }
  })
})
<|endoftext|>