<commit_before>import React from 'react';
import { createRoot } from 'react-dom/client';

const createConfirmation = (Component, unmountDelay = 1000, mountingNode) => {
  return (props) => {
    const wrapper = (mountingNode || document.body).appendChild(document.createElement('div'));
    const root = createRoot(wrapper);

    const promise = new Promise((resolve, reject) => {
      try {
        root.render(
          <Component
            reject={reject}
            resolve={resolve}
            dispose={dispose}
            {...props}
          />
        );
      } catch (e) {
        console.error(e);
        throw e;
      }
    })

    function dispose() {
      setTimeout(() => {
        root.unmount();
        setTimeout(() => {
            if (document.body.contains(wrapper)) {
              document.body.removeChild(wrapper)
            }
        });
      }, unmountDelay);
    }

    return promise.then((result) => {
      dispose();
      return result;
    }, (result) => {
      dispose();
      return Promise.reject(result);
    });
  }
}

export default createConfirmation;
<commit_msg>fixed error on disposing dialog when mounting node is specified<commit_after>import React from 'react';
import { createRoot } from 'react-dom/client';

const createConfirmation = (Component, unmountDelay = 1000, mountingNode) => {
  return (props) => {
    const wrapper = (mountingNode || document.body).appendChild(document.createElement('div'));
    const root = createRoot(wrapper);

    const promise = new Promise((resolve, reject) => {
      try {
        root.render(
          <Component
            reject={reject}
            resolve={resolve}
            dispose={dispose}
            {...props}
          />
        );
      } catch (e) {
        console.error(e);
        throw e;
      }
    })

    function dispose() {
      setTimeout(() => {
        root.unmount();
        setTimeout(() => {
          if (wrapper && wrapper.parentNode) {
            wrapper.parentNode.removeChild(wrapper);
          }
        });
      }, unmountDelay);
    }

    return promise.then((result) => {
      dispose();
      return result;
    }, (result) => {
      dispose();
      return Promise.reject(result);
    });
  }
}

export default createConfirmation;
<|endoftext|>


<commit_before>var Promise = require('bluebird');
var LRU     = require('lru-cache');
var es      = require('./es-client');
var debug   = require('debug')('elasticsearch');
var cache   = LRU({max: 500});

function parseAggregation(response, key) {
    var counts = {};

    var agg     = response.aggregations[key];
    var buckets = agg[key] ? agg[key].buckets : agg.buckets;

    buckets.forEach(function (bucket) {
        counts[bucket.key_as_string || bucket.key] = bucket.doc_count;
    });

    return counts;
}

function countsFor(opts) {
    var cacheHit = cache.get(opts);

    if (cacheHit) {
        return Promise.resolve(cacheHit);
    } else {
        var aggregations = {
            monthly: {
                date_histogram: {
                    field: "time",
                    interval: opts.interval,
                    time_zone: 2
                }
            },

            parties: {
                terms: {
                    field: 'party'
                }
            },

            people: {
                terms: {
                    field: 'name'
                }
            }
        };

        var body = {
            aggregations: aggregations,
            size: 0
        };

        if (opts.query != '*') {
            var query = {
                query_string: {
                    query: opts.query,
                    default_operator: 'AND',
                    default_field: 'text'
                }
            };

            body = {
                aggregations: {
                    monthly: {
                        filter: { query: query },
                        aggs: { monthly: aggregations.monthly }
                    },
                    parties: {
                        filter: { query: query },
                        aggs: { parties: aggregations.parties }
                    },
                    people: {
                        filter: { query: query },
                        aggs: { people: aggregations.people }
                    }
                }
            };

            body.highlight = { fields: { text: {} } };

            body.query = query;
            body.size = 10;
            body.sort = '_score';
        }

        debug('request', JSON.stringify(body));

        return es.search({ index: 'hdo-transcripts', body: body })
            .then(function (response) {
                debug('response', JSON.stringify(response));

                var result = {};

                result.counts = parseAggregation(response, 'monthly');
                result.hits = response.hits.hits;
                result.total = response.hits.total;

                if (response.aggregations.parties) {
                    result.parties = parseAggregation(response, 'parties');
                }

                if (response.aggregations.people) {
                    result.people = parseAggregation(response, 'people');
                    // if (result.people.Presidenten) {
                    //     delete result.people.Presidenten;
                    // } else {
                    //     var names = Object.keys(result.people);
                    //     delete result.people[names[names.length - 1]];
                    // }
                }

                cache[opts] = result;

                return result;
            });
    }
}

function search(opts) {
    opts.interval = opts.interval || 'month';

    return Promise.join(
        countsFor({query: '*', interval: opts.interval} ),
        countsFor(opts)
    ).spread(function (allResults, queryResults) {
        var keys = Object.keys(allResults.counts);
        keys.pop(); // don't want current incomplete period

        var counts = keys.map(function (key) {
            var total = allResults.counts[key];
            var val   = queryResults.counts[key] || 0.0;

            return {
                date: key,
                count: val,
                total: total,
                pct: (val / total) * 100
            };
        });

        return {
            totalCount: allResults.total,
            hitCount: queryResults.total,
            counts: counts,
            hits: queryResults.hits,
            partyCounts: queryResults.parties,
            peopleCounts: queryResults.people
        };
    });
}

module.exports = {  search: search };<commit_msg>Fix LRU cache for search API<commit_after>var Promise    = require('bluebird');
var LRU        = require('lru-cache');
var es         = require('./es-client');
var debugg     = require('debug');
var debug      = debugg('elasticsearch');
var debugCache = debugg('cache');
var cache      = LRU({max: 500});

function parseAggregation(response, key) {
    var counts = {};

    var agg     = response.aggregations[key];
    var buckets = agg[key] ? agg[key].buckets : agg.buckets;

    buckets.forEach(function (bucket) {
        counts[bucket.key_as_string || bucket.key] = bucket.doc_count;
    });

    return counts;
}

function parseResponse(response) {
    debug('response', JSON.stringify(response));

    var result = {};

    result.counts = parseAggregation(response, 'monthly');
    result.hits = response.hits.hits;
    result.total = response.hits.total;

    if (response.aggregations.parties) {
        result.parties = parseAggregation(response, 'parties');
    }

    if (response.aggregations.people) {
        result.people = parseAggregation(response, 'people');
        // if (result.people.Presidenten) {
        //     delete result.people.Presidenten;
        // } else {
        //     var names = Object.keys(result.people);
        //     delete result.people[names[names.length - 1]];
        // }
    }

    return result;
}

function countsFor(opts) {
    var cacheHit = cache.get(opts);

    if (cacheHit) {
        debugCache('cache hit');
        return Promise.resolve(cacheHit);
    } else {
        debugCache('cache miss');

        var aggregations = {
            monthly: {
                date_histogram: {
                    field: "time",
                    interval: opts.interval,
                    time_zone: 2
                }
            },

            parties: {
                terms: {
                    field: 'party'
                }
            },

            people: {
                terms: {
                    field: 'name'
                }
            }
        };

        var body = {
            aggregations: aggregations,
            size: 0
        };

        if (opts.query != '*') {
            var query = {
                query_string: {
                    query: opts.query,
                    default_operator: 'AND',
                    default_field: 'text'
                }
            };

            body = {
                aggregations: {
                    monthly: {
                        filter: { query: query },
                        aggs: { monthly: aggregations.monthly }
                    },
                    parties: {
                        filter: { query: query },
                        aggs: { parties: aggregations.parties }
                    },
                    people: {
                        filter: { query: query },
                        aggs: { people: aggregations.people }
                    }
                }
            };

            body.highlight = { fields: { text: {} } };

            body.query = query;
            body.size = 10;
            body.sort = '_score';
        }

        debug('request', JSON.stringify(body));

        return es.search({ index: 'hdo-transcripts', body: body })
            .then(parseResponse).then(function (result) {
                debugCache("caching response for", opts);

                cache.set(opts, result);
                return result;
            });
    }
}

function search(opts) {
    opts.interval = opts.interval || 'month';

    return Promise.join(
        countsFor({query: '*', interval: opts.interval} ),
        countsFor(opts)
    ).spread(function (allResults, queryResults) {
        var keys = Object.keys(allResults.counts);
        keys.pop(); // don't want current incomplete period

        var counts = keys.map(function (key) {
            var total = allResults.counts[key];
            var val   = queryResults.counts[key] || 0.0;

            return {
                date: key,
                count: val,
                total: total,
                pct: (val / total) * 100
            };
        });

        return {
            totalCount: allResults.total,
            hitCount: queryResults.total,
            counts: counts,
            hits: queryResults.hits,
            partyCounts: queryResults.parties,
            peopleCounts: queryResults.people
        };
    });
}

module.exports = {  search: search };<|endoftext|>


<commit_before>
/*
    ==============================================
    WELCOME TO BUILD YOUR OWN FUNCTIONS TDD STYLE
    ==============================================
    - In this section you will replicate a Javascript Array methods.
    - We have multiple tests that should be solved, make sure to go
      one by one and read the error messages to get clues.
    - To solve the tests you should read the errors and try to solve them
      by writing the function that has already been declared.
      You are very welcomed to read the tests to gain insight but
      DO NOT change them, only the function.
    - Bellow we provide a short description of the function as well as the expected
      syntax. Documentation is unbeatable, so you are encouraged to click the link
      and gain further insight.
    - Try mixing it up for added difficulty. Go low level and avoid using other
      array methods. Can it be done without for loops?
    - ONWARDS!

    Description:
    The indexOf() method returns the first index at which a given element
    can be found in the array, or -1 if it is not present.

    Syntax:
    indexOf(array, searchElement[, fromIndex])

    Link to docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexof
 */

////function indexOf(/*function parameters*/) {
function indexOf(array, searchElement, fromIndex) {
  ////
  return array.indexOf(searchElement, fromIndex);
  /*
    Your code goes here
  */
}

describe("specs to recreate indexOf array method", () => {
  describe("indexOf functionality", ()=>{
    it("returns the index of the searched element in the array", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c");
      assert.equal(expected, actual);
    });
    it("returns -1 if the searched element is not found in the array", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "e");
      assert.equal(expected, actual);
    });
    it("begins looking for the searched element starting from the indexFrom value", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "a", 2);
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom is 0", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c", 0);
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom has a negative value", () => {
      const expected = 1;
      const actual = indexOf(["a", "b", "c"], "b", -5);
      assert.equal(expected, actual);
    });
  })
  describe("indexOf parameters", ()=>{
    it("throws an error if the first argument is a number", () => {
      assert.throws(() => indexOf(47, 2), TypeError);
    });
    it("parses fromIndex if it's a string containing a number", () => {
      const expected = 1;
      const actual = indexOf(["a", "b", "c"], "b", "1");
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom can't be parsed into a number", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c", "hello");
      assert.equal(expected, actual);
    });
    it("returns -1 if the indexFrom is greater than or equal to the array's length", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "a", 3);
      assert.equal(expected, actual);
    });
  });
});
<commit_msg>Consistent formatting.<commit_after>/*
  ==============================================
  WELCOME TO BUILD YOUR OWN FUNCTIONS TDD STYLE
  ==============================================
  - In this section you will replicate a Javascript Array methods.
  - We have multiple tests that should be solved, make sure to go
    one by one and read the error messages to get clues.
  - To solve the tests you should read the errors and try to solve them
    by writing the function that has already been declared.
    You are very welcomed to read the tests to gain insight but
    DO NOT change them, only the function.
  - Bellow we provide a short description of the function as well as the expected
    syntax. Documentation is unbeatable, so you are encouraged to click the link
    and gain further insight.
  - Try mixing it up for added difficulty. Go low level and avoid using other
    array methods. Can it be done without for loops?
  - ONWARDS!

  Description:
  The indexOf() method returns the first index at which a given element
  can be found in the array, or -1 if it is not present.

  Syntax:
  indexOf(array, searchElement[, fromIndex])

  Link to docs https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexof
*/

////function indexOf(/*function parameters*/) {
function indexOf(array, searchElement, fromIndex) {
  ////
  return array.indexOf(searchElement, fromIndex);
  /*
    Your code goes here
  */
}

describe("specs to recreate indexOf array method", () => {
  describe("indexOf functionality", ()=>{
    it("returns the index of the searched element in the array", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c");
      assert.equal(expected, actual);
    });
    it("returns -1 if the searched element is not found in the array", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "e");
      assert.equal(expected, actual);
    });
    it("begins looking for the searched element starting from the indexFrom value", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "a", 2);
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom is 0", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c", 0);
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom has a negative value", () => {
      const expected = 1;
      const actual = indexOf(["a", "b", "c"], "b", -5);
      assert.equal(expected, actual);
    });
  });
  describe("indexOf parameters", ()=>{
    it("throws an error if the first argument is a number", () => {
      assert.throws(() => indexOf(47, 2), TypeError);
    });
    it("parses fromIndex if it's a string containing a number", () => {
      const expected = 1;
      const actual = indexOf(["a", "b", "c"], "b", "1");
      assert.equal(expected, actual);
    });
    it("searches the whole array if indexFrom can't be parsed into a number", () => {
      const expected = 2;
      const actual = indexOf(["a", "b", "c"], "c", "hello");
      assert.equal(expected, actual);
    });
    it("returns -1 if the indexFrom is greater than or equal to the array's length", () => {
      const expected = -1;
      const actual = indexOf(["a", "b", "c"], "a", 3);
      assert.equal(expected, actual);
    });
  });
});
<|endoftext|>


<commit_before>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

// new re-factored Listing object
// removed rarely used functionality
//
// opts:
//   parent

//   method (method to call on server)
//   args (additional args to method)
//   get_args (method to return args as dict)

//   show_filters [false]
//   doctype
//   filter_fields (if given, this list is rendered, else built from doctype)

//   query or get_query (will be deprecated)
//   query_max
//   buttons_in_frame

//   no_result_message ("No result")

//   page_length (20)
//   hide_refresh (False)
//   no_toolbar
//   new_doctype
//   [function] render_row(parent, data)
//   [function] onrun
//   no_loading (no ajax indicator)

frappe.provide('frappe.ui');
frappe.ui.Listing = Class.extend({
	init: function(opts) {
		this.opts = opts || {};
		this.page_length = 20;
		this.start = 0;
		this.data = [];
		if(opts) {
			this.make();
		}
	},
	prepare_opts: function() {
		if(this.opts.new_doctype) {
			if(frappe.boot.user.can_create.indexOf(this.opts.new_doctype)==-1) {
				this.opts.new_doctype = null;
			} else {
				this.opts.new_doctype = this.opts.new_doctype;
			}
		}
		if(!this.opts.no_result_message) {
			this.opts.no_result_message = __('Nothing to show');
		}
		if(!this.opts.page_length) {
			this.opts.page_length = 20;
		}
		this.opts._more = __("More");
	},
	make: function(opts) {
		if(opts) {
			this.opts = opts;
		}
		this.prepare_opts();
		$.extend(this, this.opts);

		$(this.parent).html(repl('\

\

\
					<input type="checkbox" data-docstatus="2">\
						'+__('Cancelled')+'\

\

\
					<input type="checkbox" data-docstatus="1" checked="checked">\
						'+__('Submitted')+'\

\

\
					<input type="checkbox" data-docstatus="0" checked="checked">\
						'+__('Drafts')+'\

\

\

\

%(title)s
\
				\

\

\

\

\
							<button class="btn btn-info search-btn">\
								 \
								Search</button>\
							<button class="btn btn-default add-filter-btn">\
								 \
								Add Filter</button>\

\

\

\
				\

\

\

\

\

\

\

\
					%(no_result_message)s\

\

\

\

\

\

\

\
							<button class="btn btn-default btn-more">%(_more)s...</button>\

\

\

\
								<button type="button" class="btn btn-default btn-small btn-info" data-value="20">20</button>\
								<button type="button" class="btn btn-default btn-small" data-value="100">100</button>\
								<button type="button" class="btn btn-default btn-small" data-value="500">500</button>\

\

\

\

\

\
		', this.opts));
		this.$w = $(this.parent).find('.frappe-list');
		this.set_events();

		if(this.appframe) {
			this.$w.find('.list-toolbar-wrapper').toggle(false);
		}

		if(this.show_filters) {
			this.make_filters();
		}
		this.setup_docstatus_filter();
	},
	add_button: function(label, click, icon) {
		if(this.appframe) {
			return this.appframe.add_button(label, click, icon)
		} else {
			$button = $('<button class="btn btn-default"></button>')
				.appendTo(this.$w.find('.list-toolbar').removeClass("hide"))
				.html((icon ? (" ") : "") + label)
				.click(click);
			return $button
		}
	},
	show_view: function($btn, $div, $btn_unsel, $div_unsel) {
		$btn_unsel.removeClass('btn-info');
		$btn_unsel.find('i').removeClass('icon-white');
		$div_unsel.toggle(false);

		$btn.addClass('btn-info');
		$btn.find('i').addClass('icon-white');
		$div.toggle(true);
	},
	set_events: function() {
		var me = this;

		// next page
		this.$w.find('.btn-more').click(function() {
			me.run({append: true });
		});

		this.$w.find(".btn-group-paging .btn").click(function() {
			me.page_length = cint($(this).attr("data-value"));
			me.run({append: true});
		})

		// title
		if(this.title) {
			this.$w.find('h3').html(this.title).toggle(true);
		}

		// hide-refresh
		if(!(this.hide_refresh || this.no_refresh)) {
			this.add_button('Refresh', function() {
				me.run();
			}, 'icon-refresh');

		}

		// new
		if(this.new_doctype) {
			if(this.appframe) {
				this.appframe.set_title_right(" " + __('New'), function() {
					(me.custom_new_doc || me.make_new_doc).apply(me, [me.new_doctype]); });
			}
			this.add_button(__('New'), function() {
				(me.custom_new_doc || me.make_new_doc).apply(me, [me.new_doctype]);
			}, 'icon-plus');
		}

		// hide-filter
		if(me.show_filters) {
			this.add_button(__('Filter'), function() {
				me.filter_list.show_filters();
			}, 'icon-search').addClass('btn-filter');
		}

		if(me.no_toolbar || me.hide_toolbar) {
			me.$w.find('.list-toolbar-wrapper').toggle(false);
		}
	},

	make_new_doc: function(doctype) {
		var me = this;
		frappe.model.with_doctype(doctype, function() {
			var doc = frappe.model.get_new_doc(doctype);
			if(me.filter_list) {
				$.each(me.filter_list.get_filters(), function(i, f) {
					if(f[0]===doctype && f[2]==="=" && f[1]!=="name")
						doc[f[1]]=f[3];
				})
			}
			frappe.set_route("Form", doctype, doc.name);
		});
	},

	make_filters: function() {
		this.filter_list = new frappe.ui.FilterList({
			listobj: this,
			$parent: this.$w.find('.list-filters').toggle(true),
			doctype: this.doctype,
			filter_fields: this.filter_fields
		});
	},

	setup_docstatus_filter: function() {
		var me = this;
		this.can_submit = frappe.model.is_submittable(me.doctype);
		if(this.can_submit) {
			$(this.parent).find('.show-docstatus').removeClass('hide');
			$(this.parent).find('.show-docstatus input').click(function() {
				me.run();
			})
		}
	},

	clear: function() {
		this.data = [];
		this.$w.find('.result-list').empty();
		this.$w.find('.result').toggle(true);
		this.$w.find('.no-result').toggle(false);
		this.start = 0;
	},
	run: function(more) {
		var me = this;
		if(!more) {
			this.start = 0;
			if(this.onreset) this.onreset();
		}

		if(!me.opts.no_loading)
			me.set_working(true);

		return frappe.call({
			method: this.opts.method || 'frappe.widgets.query_builder.runquery',
			type: "GET",
			args: this.get_call_args(),
			callback: function(r) {
				if(!me.opts.no_loading)
					me.set_working(false);
				me.dirty = false;
				me.render_results(r);
			},
			no_spinner: this.opts.no_loading
		});
	},
	set_working: function(flag) {
		this.$w.find('.img-load').toggle(flag);
	},
	get_call_args: function() {
		// load query
		if(!this.method) {
			var query = this.get_query ? this.get_query() : this.query;
			query = this.add_limits(query);
			var args={
				query_max: this.query_max,
				as_dict: 1
			}
			args.simple_query = query;
		} else {
			var args = {
				limit_start: this.start,
				limit_page_length: this.page_length
			}
		}

		args.docstatus = this.can_submit ? $.map($(this.parent).find('.show-docstatus :checked'),
			function(inp) {
				return $(inp).attr('data-docstatus');
			}) : []

		// append user-defined arguments
		if(this.args)
			$.extend(args, this.args)

		if(this.get_args) {
			$.extend(args, this.get_args());
		}
		return args;
	},
	render_results: function(r) {
		if(this.start===0) this.clear();

		this.$w.find('.paging-button').toggle(false);

		if(r.message) {
			r.values = this.get_values_from_response(r.message);
		}

		if(r.values && r.values.length) {
			this.data = this.data.concat(r.values);
			this.render_list(r.values);
			this.update_paging(r.values);
		} else {
			if(this.start===0) {
				this.$w.find('.result').toggle(false);

				var msg = this.get_no_result_message
					? this.get_no_result_message()
					: (this.no_result_message
						? this.no_result_message
						: __("Nothing to show"));

				this.$w.find('.no-result')
					.html(msg)
					.toggle(true);
			}
		}

		// callbacks
		if(this.onrun) this.onrun();
		if(this.callback) this.callback(r);
		this.$w.trigger("render-complete");
	},

	get_values_from_response: function(data) {
		// make dictionaries from keys and values
		if(data.keys && $.isArray(data.keys)) {
			return frappe.utils.dict(data.keys, data.values);
		} else {
			return data;
		}
	},

	render_list: function(values) {
		var m = Math.min(values.length, this.page_length);
		this.data = values;
		if(this.filter_list)
			this.filter_values = this.filter_list.get_filters();

		// render the rows
		for(var i=0; i < m; i++) {
			this.render_row(this.add_row(), values[i], this, i);
		}
	},
	update_paging: function(values) {
		if(values.length >= this.page_length) {
			this.$w.find('.paging-button').toggle(true);
			this.start += this.page_length;
		}
	},
	add_row: function() {
		return $('
').appendTo(this.$w.find('.result-list')).get(0);
	},
	refresh: function() {
		this.run();
	},
	add_limits: function(query) {
		query += ' LIMIT ' + this.start + ',' + (this.page_length+1);
		return query
	},
	set_filter: function(fieldname, label, doctype) {
		if(!doctype) doctype = this.doctype;
		var filter = this.filter_list.get_filter(fieldname);
		if(filter) {
			var v = filter.field.get_parsed_value();
			if(v.indexOf(label)!=-1) {
				// already set
				return this;
			} else {
				// second filter set for this field
				if(fieldname=='_user_tags') {
					// and for tags
					this.filter_list.add_filter(doctype, fieldname,
						'like', '%' + label);
				} else {
					// or for rest using "in"
					filter.set_values(doctype, fieldname, 'in', v + ', ' + label);
				}
			}
		} else {
			// no filter for this item,
			// setup one
			if(['_user_tags', '_comments'].indexOf(fieldname)!==-1) {
				this.filter_list.add_filter(doctype, fieldname,
					'like', '%' + label);
			} else {
				this.filter_list.add_filter(doctype, fieldname, '=', label);
			}
		}
		return this;
	}
});
<commit_msg>listing.js: hotfix for toolbar disappears<commit_after>// Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt

// new re-factored Listing object
// removed rarely used functionality
//
// opts:
//   parent

//   method (method to call on server)
//   args (additional args to method)
//   get_args (method to return args as dict)

//   show_filters [false]
//   doctype
//   filter_fields (if given, this list is rendered, else built from doctype)

//   query or get_query (will be deprecated)
//   query_max
//   buttons_in_frame

//   no_result_message ("No result")

//   page_length (20)
//   hide_refresh (False)
//   no_toolbar
//   new_doctype
//   [function] render_row(parent, data)
//   [function] onrun
//   no_loading (no ajax indicator)

frappe.provide('frappe.ui');
frappe.ui.Listing = Class.extend({
	init: function(opts) {
		this.opts = opts || {};
		this.page_length = 20;
		this.start = 0;
		this.data = [];
		if(opts) {
			this.make();
		}
	},
	prepare_opts: function() {
		if(this.opts.new_doctype) {
			if(frappe.boot.user.can_create.indexOf(this.opts.new_doctype)==-1) {
				this.opts.new_doctype = null;
			} else {
				this.opts.new_doctype = this.opts.new_doctype;
			}
		}
		if(!this.opts.no_result_message) {
			this.opts.no_result_message = __('Nothing to show');
		}
		if(!this.opts.page_length) {
			this.opts.page_length = 20;
		}
		this.opts._more = __("More");
	},
	make: function(opts) {
		if(opts) {
			this.opts = opts;
		}
		this.prepare_opts();
		$.extend(this, this.opts);

		$(this.parent).html(repl('\

\

\
					<input type="checkbox" data-docstatus="2">\
						'+__('Cancelled')+'\

\

\
					<input type="checkbox" data-docstatus="1" checked="checked">\
						'+__('Submitted')+'\

\

\
					<input type="checkbox" data-docstatus="0" checked="checked">\
						'+__('Drafts')+'\

\

\

\

%(title)s
\
				\

\

\

\

\
							<button class="btn btn-info search-btn">\
								 \
								Search</button>\
							<button class="btn btn-default add-filter-btn">\
								 \
								Add Filter</button>\

\

\

\
				\

\

\

\

\

\

\

\
					%(no_result_message)s\

\

\

\

\

\

\

\
							<button class="btn btn-default btn-more">%(_more)s...</button>\

\

\

\
								<button type="button" class="btn btn-default btn-small btn-info" data-value="20">20</button>\
								<button type="button" class="btn btn-default btn-small" data-value="100">100</button>\
								<button type="button" class="btn btn-default btn-small" data-value="500">500</button>\

\

\

\

\

\
		', this.opts));
		this.$w = $(this.parent).find('.frappe-list');
		this.set_events();

		if(this.appframe) {
			this.$w.find('.list-toolbar-wrapper').toggle(false);
		}

		if(this.show_filters) {
			this.make_filters();
		}
		this.setup_docstatus_filter();
	},
	add_button: function(label, click, icon) {
		if(this.appframe) {
			return this.appframe.add_button(label, click, icon)
		} else {
			this.$w.find('.list-toolbar-wrapper').removeClass("hide");
			$button = $('<button class="btn btn-default"></button>')
				.appendTo(this.$w.find('.list-toolbar'))
				.html((icon ? (" ") : "") + label)
				.click(click);
			return $button
		}
	},
	show_view: function($btn, $div, $btn_unsel, $div_unsel) {
		$btn_unsel.removeClass('btn-info');
		$btn_unsel.find('i').removeClass('icon-white');
		$div_unsel.toggle(false);

		$btn.addClass('btn-info');
		$btn.find('i').addClass('icon-white');
		$div.toggle(true);
	},
	set_events: function() {
		var me = this;

		// next page
		this.$w.find('.btn-more').click(function() {
			me.run({append: true });
		});

		this.$w.find(".btn-group-paging .btn").click(function() {
			me.page_length = cint($(this).attr("data-value"));
			me.run({append: true});
		})

		// title
		if(this.title) {
			this.$w.find('h3').html(this.title).toggle(true);
		}

		// hide-refresh
		if(!(this.hide_refresh || this.no_refresh)) {
			this.add_button('Refresh', function() {
				me.run();
			}, 'icon-refresh');

		}

		// new
		if(this.new_doctype) {
			if(this.appframe) {
				this.appframe.set_title_right(" " + __('New'), function() {
					(me.custom_new_doc || me.make_new_doc).apply(me, [me.new_doctype]); });
			}
			this.add_button(__('New'), function() {
				(me.custom_new_doc || me.make_new_doc).apply(me, [me.new_doctype]);
			}, 'icon-plus');
		}

		// hide-filter
		if(me.show_filters) {
			this.add_button(__('Filter'), function() {
				me.filter_list.show_filters();
			}, 'icon-search').addClass('btn-filter');
		}

		if(me.no_toolbar || me.hide_toolbar) {
			me.$w.find('.list-toolbar-wrapper').toggle(false);
		}
	},

	make_new_doc: function(doctype) {
		var me = this;
		frappe.model.with_doctype(doctype, function() {
			var doc = frappe.model.get_new_doc(doctype);
			if(me.filter_list) {
				$.each(me.filter_list.get_filters(), function(i, f) {
					if(f[0]===doctype && f[2]==="=" && f[1]!=="name")
						doc[f[1]]=f[3];
				})
			}
			frappe.set_route("Form", doctype, doc.name);
		});
	},

	make_filters: function() {
		this.filter_list = new frappe.ui.FilterList({
			listobj: this,
			$parent: this.$w.find('.list-filters').toggle(true),
			doctype: this.doctype,
			filter_fields: this.filter_fields
		});
	},

	setup_docstatus_filter: function() {
		var me = this;
		this.can_submit = frappe.model.is_submittable(me.doctype);
		if(this.can_submit) {
			$(this.parent).find('.show-docstatus').removeClass('hide');
			$(this.parent).find('.show-docstatus input').click(function() {
				me.run();
			})
		}
	},

	clear: function() {
		this.data = [];
		this.$w.find('.result-list').empty();
		this.$w.find('.result').toggle(true);
		this.$w.find('.no-result').toggle(false);
		this.start = 0;
	},
	run: function(more) {
		var me = this;
		if(!more) {
			this.start = 0;
			if(this.onreset) this.onreset();
		}

		if(!me.opts.no_loading)
			me.set_working(true);

		return frappe.call({
			method: this.opts.method || 'frappe.widgets.query_builder.runquery',
			type: "GET",
			args: this.get_call_args(),
			callback: function(r) {
				if(!me.opts.no_loading)
					me.set_working(false);
				me.dirty = false;
				me.render_results(r);
			},
			no_spinner: this.opts.no_loading
		});
	},
	set_working: function(flag) {
		this.$w.find('.img-load').toggle(flag);
	},
	get_call_args: function() {
		// load query
		if(!this.method) {
			var query = this.get_query ? this.get_query() : this.query;
			query = this.add_limits(query);
			var args={
				query_max: this.query_max,
				as_dict: 1
			}
			args.simple_query = query;
		} else {
			var args = {
				limit_start: this.start,
				limit_page_length: this.page_length
			}
		}

		args.docstatus = this.can_submit ? $.map($(this.parent).find('.show-docstatus :checked'),
			function(inp) {
				return $(inp).attr('data-docstatus');
			}) : []

		// append user-defined arguments
		if(this.args)
			$.extend(args, this.args)

		if(this.get_args) {
			$.extend(args, this.get_args());
		}
		return args;
	},
	render_results: function(r) {
		if(this.start===0) this.clear();

		this.$w.find('.paging-button').toggle(false);

		if(r.message) {
			r.values = this.get_values_from_response(r.message);
		}

		if(r.values && r.values.length) {
			this.data = this.data.concat(r.values);
			this.render_list(r.values);
			this.update_paging(r.values);
		} else {
			if(this.start===0) {
				this.$w.find('.result').toggle(false);

				var msg = this.get_no_result_message
					? this.get_no_result_message()
					: (this.no_result_message
						? this.no_result_message
						: __("Nothing to show"));

				this.$w.find('.no-result')
					.html(msg)
					.toggle(true);
			}
		}

		// callbacks
		if(this.onrun) this.onrun();
		if(this.callback) this.callback(r);
		this.$w.trigger("render-complete");
	},

	get_values_from_response: function(data) {
		// make dictionaries from keys and values
		if(data.keys && $.isArray(data.keys)) {
			return frappe.utils.dict(data.keys, data.values);
		} else {
			return data;
		}
	},

	render_list: function(values) {
		var m = Math.min(values.length, this.page_length);
		this.data = values;
		if(this.filter_list)
			this.filter_values = this.filter_list.get_filters();

		// render the rows
		for(var i=0; i < m; i++) {
			this.render_row(this.add_row(), values[i], this, i);
		}
	},
	update_paging: function(values) {
		if(values.length >= this.page_length) {
			this.$w.find('.paging-button').toggle(true);
			this.start += this.page_length;
		}
	},
	add_row: function() {
		return $('
').appendTo(this.$w.find('.result-list')).get(0);
	},
	refresh: function() {
		this.run();
	},
	add_limits: function(query) {
		query += ' LIMIT ' + this.start + ',' + (this.page_length+1);
		return query
	},
	set_filter: function(fieldname, label, doctype) {
		if(!doctype) doctype = this.doctype;
		var filter = this.filter_list.get_filter(fieldname);
		if(filter) {
			var v = filter.field.get_parsed_value();
			if(v.indexOf(label)!=-1) {
				// already set
				return this;
			} else {
				// second filter set for this field
				if(fieldname=='_user_tags') {
					// and for tags
					this.filter_list.add_filter(doctype, fieldname,
						'like', '%' + label);
				} else {
					// or for rest using "in"
					filter.set_values(doctype, fieldname, 'in', v + ', ' + label);
				}
			}
		} else {
			// no filter for this item,
			// setup one
			if(['_user_tags', '_comments'].indexOf(fieldname)!==-1) {
				this.filter_list.add_filter(doctype, fieldname,
					'like', '%' + label);
			} else {
				this.filter_list.add_filter(doctype, fieldname, '=', label);
			}
		}
		return this;
	}
});
<|endoftext|>


<commit_before><commit_msg>Fixed problem with fully-qualified variable names.<commit_after><|endoftext|>


<commit_before>// ==UserScript==
// @name         agario-bots
// @version      1.0.0
// @description  Free open source agar.io bots
// @author       Nel#0001
// @run-at       document-start
// @match        *://agar.io/*
// ==/UserScript==

function modifyAgarioHTML(agarioHTML, modifiedCoreJS){
    return agarioHTML
        .replace(/`)
}

function modifyCoreJS(coreJS){
    return coreJS
        .replace(/\(function\(\w+\){/, `
            $&
            class Writer {
                constructor(size){
                    this.message = new DataView(new ArrayBuffer(size))
                    this.byteOffset = 0
                }
                writeUint8(value){
                    this.message.setUint8(this.byteOffset++, value)
                }
                writeInt32(value){
                    this.message.setInt32(this.byteOffset, value, true)
                    this.byteOffset += 4
                }
                writeUint32(value){
                    this.message.setUint32(this.byteOffset, value, true)
                    this.byteOffset += 4
                }
                writeString(string){
                    for(let i = 0; i < string.length; i++) this.writeUint8(string.charCodeAt(i))
                    this.writeUint8(0)
                }
            }
            const User = {
                isConnected: false,
                startedBots: false,
                protocolVersion: 0,
                clientVersion: 0,
                enabledBotsAI: false,
                macroFeedInterval: null,
                stopMovement: false,
                isAlive: false,
                serverURL: '',
                serverKeyBytes: [],
                mouseX: 0,
                mouseY: 0,
                mapOffsetX: 0,
                mapOffsetY: 0,
                startBotsKey: 'x',
                stopBotsKey: 'y',
                splitBotsKey: 't',
                ejectBotsKey: 'a',
                aiBotsKey: 'f',
                macroFeedKey: 'e',
                doubleSplitKey: 'q',
                sixteenSplitKey: 'r',
                stopMovementKey: 's',
                extendedScale: false,
                mouseWheelSpeed: 0.9,
                hideMapGrid: false,
                showAllPlayersMass: false,
                connection: {
                    botsName: '#FreeAgarioBots',
                    botsAmount: 50,
                    useRemote: false,
                    serverHost: 'localhost',
                    serverPort: 1337,
                    ws: null,
                    messages: {
                        handshake(protocolVersion, clientVersion){
                            const writer = new Writer(9)
                            writer.writeUint8(0)
                            writer.writeUint32(protocolVersion)
                            writer.writeUint32(clientVersion)
                            return writer.message
                        },
                        startBots(botsName, botsAmount, isAlive, serverURL, serverKeyBytes){
                            const writer = new Writer(9 + serverURL.length + botsName.length)
                            writer.writeUint8(1)
                            writer.writeString(botsName)
                            writer.writeUint8(Number(botsAmount))
                            writer.writeUint8(Number(isAlive))
                            writer.writeString(serverURL)
                            writer.writeInt32(new Int32Array(new Int8Array(serverKeyBytes).buffer)[0])
                            return writer.message
                        },
                        mousePosition(mouseX, mouseY){
                            const writer = new Writer(9)
                            writer.writeUint8(7)
                            writer.writeInt32(mouseX)
                            writer.writeInt32(mouseY)
                            return writer.message
                        }
                    },
                    getURL(){
                        return \`\${this.useRemote ? 'wss://' : 'ws://'}\${this.serverHost}:\${this.serverPort}\`
                    },
                    connect(){
                        this.ws = new WebSocket(this.getURL())
                        this.ws.binaryType = 'arraybuffer'
                        this.ws.onopen = this.onopen.bind(this)
                        this.ws.onerror = this.onerror.bind(this)
                        this.ws.onclose = this.onclose.bind(this)
                    },
                    sendMessage(message){
                        if(this.ws && this.ws.readyState === WebSocket.OPEN) this.ws.send(message.buffer)
                    },
                    onopen(){
                        if(User.protocolVersion && User.clientVersion){
                            this.sendMessage(this.messages.handshake(User.protocolVersion, User.clientVersion))
                            document.getElementById('userStatus').style.color = 'green'
                            document.getElementById('userStatus').innerText = 'Connected'
                            User.isConnected = true
                        }
                        else {
                            alert('Find a new party to complete the handshake')
                            this.ws.close()
                        }
                    },
                    onerror(){
                        if(this.ws.readyState === WebSocket.CONNECTING || this.ws.readyState === WebSocket.OPEN) this.ws.close()
                    },
                    onclose(){
                        document.getElementById('userStatus').style.color = 'red'
                        document.getElementById('userStatus').innerText = 'Disconnected'
                        setTimeout(this.connect.bind(this), 1000)
                        User.isConnected = false
                    }
                }
            }
            WebSocket.prototype.storedSend = WebSocket.prototype.send
            WebSocket.prototype.send = function(buffer){
                this.storedSend(buffer)
                const message = new DataView(new Uint8Array(buffer).buffer)
                if(!User.protocolVersion && message.getUint8(0) === 254 && message.byteLength === 5) User.protocolVersion = message.getUint32(1, true)
                else if(message.getUint8(0) === 255 && message.byteLength === 5){
                    if(!User.clientVersion) User.clientVersion = message.getUint32(1, true)
                    else if(!User.isConnected) User.connection.connect()
                }
            }
            document.addEventListener('keydown', e => {
                if(!document.getElementById('overlays')){
                    switch(e.key){
                        case User.startBotsKey:
                            if(!User.startedBots && User.serverURL.includes('?party_id=')){
                                User.connection.sendMessage(User.connection.messages.startBots(User.connection.botsName, User.connection.botsAmount, User.isAlive, User.serverURL, User.serverKeyBytes))
                                document.getElementById('botsStatus').style.color = 'green'
                                document.getElementById('botsStatus').innerText = 'Started'
                                User.startedBots = true
                            }
                            else alert('You must be in party mode and have bots stopped in order to start them')
                            break
                        case User.stopBotsKey:
                            if(User.startedBots){
                                User.connection.sendMessage(new Uint8Array([2]))
                                document.getElementById('botsStatus').style.color = 'red'
                                document.getElementById('botsStatus').innerText = 'Stopped'
                                User.startedBots = false
                            }
                            else alert('You must have bots started in order to stop them')
                            break
                        case User.splitBotsKey:
                            User.connection.sendMessage(new Uint8Array([3]))
                            break
                        case User.ejectBotsKey:
                            User.connection.sendMessage(new Uint8Array([4]))
                            break
                        case User.aiBotsKey:
                            User.enabledBotsAI = !User.enabledBotsAI
                            User.connection.sendMessage(new Uint8Array([5, Number(User.enabledBotsAI)]))
                            break
                        case User.macroFeedKey:
                            if(!User.macroFeedInterval){
                                window.core.eject()
                                User.macroFeedInterval = setInterval(window.core.eject, 80)
                            }
                            break
                        case User.doubleSplitKey:
                            window.core.split()
                            setTimeout(window.core.split, 40)
                            break
                        case User.sixteenSplitKey:
                            window.core.split()
                            setTimeout(window.core.split, 40)
                            setTimeout(window.core.split, 80)
                            setTimeout(window.core.split, 120)
                            break
                        case User.stopMovementKey:
                            User.stopMovement = !User.stopMovement
                            break
                    }
                }
            })
            document.addEventListener('keyup', e => {
                if(!document.getElementById('overlays') && e.key === User.macroFeedKey && User.macroFeedInterval){
                    clearInterval(User.macroFeedInterval)
                    User.macroFeedInterval = null
                }
            })
            setTimeout(() => {
                document.getElementById('mainui-ads').style.height = '340px'
                document.getElementById('mainui-ads').innerHTML = \`

                            INFO
                             |
                            SETTINGS
                             |
                            KEYS

                            User Server Status: Disconnected

                            Bots Status: Stopped

                            Use Remote Server: <input type="checkbox" id="useRemote">

                            Bots Name: <input type="text" id="botsName" style="color: #31BAFF; border: none; outline: none;" placeholder="Bots Name" value="#FreeAgarioBots" maxlength="15" spellcheck="false">

                            Bots Amount: <input type="text" id="botsAmount" style="color: #F82626; border: none; outline: none;" placeholder="Min=50/Max=150" value="50" maxlength="3" spellcheck="false">

                            Server Host: <input type="text" id="serverHost" style="color: #31BAFF; border: none; outline: none;" placeholder="Hostname/IP" value="localhost" maxlength="50" spellcheck="false">

                            Server Port: <input type="text" id="serverPort" style="color: #FFAD31; border: none; outline: none;" placeholder="0-65535" value="1337" maxlength="5" spellcheck="false">

                            Developed by Nel#0001

                            Zoom Speed: <input type="text" id="mouseWheelSpeed" style="border: none; outline: none;" placeholder="0.01-0.99" value="0.9" maxlength="4" spellcheck="false">

                            Extended Scale/Zoom: <input type="checkbox" id="extendedScale">

                            Hide Map Grid: <input type="checkbox" id="hideMapGrid">

                            Show Other Players Mass: <input type="checkbox" id="showAllPlayersMass">

                            Start Bots: <input type="text" id="startBotsKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="x" maxlength="1" spellcheck="false">

                            Stop Bots: <input type="text" id="stopBotsKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="y" maxlength="1" spellcheck="false">

                            Split Bots: <input type="text" id="splitBotsKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="t" maxlength="1" spellcheck="false">

                            Eject Bots: <input type="text" id="ejectBotsKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="a" maxlength="1" spellcheck="false">

                            AI Bots: <input type="text" id="aiBotsKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="f" maxlength="1" spellcheck="false">

                            Macro Feed: <input type="text" id="macroFeedKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="e" maxlength="1" spellcheck="false">

                            Double Split: <input type="text" id="doubleSplitKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="q" maxlength="1" spellcheck="false">

                            Sixteen Split: <input type="text" id="sixteenSplitKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="r" maxlength="1" spellcheck="false">

                            Stop Move: <input type="text" id="stopMovementKey" style="color: #3FAAEF; border: none; outline: none;" placeholder="a-z0-9 (lowercase)" value="s" maxlength="1" spellcheck="false">

                \`
                const tabs = ['tabInfo', 'tabSettings', 'tabKeys']
                for(const tab of tabs){
                    document.getElementById(tab).addEventListener('click', function(){
                        for(const t of tabs){
                            if(this !== document.getElementById(t)) document.getElementById(\`\${t}Panel\`).style.display = 'none'
                            else document.getElementById(\`\${t}Panel\`).style.display = 'block'
                        }
                    })
                }
                const checkboxes = ['useRemote', 'extendedScale', 'hideMapGrid', 'showAllPlayersMass']
                for(const checkbox of checkboxes){
                    if(localStorage.getItem(checkbox) !== null){
                        switch(checkbox){
                            case 'useRemote':
                                User.connection.useRemote = JSON.parse(localStorage.getItem(checkbox))
                                document.getElementById(checkbox).checked = User.connection.useRemote
                                break
                            default:
                                User[checkbox] = JSON.parse(localStorage.getItem(checkbox))
                                document.getElementById(checkbox).checked = User[checkbox]
                        }
                    }
                    document.getElementById(checkbox).addEventListener('click', function(){
                        switch(checkbox){
                            case 'useRemote':
                                User.connection.useRemote = this.checked
                                localStorage.setItem(checkbox, User.connection.useRemote)
                                break
                            default:
                                User[checkbox] = this.checked
                                localStorage.setItem(checkbox, User[checkbox])
                        }
                    })
                }
                const inputs = ['botsName', 'botsAmount', 'serverHost', 'serverPort', 'mouseWheelSpeed', 'startBotsKey', 'stopBotsKey', 'splitBotsKey', 'ejectBotsKey', 'aiBotsKey', 'macroFeedKey', 'doubleSplitKey', 'sixteenSplitKey', 'stopMovementKey']
                for(const input of inputs){
                    if(localStorage.getItem(input) !== null){
                        switch(input){
                            case 'botsName':
                                User.connection.botsName = localStorage.getItem(input)
                                document.getElementById(input).value = User.connection.botsName
                                break
                            case 'botsAmount':
                                User.connection.botsAmount = JSON.parse(localStorage.getItem(input))
                                document.getElementById(input).value = User.connection.botsAmount
                                break
                            case 'serverHost':
                                User.connection.serverHost = localStorage.getItem(input)
                                document.getElementById(input).value = User.connection.serverHost
                                break
                            case 'serverPort':
                                User.connection.serverPort = JSON.parse(localStorage.getItem(input))
                                document.getElementById(input).value = User.connection.serverPort
                                break
                            case 'mouseWheelSpeed':
                                User.mouseWheelSpeed = JSON.parse(localStorage.getItem(input))
                                document.getElementById(input).value = User.mouseWheelSpeed
                                break
                            default:
                                User[input] = localStorage.getItem(input)
                                document.getElementById(input).value = User[input]
                        }
                    }
                    document.getElementById(input).addEventListener('input', function(){
                        switch(input){
                            case 'botsName':
                                User.connection.botsName = this.value
                                localStorage.setItem(input, User.connection.botsName)
                                break
                            case 'botsAmount':
                                User.connection.botsAmount = Number(this.value)
                                localStorage.setItem(input, User.connection.botsAmount)
                                break
                            case 'serverHost':
                                User.connection.serverHost = this.value
                                localStorage.setItem(input, User.connection.serverHost)
                                break
                            case 'serverPort':
                                User.connection.serverPort = Number(this.value)
                                localStorage.setItem(input, User.connection.serverPort)
                                break
                            case 'mouseWheelSpeed':
                                User.mouseWheelSpeed = Number(this.value)
                                localStorage.setItem(input, User.mouseWheelSpeed)
                                break
                            default:
                                User[input] = this.value
                                localStorage.setItem(input, User[input])
                        }
                    })
                }
            }, 2000)
        `)
        .replace(/setTarget:function\((\w+),(\w+)\){/, `
            $&
            if(User.stopMovement){
                $1 = innerWidth / 2
                $2 = innerHeight / 2
            }
        `)
        .replace(/if\(\w+\.MC&&\w+\.MC\.onPlayerSpawn\)/, `
            $&
            User.isAlive = true
            User.connection.sendMessage(new Uint8Array([6, Number(User.isAlive)]))
        `)
        .replace(/if\(\w+\.MC&&\w+\.MC\.onPlayerDeath\)/, `
            $&
            User.isAlive = false
            User.connection.sendMessage(new Uint8Array([6, Number(User.isAlive)]))
        `)
        .replace(/new\s+WebSocket\((\w+\(\w+\))\)/, `
            $&
            if(User.startedBots){
                User.connection.sendMessage(new Uint8Array([2]))
                document.getElementById('botsStatus').style.color = 'red'
                document.getElementById('botsStatus').innerText = 'Stopped'
                User.startedBots = false
            }
            User.enabledBotsAI = false
            User.macroFeedInterval = null
            User.stopMovement = false
            User.isAlive = false
            User.serverURL = $1
            User.serverKeyBytes = []
        `)
        .replace(/\w+\[\w+>>2]=\w+>>>15\^\w+;\w+=0;do{\w+=\w+\+\w+\|0;\w+\[\w+>>0]=\w+\[\w+>>0]\^(\w+\[\w+\+\(\w+&3\)>>0])/, `
            $&
            if(User.serverKeyBytes.length !== 4) User.serverKeyBytes.push($1)
        `)
        .replace(/(\w+)=~~\(\+\w+\[\w+\+\d+>>3]\+\s+\+\(\(\w+\[\w+\+\d+>>2]\|0\)-\(\(\w+\[\d+]\|0\)\/2\|0\)\|0\)\/\w+\);(\w+)=~~\(\+\w+\[\w+\+\d+>>3]\+\s+\+\(\(\w+\[\w+\+\d+>>2]\|0\)-\(\(\w+\[\d+]\|0\)\/2\|0\)\|0\)\/\w+\)/, `
            $&
            User.mouseX = $1 - User.mapOffsetX
            User.mouseY = $2 - User.mapOffsetY
            User.connection.sendMessage(User.connection.messages.mousePosition(User.mouseX, User.mouseY))
        `)
        .replace(/\w+\[\w+\+272>>3]=(\w+);\w+\[\w+\+280>>3]=(\w+);\w+\[\w+\+288>>3]=(\w+);\w+\[\w+\+296>>3]=(\w+)/, `
            $&
            if(~~($3 - $1) === 14142 && ~~($4 - $2) === 14142){
                User.mapOffsetX = ($1 + $3) / 2
                User.mapOffsetY = ($2 + $4) / 2
            }
        `)
        .replace(/;if\((\w+)<1\.0\)/, ';if($1 < (User.extendedScale ? 0.05 : 1))')
        .replace(/\(\.9,/, '(User.mouseWheelSpeed,')
        .replace(/(\w+\(\d+,\w+\|0,\.5,\.5\)\|0);(\w+\(\d+,\w+\|0,\.5,50\.5\)\|0);(\w+\(\d+,\w+\|0,\.5,\.5\)\|0);(\w+\(\d+,\w+\|0,50\.5,\.5\)\|0)/, `
            $1
            if(!User.hideMapGrid) $2
            $3
            if(!User.hideMapGrid) $4
        `)
        .replace(/while\(0\);(\w+)=\(\w+\|0\)!=\(\w+\|0\);/, `
            $&
            if(User.showAllPlayersMass) $1 = true
        `)
}

(async () => {
    if(location.protocol === 'http:') location.replace('https://agar.io')
    else {
        stop()
        const agarioRes = await fetch('https://agar.io')
        const agarioHTML = await agarioRes.text()
        const coreRes = await fetch('https://agar.io/agario.core.js')
        const coreJS = await coreRes.text()
        document.open()
        document.write(modifyAgarioHTML(agarioHTML, modifyCoreJS(coreJS)))
        document.close()
    }
})()
<commit_msg>Delete agario-bots.user.js<commit_after><|endoftext|>


<commit_before>(function () {
    'use strict';

    /**
     * @param {Base} Base
     * @param {$rootScope.Scope} $scope
     * @param {User} user
     * @param {app.utils} utils
     * @return {GatewaySignCtrl}
     */
    const controller = function (Base, $scope, user, utils) {

        class GatewaySignCtrl extends Base {

            /**
             * @param {object} search
             * @param {string} search.n Gateaway application name
             * @param {string} search.r Gateaway referrer
             * @param {string} search.i Gateaway icon path
             * @param {string} search.d Gateaway data for sign
             * @param {string} search.s Gateaway path for success auth
             * @param {string} search.e Gateaway path for error auth
             */
            constructor(search) {
                super($scope);
                this.appName = search.n;
                this.referrer = search.r;
                this.iconPath = search.i;
                this.data = search.d;
                this.successPath = search.s;
                this.errorPath = search.e;
                this.hasIcon = false;
                this.noReferer = false;
                this.titleLiteral = 'modal.gatewaySign.title';
                this.imageSrc = null;
                this.noCloseIcon = true;

                this._checkParams();
                this._setImageUrl();
            }

            cancel() {
                this._sendError('User cancel!');
            }

            send() {
                return user.getSeed()
                    .then((seed) => {
                        /**
                         * @type {ITransactionClass}
                         */
                        const Sign = Waves.Transactions.createSignableData([
                            new Waves.byteProcessors.StringWithLength('prefix'),
                            new Waves.byteProcessors.StringWithLength('host'),
                            new Waves.byteProcessors.StringWithLength('data')
                        ]);

                        const prefix = 'WavesWalletAuthentication';
                        const host = GatewaySignCtrl._getDomain(this.referrer);

                        return new Sign({ prefix, host, data: this.data })
                            .prepareForAPI(seed.keyPair.privateKey)
                            .then(({ signature }) => {
                                const search = `?s=${signature}&p=${seed.keyPair.publicKey}&a=${user.address}`;
                                const successPath = this.successPath || '';
                                const url = `${this.referrer}/${successPath}${search}`;
                                location.replace(GatewaySignCtrl._normalizeUrl(url));
                            });
                    }).catch((e) => {
                        this._sendError(String(e));
                    });
            }

            _setImageUrl() {
                if (!this.iconPath) {
                    this.hasIcon = false;
                    return null;
                }

                try {
                    const url = new URL(this.iconPath);
                    utils.loadImage(url.href).then(() => {
                        this.imageSrc = url.href;
                        this.hasIcon = true;
                    }).catch(() => {
                        this.hasIcon = false;
                    });
                } catch (e) {
                    const url = GatewaySignCtrl._normalizeUrl(`${this.referrer}/${this.iconPath}`);
                    utils.loadImage(url).then(() => {
                        this.imageSrc = url;
                        this.hasIcon = true;
                    }).catch(() => {
                        this.hasIcon = false;
                    });
                }
            }

            /**
             * @private
             */
            _checkParams() {
                if (!this.referrer) {
                    this.titleLiteral = 'modal.gatewaySign.error.title';
                    this.noReferer = true;
                }

                if (!this.data) {
                    this._sendError('Has no data for sign!');
                }

                if (!this.appName) {
                    this._sendError('Has no app name!');
                }
            }

            /**
             * @param {string} message
             * @private
             */
            _sendError(message) {
                const errorPath = this.errorPath || '';
                const url = `${this.referrer}/${errorPath}?m=${message}`;
                location.replace(GatewaySignCtrl._normalizeUrl(url));
            }

            /**
             * @param {string} referer
             * @return {string}
             * @private
             */
            static _getDomain(referer) {
                const url = new URL(referer);
                if (url.protocol !== 'https:') {
                    throw new Error('Protocol must be "https:"');
                }
                return url.hostname;
            }

            /**
             * @param {string} urlString
             * @return {string}
             * @private
             */
            static _normalizeUrl(urlString) {
                const url = new URL(urlString);
                const protocol = `${url.protocol}//`;
                return protocol + (`${url.host}/${url.pathname}/${url.search}${url.hash}`.replace(/\/\//g, '/'))
                    .replace(/\/$/, '');
            }

        }

        return new GatewaySignCtrl(this.search);
    };

    controller.$inject = ['Base', '$scope', 'user', 'utils'];

    angular.module('app.utils').controller('GatewaySignCtrl', controller);
})();
<commit_msg>CLIENT-706: fix url normalaze<commit_after>(function () {
    'use strict';

    /**
     * @param {Base} Base
     * @param {$rootScope.Scope} $scope
     * @param {User} user
     * @param {app.utils} utils
     * @return {GatewaySignCtrl}
     */
    const controller = function (Base, $scope, user, utils) {

        class GatewaySignCtrl extends Base {

            /**
             * @param {object} search
             * @param {string} search.n Gateaway application name
             * @param {string} search.r Gateaway referrer
             * @param {string} search.i Gateaway icon path
             * @param {string} search.d Gateaway data for sign
             * @param {string} search.s Gateaway path for success auth
             * @param {string} search.e Gateaway path for error auth
             */
            constructor(search) {
                super($scope);
                this.appName = search.n;
                this.referrer = search.r;
                this.iconPath = search.i;
                this.data = search.d;
                this.successPath = search.s;
                this.errorPath = search.e;
                this.hasIcon = false;
                this.noReferer = false;
                this.titleLiteral = 'modal.gatewaySign.title';
                this.imageSrc = null;
                this.noCloseIcon = true;

                this._checkParams();
                this._setImageUrl();
            }

            cancel() {
                this._sendError('User cancel!');
            }

            send() {
                return user.getSeed()
                    .then((seed) => {
                        /**
                         * @type {ITransactionClass}
                         */
                        const Sign = Waves.Transactions.createSignableData([
                            new Waves.byteProcessors.StringWithLength('prefix'),
                            new Waves.byteProcessors.StringWithLength('host'),
                            new Waves.byteProcessors.StringWithLength('data')
                        ]);

                        const prefix = 'WavesWalletAuthentication';
                        const host = GatewaySignCtrl._getDomain(this.referrer);

                        return new Sign({ prefix, host, data: this.data })
                            .prepareForAPI(seed.keyPair.privateKey)
                            .then(({ signature }) => {
                                const search = `?s=${signature}&p=${seed.keyPair.publicKey}&a=${user.address}`;
                                const successPath = this.successPath || '';
                                const url = `${this.referrer}/${successPath}${search}`;
                                location.replace(GatewaySignCtrl._normalizeUrl(url));
                            });
                    }).catch((e) => {
                        this._sendError(String(e));
                    });
            }

            _setImageUrl() {
                if (!this.iconPath) {
                    this.hasIcon = false;
                    return null;
                }

                try {
                    const url = new URL(this.iconPath);
                    utils.loadImage(url.href).then(() => {
                        this.imageSrc = url.href;
                        this.hasIcon = true;
                    }).catch(() => {
                        this.hasIcon = false;
                    });
                } catch (e) {
                    const url = GatewaySignCtrl._normalizeUrl(`${this.referrer}/${this.iconPath}`);
                    utils.loadImage(url).then(() => {
                        this.imageSrc = url;
                        this.hasIcon = true;
                    }).catch(() => {
                        this.hasIcon = false;
                    });
                }
            }

            /**
             * @private
             */
            _checkParams() {
                if (!this.referrer) {
                    this.titleLiteral = 'modal.gatewaySign.error.title';
                    this.noReferer = true;
                }

                if (!this.data) {
                    this._sendError('Has no data for sign!');
                }

                if (!this.appName) {
                    this._sendError('Has no app name!');
                }
            }

            /**
             * @param {string} message
             * @private
             */
            _sendError(message) {
                const errorPath = this.errorPath || '';
                const url = `${this.referrer}/${errorPath}?m=${message}`;
                location.replace(GatewaySignCtrl._normalizeUrl(url));
            }

            /**
             * @param {string} referer
             * @return {string}
             * @private
             */
            static _getDomain(referer) {
                const url = new URL(referer);
                if (url.protocol !== 'https:') {
                    throw new Error('Protocol must be "https:"');
                }
                return url.hostname;
            }

            /**
             * @param {string} urlString
             * @return {string}
             * @private
             */
            static _normalizeUrl(urlString) {
                const url = new URL(urlString);
                const protocol = `${url.protocol}//`;
                return protocol + (`${url.host}/${url.pathname}/${url.search}${url.hash}`.replace(/\/+/g, '/'))
                    .replace(/\/$/, '');
            }

        }

        return new GatewaySignCtrl(this.search);
    };

    controller.$inject = ['Base', '$scope', 'user', 'utils'];

    angular.module('app.utils').controller('GatewaySignCtrl', controller);
})();
<|endoftext|>


<commit_before>var util = require('util'),
    Requester = require('./Requester'),
    _ = require('lodash'),
    uuid = require('node-uuid');

var TimeBalancedRequester = function(advertisement, discoveryOptions) {
    Requester.call(this, advertisement, discoveryOptions);
    this.responders = {};
    this.timers = {};
    this.requestId = 0;
    this.callbacks = {};
    this.MAX_REQUESTS = 50;
    this.TIMEOUT = 10000;

    // neglect requests from calculation that took longer then the given timeout
    var that = this;
    setInterval(function() {
        var index, id, now = new Date();
        for (index in that.responders) {
            for (id in that.responders[index]) {
                if (now - that.responders[index][id].sent > that.TIMEOUT) {
                    if (that.timers[id]) clearInterval(that.timers[id]); //if the response is lost, don't leave garbage
                    delete that.responders[index][id];
                }
            }
        }
    }, 100);

    // clear all gathered data when a socket disconnects
    this.on('ready', function(sock) {
        sock.on('disconnect', function(sock) {
            if (sock.uuid) {
                var responder = that.responders[sock.uuid],
                    id;
                for (id in responder) { //clear timers and callbacks for the disconnected socket
                    clearInterval(that.timers[id]);
                    delete that.timers[id];
                    that.callbacks[id] && delete that.callbacks[id];
                }
                delete that.responders[sock.uuid];
            }
        });
    });
};
util.inherits(TimeBalancedRequester, Requester);

TimeBalancedRequester.prototype.send = function() {
    var that = this;
    var args = Array.prototype.slice.call(arguments);
    var id = this.requestId++;

    var hasCallback = 'function' == typeof args[args.length - 1];
    if (hasCallback)
        this.callbacks[id] = args.pop();

    var index = 0;
    if (this.sock && this.sock.socks) {
        var socks = this.sock.socks;
        var len = socks.length;
        var minAvgResponseTime = Number.MAX_VALUE;
        var minIndex = 0, average = 0;

        if (len > 0 && _.size(this.responders) == len) { //have data for all sockets
            for (index in this.responders) {
                var item = this.responders[index];
                var totalTime = 0, request;
                for (request in item) {
                    var time = item[request].time;
                    totalTime += time;
                }
                average = totalTime / _.size(item) || 0;

                if (average < minAvgResponseTime) {
                    minAvgResponseTime = average;
                    minIndex = index;
                }
            }

            minIndex = _.findIndex(socks, {uuid: minIndex});

            // explore other servers every once in a while
            if (id % this.MAX_REQUESTS == 0)
                minIndex = Math.floor(Math.random() * len);

        } else if (len != 0) { //there is a new socket to be explored use it
            minIndex = len - 1;
        };

        var n = this.sock.n = (minIndex % len) || 0; //select the socket to be used
        if (socks[n] && typeof socks[n].uuid == "undefined")
            socks[n].uuid = uuid.v4(); // assign a unique identifier to that socket

        index = (socks[n] && socks[n].uuid) || 0; //save the index of selected socket
    }

    var cb = function(err, res) {
        var lastRequests = that.responders[index];
        if (lastRequests && lastRequests[id])
            lastRequests[id].time = (new Date() - lastRequests[id].sent); //save response time
        clearInterval(that.timers[id]); //clear timers for that request
        delete that.timers[id];

        if (!that.callbacks[id]) return; //missing callback
        that.callbacks[id](res);
        delete that.callbacks[id];
    };

    args.push(cb);
    this.sock && this.sock.send.apply(this.sock, args); //send request to socket

    if (index == 0) return; // this request is queued

    if (!this.responders[index]) this.responders[index] = {}; //create if this is the first request for that index
    this.responders[index][id] = { // create a container object
        time: 0,
        sent: new Date()
    };

    // increment responseTime of that request every 0.5 seconds
    this.timers[id] = setInterval(function() {
        var lastRequests = that.responders[index];
        if (lastRequests[id]) lastRequests[id].time += 500;
    }, 500);

};

TimeBalancedRequester.prototype.close = function() {
    if (this.discovery) {
        this.discovery.stop();

        this.discovery.broadcast &&
            this.discovery.broadcast.socket &&
            this.discovery.broadcast.socket.close();
    }
};

module.exports = TimeBalancedRequester;
<commit_msg>Increment sampling period for outstanding requests is now 0.1 secs for finer resolution.<commit_after>var util = require('util'),
    Requester = require('./Requester'),
    _ = require('lodash'),
    uuid = require('node-uuid');

var TimeBalancedRequester = function(advertisement, discoveryOptions) {
    Requester.call(this, advertisement, discoveryOptions);
    this.responders = {};
    this.timers = {};
    this.requestId = 0;
    this.callbacks = {};
    this.MAX_REQUESTS = 50;
    this.TIMEOUT = 10000;

    // neglect requests from calculation that took longer then the given timeout
    var that = this;
    setInterval(function() {
        var index, id, now = new Date();
        for (index in that.responders) {
            for (id in that.responders[index]) {
                if (now - that.responders[index][id].sent > that.TIMEOUT) {
                    if (that.timers[id]) clearInterval(that.timers[id]); //if the response is lost, don't leave garbage
                    delete that.responders[index][id];
                }
            }
        }
    }, 100);

    // clear all gathered data when a socket disconnects
    this.on('ready', function(sock) {
        sock.on('disconnect', function(sock) {
            if (sock.uuid) {
                var responder = that.responders[sock.uuid],
                    id;
                for (id in responder) { //clear timers and callbacks for the disconnected socket
                    clearInterval(that.timers[id]);
                    delete that.timers[id];
                    that.callbacks[id] && delete that.callbacks[id];
                }
                delete that.responders[sock.uuid];
            }
        });
    });
};
util.inherits(TimeBalancedRequester, Requester);

TimeBalancedRequester.prototype.send = function() {
    var that = this;
    var args = Array.prototype.slice.call(arguments);
    var id = this.requestId++;

    var hasCallback = 'function' == typeof args[args.length - 1];
    if (hasCallback)
        this.callbacks[id] = args.pop();

    var index = 0;
    if (this.sock && this.sock.socks) {
        var socks = this.sock.socks;
        var len = socks.length;
        var minAvgResponseTime = Number.MAX_VALUE;
        var minIndex = 0, average = 0;

        if (len > 0 && _.size(this.responders) == len) { //have data for all sockets
            for (index in this.responders) {
                var item = this.responders[index];
                var totalTime = 0, request;
                for (request in item) {
                    var time = item[request].time;
                    totalTime += time;
                }
                average = totalTime / _.size(item) || 0;

                if (average < minAvgResponseTime) {
                    minAvgResponseTime = average;
                    minIndex = index;
                }
            }

            minIndex = _.findIndex(socks, {uuid: minIndex});

            // explore other servers every once in a while
            if (id % this.MAX_REQUESTS == 0)
                minIndex = Math.floor(Math.random() * len);

        } else if (len != 0) { //there is a new socket to be explored use it
            minIndex = len - 1;
        };

        var n = this.sock.n = (minIndex % len) || 0; //select the socket to be used
        if (socks[n] && typeof socks[n].uuid == "undefined")
            socks[n].uuid = uuid.v4(); // assign a unique identifier to that socket

        index = (socks[n] && socks[n].uuid) || 0; //save the index of selected socket
    }

    var cb = function(err, res) {
        var lastRequests = that.responders[index];
        if (lastRequests && lastRequests[id])
            lastRequests[id].time = (new Date() - lastRequests[id].sent); //save response time
        clearInterval(that.timers[id]); //clear timers for that request
        delete that.timers[id];

        if (!that.callbacks[id]) return; //missing callback
        that.callbacks[id](res);
        delete that.callbacks[id];
    };

    args.push(cb);
    this.sock && this.sock.send.apply(this.sock, args); //send request to socket

    if (index == 0) return; // this request is queued

    if (!this.responders[index]) this.responders[index] = {}; //create if this is the first request for that index
    this.responders[index][id] = { // create a container object
        time: 0,
        sent: new Date()
    };

    // increment responseTime of that request every 0.1 seconds
    this.timers[id] = setInterval(function() {
        var lastRequests = that.responders[index];
        if (lastRequests[id]) lastRequests[id].time += 100;
    }, 100);
};

TimeBalancedRequester.prototype.close = function() {
    if (this.discovery) {
        this.discovery.stop();

        this.discovery.broadcast &&
            this.discovery.broadcast.socket &&
            this.discovery.broadcast.socket.close();
    }
};

module.exports = TimeBalancedRequester;
<|endoftext|>


<commit_before>function sendToRDStation(room) {
	if (!RocketChat.settings.get('Livechat_RDStation_Token')) {
		return room;
	}

	const livechatData = RocketChat.Livechat.getLivechatRoomGuestInfo(room);

	if (!livechatData.visitor.email) {
		return room;
	}

	const options = {
		headers: {
			'Content-Type': 'application/json',
		},
		data: {
			token_rdstation: RocketChat.settings.get('Livechat_RDStation_Token'),
			identificador: 'rocketchat-livechat',
			client_id: livechatData.visitor._id,
			email: livechatData.visitor.email,
		},
	};

	options.data.nome = livechatData.visitor.name || livechatData.visitor.username;

	if (livechatData.visitor.phone) {
		options.data.telefone = livechatData.visitor.phone;
	}

	if (livechatData.tags) {
		options.data.tags = livechatData.tags;
	}

	Object.keys(livechatData.customFields || {}).forEach((field) => {
		options.data[field] = livechatData.customFields[field];
	});

	Object.keys(livechatData.visitor.customFields || {}).forEach((field) => {
		options.data[field] = livechatData.visitor.customFields[field];
	});

	try {
		HTTP.call('POST', 'https://www.rdstation.com.br/api/1.3/conversions', options);
	} catch (e) {
		console.error('Error sending lead to RD Station ->', e);
	}

	return room;
}

RocketChat.callbacks.add('livechat.closeRoom', sendToRDStation, RocketChat.callbacks.priority.MEDIUM, 'livechat-rd-station-close-room');

RocketChat.callbacks.add('livechat.saveInfo', sendToRDStation, RocketChat.callbacks.priority.MEDIUM, 'livechat-rd-station-save-info');
<commit_msg>[FIX] Livechat integration with RDStation (#12257)<commit_after>function sendToRDStation(room) {
	if (!RocketChat.settings.get('Livechat_RDStation_Token')) {
		return room;
	}

	const livechatData = RocketChat.Livechat.getLivechatRoomGuestInfo(room);

	if (!livechatData.visitor.email) {
		return room;
	}

	const email = Array.isArray(livechatData.visitor.email) ? livechatData.visitor.email[0].address : livechatData.visitor.email;

	const options = {
		headers: {
			'Content-Type': 'application/json',
		},
		data: {
			token_rdstation: RocketChat.settings.get('Livechat_RDStation_Token'),
			identificador: 'rocketchat-livechat',
			client_id: livechatData.visitor._id,
			email,
		},
	};

	options.data.nome = livechatData.visitor.name || livechatData.visitor.username;

	if (livechatData.visitor.phone) {
		options.data.telefone = livechatData.visitor.phone;
	}

	if (livechatData.tags) {
		options.data.tags = livechatData.tags;
	}

	Object.keys(livechatData.customFields || {}).forEach((field) => {
		options.data[field] = livechatData.customFields[field];
	});

	Object.keys(livechatData.visitor.customFields || {}).forEach((field) => {
		options.data[field] = livechatData.visitor.customFields[field];
	});

	try {
		HTTP.call('POST', 'https://www.rdstation.com.br/api/1.3/conversions', options);
	} catch (e) {
		console.error('Error sending lead to RD Station ->', e);
	}

	return room;
}

RocketChat.callbacks.add('livechat.closeRoom', sendToRDStation, RocketChat.callbacks.priority.MEDIUM, 'livechat-rd-station-close-room');

RocketChat.callbacks.add('livechat.saveInfo', sendToRDStation, RocketChat.callbacks.priority.MEDIUM, 'livechat-rd-station-save-info');
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandNotifications, clearNotifications, scrollTopNotifications } from '../../actions/notifications';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import NotificationContainer from './containers/notification_container';
import { ScrollContainer } from 'react-router-scroll';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container';
import { createSelector } from 'reselect';
import Immutable from 'immutable';
import LoadMore from '../../components/load_more';
import ClearColumnButton from './components/clear_column_button';
import { openModal } from '../../actions/modal';

const messages = defineMessages({
  title: { id: 'column.notifications', defaultMessage: 'Notifications' },
  clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
  clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
});

const getNotifications = createSelector([
  state => Immutable.List(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
  state => state.getIn(['notifications', 'items']),
], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));

const mapStateToProps = state => ({
  notifications: getNotifications(state),
  isLoading: state.getIn(['notifications', 'isLoading'], true),
  isUnread: state.getIn(['notifications', 'unread']) > 0,
});

class Notifications extends React.PureComponent {

  static propTypes = {
    columnId: PropTypes.string,
    notifications: ImmutablePropTypes.list.isRequired,
    dispatch: PropTypes.func.isRequired,
    shouldUpdateScroll: PropTypes.func,
    intl: PropTypes.object.isRequired,
    isLoading: PropTypes.bool,
    isUnread: PropTypes.bool,
    multiColumn: PropTypes.bool,
  };

  static defaultProps = {
    trackScroll: true,
  };

  handleScroll = (e) => {
    const { scrollTop, scrollHeight, clientHeight } = e.target;
    const offset = scrollHeight - scrollTop - clientHeight;
    this._oldScrollPosition = scrollHeight - scrollTop;

    if (250 > offset && !this.props.isLoading) {
      this.props.dispatch(expandNotifications());
    } else if (scrollTop < 100) {
      this.props.dispatch(scrollTopNotifications(true));
    } else {
      this.props.dispatch(scrollTopNotifications(false));
    }
  }

  componentDidUpdate (prevProps) {
    if (this.node.scrollTop > 0 && (prevProps.notifications.size < this.props.notifications.size && prevProps.notifications.first() !== this.props.notifications.first() && !!this._oldScrollPosition)) {
      this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
    }
  }

  handleLoadMore = (e) => {
    e.preventDefault();
    this.props.dispatch(expandNotifications());
  }

  handleClear = () => {
    const { dispatch, intl } = this.props;

    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.clearMessage),
      confirm: intl.formatMessage(messages.clearConfirm),
      onConfirm: () => dispatch(clearNotifications()),
    }));
  }

  handlePin = () => {
    const { columnId, dispatch } = this.props;

    if (columnId) {
      dispatch(removeColumn(columnId));
    } else {
      dispatch(addColumn('NOTIFICATIONS', {}));
    }
  }

  handleMove = (dir) => {
    const { columnId, dispatch } = this.props;
    dispatch(moveColumn(columnId, dir));
  }

  handleHeaderClick = () => {
    this.column.scrollTop();
  }

  setRef = (c) => {
    this.node = c;
  }

  setColumnRef = c => {
    this.column = c;
  }

  render () {
    const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn } = this.props;
    const pinned = !!columnId;

    let loadMore       = '';
    let scrollableArea = '';
    let unread         = '';
    let scrollContainer = '';

    if (!isLoading && notifications.size > 0) {
      loadMore = <LoadMore onClick={this.handleLoadMore} />;
    }

    if (isUnread) {
      unread =
;
    }

    if (isLoading && this.scrollableArea) {
      scrollableArea = this.scrollableArea;
    } else if (notifications.size > 0) {
      scrollableArea = (

          {unread}

            {notifications.map(item => <NotificationContainer key={item.get('id')} notification={item} accountId={item.get('account')} />)}
            {loadMore}

      );
    } else {
      scrollableArea = (

          <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />

      );
    }

    if (pinned) {
      scrollContainer = scrollableArea;
    } else {
      scrollContainer = (
        <ScrollContainer scrollKey={`notifications-${columnId}`} shouldUpdateScroll={shouldUpdateScroll}>
          {scrollableArea}
        </ScrollContainer>
      );
    }

    this.scrollableArea = scrollableArea;

    return (
      <Column ref={this.setColumnRef}>
        <ColumnHeader
          icon='bell'
          active={isUnread}
          title={intl.formatMessage(messages.title)}
          onPin={this.handlePin}
          onMove={this.handleMove}
          onClick={this.handleHeaderClick}
          pinned={pinned}
          multiColumn={multiColumn}
        >
          <ColumnSettingsContainer />
        </ColumnHeader>

        {scrollContainer}
      </Column>
    );
  }

}

export default connect(mapStateToProps)(injectIntl(Notifications));
<commit_msg>Fix LoadMore in Notifications. (#3590)<commit_after>import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import { expandNotifications, clearNotifications, scrollTopNotifications } from '../../actions/notifications';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import NotificationContainer from './containers/notification_container';
import { ScrollContainer } from 'react-router-scroll';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container';
import { createSelector } from 'reselect';
import Immutable from 'immutable';
import LoadMore from '../../components/load_more';
import ClearColumnButton from './components/clear_column_button';
import { openModal } from '../../actions/modal';

const messages = defineMessages({
  title: { id: 'column.notifications', defaultMessage: 'Notifications' },
  clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
  clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
});

const getNotifications = createSelector([
  state => Immutable.List(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
  state => state.getIn(['notifications', 'items']),
], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));

const mapStateToProps = state => ({
  notifications: getNotifications(state),
  isLoading: state.getIn(['notifications', 'isLoading'], true),
  isUnread: state.getIn(['notifications', 'unread']) > 0,
  hasMore: !!state.getIn(['notifications', 'next']),
});

class Notifications extends React.PureComponent {

  static propTypes = {
    columnId: PropTypes.string,
    notifications: ImmutablePropTypes.list.isRequired,
    dispatch: PropTypes.func.isRequired,
    shouldUpdateScroll: PropTypes.func,
    intl: PropTypes.object.isRequired,
    isLoading: PropTypes.bool,
    isUnread: PropTypes.bool,
    multiColumn: PropTypes.bool,
    hasMore: PropTypes.bool,
  };

  static defaultProps = {
    trackScroll: true,
  };

  handleScroll = (e) => {
    const { scrollTop, scrollHeight, clientHeight } = e.target;
    const offset = scrollHeight - scrollTop - clientHeight;
    this._oldScrollPosition = scrollHeight - scrollTop;

    if (250 > offset && !this.props.isLoading) {
      if (this.props.hasMore) {
        this.props.dispatch(expandNotifications());
      }
    } else if (scrollTop < 100) {
      this.props.dispatch(scrollTopNotifications(true));
    } else {
      this.props.dispatch(scrollTopNotifications(false));
    }
  }

  componentDidUpdate (prevProps) {
    if (this.node.scrollTop > 0 && (prevProps.notifications.size < this.props.notifications.size && prevProps.notifications.first() !== this.props.notifications.first() && !!this._oldScrollPosition)) {
      this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition;
    }
  }

  handleLoadMore = (e) => {
    e.preventDefault();
    this.props.dispatch(expandNotifications());
  }

  handleClear = () => {
    const { dispatch, intl } = this.props;

    dispatch(openModal('CONFIRM', {
      message: intl.formatMessage(messages.clearMessage),
      confirm: intl.formatMessage(messages.clearConfirm),
      onConfirm: () => dispatch(clearNotifications()),
    }));
  }

  handlePin = () => {
    const { columnId, dispatch } = this.props;

    if (columnId) {
      dispatch(removeColumn(columnId));
    } else {
      dispatch(addColumn('NOTIFICATIONS', {}));
    }
  }

  handleMove = (dir) => {
    const { columnId, dispatch } = this.props;
    dispatch(moveColumn(columnId, dir));
  }

  handleHeaderClick = () => {
    this.column.scrollTop();
  }

  setRef = (c) => {
    this.node = c;
  }

  setColumnRef = c => {
    this.column = c;
  }

  render () {
    const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
    const pinned = !!columnId;

    let loadMore       = '';
    let scrollableArea = '';
    let unread         = '';
    let scrollContainer = '';

    if (!isLoading && notifications.size > 0 && hasMore) {
      loadMore = <LoadMore onClick={this.handleLoadMore} />;
    }

    if (isUnread) {
      unread =
;
    }

    if (isLoading && this.scrollableArea) {
      scrollableArea = this.scrollableArea;
    } else if (notifications.size > 0) {
      scrollableArea = (

          {unread}

            {notifications.map(item => <NotificationContainer key={item.get('id')} notification={item} accountId={item.get('account')} />)}
            {loadMore}

      );
    } else {
      scrollableArea = (

          <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />

      );
    }

    if (pinned) {
      scrollContainer = scrollableArea;
    } else {
      scrollContainer = (
        <ScrollContainer scrollKey={`notifications-${columnId}`} shouldUpdateScroll={shouldUpdateScroll}>
          {scrollableArea}
        </ScrollContainer>
      );
    }

    this.scrollableArea = scrollableArea;

    return (
      <Column ref={this.setColumnRef}>
        <ColumnHeader
          icon='bell'
          active={isUnread}
          title={intl.formatMessage(messages.title)}
          onPin={this.handlePin}
          onMove={this.handleMove}
          onClick={this.handleHeaderClick}
          pinned={pinned}
          multiColumn={multiColumn}
        >
          <ColumnSettingsContainer />
        </ColumnHeader>

        {scrollContainer}
      </Column>
    );
  }

}

export default connect(mapStateToProps)(injectIntl(Notifications));
<|endoftext|>