<commit_before>
// internal libs
import Router from 'src/apps/router';
import { XRegExp } from 'src/libs/dependencies';

describe('Class Router', () => {

	describe('prototype._addRoute', () => {

		it('should add route and name to prototype._routes', () => {

			let router = Router.create();

			// add static routes
			router._addRoute('/this/is/a/route/1', 'name1');
			router._addRoute('/this/is/a/route/2', 'name2');
			router._addRoute('/this/is/{{a}}/route/4', 'name4');
			router._addRoute('/this/is/{{b}}/route/5', 'name5');
			router._addRoute('/this/is/{{c}}/route/6', 'name6');

			// this should not be added because it can exists only one route
			router._addRoute('/this/is/a/route/2', 'name2');
			router._addRoute('/this/is/{{a}}/route/4', 'name4');

			// Test: contain correct added routes
			router.getRoutes('static').should.containEql({
				'/this/is/a/route/1': 'name1',
				'/this/is/a/route/2': 'name2',
			});

			router.getRoutes('dynamic').should.containEql({
				'/this/is/{{a}}/route/4': 'name4',
				'/this/is/{{b}}/route/5': 'name5',
				'/this/is/{{c}}/route/6': 'name6',
			});

			router.destroy();

		});

	});

	describe('prototype._addPromise', () => {

		it('should add and create promise collection on prototype.promise', () => {

			let router = Router.create();

			let promise1 = router._addPromise('name1');
			let promise2 = router._addPromise('name2');

			promise1.should.be.Promise();
			promise2.should.be.Promise();

			router.promise.should.have.propertyByPath('name1', 0).and.is.Function(); // resolve function
			router.promise.should.have.propertyByPath('name2', 0).and.is.Function(); // resolve function

			// cleanup: resolve promises
			router.promise.name1[0]();
			router.promise.name2[0]();
			// cleanup: destroy router
			router.destroy();

		});
	});

	describe('prototype._urlFragmentChanged', () => {

		it('should check if fragment is changed in combination with _setURLFragment', () => {

			let router = Router.create();

			router._setURLFragment('/');
			router._urlFragmentChanged('/some/url/fragment').should.be.true();

			router._setURLFragment('/some/url/fragment');
			router._urlFragmentChanged('/some/url/fragment').should.be.false();

			router.destroy();

		});

	});

	describe('prototype.createURL', () => {

		it('should return url object with additional "fragment" property', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			let url = router.createURL('http://www.mydomain.com/path/to/somewhere.html?a=1&b=2');
			url.fragment.should.equal('/path/to/somewhere.html?a=1&b=2');
			// cleanup
			router.destroy();

		});

	});

	describe('prototype.Promise', () => {

		it('should return promise object', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			router.Promise(function(){}).should.be.Promise();
			// cleanup
			router.destroy();

		});

	});

	describe('prototype.off', () => {

	});

	describe('prototype.on', () => {

		it('should throw an error if no argeument passed', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on() }).should.throw();
			// cleanup
			router.destroy();

		});

		it('should not throw an error if no handler passed', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on('urlchange') }).should.not.throw();
			// cleanup
			router.destroy();

		});

		it('should not throw an error if passed event with handler', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on('someevent', () => {}) }).should.not.throw();
			// cleanup
			router.destroy();

		});

		it('should return promise if passed only event', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			router.on('urlchange').should.be.Promise();
			router.on('urlchange').then(() => {}).should.be.Promise();
			// cleanup
			router.destroy();

		});

		it('should return null if passed event and handler', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			should(router.on('my route', (event) => {})).be.null();
			// cleanup
			router.destroy();

		});

		it('should handle promise or handler correctly', (done) => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			let spy_handler = sinon.spy(() => {});
			// test wihout trigger args
			router.on('urlchange', spy_handler);

			// test if promise resolved
			router.on('urlchange').should.be.finally.propertyByPath('thats').eql('nice');
			router.on('urlchange').should.be.finally.propertyByPath('thats').eql('nice');
			router.on('urlchange').then(({thats}) => {
				return `#${thats}#`;
			}).should.be.finally.eql('#nice#');
			// test promise count
			router.promise.urlchange.should.be.length(3);

			router.trigger('urlchange', { thats: 'nice' });
			spy_handler.callCount.should.equal(1);
			router.promise.urlchange.should.be.length(0);

			// test with trigger args
			router.trigger('urlchange', { hello: 'world' });
			spy_handler.callCount.should.equal(2);
			spy_handler.args[1][0].should.propertyByPath('hello').eql('world');

			// need this timeout for promise tests
			setTimeout(() => {
				// cleanup
				router.destroy();
				done();
			}, 10);

		});

		it('should assign routes correctly', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});

			router.on('Startpage /index.html');
			router.on('Resultpage /results.html');
			router.on('Detailpage /details.html');
			router.on('Configurator /configurator.html');

			// test routes count
			Object.keys(router.getRoutes('static')).should.be.length(4);

			// add registered route
			router.on('Detailpage /details.html');
			// routes count must be the same
			Object.keys(router.getRoutes('static')).should.be.length(4);

			// cleanup
			router.destroy();

		});

		it('should trigger correct handler on explicitly triggering', () => {

			let router = Router.create({
				scope: document.createElement('div'),
			});

			let spy_startpage_handler = sinon.spy(() => {});
			let spy_resultpage_handler = sinon.spy(() => {});
			let spy_detailpage_handler = sinon.spy(() => {});
			let spy_configurator_handler = sinon.spy(() => {});

			router.on('Startpage /index.html', spy_startpage_handler);
			router.on('Resultpage /results.html', spy_resultpage_handler);
			router.on('Detailpage /details.html', spy_detailpage_handler);
			router.on('Configurator /configurator.html', spy_configurator_handler);
			// router.on('Product /product/{{id}}-{{name}}.html', spy_configurator_handler);

			router.trigger('Startpage');
			router.trigger('Resultpage');
			router.trigger('Resultpage');
			router.trigger('Detailpage');
			router.trigger('Configurator');
			// router.trigger('Product', { id: 123, name: 'foo' });
			// router.trigger('Product', { id: 123 }); // should throw error because name missing

			spy_startpage_handler.callCount.should.equal(1);
			spy_resultpage_handler.callCount.should.equal(2);
			spy_detailpage_handler.callCount.should.equal(1);
			spy_configurator_handler.callCount.should.equal(1);

			router.destroy();

		});

		it.skip('should route to correct href if handler is linkname (idea not implemented)', () => {

			let router = Router.create({
				scope: document.createElement('div'),
			});

			router.on('Google /index.html', 'https://www.google.de');
			router.on('AbsoluteURL /absolute.html', '/absolute-internal-url.html');
			router.on('RelativeURL /relative.html', 'relative-internal-url.html');

			router.trigger('Google');
			router.trigger('AbsoluteURL');
			router.trigger('RelativeURL');

		});

	});

	describe('prototype._isDynamicURL method', () => {

		it('should return true if has variable in url otherwise false', () => {

			let router = Router.create();

			router._isDynamicURL('{{a}}').should.be.true();
			router._isDynamicURL('a').should.be.false();

			router.destroy();

		});

	});

	describe('prototype._convertURLToRegex method', () => {

		it('should convert passed url to regex', () => {

			let router = Router.create();

			router._convertURLToXRegexExp('{{year}}').should.be.equal('(?<year>.*?)');
			router._convertURLToXRegexExp('{{hour}}:{{min}}').should.be.equal('(?<hour>.*?):(?<min>.*?)');
			router._convertURLToXRegexExp('{{a}}/{{b}}/{{c}}').should.be.equal('(?.*?)\\/(?.*?)\\/(?<c>.*?)');

			router.destroy();

		});

	});

	describe('prototype._matchURL', () => {

		it('should return matchedObject by passed fragment', () => {

			let router = Router.create();
			router._addRoute('/this/is/a/route/1', 'route1');

			// return matched object
			router._matchURL('/this/is/a/route/1').should.containEql({
				name: 'route1', params: {}
			});
			// return not matched object
			router._matchURL('/not/added/route').should.containEql({
				name: null, params: {}
			});

			router.destroy();

		});

	});

	describe('click on anchor or brower.back()', () => {

		it('should call handler _onUrlchange', () => {

			// setup
			// http://stackoverflow.com/questions/25578112/spying-on-a-method-with-sinon-method-bound-to-event-listener-method-was-execut#25578185
			let spy_onUrlchange = sinon.spy(Router.prototype, "_onUrlchange");
			let spy_urlchange = sinon.spy(() => {});
			let element = null;
			element = document.createElement("div");
			let router = Router.create({
				scope: element,
			});

			router.on('urlchange', spy_urlchange);

			element.classList.add('anchor-container');
			element.innerHTML = `
				 Index
				 Details
				 Params
				 Params
			`;

			document.body.appendChild(element);

			// run test
			element.querySelector('.foo').click(); spy_onUrlchange.callCount.should.equal(1);
			element.querySelector('.bar').click(); spy_onUrlchange.callCount.should.equal(2);
			element.querySelector('.baz').click(); spy_onUrlchange.callCount.should.equal(3);
			element.querySelector('.qux').click(); spy_onUrlchange.callCount.should.equal(4);
			element.querySelector('.qux').click(); spy_onUrlchange.callCount.should.equal(4);

			spy_urlchange.callCount.should.equal(4);

			// test pushstate works. If not work we are on different site (out of tests)
			element.querySelectorAll('.foo').should.length(1);

			// test popstate
			// chrome: with karma + mocha it does not work (.back/forward)!
			// Dont know why!? I have tested for Chrome manually and its works
			router.back();
			router.back();
			router.back();

			console.log('Should 7:', spy_onUrlchange.callCount);

			// cleanup
			document.body.removeChild(element);
			router._onUrlchange.restore();
			router.destroy();

		});

		it('should match registered route (static pathname) without dynamic parameter', (done) => {

			let element = null;
			element = document.createElement("div");
			let router = Router.create({
				scope: element,
			});
			element.classList.add('anchor-container');
			element.innerHTML = `
				 Index
				 Some path URL
				 Not registered URL
			`;
			document.body.appendChild(element);

			let spy_index_handler = sinon.spy(() => {});
			let spy_somePathURL_handler = sinon.spy(() => {});

			// register by handler
			router.on('Index /index.html', spy_index_handler);
			router.on('SomePathURL /some/path/url.html', spy_somePathURL_handler);
			// register by promise
			router.on('SomePathURL /some/path/url.html').then(() => {
				return 'matched';
			}).should.be.finally.eql('matched');

			element.querySelector('.index').click();
			element.querySelector('.some-path-url').click();
			element.querySelector('.not-registered-url').click();

			spy_index_handler.callCount.should.equal(1);
			spy_somePathURL_handler.callCount.should.equal(1);

			element.querySelector('.some-path-url').click();

			spy_index_handler.callCount.should.equal(1);
			spy_somePathURL_handler.callCount.should.equal(2);

			// need this timeout for promise tests
			setTimeout(() => {
				// cleanup
				document.body.removeChild(element);
				router.destroy();
				done();
			}, 10);

		});

	});

});
<commit_msg>test/libs/router.spec.js: move test<commit_after>
// internal libs
import Router from 'src/apps/router';
import { XRegExp } from 'src/libs/dependencies';

describe('Class Router', () => {

	describe('prototype._isDynamicURL method', () => {

		it('should return true if has variable in url otherwise false', () => {

			let router = Router.create();

			router._isDynamicURL('{{a}}').should.be.true();
			router._isDynamicURL('a').should.be.false();

			router.destroy();

		});

	});

	describe('prototype._addRoute', () => {

		it('should add route and name to prototype._routes', () => {

			let router = Router.create();

			// add static routes
			router._addRoute('/this/is/a/route/1', 'name1');
			router._addRoute('/this/is/a/route/2', 'name2');
			router._addRoute('/this/is/{{a}}/route/4', 'name4');
			router._addRoute('/this/is/{{b}}/route/5', 'name5');
			router._addRoute('/this/is/{{c}}/route/6', 'name6');

			// this should not be added because it can exists only one route
			router._addRoute('/this/is/a/route/2', 'name2');
			router._addRoute('/this/is/{{a}}/route/4', 'name4');

			// Test: contain correct added routes
			router.getRoutes('static').should.containEql({
				'/this/is/a/route/1': 'name1',
				'/this/is/a/route/2': 'name2',
			});

			router.getRoutes('dynamic').should.containEql({
				'/this/is/{{a}}/route/4': 'name4',
				'/this/is/{{b}}/route/5': 'name5',
				'/this/is/{{c}}/route/6': 'name6',
			});

			router.destroy();

		});

	});

	describe('prototype._addPromise', () => {

		it('should add and create promise collection on prototype.promise', () => {

			let router = Router.create();

			let promise1 = router._addPromise('name1');
			let promise2 = router._addPromise('name2');

			promise1.should.be.Promise();
			promise2.should.be.Promise();

			router.promise.should.have.propertyByPath('name1', 0).and.is.Function(); // resolve function
			router.promise.should.have.propertyByPath('name2', 0).and.is.Function(); // resolve function

			// cleanup: resolve promises
			router.promise.name1[0]();
			router.promise.name2[0]();
			// cleanup: destroy router
			router.destroy();

		});
	});

	describe('prototype._urlFragmentChanged', () => {

		it('should check if fragment is changed in combination with _setURLFragment', () => {

			let router = Router.create();

			router._setURLFragment('/');
			router._urlFragmentChanged('/some/url/fragment').should.be.true();

			router._setURLFragment('/some/url/fragment');
			router._urlFragmentChanged('/some/url/fragment').should.be.false();

			router.destroy();

		});

	});

	describe('prototype.createURL', () => {

		it('should return url object with additional "fragment" property', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			let url = router.createURL('http://www.mydomain.com/path/to/somewhere.html?a=1&b=2');
			url.fragment.should.equal('/path/to/somewhere.html?a=1&b=2');
			// cleanup
			router.destroy();

		});

	});

	describe('prototype.Promise', () => {

		it('should return promise object', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			router.Promise(function(){}).should.be.Promise();
			// cleanup
			router.destroy();

		});

	});

	describe('prototype.off', () => {

	});

	describe('prototype.on', () => {

		it('should throw an error if no argeument passed', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on() }).should.throw();
			// cleanup
			router.destroy();

		});

		it('should not throw an error if no handler passed', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on('urlchange') }).should.not.throw();
			// cleanup
			router.destroy();

		});

		it('should not throw an error if passed event with handler', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			(() => { router.on('someevent', () => {}) }).should.not.throw();
			// cleanup
			router.destroy();

		});

		it('should return promise if passed only event', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			router.on('urlchange').should.be.Promise();
			router.on('urlchange').then(() => {}).should.be.Promise();
			// cleanup
			router.destroy();

		});

		it('should return null if passed event and handler', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			// test
			should(router.on('my route', (event) => {})).be.null();
			// cleanup
			router.destroy();

		});

		it('should handle promise or handler correctly', (done) => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});
			let spy_handler = sinon.spy(() => {});
			// test wihout trigger args
			router.on('urlchange', spy_handler);

			// test if promise resolved
			router.on('urlchange').should.be.finally.propertyByPath('thats').eql('nice');
			router.on('urlchange').should.be.finally.propertyByPath('thats').eql('nice');
			router.on('urlchange').then(({thats}) => {
				return `#${thats}#`;
			}).should.be.finally.eql('#nice#');
			// test promise count
			router.promise.urlchange.should.be.length(3);

			router.trigger('urlchange', { thats: 'nice' });
			spy_handler.callCount.should.equal(1);
			router.promise.urlchange.should.be.length(0);

			// test with trigger args
			router.trigger('urlchange', { hello: 'world' });
			spy_handler.callCount.should.equal(2);
			spy_handler.args[1][0].should.propertyByPath('hello').eql('world');

			// need this timeout for promise tests
			setTimeout(() => {
				// cleanup
				router.destroy();
				done();
			}, 10);

		});

		it('should assign routes correctly', () => {

			// setup
			let router = Router.create({
				scope: document.createElement('div'),
			});

			router.on('Startpage /index.html');
			router.on('Resultpage /results.html');
			router.on('Detailpage /details.html');
			router.on('Configurator /configurator.html');

			// test routes count
			Object.keys(router.getRoutes('static')).should.be.length(4);

			// add registered route
			router.on('Detailpage /details.html');
			// routes count must be the same
			Object.keys(router.getRoutes('static')).should.be.length(4);

			// cleanup
			router.destroy();

		});

		it('should trigger correct handler on explicitly triggering', () => {

			let router = Router.create({
				scope: document.createElement('div'),
			});

			let spy_startpage_handler = sinon.spy(() => {});
			let spy_resultpage_handler = sinon.spy(() => {});
			let spy_detailpage_handler = sinon.spy(() => {});
			let spy_configurator_handler = sinon.spy(() => {});

			router.on('Startpage /index.html', spy_startpage_handler);
			router.on('Resultpage /results.html', spy_resultpage_handler);
			router.on('Detailpage /details.html', spy_detailpage_handler);
			router.on('Configurator /configurator.html', spy_configurator_handler);
			// router.on('Product /product/{{id}}-{{name}}.html', spy_configurator_handler);

			router.trigger('Startpage');
			router.trigger('Resultpage');
			router.trigger('Resultpage');
			router.trigger('Detailpage');
			router.trigger('Configurator');
			// router.trigger('Product', { id: 123, name: 'foo' });
			// router.trigger('Product', { id: 123 }); // should throw error because name missing

			spy_startpage_handler.callCount.should.equal(1);
			spy_resultpage_handler.callCount.should.equal(2);
			spy_detailpage_handler.callCount.should.equal(1);
			spy_configurator_handler.callCount.should.equal(1);

			router.destroy();

		});

		it.skip('should route to correct href if handler is linkname (idea not implemented)', () => {

			let router = Router.create({
				scope: document.createElement('div'),
			});

			router.on('Google /index.html', 'https://www.google.de');
			router.on('AbsoluteURL /absolute.html', '/absolute-internal-url.html');
			router.on('RelativeURL /relative.html', 'relative-internal-url.html');

			router.trigger('Google');
			router.trigger('AbsoluteURL');
			router.trigger('RelativeURL');

		});

	});

	describe('prototype._convertURLToRegex method', () => {

		it('should convert passed url to regex', () => {

			let router = Router.create();

			router._convertURLToXRegexExp('{{year}}').should.be.equal('(?<year>.*?)');
			router._convertURLToXRegexExp('{{hour}}:{{min}}').should.be.equal('(?<hour>.*?):(?<min>.*?)');
			router._convertURLToXRegexExp('{{a}}/{{b}}/{{c}}').should.be.equal('(?.*?)\\/(?.*?)\\/(?<c>.*?)');

			router.destroy();

		});

	});

	describe('prototype._matchURL', () => {

		it('should return matchedObject by passed fragment', () => {

			let router = Router.create();
			router._addRoute('/this/is/a/route/1', 'route1');

			// return matched object
			router._matchURL('/this/is/a/route/1').should.containEql({
				name: 'route1', params: {}
			});
			// return not matched object
			router._matchURL('/not/added/route').should.containEql({
				name: null, params: {}
			});

			router.destroy();

		});

	});

	describe('click on anchor or brower.back()', () => {

		it('should call handler _onUrlchange', () => {

			// setup
			// http://stackoverflow.com/questions/25578112/spying-on-a-method-with-sinon-method-bound-to-event-listener-method-was-execut#25578185
			let spy_onUrlchange = sinon.spy(Router.prototype, "_onUrlchange");
			let spy_urlchange = sinon.spy(() => {});
			let element = null;
			element = document.createElement("div");
			let router = Router.create({
				scope: element,
			});

			router.on('urlchange', spy_urlchange);

			element.classList.add('anchor-container');
			element.innerHTML = `
				 Index
				 Details
				 Params
				 Params
			`;

			document.body.appendChild(element);

			// run test
			element.querySelector('.foo').click(); spy_onUrlchange.callCount.should.equal(1);
			element.querySelector('.bar').click(); spy_onUrlchange.callCount.should.equal(2);
			element.querySelector('.baz').click(); spy_onUrlchange.callCount.should.equal(3);
			element.querySelector('.qux').click(); spy_onUrlchange.callCount.should.equal(4);
			element.querySelector('.qux').click(); spy_onUrlchange.callCount.should.equal(4);

			spy_urlchange.callCount.should.equal(4);

			// test pushstate works. If not work we are on different site (out of tests)
			element.querySelectorAll('.foo').should.length(1);

			// test popstate
			// chrome: with karma + mocha it does not work (.back/forward)!
			// Dont know why!? I have tested for Chrome manually and its works
			router.back();
			router.back();
			router.back();

			console.log('Should 7:', spy_onUrlchange.callCount);

			// cleanup
			document.body.removeChild(element);
			router._onUrlchange.restore();
			router.destroy();

		});

		it('should match registered route (static pathname) without dynamic parameter', (done) => {

			let element = null;
			element = document.createElement("div");
			let router = Router.create({
				scope: element,
			});
			element.classList.add('anchor-container');
			element.innerHTML = `
				 Index
				 Some path URL
				 Not registered URL
			`;
			document.body.appendChild(element);

			let spy_index_handler = sinon.spy(() => {});
			let spy_somePathURL_handler = sinon.spy(() => {});

			// register by handler
			router.on('Index /index.html', spy_index_handler);
			router.on('SomePathURL /some/path/url.html', spy_somePathURL_handler);
			// register by promise
			router.on('SomePathURL /some/path/url.html').then(() => {
				return 'matched';
			}).should.be.finally.eql('matched');

			element.querySelector('.index').click();
			element.querySelector('.some-path-url').click();
			element.querySelector('.not-registered-url').click();

			spy_index_handler.callCount.should.equal(1);
			spy_somePathURL_handler.callCount.should.equal(1);

			element.querySelector('.some-path-url').click();

			spy_index_handler.callCount.should.equal(1);
			spy_somePathURL_handler.callCount.should.equal(2);

			// need this timeout for promise tests
			setTimeout(() => {
				// cleanup
				document.body.removeChild(element);
				router.destroy();
				done();
			}, 10);

		});

	});

});
<|endoftext|>


<commit_before>'use strict';

/* Object containing all feature methods to
 be added to the String class*/
const stringExtension = {
  /**
  * Check if this String contains any of the English vowels
  * (in upper or lower case).
  * @return{Boolean} - True if this String contains a vowel
  * otherwise False.
  */
  hasVowels() {
    const match = /[aeiou]/i;
    return match.test(this);
  },
  /**
  * Replaces all lower case alphabets in this String with uppercase
  * @return{String} - This String with all lower case alphabets
  * converted to upper case
  */
  toUpper() {
    return this.replace(/[a-z]/g, match =>
      String.fromCharCode(match.charCodeAt() - 32));
  },
  /**
  * Replaces all upper case alphabets in this String with lower case
  * @return{String} - This String with all upper case alphabets transformed
  * to lower case
  */
  toLower() {
    return this.replace(/[A-Z]/g, match =>
      String.fromCharCode(match.charCodeAt() + 32));
  },
  /**
  * Transforms the first character of the String (if it's an alphabet)
  * from lower case to upper case.
  * @return{String} - This string with it's first character (if it's an
  * alphabet) converted to upper case.
  */
  ucFirst() {
    return this.replace(/^[a-z]/, match => match.toUpper());
  },
  /**
  * Check if this String ends with a question (?) mark
  * @return{Boolean} - True if this string ends with a question mark
  * otherwise False.
  */
  isQuestion() {
    const match = /\?$/;
    return match.test(this);
  },
  /**
  * Fetch an array containing all the words in this String
  * @return{Array} - An array containing all the words in this string
  */
  words() {
    // remove trailing white spaces
    return this.trim()
      .split(/\s+/g);
  },
  /**
  * Fetch the number of words in this String
  * @return{Number} - The number of words in this String
  */
  wordCount() {
    return this.words().length;
  },
  /**
  * Create a currency representation of this String
  * @return{String} - A String formated to Currency
  */
  toCurrency() {
    // get the mantissa and characteristic
    const parts = this.split('.');
    if (parts.length > 2) {
      throw new Error('Invalid String');
    }
    const mantissa = parts[1] ? parts[1] : '00';
    const builtCharacteristic =
      parts[0].replace(/\B(?=([0-9]{3})+(?![0-9]))/g, ',');
    return `${builtCharacteristic}.${mantissa}`;
  },
  /**
  * Create a number from this currency formatted String
  * @return{Number} - A Number representation of this String
  */
  fromCurrency() {
    return Number(this.replace(/,/g, ''));
  },
  /**
  * @return{String} - Returns each alphabetic character in this String
  * as an inverse of its current case
  */
  inverseCase() {
    return this.replace(/[a-zA-Z]/g, (match) => {
      if (match.charCodeAt() > 90) {
        return match.toUpper();
      }
      return match.toLower();
    });
  },
  /**
  * @return{String} - Returns the alphabetic characters in this String
  * in alternating cases, starting with a lower case
  */
  alternatingCase() {
    let toSmall = true;
    return this.replace(/[a-zA-Z]/g, (match) => {
      if (toSmall) {
        toSmall = false;
        return match.toLower();
      }
      toSmall = true;
      return match.toUpper();
    });
  },
  /**
  * Fetch characters in the middle of a String. Double character for
  * even length String and single character for odd length String
  * @return{String} - The character(s) in the middle of the string
  */
  getMiddle() {
    const halfLength = Math.floor(this.length / 2);
    if (this.length % 2 === 0) { // String with even length
      return this.substring(halfLength - 1, halfLength + 1);
    }
    // String with odd length
    return this.substring(halfLength, halfLength + 1);
  },
  /**
  * converts all Numbers in this String to their
  * English word
  * @return{String} - This String with all Numbers contained in it
  * transformed to their English word
  */
  numberWords() {
    const numberText = [
      'zero ',
      'one ',
      'two ',
      'three ',
      'four ',
      'five ',
      'six ',
      'seven ',
      'eight ',
      'nine '
    ];
    const convertedString = this.replace(/[0-9]/g, match =>
      numberText[match]);
    return convertedString.replace(/\s$/, '');
  },
  /**
  * Checks if a String is a digit (one number)
  * @return{Boolean} - True if the String is a single digit
  * False otherwise
  */
  isDigit() {
    const match = /^[0-9]$/;
    return match.test(this);
  },
  /**
  * Returns true if this String contains any character repeated twice
  * in a sequence (including whitespace characters)
  * @return{Boolean} - True if this String contains double characters
  * otherwise False
  */
  doubleCheck() {
    const match = /(.)\1/g;
    return match.test(this);
  }
};

// Assign new methods to the String class
Object.assign(String.prototype, stringExtension);
<commit_msg>refactor words method to handle empty Strings<commit_after>'use strict'

/* Object containing all feature methods to
 be added to the String class*/
const stringExtension = {
  /**
  * Check if this String contains any of the English vowels
  * (in upper or lower case).
  * @return{Boolean} - True if this String contains a vowel
  * otherwise False.
  */
  hasVowels() {
    const match = /[aeiou]/i;
    return match.test(this);
  },
  /**
  * Replaces all lower case alphabets in this String with uppercase
  * @return{String} - This String with all lower case alphabets
  * converted to upper case
  */
  toUpper() {
    return this.replace(/[a-z]/g, match =>
      String.fromCharCode(match.charCodeAt() - 32));
  },
  /**
  * Replaces all upper case alphabets in this String with lower case
  * @return{String} - This String with all upper case alphabets transformed
  * to lower case
  */
  toLower() {
    return this.replace(/[A-Z]/g, match =>
      String.fromCharCode(match.charCodeAt() + 32));
  },
  /**
  * Transforms the first character of the String (if it's an alphabet)
  * from lower case to upper case.
  * @return{String} - This string with it's first character (if it's an
  * alphabet) converted to upper case.
  */
  ucFirst() {
    return this.replace(/^[a-z]/, match => match.toUpper());
  },
  /**
  * Check if this String ends with a question (?) mark
  * @return{Boolean} - True if this string ends with a question mark
  * otherwise False.
  */
  isQuestion() {
    const match = /\?$/;
    return match.test(this);
  },
  /**
  * Fetch an array containing all the words in this String
  * @return{Array} - An array containing all the words in this string
  */
  words() {
    // Check for empty String
    if (!this) {
      return [];
    }
    // remove trailing white spaces
    return this.trim()
      .split(/\s+/g);
  },
  /**
  * Fetch the number of words in this String
  * @return{Number} - The number of words in this String
  */
  wordCount() {
    return this.words().length;
  },
  /**
  * Create a currency representation of this String
  * @return{String} - A String formated to Currency
  */
  toCurrency() {
    // get the mantissa and characteristic
    const parts = this.split('.');
    if (parts.length > 2) {
      throw new Error('Invalid String');
    }
    const mantissa = parts[1] ? parts[1] : '00';
    const builtCharacteristic =
      parts[0].replace(/\B(?=([0-9]{3})+(?![0-9]))/g, ',');
    return `${builtCharacteristic}.${mantissa}`;
  },
  /**
  * Create a number from this currency formatted String
  * @return{Number} - A Number representation of this String
  */
  fromCurrency() {
    return Number(this.replace(/,/g, ''));
  },
  /**
  * @return{String} - Returns each alphabetic character in this String
  * as an inverse of its current case
  */
  inverseCase() {
    return this.replace(/[a-zA-Z]/g, (match) => {
      if (match.charCodeAt() > 90) {
        return match.toUpper();
      }
      return match.toLower();
    });
  },
  /**
  * @return{String} - Returns the alphabetic characters in this String
  * in alternating cases, starting with a lower case
  */
  alternatingCase() {
    let toSmall = true;
    return this.replace(/[a-zA-Z]/g, (match) => {
      if (toSmall) {
        toSmall = false;
        return match.toLower();
      }
      toSmall = true;
      return match.toUpper();
    });
  },
  /**
  * Fetch characters in the middle of a String. Double character for
  * even length String and single character for odd length String
  * @return{String} - The character(s) in the middle of the string
  */
  getMiddle() {
    const halfLength = Math.floor(this.length / 2);
    if (this.length % 2 === 0) { // String with even length
      return this.substring(halfLength - 1, halfLength + 1);
    }
    // String with odd length
    return this.substring(halfLength, halfLength + 1);
  },
  /**
  * converts all Numbers in this String to their
  * English word
  * @return{String} - This String with all Numbers contained in it
  * transformed to their English word
  */
  numberWords() {
    const numberText = [
      'zero ',
      'one ',
      'two ',
      'three ',
      'four ',
      'five ',
      'six ',
      'seven ',
      'eight ',
      'nine '
    ];
    const convertedString = this.replace(/[0-9]/g, match =>
      numberText[match]);
    return convertedString.replace(/\s$/, '');
  },
  /**
  * Checks if a String is a digit (one number)
  * @return{Boolean} - True if the String is a single digit
  * False otherwise
  */
  isDigit() {
    const match = /^[0-9]$/;
    return match.test(this);
  },
  /**
  * Returns true if this String contains any character repeated twice
  * in a sequence (including whitespace characters)
  * @return{Boolean} - True if this String contains double characters
  * otherwise False
  */
  doubleCheck() {
    const match = /(.)\1/g;
    return match.test(this);
  }
};

// Assign new methods to the String class
Object.assign(String.prototype, stringExtension);
<|endoftext|>


<commit_before>/* NODE MODULES */
var net = require('net'),
    tcpEventify = require('../utils/tcpEventify');

/** LOCAL OBJECT
 * @property {string} basepath - The path to look for unix sockets
 * @property {object} retry - Describes the number and delay between connection retries
 */
var FIND = {
    basepath: __dirname + '/../sockets/',
    retry: {
        attempts: 10,
        timeout: {
            local: 100,
            remote: 1000
        }
    }
};

/** MODULE INTERFACE
 *@method {function} globalNamespace - Looks for an existing global namespace
 */
module.exports = {
    globalNamespace: globalNamespace
};

/*----------------------------------------------------------------------------*/

/** Looks for an existing global namespace
 * @param {string} name - The name of the namespace
 * @returns {function} found - A function to be called when the namespace is found
 */
function globalNamespace(name, found) {
    console.log('Searching global namespace "/' + name + '"');
    connectToNamespace(name, found, undefined, FIND.retry.timeout.remote);
}

/** Connects to a unix-socket-based namespace
 * @param {string} name - The name of the namespace
 * @returns {function} found - A function to be called when the namespace is found
 * @param {number} maxAttempts - The max number of connection attempts to be done
 * @param {number} retryTimeout - The delay between unsuccessful connection attempts
 * @returns
 */
function connectToNamespace(name, found, maxAttempts, retryTimeout) {
    var numAttempts = maxAttempts || FIND.retry.attempts,
        timeout = retryTimeout || FIND.retry.timeout,
        namespace,
        attempts = 0,
        socket;

    // Try to connect to the namespace's unix socket server
    (function connect() {
        socket = net.connect(FIND.basepath + name + '.sock', function () {
            var eventedSocket = tcpEventify(socket);
            // Expose the socket methods through the 'emit' and 'on' methods
            // of the namespace object to be returned
            namespace = {
                emit: function (eventName, message) {
                    eventedSocket.send(eventName, message);
                },
                on: function (eventName, listener) {
                    eventedSocket.send('on', eventName);
                    eventedSocket.on(eventName, listener);
                }
            };
            // Return the found namespace object by invoking the callback
            found(namespace);
        });
        socket.on('error', function (err) {
            // Retry on unsuccessful connection attempts
            if (err.errno === 'ECONNREFUSED') {
                if (++attempts <= numAttempts) {
                    setTimeout(function () {
                        connect();
                    }, timeout);
                }
            }
        });
    })();
}<commit_msg>Specified the use of local timeout on retry attempts.<commit_after>/* NODE MODULES */
var net = require('net'),
    tcpEventify = require('../utils/tcpEventify');

/** LOCAL OBJECT
 * @property {string} basepath - The path to look for unix sockets
 * @property {object} retry - Describes the number and delay between connection retries
 */
var FIND = {
    basepath: __dirname + '/../sockets/',
    retry: {
        attempts: 10,
        timeout: {
            local: 100,
            remote: 1000
        }
    }
};

/** MODULE INTERFACE
 *@method {function} globalNamespace - Looks for an existing global namespace
 */
module.exports = {
    globalNamespace: globalNamespace
};

/*----------------------------------------------------------------------------*/

/** Looks for an existing global namespace
 * @param {string} name - The name of the namespace
 * @returns {function} found - A function to be called when the namespace is found
 */
function globalNamespace(name, found) {
    console.log('Searching global namespace "/' + name + '"');
    connectToNamespace(name, found, undefined, FIND.retry.timeout.remote);
}

/** Connects to a unix-socket-based namespace
 * @param {string} name - The name of the namespace
 * @returns {function} found - A function to be called when the namespace is found
 * @param {number} maxAttempts - The max number of connection attempts to be done
 * @param {number} retryTimeout - The delay between unsuccessful connection attempts
 * @returns
 */
function connectToNamespace(name, found, maxAttempts, retryTimeout) {
    var numAttempts = maxAttempts || FIND.retry.attempts,
        timeout = retryTimeout || FIND.retry.timeout.local,
        namespace,
        attempts = 0,
        socket;

    // Try to connect to the namespace's unix socket server
    (function connect() {
        socket = net.connect(FIND.basepath + name + '.sock', function () {
            var eventedSocket = tcpEventify(socket);
            // Expose the socket methods through the 'emit' and 'on' methods
            // of the namespace object to be returned
            namespace = {
                emit: function (eventName, message) {
                    eventedSocket.send(eventName, message);
                },
                on: function (eventName, listener) {
                    eventedSocket.send('on', eventName);
                    eventedSocket.on(eventName, listener);
                }
            };
            // Return the found namespace object by invoking the callback
            found(namespace);
        });
        socket.on('error', function (err) {
            // Retry on unsuccessful connection attempts
            if (err.errno === 'ECONNREFUSED') {
                if (++attempts <= numAttempts) {
                    setTimeout(function () {
                        connect();
                    }, timeout);
                }
            }
        });
    })();
}<|endoftext|>


<commit_before>;jQuery(document).ready(function ($) {
    // Хук начала инициализации javascript-составляющих шаблона
    ls.hook.run('ls_template_init_start', [], window);

    $('html').removeClass('no-js');

    // Определение браузера
    if ($.browser.opera) {
        $('body').addClass('opera opera' + parseInt($.browser.version));
    }
    if ($.browser.mozilla) {
        $('body').addClass('mozilla mozilla' + parseInt($.browser.version));
    }
    if ($.browser.webkit) {
        $('body').addClass('webkit webkit' + parseInt($.browser.version));
    }
    if ($.browser.msie) {
        $('body').addClass('ie');
        if (parseInt($.browser.version) > 8) {
            $('body').addClass('ie' + parseInt($.browser.version));
        }
    }

    // Всплывающие окна
    $('.js-modal-auth-login').click(function () {
        $('#modal-auth').modal();
        var tab = $('#modal-auth .js-tab-login').tab('show');
        return false;
    });

    $('.js-modal-auth-registration').click(function () {
        $('#modal-auth').modal();
        $('#modal-auth .js-tab-registration').tab('show');
        $('.captcha-image').prop('src', ls.routerUrl('captcha') + '?n=' + Math.random());
        return false;
    });

    $('.js-modal-blog_delete').click(function () {
        ls.modal.show('#modal-blog_delete');
        return false;
    });

    $('[id^=modal-].modal').on('shown.bs.modal', function(e){
        $(this).find('.js-focus-in:visible').first().focus();
    });

    /* Special toggles */
    $('[data-toggle=file][data-target]').each(function () {
        var target = $($(this).data('target')).first();
        if (target.length && target.is('input') && target.attr('type') == 'file') {
            target.css({opacity: 0, position: 'absolute', width: 0});
        }
        $(this).click(function () {
            var target = $($(this).data('target')).first();
            if (target.length && target.is('input') && target.attr('type') == 'file') {
                target.click();
            }
            return false;
        });
    });

    /* Datepicker */
    /**
     * TODO: навесить языки на datepicker
     */
    $('.date-picker').datepicker({
        dateFormat: 'dd.mm.yy',
        dayNamesMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
        monthNames: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
        firstDay: 1
    });

    // Поиск по тегам
    $('.js-tag-search-form').submit(function () {
        window.location = ls.routerUrl('tag') + encodeURIComponent($(this).find('.js-tag-search').val()) + '/';
        return false;
    });

    // Автокомплит
    ls.autocomplete.add($(".autocomplete-tags-sep,.js-autocomplete-tags-sep"), ls.routerUrl('ajax') + 'autocompleter/tag/', true);
    ls.autocomplete.add($(".autocomplete-tags,.js-autocomplete-tags"), ls.routerUrl('ajax') + 'autocompleter/tag/', false);
    ls.autocomplete.add($(".autocomplete-users-sep,.js-autocomplete-users-sep"), ls.routerUrl('ajax') + 'autocompleter/user/', true);
    ls.autocomplete.add($(".autocomplete-users,.js-autocomplete-users"), ls.routerUrl('ajax') + 'autocompleter/user/', false);

    // Autofocus
    $('form').each(function(){
        $(this).find('.js-focus-in:visible').first().focus();
    });

    // Stylization of [type=file]
    $('body').on('change', '.btn-file input[type=file]', function(){
        var input = $(this),
            value = input.val(), // get value
            pos = value.lastIndexOf('/') > value.lastIndexOf('\\') ? value.lastIndexOf('/') : value.lastIndexOf('\\'),
            fileName = value.substring(pos + 1); // get file name
        // remove existing file info
        input.next().remove();
        // append file info
        $(': ' + fileName + '').insertAfter(input);
    });

    // Скролл
    $(window)._scrollable();

    // Тул-бар топиков
    ls.toolbar.topic.init();
    // Кнопка "UP"
    ls.toolbar.up.init();

    $('.js-title-comment, .js-title-topic').tooltip({
        placement: 'left'
    });

    $('.js-tip-help').tooltip({
        placement: 'right'
    });

    /*
    // Всплывающие сообщения
    if (ls.registry.get('block_stream_show_tip')) {
        $('.js-title-comment, .js-title-topic').poshytip({
            className: 'infobox-yellow',
            alignTo: 'target',
            alignX: 'left',
            alignY: 'center',
            offsetX: 10,
            liveEvents: true,
            showTimeout: 1000
        });
    }

    $('.js-title-talk').poshytip({
        className: 'infobox-yellow',
        alignTo: 'target',
        alignX: 'left',
        alignY: 'center',
        offsetX: 10,
        liveEvents: true,
        showTimeout: 500
    });

    $('.js-infobox-vote-topic').poshytip({
        content: function () {
            var id = $(this).attr('id').replace('vote_total_topic_', 'vote-info-topic-');
            return $('#' + id).html();
        },
        className: 'infobox-standart',
        alignTo: 'target',
        alignX: 'center',
        alignY: 'top',
        offsetX: 2,
        liveEvents: true,
        showTimeout: 100
    });

    $('.js-tip-help').poshytip({
        className: 'infobox-standart',
        alignTo: 'target',
        alignX: 'right',
        alignY: 'center',
        offsetX: 5,
        liveEvents: true,
        showTimeout: 500
    });

    $('.js-infobox').poshytip({
        className: 'infobox-standart',
        liveEvents: true,
        showTimeout: 300
    });
    */

    // подсветка кода
    prettyPrint();

    var inputs = $('input.input-text, textarea');
    // эмуляция border-sizing в IE 7-
    // ls.ie.bordersizing(inputs);
    // эмуляция placeholder'ов в IE
    inputs.placeholder();

    // комментарии
    //ls.comments.options.folding = false;
    //ls.comments.init();

    // избранное
    ls.hook.add('ls_favourite_toggle_after', function (idTarget, objFavourite, type, params, result) {
        $('#fav_count_' + type + '_' + idTarget).text((result.iCount > 0) ? result.iCount : '');
    });

    // лента активности
    //ls.hook.add('ls_stream_append_user_after', function (length, data) {
    //    if (length == 0) {
    //        $('#strm_u_' + data.uid).parent().find('a').before(' ');
    //    }
    //});

    // опрос
    ls.hook.add('ls_pool_add_answer_after', function (removeAnchor) {
        var removeAnchor = $('').attr('title', ls.lang.get('delete')).click(function (e) {
            e.preventDefault();
            return this.removeAnswer(e.target);
        }.bind(ls.poll));
        $(this).find('a').remove();
        $(this).append(removeAnchor);
    });

    /****************
     * TALK
     */

        // Добавляем или удаляем друга из списка получателей
    $('#friends input:checkbox').change(function () {
        ls.talk.toggleRecipient($('#' + $(this).attr('id') + '_label').text(), $(this).attr('checked'));
    });

    // Добавляем всех друзей в список получателей
    $('#friend_check_all').click(function () {
        $('#friends input:checkbox').each(function (index, item) {
            ls.talk.toggleRecipient($('#' + $(item).attr('id') + '_label').text(), true);
            $(item).attr('checked', true);
        });
        return false;
    });

    // Удаляем всех друзей из списка получателей
    $('#friend_uncheck_all').click(function () {
        $('#friends input:checkbox').each(function (index, item) {
            ls.talk.toggleRecipient($('#' + $(item).attr('id') + '_label').text(), false);
            $(item).attr('checked', false);
        });
        return false;
    });

    // Удаляем пользователя из черного списка
    $("#black_list_block").delegate("a.delete", "click", function () {
        ls.talk.removeFromBlackList(this);
        return false;
    });

    // Удаляем пользователя из переписки
    $("#speaker_list_block").delegate("a.delete", "click", function () {
        ls.talk.removeFromTalk(this, $('#talk_id').val());
        return false;
    });

    // Help-tags link
    $('.js-tags-help-link').click(function () {
        var target = ls.registry.get('tags-help-target-id'),
            text='';
        if (!target || !$('#' + target).length) {
            return false;
        }
        target = $('#' + target);
        if ($(this).data('insert')) {
            text = $(this).data('insert');
        } else {
            text = $(this).text();
        }
        $.markItUp({target: target, replaceWith: text});
        return false;
    });

    // Фикс бага с z-index у встроенных видео
    $("iframe").each(function () {
        var ifr_source = $(this).attr('src');

        if (ifr_source) {
            var wmode = "wmode=opaque";

            if (ifr_source.indexOf('?') != -1)
                $(this).attr('src', ifr_source + '&' + wmode);
            else
                $(this).attr('src', ifr_source + '?' + wmode);
        }
    });

    if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
        var msViewportStyle = document.createElement("style");
        msViewportStyle.appendChild(
            document.createTextNode(
                "@-ms-viewport{width:auto!important}"
            )
        );
        document.getElementsByTagName("head")[0].appendChild(msViewportStyle)
    }

    if (tinyMCE && ls.settings && ls.settings.presets.tinymce) {
        var cssUrl = ls.getAssetUrl('template-tinymce.css');
        if (cssUrl) {
            ls.settings.presets.tinymce.default['content_css'] = cssUrl;
        }
    }

    // Хук конца инициализации javascript-составляющих шаблона
    ls.hook.run('ls_template_init_end', [], window);
});
<commit_msg>Исправлена ошибка отображения тултипа в виджете прямого эфира в теме start-kit. Раньше тултип не выводился из-за того, что прямой эфир подгружался после инициализации bootstrap-плагина.<commit_after>;jQuery(document).ready(function ($) {
    // Хук начала инициализации javascript-составляющих шаблона
    ls.hook.run('ls_template_init_start', [], window);

    $('html').removeClass('no-js');

    // Определение браузера
    if ($.browser.opera) {
        $('body').addClass('opera opera' + parseInt($.browser.version));
    }
    if ($.browser.mozilla) {
        $('body').addClass('mozilla mozilla' + parseInt($.browser.version));
    }
    if ($.browser.webkit) {
        $('body').addClass('webkit webkit' + parseInt($.browser.version));
    }
    if ($.browser.msie) {
        $('body').addClass('ie');
        if (parseInt($.browser.version) > 8) {
            $('body').addClass('ie' + parseInt($.browser.version));
        }
    }

    // Всплывающие окна
    $('.js-modal-auth-login').click(function () {
        $('#modal-auth').modal();
        var tab = $('#modal-auth .js-tab-login').tab('show');
        return false;
    });

    $('.js-modal-auth-registration').click(function () {
        $('#modal-auth').modal();
        $('#modal-auth .js-tab-registration').tab('show');
        $('.captcha-image').prop('src', ls.routerUrl('captcha') + '?n=' + Math.random());
        return false;
    });

    $('.js-modal-blog_delete').click(function () {
        ls.modal.show('#modal-blog_delete');
        return false;
    });

    $('[id^=modal-].modal').on('shown.bs.modal', function(e){
        $(this).find('.js-focus-in:visible').first().focus();
    });

    /* Special toggles */
    $('[data-toggle=file][data-target]').each(function () {
        var target = $($(this).data('target')).first();
        if (target.length && target.is('input') && target.attr('type') == 'file') {
            target.css({opacity: 0, position: 'absolute', width: 0});
        }
        $(this).click(function () {
            var target = $($(this).data('target')).first();
            if (target.length && target.is('input') && target.attr('type') == 'file') {
                target.click();
            }
            return false;
        });
    });

    /* Datepicker */
    /**
     * TODO: навесить языки на datepicker
     */
    $('.date-picker').datepicker({
        dateFormat: 'dd.mm.yy',
        dayNamesMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
        monthNames: ['Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'],
        firstDay: 1
    });

    // Поиск по тегам
    $('.js-tag-search-form').submit(function () {
        window.location = ls.routerUrl('tag') + encodeURIComponent($(this).find('.js-tag-search').val()) + '/';
        return false;
    });

    // Автокомплит
    ls.autocomplete.add($(".autocomplete-tags-sep,.js-autocomplete-tags-sep"), ls.routerUrl('ajax') + 'autocompleter/tag/', true);
    ls.autocomplete.add($(".autocomplete-tags,.js-autocomplete-tags"), ls.routerUrl('ajax') + 'autocompleter/tag/', false);
    ls.autocomplete.add($(".autocomplete-users-sep,.js-autocomplete-users-sep"), ls.routerUrl('ajax') + 'autocompleter/user/', true);
    ls.autocomplete.add($(".autocomplete-users,.js-autocomplete-users"), ls.routerUrl('ajax') + 'autocompleter/user/', false);

    // Autofocus
    $('form').each(function(){
        $(this).find('.js-focus-in:visible').first().focus();
    });

    // Stylization of [type=file]
    $('body').on('change', '.btn-file input[type=file]', function(){
        var input = $(this),
            value = input.val(), // get value
            pos = value.lastIndexOf('/') > value.lastIndexOf('\\') ? value.lastIndexOf('/') : value.lastIndexOf('\\'),
            fileName = value.substring(pos + 1); // get file name
        // remove existing file info
        input.next().remove();
        // append file info
        $(': ' + fileName + '').insertAfter(input);
    });

    // Скролл
    $(window)._scrollable();

    // Тул-бар топиков
    ls.toolbar.topic.init();
    // Кнопка "UP"
    ls.toolbar.up.init();

    $('body').tooltip({
        selector: '.js-title-comment, .js-title-topic',
        placement: 'left'
    });

    $('.js-tip-help').tooltip({
        placement: 'right'
    });

    /*
    // Всплывающие сообщения
    if (ls.registry.get('block_stream_show_tip')) {
        $('.js-title-comment, .js-title-topic').poshytip({
            className: 'infobox-yellow',
            alignTo: 'target',
            alignX: 'left',
            alignY: 'center',
            offsetX: 10,
            liveEvents: true,
            showTimeout: 1000
        });
    }

    $('.js-title-talk').poshytip({
        className: 'infobox-yellow',
        alignTo: 'target',
        alignX: 'left',
        alignY: 'center',
        offsetX: 10,
        liveEvents: true,
        showTimeout: 500
    });

    $('.js-infobox-vote-topic').poshytip({
        content: function () {
            var id = $(this).attr('id').replace('vote_total_topic_', 'vote-info-topic-');
            return $('#' + id).html();
        },
        className: 'infobox-standart',
        alignTo: 'target',
        alignX: 'center',
        alignY: 'top',
        offsetX: 2,
        liveEvents: true,
        showTimeout: 100
    });

    $('.js-tip-help').poshytip({
        className: 'infobox-standart',
        alignTo: 'target',
        alignX: 'right',
        alignY: 'center',
        offsetX: 5,
        liveEvents: true,
        showTimeout: 500
    });

    $('.js-infobox').poshytip({
        className: 'infobox-standart',
        liveEvents: true,
        showTimeout: 300
    });
    */

    // подсветка кода
    prettyPrint();

    var inputs = $('input.input-text, textarea');
    // эмуляция border-sizing в IE 7-
    // ls.ie.bordersizing(inputs);
    // эмуляция placeholder'ов в IE
    inputs.placeholder();

    // комментарии
    //ls.comments.options.folding = false;
    //ls.comments.init();

    // избранное
    ls.hook.add('ls_favourite_toggle_after', function (idTarget, objFavourite, type, params, result) {
        $('#fav_count_' + type + '_' + idTarget).text((result.iCount > 0) ? result.iCount : '');
    });

    // лента активности
    //ls.hook.add('ls_stream_append_user_after', function (length, data) {
    //    if (length == 0) {
    //        $('#strm_u_' + data.uid).parent().find('a').before(' ');
    //    }
    //});

    // опрос
    ls.hook.add('ls_pool_add_answer_after', function (removeAnchor) {
        var removeAnchor = $('').attr('title', ls.lang.get('delete')).click(function (e) {
            e.preventDefault();
            return this.removeAnswer(e.target);
        }.bind(ls.poll));
        $(this).find('a').remove();
        $(this).append(removeAnchor);
    });

    /****************
     * TALK
     */

        // Добавляем или удаляем друга из списка получателей
    $('#friends input:checkbox').change(function () {
        ls.talk.toggleRecipient($('#' + $(this).attr('id') + '_label').text(), $(this).attr('checked'));
    });

    // Добавляем всех друзей в список получателей
    $('#friend_check_all').click(function () {
        $('#friends input:checkbox').each(function (index, item) {
            ls.talk.toggleRecipient($('#' + $(item).attr('id') + '_label').text(), true);
            $(item).attr('checked', true);
        });
        return false;
    });

    // Удаляем всех друзей из списка получателей
    $('#friend_uncheck_all').click(function () {
        $('#friends input:checkbox').each(function (index, item) {
            ls.talk.toggleRecipient($('#' + $(item).attr('id') + '_label').text(), false);
            $(item).attr('checked', false);
        });
        return false;
    });

    // Удаляем пользователя из черного списка
    $("#black_list_block").delegate("a.delete", "click", function () {
        ls.talk.removeFromBlackList(this);
        return false;
    });

    // Удаляем пользователя из переписки
    $("#speaker_list_block").delegate("a.delete", "click", function () {
        ls.talk.removeFromTalk(this, $('#talk_id').val());
        return false;
    });

    // Help-tags link
    $('.js-tags-help-link').click(function () {
        var target = ls.registry.get('tags-help-target-id'),
            text='';
        if (!target || !$('#' + target).length) {
            return false;
        }
        target = $('#' + target);
        if ($(this).data('insert')) {
            text = $(this).data('insert');
        } else {
            text = $(this).text();
        }
        $.markItUp({target: target, replaceWith: text});
        return false;
    });

    // Фикс бага с z-index у встроенных видео
    $("iframe").each(function () {
        var ifr_source = $(this).attr('src');

        if (ifr_source) {
            var wmode = "wmode=opaque";

            if (ifr_source.indexOf('?') != -1)
                $(this).attr('src', ifr_source + '&' + wmode);
            else
                $(this).attr('src', ifr_source + '?' + wmode);
        }
    });

    if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
        var msViewportStyle = document.createElement("style");
        msViewportStyle.appendChild(
            document.createTextNode(
                "@-ms-viewport{width:auto!important}"
            )
        );
        document.getElementsByTagName("head")[0].appendChild(msViewportStyle)
    }

    if (tinyMCE && ls.settings && ls.settings.presets.tinymce) {
        var cssUrl = ls.getAssetUrl('template-tinymce.css');
        if (cssUrl) {
            ls.settings.presets.tinymce.default['content_css'] = cssUrl;
        }
    }

    // Хук конца инициализации javascript-составляющих шаблона
    ls.hook.run('ls_template_init_end', [], window);
});
<|endoftext|>


<commit_before>( function( mw, $ ) { "use strict";
var NativeBridge = {
	callbacksCount : 1,
	callbacks : {},

	// Automatically called by native layer when a result is available
	resultForCallback : function resultForCallback(callbackId, resultArray) {
		try {
			var callback = NativeBridge.callbacks[callbackId];
			if (!callback) return;

			callback.apply(null,resultArray);
		} catch(e) {alert(e)}
	},

	// Use this in javascript to request native objective-c code
	// functionName : string (I think the name is explicit :p)
	// args : array of arguments
	// callback : function with n-arguments that is going to be called when the native code returned
	call : function call(functionName, args, callback) {

		var hasCallback = callback && typeof callback == "function";
		var callbackId = hasCallback ? NativeBridge.callbacksCount++ : 0;

		if (hasCallback)
			NativeBridge.callbacks[callbackId] = callback;

		var iframe = document.createElement("IFRAME");
		iframe.setAttribute("src", "js-frame:" + functionName + ":" + callbackId+ ":" + encodeURIComponent(JSON.stringify(args)));
		document.documentElement.appendChild(iframe);
		iframe.parentNode.removeChild(iframe);
		iframe = null;
	}
};

NativeBridge.videoPlayer = NativeBridge.videoPlayer  || {
	proxyElement: null,
	embedPlayer: null,
	isJsCallbackReady: false,
	bindPostfix: ".nativeBridge",
	playerMethods: [ 'stop', 'play', 'pause', 'setPlayerSource', 'bindPlayerEvents', 'showNativePlayer', 'hideNativePlayer', 'toggleFullscreen', 'notifyKPlayerEvent', 'notifyKPlayerEvaluated', 'notifyJsReady' ],
	registePlayer: function (proxyElement) {
		var _this = this;
		this.proxyElement = proxyElement;
		for (var i = 0; i < this.playerMethods.length; i++) {
			(function (method) {
				_this.proxyElement[method] = function (arg) {
					_this.execute(method, arg);
				}
			})(this.playerMethods[i]);
		}
		this.proxyElement.attr = function( attributeName, attributeValue ){
			if( attributeName && attributeValue === undefined ) {
				return _this.proxyElement[ attributeName ];
			} else if( attributeName && attributeValue ){
				_this.proxyElement[attributeName] = attributeValue;
				_this.execute('setAttribute', [ attributeName, attributeValue ]);
			}
		}

		this.bindNativeEvents();
		this.notifyJsReadyFunc();
	},

	notifyJsReadyFunc: function() {
		if ( this.isJsCallbackReady && this.proxyElement ) {
			this.proxyElement.notifyJsReady( [] );
		}
	},

	registerEmbedPlayer: function( embedPlayer ) {
		this.embedPlayer = embedPlayer;
	},
	sendNotification: function( eventName, eventValue ) {
		this.embedPlayer.sendNotification( eventName, JSON.parse( eventValue ));
	},
	/**
	 *
	 * @param object
	 * @returns String rerpesantation of given object
	 */
	getObjectString: function ( object ) {
		var stringValue = object;
		if ( typeof object === "object" ) {
			stringValue =  JSON.stringify( object );
		}
		return stringValue;
	},
	addJsListener: function( eventName ){
		var _this = this;
		this.embedPlayer.addJsListener( eventName + this.bindPostfix, function( val ) {
			_this.embedPlayer.getPlayerElement().notifyKPlayerEvent( [ eventName, _this.getObjectString( val ) ] );
		});
	},
	removeJsListener: function( eventName ) {
		this.embedPlayer.removeJsListener( eventName + this.bindPostfix );
	},
	setKDPAttribute: function( host, prop, value ) {
		this.embedPlayer.setKDPAttribute( host, prop, value );
	},
	/**
	 * will evaluate given expression and send the resulted value back to native code with the given callbackName
	 * @param expression
	 * @param callbackName
	 */
	asyncEvaluate: function( expression, callbackName ) {
		var result = this.embedPlayer.evaluate( expression );
		this.embedPlayer.getPlayerElement().notifyKPlayerEvaluated( [ callbackName, this.getObjectString( result ) ]);
	},
	//this function should be called from IOS/Andorid
	trigger: function (eventName, eventValue) {
	//	mw.log('nativeBridge.js --> trigger:' + eventName + ' ' + eventValue);

		if (eventValue === "(null)") {
			//set undefined
			eventValue = void(0);
		}

		if(eventValue != undefined){
			var jsEventValue = this.stringConvertion( eventValue );
		}

		$( this.proxyElement).trigger( eventName, [jsEventValue] );

		if (eventName == 'timeupdate'){
			this.proxyElement['currentTime'] = jsEventValue;
		}else if (eventName == 'progress'){
			this.proxyElement['progress'] = jsEventValue;
		}else if (eventName == 'visible'){
			this.proxyElement['visible']  = jsEventValue;
		} else if (eventName == 'durationchange') {
			this.proxyElement['duration'] = jsEventValue;
		}
	},
	execute: function (command, args) {
		args = args || [];
		console.log(command);
		console.log(args);
		NativeBridge.call(command , args);
	},
	bindNativeEvents: function(){
		console.log('bindNativeEvents');
		this.proxyElement.bindPlayerEvents( [] );
	},
	log:function(message,arg){
		console.log(message,arg);
	},

	stringConvertion: function (str){
		var value = parseFloat(str);

		if(isNaN(value)){
			if(value == 'true'){
				return true;
			}else if(value == 'false'){
				return false;
			}else{
				return str;
			}
		}

		return value;
	}
};

if ( mw.getConfig('EmbedPlayer.ForceNativeComponent') === true ) {
	window["NativeBridge"] = NativeBridge;
	kWidget.addReadyCallback(  function() {
		NativeBridge.videoPlayer.isJsCallbackReady = true;
		NativeBridge.videoPlayer.notifyJsReadyFunc();
	} );
}

})( window.mw, window.jQuery );<commit_msg>#FEC-636 #comment fix for iOS - calling notifyJsReadyFunc #time 3h<commit_after>( function( mw, $ ) { "use strict";
var NativeBridge = {
	callbacksCount : 1,
	callbacks : {},

	// Automatically called by native layer when a result is available
	resultForCallback : function resultForCallback(callbackId, resultArray) {
		try {
			var callback = NativeBridge.callbacks[callbackId];
			if (!callback) return;

			callback.apply(null,resultArray);
		} catch(e) {alert(e)}
	},

	// Use this in javascript to request native objective-c code
	// functionName : string (I think the name is explicit :p)
	// args : array of arguments
	// callback : function with n-arguments that is going to be called when the native code returned
	call : function call(functionName, args, callback) {

		var hasCallback = callback && typeof callback == "function";
		var callbackId = hasCallback ? NativeBridge.callbacksCount++ : 0;

		if (hasCallback)
			NativeBridge.callbacks[callbackId] = callback;

		var iframe = document.createElement("IFRAME");
		iframe.setAttribute("src", "js-frame:" + functionName + ":" + callbackId+ ":" + encodeURIComponent(JSON.stringify(args)));
		document.documentElement.appendChild(iframe);
		iframe.parentNode.removeChild(iframe);
		iframe = null;
	}
};

NativeBridge.videoPlayer = NativeBridge.videoPlayer  || {
	proxyElement: null,
	embedPlayer: null,
	isJsCallbackReady: false,
	bindPostfix: ".nativeBridge",
	playerMethods: [ 'stop', 'play', 'pause', 'setPlayerSource', 'bindPlayerEvents', 'showNativePlayer', 'hideNativePlayer', 'toggleFullscreen', 'notifyKPlayerEvent', 'notifyKPlayerEvaluated', 'notifyJsReady' ],
	registePlayer: function (proxyElement) {
		var _this = this;
		this.proxyElement = proxyElement;
		for (var i = 0; i < this.playerMethods.length; i++) {
			(function (method) {
				_this.proxyElement[method] = function (arg) {
					_this.execute(method, arg);
				}
			})(this.playerMethods[i]);
		}
		this.proxyElement.attr = function( attributeName, attributeValue ){
			if( attributeName && attributeValue === undefined ) {
				return _this.proxyElement[ attributeName ];
			} else if( attributeName && attributeValue ){
				_this.proxyElement[attributeName] = attributeValue;
				_this.execute('setAttribute', [ attributeName, attributeValue ]);
			}
		}

		this.bindNativeEvents();
	},

	notifyJsReadyFunc: function() {
		if ( this.isJsCallbackReady && this.proxyElement ) {
			this.proxyElement.notifyJsReady( [] );
		}
	},

	registerEmbedPlayer: function( embedPlayer ) {
		this.embedPlayer = embedPlayer;
		this.notifyJsReadyFunc();
	},
	sendNotification: function( eventName, eventValue ) {
		this.embedPlayer.sendNotification( eventName, JSON.parse( eventValue ));
	},
	/**
	 *
	 * @param object
	 * @returns String rerpesantation of given object
	 */
	getObjectString: function ( object ) {
		var stringValue = object;
		if ( typeof object === "object" ) {
			stringValue =  JSON.stringify( object );
		}
		return stringValue;
	},
	addJsListener: function( eventName ){
		var _this = this;
		this.embedPlayer.addJsListener( eventName + this.bindPostfix, function( val ) {
			_this.embedPlayer.getPlayerElement().notifyKPlayerEvent( [ eventName, _this.getObjectString( val ) ] );
		});
	},
	removeJsListener: function( eventName ) {
		this.embedPlayer.removeJsListener( eventName + this.bindPostfix );
	},
	setKDPAttribute: function( host, prop, value ) {
		this.embedPlayer.setKDPAttribute( host, prop, value );
	},
	/**
	 * will evaluate given expression and send the resulted value back to native code with the given callbackName
	 * @param expression
	 * @param callbackName
	 */
	asyncEvaluate: function( expression, callbackName ) {
		var result = this.embedPlayer.evaluate( expression );
		this.embedPlayer.getPlayerElement().notifyKPlayerEvaluated( [ callbackName, this.getObjectString( result ) ]);
	},
	//this function should be called from IOS/Andorid
	trigger: function (eventName, eventValue) {
	//	mw.log('nativeBridge.js --> trigger:' + eventName + ' ' + eventValue);

		if (eventValue === "(null)") {
			//set undefined
			eventValue = void(0);
		}

		if(eventValue != undefined){
			var jsEventValue = this.stringConvertion( eventValue );
		}

		$( this.proxyElement).trigger( eventName, [jsEventValue] );

		if (eventName == 'timeupdate'){
			this.proxyElement['currentTime'] = jsEventValue;
		}else if (eventName == 'progress'){
			this.proxyElement['progress'] = jsEventValue;
		}else if (eventName == 'visible'){
			this.proxyElement['visible']  = jsEventValue;
		} else if (eventName == 'durationchange') {
			this.proxyElement['duration'] = jsEventValue;
		}
	},
	execute: function (command, args) {
		args = args || [];
		console.log(command);
		console.log(args);
		NativeBridge.call(command , args);
	},
	bindNativeEvents: function(){
		console.log('bindNativeEvents');
		this.proxyElement.bindPlayerEvents( [] );
	},
	log:function(message,arg){
		console.log(message,arg);
	},

	stringConvertion: function (str){
		var value = parseFloat(str);

		if(isNaN(value)){
			if(value == 'true'){
				return true;
			}else if(value == 'false'){
				return false;
			}else{
				return str;
			}
		}

		return value;
	}
};

if ( mw.getConfig('EmbedPlayer.ForceNativeComponent') === true ) {
	window["NativeBridge"] = NativeBridge;
	kWidget.addReadyCallback(  function() {
		NativeBridge.videoPlayer.isJsCallbackReady = true;
		NativeBridge.videoPlayer.notifyJsReadyFunc();
	} );
}

})( window.mw, window.jQuery );<|endoftext|>


<commit_before>wwm.room = (function(){
	var jqMap;
	var stMap = {
		current: 'day',
		dayArray: null,
		nightArray: null,
		memberList: [],
		onlineList: [],
		userInfo: null,
		personColor: 0
	};
	var cfMap = {
		$con: $('#view'),
		colorList: ['red', 'orange', 'yellow', 'yellowgreen', 'skyblue', 'purple', 'violet', 'pink']
	};
	var socket = io();
	function setJqMap($con) {
		jqMap = {
			$con: $con,
			$explode: $con.find('#explode-room'),
			$ban: $con.find('#ban-people-btn'),
			$changeLimit: $con.find('#change-number-btn'),
			$changeTitle: $con.find('#change-room-title'),
			$calendar: $con.find('table'),
			$day: $con.find('#day'),
			$night: $con.find('#night'),
			$back: $con.find('#room-back'),
			$dayExp: $con.find('#day-exception'),
			$notDay: $con.find('#day-exception').find('li'),
			$timeExp: $con.find('#time-exception'),
			$notTime: $con.find('#time-exception').find('li'),
			$title: $con.find('#title'),
			$total: $con.find('#total-number'),
			$sendChat: $con.find('#send-chat'),
			$chatList: $con.find('#chat-list'),
			$confirm: $con.find('#confirm-calendar')
		};
	}
	function createArray(length) {
		var arr = new Array(length || 0), i = length;
		if (arguments.length > 1) {
			var args = Array.prototype.slice.call(arguments, 1);
			while(i--) arr[length-1 - i] = createArray.apply(this, args);
		}
		return arr;
	}
	function tableToArray(cellList, busy) {
		var arrList = [];
		for (var i = 0; i < cellList.length; i++) {
			var cell = cellList[i];
			var arr = [cell.parentNode.rowIndex - 1, cell.cellIndex - 1];
			console.log(arr);
			if (stMap.current === 'day') {
				if (busy) {
					console.log(arr[0], arr[1], stMap.dayArray[arr[0]]);
					console.log('busy:stMap.dayArray[arr[0]][arr[1]]', stMap.dayArray[arr[0]][arr[1]]);
					stMap.dayArray[arr[0]][arr[1]].push(stMap.personColor);
					$(cell).addClass('busy');
				} else {
					console.log('not-busy:stMap.dayArray[arr[0]][arr[1]]', stMap.dayArray[arr[0]][arr[1]]);
					var index = stMap.dayArray[arr[0]][arr[1]].indexOf(stMap.personColor);
					if (index > -1) {
						stMap.dayArray[arr[0]][arr[1]].splice(index, 1);
					}
					$(cell).removeClass('busy');
				}
			} else { // current === 'night'
				if (busy) {
					console.log('busy:stMap.nightArray[arr[0]][arr[1]]', stMap.nightArray[arr[0]][arr[1]]);
					stMap.nightArray[arr[0]][arr[1]].push(stMap.personColor);
					$(cell).addClass('busy');
				} else {
					console.log('not-busy:stMap.nightArray[arr[0]][arr[1]]', stMap.nightArray[arr[0]][arr[1]]);
					var index = stMap.nightArray[arr[0]][arr[1]].indexOf(stMap.personColor);
					if (index > -1) {
						stMap.nightArray[arr[0]][arr[1]].splice(index, 1);
					}
					$(cell).removeClass('busy');
				}
			}
			arrList.push(arr);
		}
		return arrList;
	}
	function arrayToTable(cellList) {
		for (var i = 0; i < cellList.length; i++) {
			var cell = cellList[i];
			var $cell = jqMap.$calendar.find('tr').eq(cell[0] + 1).find('td').eq(cell[1]);
			var number = $cell.data('number') || 0;
			console.log('arrToTb:number', number);
			$cell.attr('data-number', number + 1);
		}
		renderTable(stMap.current);
	}
	function renderTable(current) {
		var i, j, k;
		if (current === 'day') {
			for (i = 0; i < 12; i++) {
				for (j = 0; j < 7; j++) {
					var $cell = jqMap.$calendar.find('tr').eq(i).find('td').eq(j);
					var number = $cell.data('number') || 0;
					if (stMap.dayArray[i][j].length > 0) {
						console.log('stMap.dayArray[i][j]', stMap.dayArray[i][j]);
						console.log('number', number);
						var $box = $('<div/>').addClass('box-' + number);
						for (k = 0; k < number; k++) {
							$box.append($('<div/>', {class: cfMap.colorList[stMap.dayArray[i][j][k] - 1]}));
						}
						$box.appendTo($cell);
					}
				}
			}
		} else {
			for (i = 0; i < 12; i++) {
				for (j = 0; j < 7; j++) {
					var $cell = jqMap.$calendar.find('tr').eq(i).find('td').eq(j);
					var number = $cell.data('number') || 0;
					if (stMap.nightArray[i][j].length > 0) {
						console.log('stMap.nightArray[i][j]', stMap.nightArray[i][j]);
						console.log('number', number);
						var $box = $('<div/>').addClass('box-' + number);
						for (k = 0; k < number; k++) {
							$box.append($('<div/>', {class: cfMap.colorList[stMap.nightArray[i][j][k] - 1]}));
						}
						$box.appendTo($cell);
					}
				}
			}
		}
	}
	function ban(e) {
		var banPromise = wwm.model.ban(e.data.id);
		banPromise.done(function(res) {});
		banPromise.fail(function(err) {});
	}
	function changeTitle(e) {
		var title = $(this).prev().val();
		var titlePromise = wwm.model.changeTitle(e.data.id, title);
		titlePromise.done(function(res) {
			jqMap.$title.text(title);
		});
		titlePromise.fail(function(err) {
			alert('제목 바꾸기 실패!');
			console.log(err);
		});
	}
	function changeLimit(e) {
		var number = $(this).prev().val();
		var limitPromise = wwm.model.changeLimit(e.data.id, number);
		limitPromise.done(function(res) {
			jqMap.$total.text(number);
		});
		limitPromise.fail(function(err) {
			alert('인원수 바꾸기 실패!');
			console.log(err);
		});
	}
	function showDayException() {
		var $this = $(this);
		if ($this.hasClass('opened')) {
			$this.removeClass('opened');
			$this.find('ul').hide();
		} else {
			$this.addClass('opened');
			$this.find('ul').show();
		}
	}
	function showTimeException() {
		var $this = $(this);
		if ($this.hasClass('opened')) {
			$this.removeClass('opened');
			$this.find('ul').hide();
		} else {
			$this.addClass('opened');
			$this.find('ul').show();
		}
	}
	function deleteRoom(e) {
		var id = e.data.id;
		var deletePromise = wwm.model.deleteRoom(id, userInfo.id);
		deletePromise.done(function(res) {
			alert('삭제되었습니다.');
			wwm.lobby.initModule(jqMap.$con);
		});
		deletePromise.fail(function(err) {
			console.log(err);
			alert('방 지우기 오류발생');
		});
	}
	function onClickCell() {
		// 어레이를 발송
		if ($(this).hasClass('busy')) {
			socket.emit('not-busy', tableToArray([this], false));
		} else {
			socket.emit('busy', tableToArray([this], true));
		}
	}
	function excludeDay(e) {
		// 해당일에 대한 어레이를 발송
		e.stopPropagation();
		alert($(this).index());
		var idx = $(this).index();
		var arr = [];
		for (var i = 0; i < 12; i++) {
			arr.push([i, idx]);
		}
		if ($(this).hasClass('selected')) {
			socket.emit('not-busy', arr);
			$(this).removeClass('selected');
		} else {
			socket.emit('busy', arr);
			$(this).addClass('selected');
		}
	}
	function excludeTime() {
		// 해당 시간에 대한 어레이를 발송
		e.stopPropagation();
		alert($(this).index());
		var idx = $(this).index();
		var time = $(this).find('input').val();
		if (!time) {
			return;
		}
		var arr = [];
		if (idx === 0) { // not before
			for (var i = 0; i < time; i++) {
				for (var j = 0; j < 7; j++) {
					arr.push([time, j]);
				}
			}
			console.log('arr', arr);
			if ($(this).hasClass('selected')) {
				socket.emit('not-busy', arr);
				$(this).removeClass('selected');
			} else { // no selected
				socket.emit('busy', arr);
				$(this).addClass('selected');
			} // not before
		} else { // not after
			for (var i = time - 1; i < 12; i++) {
				for (var j = 0; j < 7; j++) {
					console.log(arr);
					arr.push([time, j]);
				}
			}
			console.log('arr', arr);
			if ($(this).hasClass('selected')) {
				socket.emit('not-busy', arr);
				$(this).removeClass('selected');
			} else {
				socket.emit('busy', arr);
				$(this).addClass('selected');
			}
		} // not after
	}
	function goBack(e) {
		socket.emit('out', {id: userInfo.id, rid: e.data.rid});
		wwm.lobby.initModule(jqMap.$con);
	}
	function sendChat() {
		var text = $(this).prev('#chatbox').val();
		socket.emit('chat', {
			id: userInfo.id,
			text: text
		});
	}
	function confirm(e) {
		var data = e.data;
		wwm.model.confirm(data);
	}
	function toDay() {
		stMap.current = 'day';
		renderTable(stMap.current);
		jqMap.$night.css('background', 'white');
		jqMap.$day.css('background', 'gray');
	}
	function toNight() {
		stMap.current = 'night';
		renderTable(stMap.current);
		jqMap.$day.css('background', 'white');
		jqMap.$night.css('background', 'gray');
	}
	function initModule(data) {
		// data를 방 모듈에 입력.
		if (!stMap.dayArray) {stMap.dayArray = createArray(12,7);}
		if (!stMap.nightArray) {stMap.nightArray = createArray(12,7);}
		console.log(stMap.dayArray, stMap.nightArray);
		socket.emit('enter', {id: userInfo.id, rid: data.id}); // 방에 참가했음을 알림.
		socket.on('onlineList', function(list) {
			console.log('onlinelist', list);
		stMap.onlineList = list;
		});
		stMap.onlineList.push(userInfo.id);
		stMap.memberList = data.member;
		console.log('data.member', data.member);
		stMap.personColor = Array.isArray(data.member) ?  data.member.indexOf(userInfo.id) + 1 : 1;

		var parser = {
			name: userInfo.name || userInfo.properties.nickname, //유저네임
			title: data.title, //타이틀
			current: data.current, //현재원
			total: data.number //총원
		};
		console.log('userinfo.id', userInfo.id);
		console.log('data.maker', data.maker);
		if (userInfo.id == data.maker) { // 아이디가 방장 아이디와 같으면
			parser.admin = true;
		}
		var src = $('#wwm-room').text();
		dust.render(dust.loadSource(dust.compile(src)), parser, function(err, out) {
			if (err) {
				cfMap.$con.html(err);
				return;
			}
			cfMap.$con.html(out);
			setJqMap(cfMap.$con);
			jqMap.$day.css({
				background: 'gray'
			});
			socket.on('chat', function(data) {
				jqMap.$chatList.text(data.id + ' send ' + data.text);
			});
			socket.on('busy', function(list) {
				arrayToTable(list);
			});
			socket.on('not-busy', function(list) {
				arrayToTable(list);
			});
			jqMap.$calendar.find('td').click(onClickCell);
			jqMap.$explode.click({id: data.id}, deleteRoom);
			jqMap.$back.click({rid: data.id}, goBack);
			jqMap.$day.click(toDay);
			jqMap.$night.click(toNight);
			jqMap.$dayExp.click(showDayException);
			jqMap.$timeExp.click(showTimeException);
			jqMap.$ban.click({id: data.id}, ban);
			jqMap.$changeLimit.click({id: data.id}, changeLimit);
			jqMap.$changeTitle.click({id: data.id}, changeTitle);
			jqMap.$sendChat.click(sendChat);
			jqMap.$notDay.click(excludeDay);
			jqMap.$notTime.click(excludeTime);
			jqMap.$confirm.click({rid: data.id, day: stMap.dayArray, night: stMap.nightArray}, confirm);
		});
	}

	return {
		initModule: initModule
	};
}());
<commit_msg>add handler when cellArray is undefined<commit_after>wwm.room = (function(){
	var jqMap;
	var stMap = {
		current: 'day',
		dayArray: null,
		nightArray: null,
		memberList: [],
		onlineList: [],
		userInfo: null,
		personColor: 0
	};
	var cfMap = {
		$con: $('#view'),
		colorList: ['red', 'orange', 'yellow', 'yellowgreen', 'skyblue', 'purple', 'violet', 'pink']
	};
	var socket = io();
	function setJqMap($con) {
		jqMap = {
			$con: $con,
			$explode: $con.find('#explode-room'),
			$ban: $con.find('#ban-people-btn'),
			$changeLimit: $con.find('#change-number-btn'),
			$changeTitle: $con.find('#change-room-title'),
			$calendar: $con.find('table'),
			$day: $con.find('#day'),
			$night: $con.find('#night'),
			$back: $con.find('#room-back'),
			$dayExp: $con.find('#day-exception'),
			$notDay: $con.find('#day-exception').find('li'),
			$timeExp: $con.find('#time-exception'),
			$notTime: $con.find('#time-exception').find('li'),
			$title: $con.find('#title'),
			$total: $con.find('#total-number'),
			$sendChat: $con.find('#send-chat'),
			$chatList: $con.find('#chat-list'),
			$confirm: $con.find('#confirm-calendar')
		};
	}
	function createArray(length) {
		var arr = new Array(length || 0), i = length;
		if (arguments.length > 1) {
			var args = Array.prototype.slice.call(arguments, 1);
			while(i--) arr[length-1 - i] = createArray.apply(this, args);
		}
		return arr;
	}
	function tableToArray(cellList, busy) {
		var arrList = [];
		for (var i = 0; i < cellList.length; i++) {
			var cell = cellList[i];
			var arr = [cell.parentNode.rowIndex - 1, cell.cellIndex - 1];
			console.log(arr);
			var cellArray;
			if (stMap.current === 'day') {
				cellArray = stMap.dayArray[arr[0]][arr[1]];
				if (busy) {
					console.log(arr[0], arr[1], stMap.dayArray[arr[0]].join(','));
					console.log('busy:stMap.dayArray[arr[0]][arr[1]]', cellArray.join(','));
					cellArray ? cellArray.push(stMap.personColor) : cellArray = [stMap.personColor];
					console.log('cellresult', cellArray);
					$(cell).addClass('busy');
				} else {
					console.log('not-busy:stMap.dayArray[arr[0]][arr[1]]', cellArray.join(','));
					var index = cellArray.indexOf(stMap.personColor);
					if (index > -1) {
						cellArray.splice(index, 1);
					}
					$(cell).removeClass('busy');
				}
			} else { // current === 'night'
				cellArray = stMap.nightArray[arr[0]][arr[1]];
				if (busy) {
					console.log('busy:stMap.nightArray[arr[0]][arr[1]]', stMap.nightArray[arr[0]][arr[1]].join(','));
					stMap.nightArray[arr[0]][arr[1]].push(stMap.personColor);
					$(cell).addClass('busy');
				} else {
					console.log('not-busy:stMap.nightArray[arr[0]][arr[1]]', stMap.nightArray[arr[0]][arr[1]].join(','));
					var index = stMap.nightArray[arr[0]][arr[1]].indexOf(stMap.personColor);
					if (index > -1) {
						stMap.nightArray[arr[0]][arr[1]].splice(index, 1);
					}
					$(cell).removeClass('busy');
				}
			}
			arrList.push(arr);
		}
		return arrList;
	}
	function arrayToTable(cellList) {
		for (var i = 0; i < cellList.length; i++) {
			var cell = cellList[i];
			var $cell = jqMap.$calendar.find('tr').eq(cell[0] + 1).find('td').eq(cell[1]);
			var number = $cell.data('number') || 0;
			console.log('arrToTb:number', number);
			$cell.attr('data-number', number + 1);
		}
		renderTable(stMap.current);
	}
	function renderTable(current) {
		var i, j, k;
		if (current === 'day') {
			for (i = 0; i < 12; i++) {
				for (j = 0; j < 7; j++) {
					var $cell = jqMap.$calendar.find('tr').eq(i).find('td').eq(j);
					var number = $cell.data('number') || 0;
					if (stMap.dayArray[i][j].length > 0) {
						console.log('stMap.dayArray[i][j]', stMap.dayArray[i][j]);
						console.log('number', number);
						var $box = $('<div/>').addClass('box-' + number);
						for (k = 0; k < number; k++) {
							$box.append($('<div/>', {class: cfMap.colorList[stMap.dayArray[i][j][k] - 1]}));
						}
						$box.appendTo($cell);
					}
				}
			}
		} else {
			for (i = 0; i < 12; i++) {
				for (j = 0; j < 7; j++) {
					var $cell = jqMap.$calendar.find('tr').eq(i).find('td').eq(j);
					var number = $cell.data('number') || 0;
					if (stMap.nightArray[i][j].length > 0) {
						console.log('stMap.nightArray[i][j]', stMap.nightArray[i][j]);
						console.log('number', number);
						var $box = $('<div/>').addClass('box-' + number);
						for (k = 0; k < number; k++) {
							$box.append($('<div/>', {class: cfMap.colorList[stMap.nightArray[i][j][k] - 1]}));
						}
						$box.appendTo($cell);
					}
				}
			}
		}
	}
	function ban(e) {
		var banPromise = wwm.model.ban(e.data.id);
		banPromise.done(function(res) {});
		banPromise.fail(function(err) {});
	}
	function changeTitle(e) {
		var title = $(this).prev().val();
		var titlePromise = wwm.model.changeTitle(e.data.id, title);
		titlePromise.done(function(res) {
			jqMap.$title.text(title);
		});
		titlePromise.fail(function(err) {
			alert('제목 바꾸기 실패!');
			console.log(err);
		});
	}
	function changeLimit(e) {
		var number = $(this).prev().val();
		var limitPromise = wwm.model.changeLimit(e.data.id, number);
		limitPromise.done(function(res) {
			jqMap.$total.text(number);
		});
		limitPromise.fail(function(err) {
			alert('인원수 바꾸기 실패!');
			console.log(err);
		});
	}
	function showDayException() {
		var $this = $(this);
		if ($this.hasClass('opened')) {
			$this.removeClass('opened');
			$this.find('ul').hide();
		} else {
			$this.addClass('opened');
			$this.find('ul').show();
		}
	}
	function showTimeException() {
		var $this = $(this);
		if ($this.hasClass('opened')) {
			$this.removeClass('opened');
			$this.find('ul').hide();
		} else {
			$this.addClass('opened');
			$this.find('ul').show();
		}
	}
	function deleteRoom(e) {
		var id = e.data.id;
		var deletePromise = wwm.model.deleteRoom(id, userInfo.id);
		deletePromise.done(function(res) {
			alert('삭제되었습니다.');
			wwm.lobby.initModule(jqMap.$con);
		});
		deletePromise.fail(function(err) {
			console.log(err);
			alert('방 지우기 오류발생');
		});
	}
	function onClickCell() {
		// 어레이를 발송
		if ($(this).hasClass('busy')) {
			socket.emit('not-busy', tableToArray([this], false));
		} else {
			socket.emit('busy', tableToArray([this], true));
		}
	}
	function excludeDay(e) {
		// 해당일에 대한 어레이를 발송
		e.stopPropagation();
		alert($(this).index());
		var idx = $(this).index();
		var arr = [];
		for (var i = 0; i < 12; i++) {
			arr.push([i, idx]);
		}
		if ($(this).hasClass('selected')) {
			socket.emit('not-busy', arr);
			$(this).removeClass('selected');
		} else {
			socket.emit('busy', arr);
			$(this).addClass('selected');
		}
	}
	function excludeTime() {
		// 해당 시간에 대한 어레이를 발송
		e.stopPropagation();
		alert($(this).index());
		var idx = $(this).index();
		var time = $(this).find('input').val();
		if (!time) {
			return;
		}
		var arr = [];
		if (idx === 0) { // not before
			for (var i = 0; i < time; i++) {
				for (var j = 0; j < 7; j++) {
					arr.push([time, j]);
				}
			}
			console.log('arr', arr);
			if ($(this).hasClass('selected')) {
				socket.emit('not-busy', arr);
				$(this).removeClass('selected');
			} else { // no selected
				socket.emit('busy', arr);
				$(this).addClass('selected');
			} // not before
		} else { // not after
			for (var i = time - 1; i < 12; i++) {
				for (var j = 0; j < 7; j++) {
					console.log(arr);
					arr.push([time, j]);
				}
			}
			console.log('arr', arr);
			if ($(this).hasClass('selected')) {
				socket.emit('not-busy', arr);
				$(this).removeClass('selected');
			} else {
				socket.emit('busy', arr);
				$(this).addClass('selected');
			}
		} // not after
	}
	function goBack(e) {
		socket.emit('out', {id: userInfo.id, rid: e.data.rid});
		wwm.lobby.initModule(jqMap.$con);
	}
	function sendChat() {
		var text = $(this).prev('#chatbox').val();
		socket.emit('chat', {
			id: userInfo.id,
			text: text
		});
	}
	function confirm(e) {
		var data = e.data;
		wwm.model.confirm(data);
	}
	function toDay() {
		stMap.current = 'day';
		renderTable(stMap.current);
		jqMap.$night.css('background', 'white');
		jqMap.$day.css('background', 'gray');
	}
	function toNight() {
		stMap.current = 'night';
		renderTable(stMap.current);
		jqMap.$day.css('background', 'white');
		jqMap.$night.css('background', 'gray');
	}
	function initModule(data) {
		// data를 방 모듈에 입력.
		if (!stMap.dayArray) {stMap.dayArray = createArray(12,7);}
		if (!stMap.nightArray) {stMap.nightArray = createArray(12,7);}
		console.log(stMap.dayArray, stMap.nightArray);
		socket.emit('enter', {id: userInfo.id, rid: data.id}); // 방에 참가했음을 알림.
		socket.on('onlineList', function(list) {
			console.log('onlinelist', list);
		stMap.onlineList = list;
		});
		stMap.onlineList.push(userInfo.id);
		stMap.memberList = data.member;
		console.log('data.member', data.member);
		stMap.personColor = Array.isArray(data.member) ?  data.member.indexOf(userInfo.id) + 1 : 1;

		var parser = {
			name: userInfo.name || userInfo.properties.nickname, //유저네임
			title: data.title, //타이틀
			current: data.current, //현재원
			total: data.number //총원
		};
		console.log('userinfo.id', userInfo.id);
		console.log('data.maker', data.maker);
		if (userInfo.id == data.maker) { // 아이디가 방장 아이디와 같으면
			parser.admin = true;
		}
		var src = $('#wwm-room').text();
		dust.render(dust.loadSource(dust.compile(src)), parser, function(err, out) {
			if (err) {
				cfMap.$con.html(err);
				return;
			}
			cfMap.$con.html(out);
			setJqMap(cfMap.$con);
			jqMap.$day.css({
				background: 'gray'
			});
			socket.on('chat', function(data) {
				jqMap.$chatList.text(data.id + ' send ' + data.text);
			});
			socket.on('busy', function(list) {
				arrayToTable(list);
			});
			socket.on('not-busy', function(list) {
				arrayToTable(list);
			});
			jqMap.$calendar.find('td').click(onClickCell);
			jqMap.$explode.click({id: data.id}, deleteRoom);
			jqMap.$back.click({rid: data.id}, goBack);
			jqMap.$day.click(toDay);
			jqMap.$night.click(toNight);
			jqMap.$dayExp.click(showDayException);
			jqMap.$timeExp.click(showTimeException);
			jqMap.$ban.click({id: data.id}, ban);
			jqMap.$changeLimit.click({id: data.id}, changeLimit);
			jqMap.$changeTitle.click({id: data.id}, changeTitle);
			jqMap.$sendChat.click(sendChat);
			jqMap.$notDay.click(excludeDay);
			jqMap.$notTime.click(excludeTime);
			jqMap.$confirm.click({rid: data.id, day: stMap.dayArray, night: stMap.nightArray}, confirm);
		});
	}

	return {
		initModule: initModule
	};
}());
<|endoftext|>


<commit_before>// Given a binary tree, collect a tree's nodes as if you were doing this:

// Collect and remove all leaves, repeat until the tree is empty.

// Example:
// Given binary tree
//           1
//          / \
//         2   3
//        / \
//       4   5
// Returns [4, 5, 3], [2], [1].

// Explanation:
// 1. Removing the leaves [4, 5, 3] would result in this tree:

//           1
//          /
//         2
// 2. Now removing the leaf [2] would result in this tree:

//           1
// 3. Now removing the leaf [1] would result in the empty tree:

//           []
// Returns [4, 5, 3], [2], [1].

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */

var findLeaves = function(root) {
    var collection = [];

    function height(node, arr) {
        if (node === null) return -1;
        var level = 1 + Math.max(height(node.left, arr), height(node.right, arr));
        if (arr.length < level + 1) arr.push([]);
        arr[level].push(node.val);
        return level;
    }

    height(root, collection);
    return collection;
};

// ----------------------------------------------------------

// Only gets bottom level leaves

var findLeaves = function(root) {
    var collection = [];

    function helper( node, arr ){
        console.log(node);
        if( node === null ) return node;
        else if( !node.left && !node.right ){
            arr.push( node.val );
        }
        else if( node.left && !node.right) helper( node.left, arr );
        else if( !node.left && node.right) helper( node.right, arr );
        else{
            helper(node.left, arr),
            helper(node.right, arr);
            node.left = null;
            node.right = null;
        }
        return arr;
    }
    collection.push( helper(root, []) );
    return collection;
};
<commit_msg>add logic<commit_after>// Given a binary tree, collect a tree's nodes as if you were doing this:

// Collect and remove all leaves, repeat until the tree is empty.

// Example:
// Given binary tree
//           1
//          / \
//         2   3
//        / \
//       4   5
// Returns [4, 5, 3], [2], [1].

// Explanation:
// 1. Removing the leaves [4, 5, 3] would result in this tree:

//           1
//          /
//         2
// 2. Now removing the leaf [2] would result in this tree:

//           1
// 3. Now removing the leaf [1] would result in the empty tree:

//           []
// Returns [4, 5, 3], [2], [1].

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */

// Logic

// The key is to find the height of each node. Here the definition of height is:
  // The height of a node is the number of edges from the node to the deepest leaf

// create a var to store collection of nodes to return
// use a helper function to return the height of current node.
// According to the definition, the height of leaf is 0. h(node) = 1 + max(h(node.left), h(node.right)).
// The height of a node is also the its index in the array collection. For example, leaves, whose heights are 0,
// are stored in collection[0]. Once we find the height of a node, we can put it directly into the collection.

var findLeaves = function(root) {
    var collection = [];

    function height(node, arr) {
        if (node === null) return -1;
        var level = 1 + Math.max(height(node.left, arr), height(node.right, arr));
        if (arr.length < level + 1) arr.push([]);
        arr[level].push(node.val);
        return level;
    }

    height(root, collection);
    return collection;
};

// ----------------------------------------------------------

// Only gets bottom level leaves

var findLeaves = function(root) {
    var collection = [];

    function helper( node, arr ){
        console.log(node);
        if( node === null ) return node;
        else if( !node.left && !node.right ){
            arr.push( node.val );
        }
        else if( node.left && !node.right) helper( node.left, arr );
        else if( !node.left && node.right) helper( node.right, arr );
        else{
            helper(node.left, arr),
            helper(node.right, arr);
            node.left = null;
            node.right = null;
        }
        return arr;
    }
    collection.push( helper(root, []) );
    return collection;
};
<|endoftext|>