<commit_before>/*
	Copyright (c) 2010, Micah N Gorrell
	All rights reserved.

	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
	WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
	EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
	OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
	OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

enyo.kind({

name:									"TweetList",
classes:								"tweetlist enyo-fit",

published: {
	user:								null,
	resource:							"home",

	refreshTime:						-1,
	notify:								false,

    rowsPerPage:                        50 /* This is supposed to be overridden by webOSPhoneTweetList below... */
},

events: {
	onRefreshStart:						"",
	onRefreshStop:						"",
	onOpenToaster:						""
},

components: [
],

create: function()
{
	this.inherited(arguments);

	this.twitter = new TwitterAPI(this.user);
    this.createComponent({
		name:							"list",
		kind:							enyo.PulldownList,
		classes:						"enyo-fit",

		ontap:							"itemTap",
		onSetupItem:					"setupItem",
		onPullRelease:					"pullRelease",
		onPullComplete:					"pullComplete",

		horizontal:						"hidden",
		vertical:						"scroll",

		thumb:							true,
		enableSwipe:					false,
		noSelect:						true,

        rowsPerPage:					this.rowsPerPage,

		components: [{
			name:						"tweet",
			kind:						"Tweet"
		}, {
			name:						"msg",
			classes:					"hide"
		}]
	}, { owner: this });
},

destroy: function()
{
	clearTimeout(this.timeout);

	this.inherited(arguments);
},

importProps: function(inProps) {
    this.inherited(arguments);
},

rendered: function()
{
	this.inherited(arguments);

	this.results = [];

	/* Load cached tweets */
	var results = prefs.get('cachedtweets:' + this.user.user_id + ':' + this.resource) || [];

	this.twitter.cleanupTweets(results);

	// Testing... Remove the most recent entries from the cached results so that
	// we can immediately load them again as if they where new.
	// results.splice(0, 5);

	if (results && results.length) {
		this.loading = true;
		this.doRefreshStart();

		this.gotTweets(true, results);
	} else {
		this.refresh();
	}
},

pullRelease: function()
{
	if (!this.pulled) {
		this.pulled = true;

		setTimeout(enyo.bind(this, function() {
			this.refresh();
		}), 500);
	}
},

pullComplete: function()
{
	this.$.list.reset();
	this.pulled = false;
},

refresh: function(keepnew)
{
	this.setTimer();

	if (this.loading) {
		return;
	}

	if (keepnew && this.loaded) {
		var now		= new Date();
		var elapsed	= now - this.loaded;

		if ((elapsed / 1000) < this.refreshTime) {
			this.log('Auto refresh tried to run too soon...', elapsed, now, this.loaded);

			return;
		}
	}

	this.loading = true;
	this.doRefreshStart();

	var params = {
		include_entities:		true
	};

	if (this.results.length) {
		/* Request a bit of an overlap in order to try to detect gaps */
		for (var i = 4; i >= 0; i--) {
			if (this.results[i] && this.results[i].id_str) {
				params.since_id = this.results[i].id_str;
				this.sinceIndex = i;
				break;
			}
		}

		if (!params.since_id) {
			/* Really? The most recent 5 items didn't have an id? Weird... */
			while (this.results.length && !this.results[0].id_str) {
				this.results.splice(0, 1);
			}

			if (this.results.length && this.results[0].id_str) {
				params.since_id = this.results[0].id_str;
			}
		}

		/* Load as many as possible to avoid gaps, max allowed is 200 */
		params.count = 200;
	} else {
		/* Load a reasonable amount */
		params.count = 50;
	}

	this.twitter.getTweets(this.resource, enyo.bind(this, function(success, results) {
		this.gotTweets(success, results, keepnew);
	}), params);
},

gotTweets: function(success, results, keepnew)
{
	var		changed			= false;
	var		newCountIndex	= NaN;

	if (this.destroyed) {
		/*
			This can happen if the tabs are rebuilt while a refresh is in
			progress.
		*/
		return;
	}

	/* Keep track of when we last loaded */
	this.loaded = new Date();

	/* Find the newcount indicator */
	for (var i = 0, t; t = this.results[i]; i++) {
		if (t.newcount) {
			newCountIndex = i;
			break;
		}
	}

	/* Remove the previous newcount indicator */
	if (!isNaN(newCountIndex) && !keepnew) {
		this.results.splice(newCountIndex, 1);
		newCountIndex = NaN;

		changed = true;
	}

	/* Remove the "no tweets" indicator */
	if (this.results.length > 0 && this.results[0].empty) {
		this.results.splice(0, 1);

		changed = true;
	}

	if (!success) {
		/* Failed */
		this.$.list.refresh();
		this.$.list.completePull();

		ex("Refresh failed");
		this.loading = false;
		this.doRefreshStop();

		this.setTimer();
		return;
	}

	/*
		Gap detection

		We asked for 5 overlapping items to try to detect gaps, but twitter at
		times will return older items than we asked for too. If the new results
		have any items that match existing items then we have no gap.
	*/
	var match = true;
	// this.log(this.resource, 'Pre-gap  detection: There are ' + this.results.length + ' existing tweets and ' + results.length + ' new tweets');
	if (this.results.length > 0 && results.length > 0) {
		for (var n = 0, ni; ni = results[n]; n++) {
			for (var o = 0, oi; oi = this.results[o]; o++) {
				if (ni.id_str === oi.id_str) {
					/* We found a matching item, anything older is a duplicate */
					// this.log(this.resource, 'Removing duplicates from: ' + ni.id_str);
					match = true;
					results.splice(n);
					break;
				}
			}
		}

		if (match) {
			/* We found our match, there is no gap */
			// this.log(this.resource, 'No gap, we had an overlap');
		} else {
			// this.log(this.resource, 'Found a gap');
			changed = true;

			/* We have a gap! */
			this.results.unshift({
				gap: {
					before:	this.results[0].id_str,
					after:	results[results.length - 1].id_str
				}
			});
		}
	}
	this.log(this.resource, 'Post-gap detection: There are ' + this.results.length + ' existing tweets and ' + results.length + ' new tweets');

	if (keepnew && !isNaN(newCountIndex)) {
		/* Update the existing newcount indicator */
		if (results.length) {
			this.results[newCountIndex].newcount += results.length;

			changed = true;
		}
	} else if (results.length && this.results.length) {
		/* Insert a new newcount indicator */
		changed = true;
		this.results.unshift({
			newcount:	results.length
		});

		newCountIndex = 0;
	}

	if (results.length) {
		changed = true;
		this.results = results.concat(this.results);

		if (!isNaN(newCountIndex)) {
			newCountIndex += results.length;
		}

		/*
			Flush any old results to keep the total number of loaded tweets sane

			Twitter will never return more than 200 results, so keep a few extra
			for context.
		*/
		// TODO	We need to implement loading gaps, and allow loading tweets
		//		below the loaded timeline. In those cases this is obviously not
		//		the correct check to do.
		if (this.results.length > 205) {
			this.results.splice(205);
		}

		/*
			Cache the 20 most recent items

			Do not include the new count indicator. Gap indicators are okay
			though.
		*/
		var cache = this.results.slice(0, 20);

		for (var i = cache.length - 1, c; c = cache[i]; i--) {
			if (!c.id_str && !c.gap) {
				cache.splice(i, 1);
			}
		}

		prefs.set('cachedtweets:' + this.user.user_id + ':' + this.resource, cache);

		/* Scroll to the oldest new tweet */
		if (!keepnew) {
			setTimeout(enyo.bind(this, function() {
				if (!isNaN(newCountIndex) && newCountIndex > 1) {
					this.log(this.resource, 'Scrolling to: ' + (newCountIndex - 1));
					this.$.list.scrollToRow(newCountIndex - 1);

					/*
						Scroll down just a bit to show that there is another tweet
						above this one.
					*/
					setTimeout(enyo.bind(this, function() {
						var top = this.$.list.getScrollTop();
						if (top > 35) {
							this.$.list.setScrollTop(top - 35);
						} else {
							this.$.list.setScrollTop(0);
						}
					}), 30);
				} else {
					this.$.list.scrollToRow(0);
				}
			}), 500);
		}
	}

	if (this.results.length == 0) {
		changed = true;

		this.results.unshift({
			empty:		true
		});
	}

	if (changed) {
		this.$.list.setCount(this.results.length);
		this.$.list.refresh();
	}

	this.loading = false;
	this.doRefreshStop();

	if (this.pulled) {
		this.$.list.completePull();
	}
	this.setTimer();
},

setTimer: function()
{
	clearTimeout(this.timeout);

	if (isNaN(this.refreshTime) || this.refreshTime < 1) {
		return;
	}

	this.log(this.resource, 'Setting timer to refresh ' + this.refreshTime + ' from now');
	setTimeout(function() {
		this.log(this.resource, 'Refreshing...');
		this.refresh(true);
	}.bind(this), this.refreshTime * 1000);
},

itemTap: function(sender, event)
{
	var item	= this.results[event.index];

	if (!item) {
		return;
	}

	this.doOpenToaster({
		component: {
			kind:			"TweetDetails",
			item:			item,
			user:			this.user,
			twitter:		this.twitter
		},

		options:{
			notitle: true
		}
	});
},

setupItem: function(sender, event)
{
	var item	= this.results[event.index];
	var d;

	if (!item) {
		return;
	}

	if (item.id_str && this.$.tweet.id_str === item.id_str) {
		/* Already setup */
		return;
	}

	this.$.tweet.id_str = item.id_str;

	if (item.newcount) {
		if (item.newcount > 1) {
			this.$.msg.setContent(item.newcount + ' new tweets');
		} else {
			this.$.msg.setContent(item.newcount + ' new tweet');
		}

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('newcount');
		return;
	}

	if (item.gap) {
		// TODO	When tapped load the gap
		this.$.msg.setContent('Tap to load missing tweets');

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('gap');
		return;
	}

	if (item.empty) {
		this.$.msg.setContent('No tweets');

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('gap');
		return;
	}

	this.$.msg.setClasses('hide');
	this.$.msg.setContent('');

	this.$.tweet.setClasses('tweet');

	if (item.favorited) {
		this.$.tweet.addClass('favorite');
	} else {
		this.$.tweet.removeClass('favorite');
	}

	this.$.tweet.setupTweet(item);
},

smartscroll: function()
{
	if (0 == this.$.list.getScrollTop()) {
		this.$.list.scrollToBottom();
	} else {
		this.$.list.scrollToTop();
	}
}

});

/*
    Right now we are just generalizing. This should probably be tweaked based on
	screen size.  Then we can use the device name + TweetList

	(i.e. Pre3TweetList, VeerTweetList, PreTweetList.
 */
enyo.kind({
    name:                               "webOSPhoneTweetList",
    kind:                               "TweetList",

    rowsPerPage:                        2
});

<commit_msg>Preserve scroll position when auto refreshing, but move a bit to show the new count indicator<commit_after>/*
	Copyright (c) 2010, Micah N Gorrell
	All rights reserved.

	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
	WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
	MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
	EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
	OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
	WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
	OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
	ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

enyo.kind({

name:									"TweetList",
classes:								"tweetlist enyo-fit",

published: {
	user:								null,
	resource:							"home",

	refreshTime:						-1,
	notify:								false,

    rowsPerPage:                        50 /* This is supposed to be overridden by webOSPhoneTweetList below... */
},

events: {
	onRefreshStart:						"",
	onRefreshStop:						"",
	onOpenToaster:						""
},

components: [
],

create: function()
{
	this.inherited(arguments);

	this.twitter = new TwitterAPI(this.user);
    this.createComponent({
		name:							"list",
		kind:							enyo.PulldownList,
		classes:						"enyo-fit",

		ontap:							"itemTap",
		onSetupItem:					"setupItem",
		onPullRelease:					"pullRelease",
		onPullComplete:					"pullComplete",

		horizontal:						"hidden",
		vertical:						"scroll",

		thumb:							true,
		enableSwipe:					false,
		noSelect:						true,

        rowsPerPage:					this.rowsPerPage,

		components: [{
			name:						"tweet",
			kind:						"Tweet"
		}, {
			name:						"msg",
			classes:					"hide"
		}]
	}, { owner: this });
},

destroy: function()
{
	clearTimeout(this.timeout);

	this.inherited(arguments);
},

importProps: function(inProps) {
    this.inherited(arguments);
},

rendered: function()
{
	this.inherited(arguments);

	this.results = [];

	/* Load cached tweets */
	var results = prefs.get('cachedtweets:' + this.user.user_id + ':' + this.resource) || [];

	this.twitter.cleanupTweets(results);

	// Testing... Remove the most recent entries from the cached results so that
	// we can immediately load them again as if they where new.
	// results.splice(0, 5);

	if (results && results.length) {
		this.loading = true;
		this.doRefreshStart();

		this.gotTweets(true, results);
	} else {
		this.refresh();
	}
},

pullRelease: function()
{
	if (!this.pulled) {
		this.pulled = true;

		setTimeout(enyo.bind(this, function() {
			this.refresh();
		}), 500);
	}
},

pullComplete: function()
{
	this.$.list.reset();
	this.pulled = false;
},

refresh: function(keepnew)
{
	this.setTimer();

	if (this.loading) {
		return;
	}

	if (keepnew && this.loaded) {
		var now		= new Date();
		var elapsed	= now - this.loaded;

		if ((elapsed / 1000) < this.refreshTime) {
			this.log('Auto refresh tried to run too soon...', elapsed, now, this.loaded);

			return;
		}
	}

	this.loading = true;
	this.doRefreshStart();

	var params = {
		include_entities:		true
	};

	if (this.results.length) {
		/* Request a bit of an overlap in order to try to detect gaps */
		for (var i = 4; i >= 0; i--) {
			if (this.results[i] && this.results[i].id_str) {
				params.since_id = this.results[i].id_str;
				this.sinceIndex = i;
				break;
			}
		}

		if (!params.since_id) {
			/* Really? The most recent 5 items didn't have an id? Weird... */
			while (this.results.length && !this.results[0].id_str) {
				this.results.splice(0, 1);
			}

			if (this.results.length && this.results[0].id_str) {
				params.since_id = this.results[0].id_str;
			}
		}

		/* Load as many as possible to avoid gaps, max allowed is 200 */
		params.count = 200;
	} else {
		/* Load a reasonable amount */
		params.count = 50;
	}

	this.twitter.getTweets(this.resource, enyo.bind(this, function(success, results) {
		this.gotTweets(success, results, keepnew);
	}), params);
},

gotTweets: function(success, results, keepnew)
{
	var		changed			= false;
	var		newCountIndex	= NaN;

	if (this.destroyed) {
		/*
			This can happen if the tabs are rebuilt while a refresh is in
			progress.
		*/
		return;
	}

	/* Keep track of when we last loaded */
	this.loaded = new Date();

	/* Find the newcount indicator */
	for (var i = 0, t; t = this.results[i]; i++) {
		if (t.newcount) {
			newCountIndex = i;
			break;
		}
	}

	/* Remove the previous newcount indicator */
	if (!isNaN(newCountIndex) && !keepnew) {
		this.results.splice(newCountIndex, 1);
		newCountIndex = NaN;

		changed = true;
	}

	/* Remove the "no tweets" indicator */
	if (this.results.length > 0 && this.results[0].empty) {
		this.results.splice(0, 1);

		changed = true;
	}

	if (!success) {
		/* Failed */
		this.$.list.refresh();
		this.$.list.completePull();

		ex("Refresh failed");
		this.loading = false;
		this.doRefreshStop();

		this.setTimer();
		return;
	}

	/*
		Gap detection

		We asked for 5 overlapping items to try to detect gaps, but twitter at
		times will return older items than we asked for too. If the new results
		have any items that match existing items then we have no gap.
	*/
	var match = true;
	// this.log(this.resource, 'Pre-gap  detection: There are ' + this.results.length + ' existing tweets and ' + results.length + ' new tweets');
	if (this.results.length > 0 && results.length > 0) {
		for (var n = 0, ni; ni = results[n]; n++) {
			for (var o = 0, oi; oi = this.results[o]; o++) {
				if (ni.id_str === oi.id_str) {
					/* We found a matching item, anything older is a duplicate */
					// this.log(this.resource, 'Removing duplicates from: ' + ni.id_str);
					match = true;
					results.splice(n);
					break;
				}
			}
		}

		if (match) {
			/* We found our match, there is no gap */
			// this.log(this.resource, 'No gap, we had an overlap');
		} else {
			// this.log(this.resource, 'Found a gap');
			changed = true;

			/* We have a gap! */
			this.results.unshift({
				gap: {
					before:	this.results[0].id_str,
					after:	results[results.length - 1].id_str
				}
			});
		}
	}
	this.log(this.resource, 'Post-gap detection: There are ' + this.results.length + ' existing tweets and ' + results.length + ' new tweets');

	if (keepnew && !isNaN(newCountIndex)) {
		/* Update the existing newcount indicator */
		if (results.length) {
			this.results[newCountIndex].newcount += results.length;

			changed = true;
		}
	} else if (results.length && this.results.length) {
		/* Insert a new newcount indicator */
		changed = true;
		this.results.unshift({
			newcount:	results.length
		});

		newCountIndex = 0;
	}

	if (results.length) {
		changed = true;
		this.results = results.concat(this.results);

		if (!isNaN(newCountIndex)) {
			newCountIndex += results.length;
		}

		/*
			Flush any old results to keep the total number of loaded tweets sane

			Twitter will never return more than 200 results, so keep a few extra
			for context.
		*/
		// TODO	We need to implement loading gaps, and allow loading tweets
		//		below the loaded timeline. In those cases this is obviously not
		//		the correct check to do.
		if (this.results.length > 205) {
			this.results.splice(205);
		}

		/*
			Cache the 20 most recent items

			Do not include the new count indicator. Gap indicators are okay
			though.
		*/
		var cache = this.results.slice(0, 20);

		for (var i = cache.length - 1, c; c = cache[i]; i--) {
			if (!c.id_str && !c.gap) {
				cache.splice(i, 1);
			}
		}

		prefs.set('cachedtweets:' + this.user.user_id + ':' + this.resource, cache);

		/* Scroll to the oldest new tweet */
		if (!keepnew) {
			setTimeout(enyo.bind(this, function() {
				if (!isNaN(newCountIndex) && newCountIndex > 1) {
					this.log(this.resource, 'Scrolling to: ' + (newCountIndex - 1));
					this.$.list.scrollToRow(newCountIndex - 1);

					/*
						Scroll down just a bit to show that there is another tweet
						above this one.
					*/
					setTimeout(enyo.bind(this, function() {
						var top = this.$.list.getScrollTop();

						if (top > 35) {
							this.$.list.setScrollTop(top - 35);
						} else {
							this.$.list.setScrollTop(0);
						}
					}), 30);
				} else {
					this.$.list.scrollToRow(0);
				}
			}), 500);
		} else if (results.length) {
			/*
				Attempt to preserve the scroll position even though items where
				inserted at the top of the list.
			*/
			var beforetop = this.$.list.getScrollTop();

			this.$.list.scrollToRow(results.length);

			/* Now scroll back... */
			setTimeout(enyo.bind(this, function() {
				var aftertop	= this.$.list.getScrollTop();
				var top			= beforetop + aftertop;

				if (beforetop <= 35 && top > 35) {
					top -= 35;
				}

				this.$.list.setScrollTop(top);
			}), 30);
		}
	}

	if (this.results.length == 0) {
		changed = true;

		this.results.unshift({
			empty:		true
		});
	}

	if (changed) {
		this.$.list.setCount(this.results.length);
		this.$.list.refresh();
	}

	this.loading = false;
	this.doRefreshStop();

	if (this.pulled) {
		this.$.list.completePull();
	}
	this.setTimer();
},

setTimer: function()
{
	clearTimeout(this.timeout);

	if (isNaN(this.refreshTime) || this.refreshTime < 1) {
		return;
	}

	this.log(this.resource, 'Setting timer to refresh ' + this.refreshTime + ' from now');
	setTimeout(function() {
		this.log(this.resource, 'Refreshing...');
		this.refresh(true);
	}.bind(this), this.refreshTime * 1000);
},

itemTap: function(sender, event)
{
	var item	= this.results[event.index];

	if (!item) {
		return;
	}

	this.doOpenToaster({
		component: {
			kind:			"TweetDetails",
			item:			item,
			user:			this.user,
			twitter:		this.twitter
		},

		options:{
			notitle: true
		}
	});
},

setupItem: function(sender, event)
{
	var item	= this.results[event.index];
	var d;

	if (!item) {
		return;
	}

	if (item.id_str && this.$.tweet.id_str === item.id_str) {
		/* Already setup */
		return;
	}

	this.$.tweet.id_str = item.id_str;

	if (item.newcount) {
		if (item.newcount > 1) {
			this.$.msg.setContent(item.newcount + ' new tweets');
		} else {
			this.$.msg.setContent(item.newcount + ' new tweet');
		}

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('newcount');
		return;
	}

	if (item.gap) {
		// TODO	When tapped load the gap
		this.$.msg.setContent('Tap to load missing tweets');

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('gap');
		return;
	}

	if (item.empty) {
		this.$.msg.setContent('No tweets');

		this.$.tweet.setClasses('hide');
		this.$.msg.setClasses('gap');
		return;
	}

	this.$.msg.setClasses('hide');
	this.$.msg.setContent('');

	this.$.tweet.setClasses('tweet');

	if (item.favorited) {
		this.$.tweet.addClass('favorite');
	} else {
		this.$.tweet.removeClass('favorite');
	}

	this.$.tweet.setupTweet(item);
},

smartscroll: function()
{
	if (0 == this.$.list.getScrollTop()) {
		this.$.list.scrollToBottom();
	} else {
		this.$.list.scrollToTop();
	}
}

});

/*
    Right now we are just generalizing. This should probably be tweaked based on
	screen size.  Then we can use the device name + TweetList

	(i.e. Pre3TweetList, VeerTweetList, PreTweetList.
 */
enyo.kind({
    name:                               "webOSPhoneTweetList",
    kind:                               "TweetList",

    rowsPerPage:                        2
});

<|endoftext|>


<commit_before>// import React from 'react';
// import PropTypes from 'prop-types';

// // Debouncer rendering of child components with a timer
// // Adapted from https://github.com/chrisshiplet/react-delay
// export default class Debouncer extends React.Component {
//   constructor(props) {
//     super(props);
//     this.state = {
//       waiting: true
//     };
//     this.wait = this.wait.bind(this);
//     this.onDelayDone = this.onDelayDone.bind(this);
//   }

//   componentDidMount() {
//     console.log('componentDidMount');
//     this.wait();
//   }

//   componentWillReceiveProps() {
//     console.log('componentWillReceiveProps');
//     this.wait();
//   }

//   componentWillUnmount() {
//     console.log('componentWillUnmount');
//     clearTimeout(this.timer);
//   }

//   wait() {
//     clearTimeout(this.timer);
//     this.timer = setTimeout(this.onDelayDone, this.props.wait);
//   }

//   onDelayDone() {
//     const {onDone} = this.props;
//     this.setState({ waiting: false }, () => {
//       if (onDone) onDone();
//     });
//   }

//   render() {
//     const {renderFn, placeholder} = this.props;
//     const {waiting} = this.state;
//     if (!waiting) {
//       return renderFn();
//     }

//     return placeholder;
//   }
// }

// Debouncer.propTypes = {
//   renderFn: PropTypes.func.isRequired,
//   wait: PropTypes.number.isRequired,
//   placeholder: PropTypes.node,
//   onDone: PropTypes.func
// };

// Debouncer.defaultProps = {
//   wait: 250,
//   placeholder: null
// };
<commit_msg>Remove debouncer<commit_after><|endoftext|>


<commit_before>var boom = function( src, volume ) {
    this.volume = volume || 100;
    this.sound = document.createElement('audio');
    this.sound.setAttribute('src', src);

    this.play = function() {
        this.sound.play();
    }
    this.stop = function() {
        this.sound.currentTime = 0;
        this.sound.pause();
    }
    this.pause = function() {
        this.sound.pause();
    }
    this.jump = function( time ) {
        this.setTime( time );
    }
    this.toggle = function( ) {
        if ( this.sound.paused ) {
            this.sound.play();
        } else {
            this.sound.pause();
        }
    }
    this.setVolume = function( volume ) {
        if ( volume > 100 ) {
            volume = 100;
        }
        if ( volume < 0 ) {
            volume = 0;
        }
        this.volume = volume;
        this.sound.volume = volume / 100;
    },
    this.getVolume = function() {
        return this.volume;
    }
    this.increaseVolume = function( value ) {
        this.setVolume( this.volume + ( value || 1 ) );
    }
    this.decreaseVolume = function( value ) {
        this.setVolume( this.volume - ( value || 1 ) );
    }
    this.setTime = function( time ) {
        this.sound.currentTime = time;
    }
    this.getTime = function() {
        return Math.round( this.sound.currentTime * 100 ) / 100;
    }
    this.getDuration = function() {
        return Math.round( this.sound.duration * 100 ) / 100;
    }
    this.setPercent = function( time ) {
        this.sound.currentTime = this.sound.duration * time / 100;
    }
    this.getPercent = function() {
        return Math.round( (this.sound.currentTime / this.sound.duration * 100 ) * 100) / 100;
    }
    this.get = function() {
        return this.sound;
    }
    this.bind = function( evt, func ) {
        this.sound.addEventListener( evt, func, true );
    }
}<commit_msg>Methods are chainable<commit_after>var boom = function( src, volume ) {
    this.volume = volume || 100;
    this.sound = document.createElement('audio');
    this.sound.setAttribute('src', src);

    this.play = function() {
        this.sound.play();
        return this;
    }
    this.stop = function() {
        this.sound.currentTime = 0;
        this.sound.pause();
        return this;
    }
    this.pause = function() {
        this.sound.pause();
        return this;
    }
    this.jump = function( time ) {
        this.setTime( time );
        return this;
    }
    this.toggle = function( ) {
        if ( this.sound.paused ) {
            this.sound.play();
        } else {
            this.sound.pause();
        }
        return this;
    }
    this.setVolume = function( volume ) {
        if ( volume > 100 ) {
            volume = 100;
        }
        if ( volume < 0 ) {
            volume = 0;
        }
        this.volume = volume;
        this.sound.volume = volume / 100;
        return this;
    },
    this.getVolume = function() {
        return this.volume;
    }
    this.increaseVolume = function( value ) {
        this.setVolume( this.volume + ( value || 1 ) );
        return this;
    }
    this.decreaseVolume = function( value ) {
        this.setVolume( this.volume - ( value || 1 ) );
        return this;
    }
    this.setTime = function( time ) {
        this.sound.currentTime = time;
        return this;
    }
    this.getTime = function() {
        return Math.round( this.sound.currentTime * 100 ) / 100;
    }
    this.getDuration = function() {
        return Math.round( this.sound.duration * 100 ) / 100;
    }
    this.setPercent = function( time ) {
        this.sound.currentTime = this.sound.duration * time / 100;
        return this;
    }
    this.getPercent = function() {
        return Math.round( (this.sound.currentTime / this.sound.duration * 100 ) * 100) / 100;
    }
    this.get = function() {
        return this.sound;
    }
    this.bind = function( evt, func ) {
        this.sound.addEventListener( evt, func, true );
        return this;
    }
}<|endoftext|>


<commit_before>APP.controllers.meetings = (function() {

    function init() {
      if ($("section.meeting").exists()) {
        initMeetingDetail();
        if ($("#leafletMap").exists()) APP.components.leafletMap.init($('#leafletMap'));
      }

      if ($("section.create-meeting").exists()) {
        initCreateMeeting();
        APP.components.search.userAutocomplete();
      }

      if ($("section.meetings-compose-mail").exists()) {
        initComposeMailMeeting();
      }

    }

    function initComposeMailMeeting() {

      $('select#mail-user-select').SumoSelect({
        search: true,
        searchText: 'Suche nach User.',
        placeholder: 'User auswählen',
        csvDispCount: 2,
        captionFormat: '{0} Treffenmitglieder',
        captionFormatAllSelected: 'Alle Treffenmitglieder',
        selectAll: true,
        isClickAwayOk: true,
        locale: ['OK', 'Abbrechen', 'Alle auswählen']
      });

      // Highlight Users on single Click Options
      $('.select_users .opt').on('click', function(){
        var user_index = $(this).index();
        if ($(this).hasClass('selected')){
          $('.meetings-compose-mail .members a').eq(user_index).find('.avatar').addClass('active');
        } else {
          $('.meetings-compose-mail .members a').eq(user_index).find('.avatar').removeClass('active');
        }
      });

    }

    function initMeetingDetail() {

      // Submit Further Meetings & Hide Block if Empty
      $('.autosubmit-stream').submit();
      $(".autosubmit-stream").bind('ajax:complete', function() {
        if ($("[data-behavior=meetings-card-container]").is(':empty')){
          $(".meetingBlock").hide();
        }
      });

      $('.entryDescription .bbcode').linkify({
        target: "_blank"
      });

      if ($(".additonal").exists()) {
        $('.dateTime').lightSlider({
          item: 2,
          autoWidth: false,
          slideMove: 2, // slidemove will be 1 if loop is true
          slideMargin: 0,
          addClass: 'additionalSlider',
          mode: "slide",
          useCSS: true,
          cssEasing: 'ease', //'cubic-bezier(0.25, 0, 0.25, 1)',//
          easing: 'linear', //'for jquery animation',////
          speed: 750, //ms'
          auto: false,
          loop: false,
          slideEndAnimation: true,
          pause: 10000,
          keyPress: false,
          controls: true,
          prevHtml: '',
          nextHtml: '',
          adaptiveHeight:false,
          pager: false,
          //currentPagerPosition: 'middle',
          enableTouch:true,
          enableDrag:false,
          freeMove:true,
          swipeThreshold: 40,
          responsive : [
            {
              breakpoint:APP.config.majorBreakpoints.large,
              settings: {
                item: 1,
                slideMove: 1
              }
            }
          ]
        });
      }

      var buyTicket = new jBox('Modal', {
        addClass:'jBox',
        attach: '#buyTicket',
        content: $('#jBoxBuyTicket'),
        trigger: 'click',
        closeOnEsc:true,
        closeOnClick:'body',
        blockScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
        onOpen: function() {
          var meeting_id = $('#meeting_id').val();
          var page_title = 'Dein Ticket - Termin wählen - ' + document.title;
          var page_path = '/going_tos/choose_date?meeting_id='+meeting_id;
          gtag('config', window.imgraetzl.uaid, {
            'page_title': page_title,
            'page_path': page_path
          });
        }
      });

      var attendMeeting = new jBox('Modal', {
        addClass:'jBox',
        attach: '#attendMeeting',
        content: $('#jBoxAttendMeeting'),
        trigger: 'click',
        closeOnEsc:true,
        closeOnClick:'body',
        blockScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      var meetingSettings = new jBox('Tooltip', {
        addClass:'jBox',
        attach: '#meetingSettings',
        content: $('#jBoxMeetingSettings'),
        trigger: 'click',
        closeOnClick:true,
        isolateScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      var meetingSettingsPaid = new jBox('Tooltip', {
        addClass:'jBox',
        attach: '#meetingSettingsPaid',
        content: $('#jBoxMeetingSettingsPaid'),
        trigger: 'click',
        closeOnClick:true,
        isolateScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      // Analytics
      $('.meeting-attend-btn').on('click', function() {
        gtag(
          'event', 'Click :: Teilnehmen', {
          'event_category': 'Meeting'
        });
      });

      $('.meeting-unattend-btn').on('click', function() {
        gtag(
          'event', 'Click :: Nicht mehr teilnehmen', {
          'event_category': 'Meeting'
        });
      });

    }

    function initCreateMeeting() {
      APP.components.addressInput();

      $('.create-meeting').on('cocoon:after-insert', function(e, insertedItem, originalEvent) {
        $('.datepicker').pickadate({
          formatSubmit: 'yyyy-mm-dd',
          hiddenName: true,
          min: true
        });

        $('.timepicker').pickatime({
          interval: 15,
          format: 'HH:i',
          formatSubmit: 'HH:i',
          hiddenSuffix: '',
        });
      }).trigger('cocoon:after-insert');

      $("textarea").autogrow({
        onInitialize: true
      });

      // online meeting switch
      $('.online-meeting-switch').on('change', function() {
        var showOnlineFields = $(this).val() == "true";
        $('#meeting-online-fields').toggle(showOnlineFields);
        $('#meeting-offline-fields').toggle(!showOnlineFields);
        $("#meeting-offline-fields").find("input, select").attr("disabled", showOnlineFields);
      })
      $('.online-meeting-switch:checked').trigger('change');

      // check date radio on upload
      if ($("#meeting_ends_at_date").val()) {
        $(".date-switch :radio[value=range]").prop("checked", true);
      }
      else if ($(".additional-dates").find('.nested-fields').exists()) {
        $(".date-switch :radio[value=multiple]").prop("checked", true);
      }
      else {
        $(".date-switch :radio[value=single]").prop("checked", true);
      }

      // date switch
      $('.date-switch').on('change', function() {
        var checked = $(".date-switch input[type='radio']:checked").val();
        $(".date-fields").hide();
        $("#date-option-"+checked).show();
      }).trigger('change');

      // location field toggle
      $('.select-location-input').on('change', function() {
        var location = $(this).find("option:selected");
        //if (!location.data("graetzl-id")) return;
        $('#meeting-offline-fields [name*="address_street"]').val(location.data("address-street"));
        $('#meeting-offline-fields [name*="address_coords"]').val(location.data("address-coords"));
        $('#meeting-offline-fields [name*="address_zip"]').val(location.data("address-zip"));
        $('#meeting-offline-fields [name*="address_city"]').val(location.data("address-city"));
        $('#meeting-offline-fields [name*="graetzl_id"]').val(location.data("graetzl-id"));
        $('#meeting-offline-fields [name*="address_description"]').val(location.data("address-description"));
      });

      // Trigger Location Toggle on load if location_id param is set
      var location = APP.controllers.application.getUrlVars()["location_id"];
      if (typeof location !== 'undefined') {
        $('.select-location-input').trigger('change');
      }

      // platform_meeting checkbox
      if ($("input:checkbox#meeting_platform_meeting_join_request_attributes_status").exists()) {
          var $platform_join_checkbox = $('input:checkbox#meeting_platform_meeting_join_request_attributes_status');
          if(!document.getElementById('meeting_platform_meeting_join_request_attributes_status').checked) {
            $('div#meeting-platform-meeting-fields').hide();
          }
          $platform_join_checkbox.on('change', function() {
            $('div#meeting-platform-meeting-fields').slideToggle();
          });
      }

      // Hide Elements
      $('.hide').hide();

      $(".event-categories input").on("change", function() {
        maxCategories(); // init on Change
      }).trigger('change');
    }

    function maxCategories() {
      if ($(".event-categories input:checked").length >= 3) {
        $(".event-categories input:not(:checked)").each(function() {
          $(this).prop("disabled", true);
          $(this).parents(".input-checkbox").addClass("disabled");
        });
      } else {
        $(".event-categories input").prop("disabled", false);
        $(".event-categories .input-checkbox").removeClass("disabled");
      }
    }

    return {
      init: init
    };

})();
<commit_msg>js fix<commit_after>APP.controllers.meetings = (function() {

    function init() {
      if ($("section.meeting").exists()) {
        initMeetingDetail();
        if ($("#leafletMap").exists()) APP.components.leafletMap.init($('#leafletMap'));
      }

      if ($("section.create-meeting").exists()) {
        initCreateMeeting();
        APP.components.search.userAutocomplete();
      }

      if ($("section.meetings-compose-mail").exists()) {
        initComposeMailMeeting();
      }

    }

    function initComposeMailMeeting() {

      $('select#mail-user-select').SumoSelect({
        search: true,
        searchText: 'Suche nach User.',
        placeholder: 'User auswählen',
        csvDispCount: 2,
        captionFormat: '{0} Treffenmitglieder',
        captionFormatAllSelected: 'Alle Treffenmitglieder',
        selectAll: true,
        isClickAwayOk: true,
        locale: ['OK', 'Abbrechen', 'Alle auswählen']
      });

      // Highlight Users on single Click Options
      $('.select_users .opt').on('click', function(){
        var user_index = $(this).index();
        if ($(this).hasClass('selected')){
          $('.meetings-compose-mail .members a').eq(user_index).find('.img-round').addClass('active');
        } else {
          $('.meetings-compose-mail .members a').eq(user_index).find('.img-round').removeClass('active');
        }
      });

    }

    function initMeetingDetail() {

      // Submit Further Meetings & Hide Block if Empty
      $('.autosubmit-stream').submit();
      $(".autosubmit-stream").bind('ajax:complete', function() {
        if ($("[data-behavior=meetings-card-container]").is(':empty')){
          $(".meetingBlock").hide();
        }
      });

      $('.entryDescription .bbcode').linkify({
        target: "_blank"
      });

      if ($(".additonal").exists()) {
        $('.dateTime').lightSlider({
          item: 2,
          autoWidth: false,
          slideMove: 2, // slidemove will be 1 if loop is true
          slideMargin: 0,
          addClass: 'additionalSlider',
          mode: "slide",
          useCSS: true,
          cssEasing: 'ease', //'cubic-bezier(0.25, 0, 0.25, 1)',//
          easing: 'linear', //'for jquery animation',////
          speed: 750, //ms'
          auto: false,
          loop: false,
          slideEndAnimation: true,
          pause: 10000,
          keyPress: false,
          controls: true,
          prevHtml: '',
          nextHtml: '',
          adaptiveHeight:false,
          pager: false,
          //currentPagerPosition: 'middle',
          enableTouch:true,
          enableDrag:false,
          freeMove:true,
          swipeThreshold: 40,
          responsive : [
            {
              breakpoint:APP.config.majorBreakpoints.large,
              settings: {
                item: 1,
                slideMove: 1
              }
            }
          ]
        });
      }

      var buyTicket = new jBox('Modal', {
        addClass:'jBox',
        attach: '#buyTicket',
        content: $('#jBoxBuyTicket'),
        trigger: 'click',
        closeOnEsc:true,
        closeOnClick:'body',
        blockScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
        onOpen: function() {
          var meeting_id = $('#meeting_id').val();
          var page_title = 'Dein Ticket - Termin wählen - ' + document.title;
          var page_path = '/going_tos/choose_date?meeting_id='+meeting_id;
          gtag('config', window.imgraetzl.uaid, {
            'page_title': page_title,
            'page_path': page_path
          });
        }
      });

      var attendMeeting = new jBox('Modal', {
        addClass:'jBox',
        attach: '#attendMeeting',
        content: $('#jBoxAttendMeeting'),
        trigger: 'click',
        closeOnEsc:true,
        closeOnClick:'body',
        blockScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      var meetingSettings = new jBox('Tooltip', {
        addClass:'jBox',
        attach: '#meetingSettings',
        content: $('#jBoxMeetingSettings'),
        trigger: 'click',
        closeOnClick:true,
        isolateScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      var meetingSettingsPaid = new jBox('Tooltip', {
        addClass:'jBox',
        attach: '#meetingSettingsPaid',
        content: $('#jBoxMeetingSettingsPaid'),
        trigger: 'click',
        closeOnClick:true,
        isolateScroll:true,
        animation:{open: 'zoomIn', close: 'zoomOut'},
      });

      // Analytics
      $('.meeting-attend-btn').on('click', function() {
        gtag(
          'event', 'Click :: Teilnehmen', {
          'event_category': 'Meeting'
        });
      });

      $('.meeting-unattend-btn').on('click', function() {
        gtag(
          'event', 'Click :: Nicht mehr teilnehmen', {
          'event_category': 'Meeting'
        });
      });

    }

    function initCreateMeeting() {
      APP.components.addressInput();

      $('.create-meeting').on('cocoon:after-insert', function(e, insertedItem, originalEvent) {
        $('.datepicker').pickadate({
          formatSubmit: 'yyyy-mm-dd',
          hiddenName: true,
          min: true
        });

        $('.timepicker').pickatime({
          interval: 15,
          format: 'HH:i',
          formatSubmit: 'HH:i',
          hiddenSuffix: '',
        });
      }).trigger('cocoon:after-insert');

      $("textarea").autogrow({
        onInitialize: true
      });

      // online meeting switch
      $('.online-meeting-switch').on('change', function() {
        var showOnlineFields = $(this).val() == "true";
        $('#meeting-online-fields').toggle(showOnlineFields);
        $('#meeting-offline-fields').toggle(!showOnlineFields);
        $("#meeting-offline-fields").find("input, select").attr("disabled", showOnlineFields);
      })
      $('.online-meeting-switch:checked').trigger('change');

      // check date radio on upload
      if ($("#meeting_ends_at_date").val()) {
        $(".date-switch :radio[value=range]").prop("checked", true);
      }
      else if ($(".additional-dates").find('.nested-fields').exists()) {
        $(".date-switch :radio[value=multiple]").prop("checked", true);
      }
      else {
        $(".date-switch :radio[value=single]").prop("checked", true);
      }

      // date switch
      $('.date-switch').on('change', function() {
        var checked = $(".date-switch input[type='radio']:checked").val();
        $(".date-fields").hide();
        $("#date-option-"+checked).show();
      }).trigger('change');

      // location field toggle
      $('.select-location-input').on('change', function() {
        var location = $(this).find("option:selected");
        //if (!location.data("graetzl-id")) return;
        $('#meeting-offline-fields [name*="address_street"]').val(location.data("address-street"));
        $('#meeting-offline-fields [name*="address_coords"]').val(location.data("address-coords"));
        $('#meeting-offline-fields [name*="address_zip"]').val(location.data("address-zip"));
        $('#meeting-offline-fields [name*="address_city"]').val(location.data("address-city"));
        $('#meeting-offline-fields [name*="graetzl_id"]').val(location.data("graetzl-id"));
        $('#meeting-offline-fields [name*="address_description"]').val(location.data("address-description"));
      });

      // Trigger Location Toggle on load if location_id param is set
      var location = APP.controllers.application.getUrlVars()["location_id"];
      if (typeof location !== 'undefined') {
        $('.select-location-input').trigger('change');
      }

      // platform_meeting checkbox
      if ($("input:checkbox#meeting_platform_meeting_join_request_attributes_status").exists()) {
          var $platform_join_checkbox = $('input:checkbox#meeting_platform_meeting_join_request_attributes_status');
          if(!document.getElementById('meeting_platform_meeting_join_request_attributes_status').checked) {
            $('div#meeting-platform-meeting-fields').hide();
          }
          $platform_join_checkbox.on('change', function() {
            $('div#meeting-platform-meeting-fields').slideToggle();
          });
      }

      // Hide Elements
      $('.hide').hide();

      $(".event-categories input").on("change", function() {
        maxCategories(); // init on Change
      }).trigger('change');
    }

    function maxCategories() {
      if ($(".event-categories input:checked").length >= 3) {
        $(".event-categories input:not(:checked)").each(function() {
          $(this).prop("disabled", true);
          $(this).parents(".input-checkbox").addClass("disabled");
        });
      } else {
        $(".event-categories input").prop("disabled", false);
        $(".event-categories .input-checkbox").removeClass("disabled");
      }
    }

    return {
      init: init
    };

})();
<|endoftext|>


<commit_before>/**
  This component helps with Searching

  @class Search
  @namespace Discourse
  @module Discourse
**/
Discourse.Search = {

  /**
    Search for a term, with an optional filter.

    @method forTerm
    @param {String} term The term to search for
    @param {Object} opts Options for searching
      @param {String} opts.typeFilter Filter our results to one type only
      @param {Ember.Object} opts.searchContext data to help searching within a context (say, a category or user)
    @return {Promise} a promise that resolves the search results
  **/
  forTerm: function(term, opts) {
    if (!opts) opts = {};

    // Only include the data we have
    var data = { term: term, include_blurbs: 'true' };
    if (opts.typeFilter) data.type_filter = opts.typeFilter;
    if (opts.searchForId) data.search_for_id = true;

    if (opts.searchContext) {
      data.search_context = {
        type: opts.searchContext.type,
        id: opts.searchContext.id
      };
    }

    var promise = Discourse.ajax('/search', { data: data });

    promise.then(function(results){
      // Topics might not be included
      if (!results.topics) { results.topics = []; }

      var topicMap = {};
      results.topics = results.topics.map(function(topic){
        topic = Discourse.Topic.create(topic);
        topicMap[topic.id] = topic;
        return topic;
      });

      results.posts = results.posts.map(function(post){
        post = Discourse.Post.create(post);
        post.set('topic', topicMap[post.topic_id]);
        return post;
      });

      results.users = results.users.map(function(user){
        user = Discourse.User.create(user);
        return user;
      });

      results.categories = results.categories.map(function(category){
        category = Discourse.Category.create(category);
        return category;
      });

      var r = results.grouped_search_result;
      results.resultTypes = [];

      // TODO: consider refactoring front end to take a better structure
      [['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){
        var type = pair[0], name = pair[1];
        if(results[name].length > 0) {
          results.resultTypes.push({
            results: results[name],
            displayType: (opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type,
            type: type,
            more: r['more_' + name]
          });
        }
      });

      var noResults = !!((results.topics.length === 0) && (results.posts.length === 0) && (results.categories.length === 0));

      return noResults ? null : Em.Object.create(results);
    });

    return promise;
  }

};

<commit_msg>FIX: link to sub category not working in search<commit_after>/**
  This component helps with Searching

  @class Search
  @namespace Discourse
  @module Discourse
**/
Discourse.Search = {

  /**
    Search for a term, with an optional filter.

    @method forTerm
    @param {String} term The term to search for
    @param {Object} opts Options for searching
      @param {String} opts.typeFilter Filter our results to one type only
      @param {Ember.Object} opts.searchContext data to help searching within a context (say, a category or user)
    @return {Promise} a promise that resolves the search results
  **/
  forTerm: function(term, opts) {
    if (!opts) opts = {};

    // Only include the data we have
    var data = { term: term, include_blurbs: 'true' };
    if (opts.typeFilter) data.type_filter = opts.typeFilter;
    if (opts.searchForId) data.search_for_id = true;

    if (opts.searchContext) {
      data.search_context = {
        type: opts.searchContext.type,
        id: opts.searchContext.id
      };
    }

    var promise = Discourse.ajax('/search', { data: data });

    promise.then(function(results){
      // Topics might not be included
      if (!results.topics) { results.topics = []; }

      var topicMap = {};
      results.topics = results.topics.map(function(topic){
        topic = Discourse.Topic.create(topic);
        topicMap[topic.id] = topic;
        return topic;
      });

      results.posts = results.posts.map(function(post){
        post = Discourse.Post.create(post);
        post.set('topic', topicMap[post.topic_id]);
        return post;
      });

      results.users = results.users.map(function(user){
        user = Discourse.User.create(user);
        return user;
      });

      results.categories = results.categories.map(function(category){
        category = Discourse.Category.list().findProperty('id', category.id);
        return category;
      });

      var r = results.grouped_search_result;
      results.resultTypes = [];

      // TODO: consider refactoring front end to take a better structure
      [['topic','posts'],['user','users'],['category','categories']].forEach(function(pair){
        var type = pair[0], name = pair[1];
        if(results[name].length > 0) {
          results.resultTypes.push({
            results: results[name],
            displayType: (opts.searchContext && opts.searchContext.type === 'topic' && type === 'topic') ? 'post' : type,
            type: type,
            more: r['more_' + name]
          });
        }
      });

      var noResults = !!((results.topics.length === 0) && (results.posts.length === 0) && (results.categories.length === 0));

      return noResults ? null : Em.Object.create(results);
    });

    return promise;
  }

};

<|endoftext|>


<commit_before>/*!
 * jQuery Migrate - v1.0.0 - 2013-01-14
 * https://github.com/jquery/jquery-migrate
 * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
 */
(function( jQuery, window, undefined ) {
"use strict";

var warnedAbout = {};

// List of warnings already given; public read only
jQuery.migrateWarnings = [];

// Set to true to prevent console output; migrateWarnings still maintained
jQuery.migrateMute = true;

// Forget any warnings we've already given; public
jQuery.migrateReset = function() {
  warnedAbout = {};
  jQuery.migrateWarnings.length = 0;
};

function migrateWarn( msg) {
  if ( !warnedAbout[ msg ] ) {
    warnedAbout[ msg ] = true;
    jQuery.migrateWarnings.push( msg );
    if ( window.console && console.warn && !jQuery.migrateMute ) {
      console.warn( "JQMIGRATE: " + msg );
    }
  }
}

function migrateWarnProp( obj, prop, value, msg ) {
  if ( Object.defineProperty ) {
    // On ES5 browsers (non-oldIE), warn if the code tries to get prop;
    // allow property to be overwritten in case some other plugin wants it
    try {
      Object.defineProperty( obj, prop, {
        configurable: true,
        enumerable: true,
        get: function() {
          migrateWarn( msg );
          return value;
        },
        set: function( newValue ) {
          migrateWarn( msg );
          value = newValue;
        }
      });
      return;
    } catch( err ) {
      // IE8 is a dope about Object.defineProperty, can't warn there
    }
  }

  // Non-ES5 (or broken) browser; just set the property
  jQuery._definePropertyBroken = true;
  obj[ prop ] = value;
}

if ( document.compatMode === "BackCompat" ) {
  // jQuery has never supported or tested Quirks Mode
  migrateWarn( "jQuery is not compatible with Quirks Mode" );
}

var attrFn = {},
  attr = jQuery.attr,
  valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
    function() { return null; },
  valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
    function() { return undefined; },
  rnoType = /^(?:input|button)$/i,
  rnoAttrNodeType = /^[238]$/,
  rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
  ruseDefault = /^(?:checked|selected)$/i;

// jQuery.attrFn
migrateWarnProp( jQuery, "attrFn", attrFn, "jQuery.attrFn is deprecated" );

jQuery.attr = function( elem, name, value, pass ) {
  var lowerName = name.toLowerCase(),
    nType = elem && elem.nodeType;

  if ( pass ) {
    migrateWarn("jQuery.fn.attr( props, pass ) is deprecated");
    if ( elem && !rnoAttrNodeType.test( nType ) && jQuery.isFunction( jQuery.fn[ name ] ) ) {
      return jQuery( elem )[ name ]( value );
    }
  }

  // Warn if user tries to set `type` since it breaks on IE 6/7/8
  if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) ) {
    migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8");
  }

  // Restore boolHook for boolean property/attribute synchronization
  if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
    jQuery.attrHooks[ lowerName ] = {
      get: function( elem, name ) {
        // Align boolean attributes with corresponding properties
        // Fall back to attribute presence where some booleans are not supported
        var attrNode,
          property = jQuery.prop( elem, name );
        return property === true || typeof property !== "boolean" &&
          ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?

          name.toLowerCase() :
          undefined;
      },
      set: function( elem, value, name ) {
        var propName;
        if ( value === false ) {
          // Remove boolean attributes when set to false
          jQuery.removeAttr( elem, name );
        } else {
          // value is true since we know at this point it's type boolean and not false
          // Set boolean attributes to the same name and set the DOM property
          propName = jQuery.propFix[ name ] || name;
          if ( propName in elem ) {
            // Only set the IDL specifically if it already exists on the element
            elem[ propName ] = true;
          }

          elem.setAttribute( name, name.toLowerCase() );
        }
        return name;
      }
    };

    // Warn only for attributes that can remain distinct from their properties post-1.9
    if ( ruseDefault.test( lowerName ) ) {
      migrateWarn( "jQuery.fn.attr(" + lowerName + ") may use property instead of attribute" );
    }
  }

  return attr.call( jQuery, elem, name, value );
};

// attrHooks: value
jQuery.attrHooks.value = {
  get: function( elem, name ) {
    var nodeName = ( elem.nodeName || "" ).toLowerCase();
    if ( nodeName === "button" ) {
      return valueAttrGet.apply( this, arguments );
    }
    if ( nodeName !== "input" && nodeName !== "option" ) {
      migrateWarn("property-based jQuery.fn.attr('value') is deprecated");
    }
    return name in elem ?
      elem.value :
      null;
  },
  set: function( elem, value ) {
    var nodeName = ( elem.nodeName || "" ).toLowerCase();
    if ( nodeName === "button" ) {
      return valueAttrSet.apply( this, arguments );
    }
    if ( nodeName !== "input" && nodeName !== "option" ) {
      migrateWarn("property-based jQuery.fn.attr('value', val) is deprecated");
    }
    // Does not return so that setAttribute is also used
    elem.value = value;
  }
};

var matched, browser,
  oldInit = jQuery.fn.init,
  // Note this does NOT include the # XSS fix from 1.7!
  rquickExpr = /^(?:.*(<[\w\W]+>)[^>]*|#([\w\-]*))$/;

// $(html) "looks like html" rule change
jQuery.fn.init = function( selector, context, rootjQuery ) {
  var match;

  if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
      (match = rquickExpr.exec( selector )) && match[1] ) {
    // This is an HTML string according to the "old" rules; is it still?
    if ( selector.charAt( 0 ) !== "<" ) {
      migrateWarn("$(html) HTML strings must start with '<' character");
    }
    // Now process using loose rules; let pre-1.8 play too
    if ( context && context.context ) {
      // jQuery object as context; parseHTML expects a DOM object
      context = context.context;
    }
    if ( jQuery.parseHTML ) {
      return oldInit.call( this, jQuery.parseHTML( jQuery.trim(selector), context, true ),
          context, rootjQuery );
    }
  }
  return oldInit.apply( this, arguments );
};
jQuery.fn.init.prototype = jQuery.fn;

jQuery.uaMatch = function( ua ) {
  ua = ua.toLowerCase();

  var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
    /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
    /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
    /(msie) ([\w.]+)/.exec( ua ) ||
    ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
    [];

  return {
    browser: match[ 1 ] || "",
    version: match[ 2 ] || "0"
  };
};

matched = jQuery.uaMatch( navigator.userAgent );
browser = {};

if ( matched.browser ) {
  browser[ matched.browser ] = true;
  browser.version = matched.version;
}

// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
  browser.webkit = true;
} else if ( browser.webkit ) {
  browser.safari = true;
}

jQuery.browser = browser;

// Warn if the code tries to get jQuery.browser
migrateWarnProp( jQuery, "browser", browser, "jQuery.browser is deprecated" );

jQuery.sub = function() {
  function jQuerySub( selector, context ) {
    return new jQuerySub.fn.init( selector, context );
  }
  jQuery.extend( true, jQuerySub, this );
  jQuerySub.superclass = this;
  jQuerySub.fn = jQuerySub.prototype = this();
  jQuerySub.fn.constructor = jQuerySub;
  jQuerySub.sub = this.sub;
  jQuerySub.fn.init = function init( selector, context ) {
    if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
      context = jQuerySub( context );
    }

    return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
  };
  jQuerySub.fn.init.prototype = jQuerySub.fn;
  var rootjQuerySub = jQuerySub(document);
  migrateWarn( "jQuery.sub() is deprecated" );
  return jQuerySub;
};

var oldFnData = jQuery.fn.data;

jQuery.fn.data = function( name ) {
  var ret, evt,
    elem = this[0];

  // Handles 1.7 which has this behavior and 1.8 which doesn't
  if ( elem && name === "events" && arguments.length === 1 ) {
    ret = jQuery.data( elem, name );
    evt = jQuery._data( elem, name );
    if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
      migrateWarn("Use of jQuery.fn.data('events') is deprecated");
      return evt;
    }
  }
  return oldFnData.apply( this, arguments );
};

var rscriptType = /\/(java|ecma)script/i,
  oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack,
  oldFragment = jQuery.buildFragment;

jQuery.fn.andSelf = function() {
  migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()");
  return oldSelf.apply( this, arguments );
};

// Since jQuery.clean is used internally on older versions, we only shim if it's missing
if ( !jQuery.clean ) {
  jQuery.clean = function( elems, context, fragment, scripts ) {
    // Set context per 1.8 logic
    context = context || document;
    context = !context.nodeType && context[0] || context;
    context = context.ownerDocument || context;

    migrateWarn("jQuery.clean() is deprecated");

    var i, elem, handleScript, jsTags,
      ret = [];

    jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );

    // Complex logic lifted directly from jQuery 1.8
    if ( fragment ) {
      // Special handling of each script element
      handleScript = function( elem ) {
        // Check if we consider it executable
        if ( !elem.type || rscriptType.test( elem.type ) ) {
          // Detach the script and store it in the scripts array (if provided) or the fragment
          // Return truthy to indicate that it has been handled
          return scripts ?
            scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
            fragment.appendChild( elem );
        }
      };

      for ( i = 0; (elem = ret[i]) != null; i++ ) {
        // Check if we're done after handling an executable script
        if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
          // Append to fragment and handle embedded scripts
          fragment.appendChild( elem );
          if ( typeof elem.getElementsByTagName !== "undefined" ) {
            // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
            jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );

            // Splice the scripts into ret after their former ancestor and advance our index beyond them
            ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
            i += jsTags.length;
          }
        }
      }
    }

    return ret;
  };
}

jQuery.buildFragment = function( elems, context, scripts, selection ) {
  var ret,
    warning = "jQuery.buildFragment() is deprecated";

  // Set context per 1.8 logic
  context = context || document;
  context = !context.nodeType && context[0] || context;
  context = context.ownerDocument || context;

  try {
    ret = oldFragment.call( jQuery, elems, context, scripts, selection );

  // jQuery < 1.8 required arrayish context; jQuery 1.9 fails on it
  } catch( x ) {
    ret = oldFragment.call( jQuery, elems, context.nodeType ? [ context ] : context[ 0 ], scripts, selection );

    // Success from tweaking context means buildFragment was called by the user
    migrateWarn( warning );
  }

  // jQuery < 1.9 returned an object instead of the fragment itself
  if ( !ret.fragment ) {
    migrateWarnProp( ret, "fragment", ret, warning );
    migrateWarnProp( ret, "cacheable", false, warning );
  }

  return ret;
};

var eventAdd = jQuery.event.add,
  eventRemove = jQuery.event.remove,
  eventTrigger = jQuery.event.trigger,
  oldToggle = jQuery.fn.toggle,
  oldLive = jQuery.fn.live,
  oldDie = jQuery.fn.die,
  ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
  rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
  rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
  hoverHack = function( events ) {
    if ( typeof( events ) != "string" || jQuery.event.special.hover ) {
      return events;
    }
    if ( rhoverHack.test( events ) ) {
      migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'");
    }
    return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
  };

// Event props removed in 1.9, put them back if needed; no practical way to warn them
if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
  jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
}

// Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated" );

// Support for 'hover' pseudo-event and ajax event warnings
jQuery.event.add = function( elem, types, handler, data, selector ){
  if ( elem !== document && rajaxEvent.test( types ) ) {
    migrateWarn( "AJAX events should be attached to document: " + types );
  }
  eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
};
jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
  eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
};

jQuery.fn.error = function() {
  var args = Array.prototype.slice.call( arguments, 0);
  migrateWarn("jQuery.fn.error() is deprecated");
  args.splice( 0, 0, "error" );
  if ( arguments.length ) {
    return this.bind.apply( this, args );
  }
  // error event should not bubble to window, although it does pre-1.7
  this.triggerHandler.apply( this, args );
  return this;
};

jQuery.fn.toggle = function( fn, fn2 ) {

  // Don't mess with animation or css toggles
  if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
    return oldToggle.apply( this, arguments );
  }
  migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated");

  // Save reference to arguments for access in closure
  var args = arguments,
    guid = fn.guid || jQuery.guid++,
    i = 0,
    toggler = function( event ) {
      // Figure out which function to execute
      var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
      jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );

      // Make sure that clicks stop
      event.preventDefault();

      // and execute the function
      return args[ lastToggle ].apply( this, arguments ) || false;
    };

  // link all the functions, so any of them can unbind this click handler
  toggler.guid = guid;
  while ( i < args.length ) {
    args[ i++ ].guid = guid;
  }

  return this.click( toggler );
};

jQuery.fn.live = function( types, data, fn ) {
  migrateWarn("jQuery.fn.live() is deprecated");
  if ( oldLive ) {
    return oldLive.apply( this, arguments );
  }
  jQuery( this.context ).on( types, this.selector, data, fn );
  return this;
};

jQuery.fn.die = function( types, fn ) {
  migrateWarn("jQuery.fn.die() is deprecated");
  if ( oldDie ) {
    return oldDie.apply( this, arguments );
  }
  jQuery( this.context ).off( types, this.selector || "**", fn );
  return this;
};

// Turn global events into document-triggered events
jQuery.event.trigger = function( event, data, elem, onlyHandlers  ){
  if ( !elem & !rajaxEvent.test( event ) ) {
    migrateWarn( "Global events are undocumented and deprecated" );
  }
  return eventTrigger.call( this,  event, data, elem || document, onlyHandlers  );
};
jQuery.each( ajaxEvents.split("|"),
  function( _, name ) {
    jQuery.event.special[ name ] = {
      setup: function() {
        var elem = this;

        // The document needs no shimming; must be !== for oldIE
        if ( elem !== document ) {
          jQuery.event.add( document, name + "." + jQuery.guid, function() {
            jQuery.event.trigger( name, null, elem, true );
          });
          jQuery._data( this, name, jQuery.guid++ );
        }
        return false;
      },
      teardown: function() {
        if ( this !== document ) {
          jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
        }
        return false;
      }
    };
  }
);

})( jQuery, window );
<commit_msg>Remove jquery-migrate-1.0.0, we already have jquery-migrate 1.2.1 (jquery-migrate-min) through gemfile jquery-migrate-rails<commit_after><|endoftext|>


<commit_before>import Promise from 'bluebird';
import fs from 'fs';
import path from 'path';

let stat = Promise.promisify(fs.stat),
    readFile = Promise.promisify(fs.readFile);

const add = function (req, res) {
    console.log("FICK EN REQ:");
    console.log(req.body);
    if(!path.isAbsolute(req.body.folder)) {
        res.sendStatus(404);
      } else {
        stat(req.body.folder)
        .then(stats => {
          if(stats.isDirectory()) {
            saveToConfig(req.body.folder, function() {
              res.sendStatus(200);
            });
          } else {
            res.sendStatus(400)
          }
        })
      }
};

function saveToConfig(directory, cb) {
  console.log(path.join(process.cwd(), 'configuration.JSON'));
  readFile(path.join(process.cwd(), 'configuration.JSON'), 'utf8')
  .then(data => JSON.parse(data))
  .then(config => {
    config.push(
      {'folder': {
        'name': path.basename(directory),
        'path': path.dirname(directory)
      }
    });
    console.log(config);
    fs.writeFile(path.join(process.cwd(), 'configuration.JSON'), JSON.stringify(config), function(err) {
      if(err)
        console.log(err);
      cb();
    });
  });
}

module.exports = {
  add
};<commit_msg>☝ add saving new task to config file<commit_after>import Promise from 'bluebird';
import fs from 'fs';
import path from 'path';

let stat = Promise.promisify(fs.stat),
    readFile = Promise.promisify(fs.readFile);

const add = function (req, res) {
    console.log("FICK EN REQ:");
    console.log(req.body);
    if(!path.isAbsolute(req.body.folder)) {
        res.sendStatus(404);
      } else {
        stat(req.body.folder)
        .then(stats => {
          if(stats.isDirectory()) {
            saveToConfig(req.body.folder, function() {
              res.sendStatus(201);
            });
          } else {
            console.log("Skickar 420 1");
            res.sendStatus(420);
          }
        })
        .error(err => {
          console.log("Skickar 420 2");
          res.sendStatus(420);
        })
      }
};

function saveToConfig(directory, cb) {
  console.log(path.join(process.cwd(), 'configuration.JSON'));
  readFile(path.join(process.cwd(), 'configuration.JSON'), 'utf8')
  .then(data => JSON.parse(data))
  .then(config => {
    config.push(
      {'folder': {
        'name': path.basename(directory),
        'path': path.dirname(directory)
      }
    });
    console.log(config);
    fs.writeFile(path.join(process.cwd(), 'configuration.JSON'), JSON.stringify(config), function(err) {
      if(err)
        console.log(err);
      cb();
    });
    cb();
  });
}

module.exports = {
  add
};<|endoftext|>


<commit_before>//
// Copyright (c) 2013-2016 SKYARCH NETWORKS INC.
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

module.exports = function(data){
  return {
    style: 'tableExample',
    color: '#444',
    table: {
        widths: [ 120, 100, 'auto', 'auto', 'auto', 'auto',  'auto', 'auto', 'auto', 'auto'],
        // keepWithHeaderRows: 1,
        body: buildTableBody(data)
    }
  };

};

function eval_port(from_port, to_port){
  if(from_port && to_port){
    return from_port === to_port ? from_port.toString(): from_port.toString()+"-"+to_port.toString();
  }
  else{
    return 'All';
  }
}

function eval_protocol(ip_protocol){
  return ip_protocol === '-1' ? 'All' : ip_protocol;
}

function buildTableBody(data) {
    var body = [];
    var firstRow = [{ rowSpan: 2, text: t('security_groups.description'),  style: 'tableHeader' },
            { rowSpan: 2, text: t('security_groups.group_id'),style: 'tableHeader' },
            { colSpan: 4, text: t('security_groups.inbound'), style: 'tableHeader' },
            'x','x','xx',
            { colSpan: 4, text: t('security_groups.inbound'), style: 'tableHeader' },
            'x', 'x', 'x',];
    var secondRow = ['x' , 'x',
      {text: t('security_groups.type'), style: 'tableHeader' },
      {text: t('security_groups.protocol'), style: 'tableHeader' },
      {text: t('security_groups.port_range'),  style: 'tableHeader' },
      {text: t('security_groups.source'),  style: 'tableHeader' },
      {text: t('security_groups.type'),  style: 'tableHeader' },
      {text: t('security_groups.protocol'), style: 'tableHeader' },
      {text: t('security_groups.port_range'), style: 'tableHeader' },
      {text: t('security_groups.source'),  style: 'tableHeader' },
    ];

    body.push(firstRow, secondRow);

    data.forEach(function(v) {
      var inbound =  v.ip_permissions;
      var outbound = v.ip_permissions_egress;

      body.push([{text: v.description, style: 'tableHeader', rowSpan: inbound.length},
               {text: v.group_id, style: 'tableHeader', rowSpan: inbound.length},
               inbound[0].user_id_group_pairs,
               eval_protocol(inbound[0].ip_protocol),
               eval_port(inbound[0].from_port, inbound[0].to_port),
               inbound[0].ip_ranges[0].cidr_ip,
               outbound[0].user_id_group_pairs,
               eval_protocol(outbound[0].ip_protocol),
               eval_port(outbound[0].from_port, outbound[0].from_port),
               outbound[0].ip_ranges[0].cidr_ip
      ]);

      inbound.shift(); // Remove first index
      outbound.shift();  // Remove first index

      if(inbound.length > outbound.length){
        inbound.forEach(function(v,index){
            if(outbound.length > index){
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port, v.to_port),
                    v.ip_ranges[0].cidr_ip,
                    outbound[index].user_id_group_pairs,
                    eval_protocol(outbound[index].ip_protocol),
                    eval_port(outbound[index].from_port, outbound[index].to_port),
                    outbound[index].ip_ranges[0].cidr_ip
                  ]);
            }else{
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port, v.tp_port),
                    v.ip_ranges[0].cidr_ip,
                    '',
                    '',
                    '',
                    ''
                  ]);
            }

        });
      }else{
        outbound.forEach(function(v,index){
            if(inboound.length > index){
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port.toString()),
                    v.ip_ranges[0].cidr_ip,
                    inboound[index].user_id_group_pairs,
                    eval_protocol(inboound[index].ip_protocol),
                    eval_port(inboound[index].from_port,inboound[index].to_port),
                    inboound[index].ip_ranges[0].cidr_ip
                  ]);
            }else{
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port,v.to_port),
                    v.ip_ranges[0].cidr_ip,
                    '',
                    '',
                    '',
                    ''
                  ]);
            }

        });
      }
  }
  );
  console.log(body);
  return body;
}
<commit_msg>temp. remove iT18N support because of http://bit.ly/20dxMQn issue<commit_after>//
// Copyright (c) 2013-2016 SKYARCH NETWORKS INC.
//
// This software is released under the MIT License.
//
// http://opensource.org/licenses/mit-license.php
//

module.exports = function(data){
  return {
    style: 'tableExample',
    color: '#444',
    table: {
        widths: [ 120, 100, 'auto', 'auto', 'auto', 'auto',  'auto', 'auto', 'auto', 'auto'],
        // keepWithHeaderRows: 1,
        body: buildTableBody(data)
    }
  };

};

function eval_port(from_port, to_port){
  if(from_port && to_port){
    return from_port === to_port ? from_port.toString(): from_port.toString()+"-"+to_port.toString();
  }
  else{
    return 'All';
  }
}

function eval_protocol(ip_protocol){
  return ip_protocol === '-1' ? 'All' : ip_protocol;
}

function buildTableBody(data) {
    var body = [];
    var firstRow = [{ rowSpan: 2, text: 'Description',  style: 'tableHeader' },
            { rowSpan: 2, text: 'Group ID',style: 'tableHeader' },
            { colSpan: 4, text: 'Inbound', style: 'tableHeader' },
            'x','x','xx',
            { colSpan: 4, text: 'Outbound', style: 'tableHeader' },
            'x', 'x', 'x',];
    var secondRow = ['x' , 'x',
      {text: 'Type', style: 'tableHeader' },
      {text: 'Protocol', style: 'tableHeader' },
      {text: 'Source',  style: 'tableHeader' },
      {text: 'Port Range',  style: 'tableHeader' },
      {text: 'Type',  style: 'tableHeader' },
      {text: 'Protocol', style: 'tableHeader' },
      {text: 'Port Range', style: 'tableHeader' },
      {text: 'Source',  style: 'tableHeader' },
    ];

    body.push(firstRow, secondRow);

    data.forEach(function(v) {
      var inbound =  v.ip_permissions;
      var outbound = v.ip_permissions_egress;

      body.push([{text: v.description, style: 'tableHeader', rowSpan: inbound.length},
               {text: v.group_id, style: 'tableHeader', rowSpan: inbound.length},
               inbound[0].user_id_group_pairs,
               eval_protocol(inbound[0].ip_protocol),
               eval_port(inbound[0].from_port, inbound[0].to_port),
               inbound[0].ip_ranges[0].cidr_ip,
               outbound[0].user_id_group_pairs,
               eval_protocol(outbound[0].ip_protocol),
               eval_port(outbound[0].from_port, outbound[0].from_port),
               outbound[0].ip_ranges[0].cidr_ip
      ]);

      inbound.shift(); // Remove first index
      outbound.shift();  // Remove first index

      if(inbound.length > outbound.length){
        inbound.forEach(function(v,index){
            if(outbound.length > index){
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port, v.to_port),
                    v.ip_ranges[0].cidr_ip,
                    outbound[index].user_id_group_pairs,
                    eval_protocol(outbound[index].ip_protocol),
                    eval_port(outbound[index].from_port, outbound[index].to_port),
                    outbound[index].ip_ranges[0].cidr_ip
                  ]);
            }else{
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port, v.tp_port),
                    v.ip_ranges[0].cidr_ip,
                    '',
                    '',
                    '',
                    ''
                  ]);
            }

        });
      }else{
        outbound.forEach(function(v,index){
            if(inboound.length > index){
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port.toString()),
                    v.ip_ranges[0].cidr_ip,
                    inboound[index].user_id_group_pairs,
                    eval_protocol(inboound[index].ip_protocol),
                    eval_port(inboound[index].from_port,inboound[index].to_port),
                    inboound[index].ip_ranges[0].cidr_ip
                  ]);
            }else{
              body.push(['','',
                    v.user_id_group_pairs,
                    eval_protocol(v.ip_protocol),
                    eval_port(v.from_port,v.to_port),
                    v.ip_ranges[0].cidr_ip,
                    '',
                    '',
                    '',
                    ''
                  ]);
            }

        });
      }
  }
  );
  console.log(body);
  return body;
}
<|endoftext|>