<commit_before>var termops = require('./util/termops'),
    feature = require('./util/feature'),
    ops = require('./util/ops'),
    _ = require('underscore'),
    queue = require('queue-async'),
    DEBUG = process.env.DEBUG;

module.exports = index;
module.exports.update = update;
module.exports.loadDoc = loadDoc;
module.exports.generateFrequency = generateFrequency;
module.exports.store = store;

function index(geocoder, from, to, pointer, callback) {
    pointer = pointer || {};

    to.startWriting(function(err) {
        if (err) throw err;
        function process(pointer) {
            from.getIndexableDocs(pointer, function(err, docs, pointer) {
                if (err) return callback(err);
                if (!docs.length) {
                    geocoder.emit('store');
                    store(to, function(err) {
                        if (err) return callback(err);
                        to.stopWriting(callback);
                    });
                } else {
                    geocoder.emit('index', docs.length);
                    update(to, docs, function(err) {
                        if (err) return callback(err);
                        process(pointer);
                    });
                }
            });
        };
        process(pointer);
    });
}

// # Update
//
// Updates the source's index with provided docs.
//
// @param {Object} source - a Carmen source
// @param {Array} docs - an array of documents
// @param {Function} callback
function update(source, docs, callback) {
    source._geocoder._known = source._geocoder._known || { term:{} };
    var known = source._geocoder._known;

    // First pass over docs.
    // - Creates termsets (one or more arrays of termids) from document text.
    // - Tallies frequency of termids against current frequencies compiling a
    //   final in-memory frequency count of all terms involved with this set of
    //   documents to be indexed.
    // - Stores new frequencies.
    var freq = generateFrequency(docs);
    var getter = source.getGeocoderData.bind(source);

    // Ensures all shards are loaded.
    var ids = Object.keys(freq).map(function(v) { return parseInt(v, 10); });
    source._geocoder.getall(getter, 'freq', ids, function(err) {
        if (err) return callback(err);
        for (var i = 0; i < ids.length; i++) {
            var id = ids[i];
            freq[id][0] = (source._geocoder.get('freq', id) || [0])[0] + freq[id][0];
            source._geocoder.set('freq', id, freq[id]);
        }
        indexDocs(freq[0], freq, callback);
    });

    // Second pass over docs.
    // - Create term => docid index. Uses calculated frequencies to index only
    //   significant terms for each document.
    // - Create id => grid zxy index.
    function indexDocs(approxdocs, freq, callback) {
        var patch = { grid: {}, term: {}, phrase: {}, degen: {} };
        var features = {};

        for (var i = 0; i < docs.length; i++) loadDoc(docs[i], freq, patch, known);

        var q = queue(500);
        q.defer(function(features, callback) {
            feature.putFeatures(source, docs, function(err) {
                if (err) return callback(err);
                // @TODO manually calls _commit on MBTiles sources.
                // This ensures features are persisted to the store for the
                // next run which would not necessarily be the case without.
                // Given that this is a very performant pattern, commit may
                // be worth making a public function in node-mbtiles (?).
                return source._commit ? source._commit(callback) : callback();
            });
        }, features);
        _(patch).each(setParts);
        q.awaitAll(callback);

        function setParts(data, type) {
            q.defer(function(data, type, callback) {
                var ids = Object.keys(data);
                var cache = source._geocoder;
                cache.getall(getter, type, ids, function(err) {
                    if (err) return callback(err);
                    for (var i = 0; i < ids.length; i++) {
                        var id = ids[i];
                        // This merges new entries on top of old ones.
                        switch (type) {
                        case 'term':
                            var current = cache.get(type, id);
                            if (current) {
                                current.push.apply(current, data[id]);
                                if (current.length > 2000) {
                                    current.sort();
                                    current = _(current).uniq(true);
                                }
                            } else {
                                current = data[id];
                            }
                            cache.set(type, id, current);
                            break;
                        case 'grid':
                        case 'degen':
                            var current = cache.get(type, id);
                            if (current) {
                                current.push.apply(current, data[id]);
                            } else {
                                current = data[id];
                            }
                            cache.set(type, id, current);
                            break;
                        case 'phrase':
                            cache.set(type, id, data[id]);
                            break;
                        }
                    }
                    callback();
                });
            }, data, type);
        }
    }
};

function loadDoc(doc, freq, patch, known) {
    doc._hash = termops.feature(doc._id.toString());
    doc._grid = doc._grid || [];
    if (doc._zxy) for (var i = 0; i < doc._zxy.length; i++) {
        doc._grid.push(ops.zxy(doc._hash, doc._zxy[i]));
    }

    var texts = doc._text.split(',');
    var termsets = [];
    var termsmaps = [];
    var tokensets = [];
    for (var x = 0; x < texts.length; x++) {
        var tokens = termops.tokenize(texts[x]);
        if (!tokens.length) continue;
        termsets.push(termops.termsWeighted(tokens, freq));
        termsmaps.push(termops.termsMap(tokens));
        tokensets.push(tokens);
    }

    for (var x = 0; x < termsets.length; x++) {
        var terms = termsets[x];
        var sigid = null;
        var sigweight = 0;
        var termsmap = termsmaps[x];

        for (var i = 0; i < terms.length; i++) {
            // Decode the term id, weight from weighted terms.
            var id = terms[i] >>> 4 << 4 >>> 0;
            var weight = terms[i] % 16;
            if (weight > sigweight) {
                sigid = id;
                sigweight = weight;
            }

            // This check avoids doing redundant work for a term once
            // it is known to be indexed. @TODO known issue, this prevents
            // degens from being used as an approach to avoiding fnv1a term
            // collisions.
            if (known.term[id]) continue;
            known.term[id] = true;

            // Degenerate terms are indexed for all terms
            // (not just significant ones).
            var degens = termops.degens(termsmap[id]);
            for (var j = 0; j < degens.length; j = j+2) {
                var d = degens[j];
                patch.degen[d] = patch.degen[d] || [];
                patch.degen[d].push(degens[j+1]);
            }
        }

        // Generate phrase, clustered by most significant term.
        var phrase = termops.phrase(tokensets[x], termsmap[sigid]);
        patch.phrase[phrase] = patch.phrase[phrase] || terms;
        patch.term[sigid] = patch.term[sigid] || [];
        patch.term[sigid].push(phrase);
        patch.grid[phrase] = patch.grid[phrase] || [];
        patch.grid[phrase].push.apply(patch.grid[phrase], doc._grid);

        // Debug significant term selection.
        if (DEBUG) {
            var debug = termsmap;
            var oldtext = terms.map(function(id) {
                id = id >>> 4 << 4 >>> 0;
                return debug[id];
            }).join(' ');
            var sigtext = debug[sigid];
            if (oldtext !== sigtext)  console.log('%s => %s', oldtext, sigtext);
        }
    }
}

function generateFrequency(docs) {
    var freq = {};

    // Uses freq[0] as a convention for storing total # of docs.
    // @TODO determine whether 0 can really ever be a relevant term
    // when using fnv1a.
    freq[0] = [0];
    for (var i = 0; i < docs.length; i++) {
        var texts = docs[i]._text.split(',');
        for (var x = 0; x < texts.length; x++) {
            var tokens = termops.tokenize(texts[x]);
            var terms = termops.terms(tokens);
            for (var k = 0; k < terms.length; k++) {
                var id = terms[k];
                freq[id] = freq[id] || [0];
                freq[id][0]++;
                freq[0][0]++;
            }
        }
    }

    return freq;
}

// ## Store
//
// Serialize and make permanent the index currently in memory for a source.
function store(source, callback) {
    if (source._geocoder._known) delete source._geocoder._known;

    var tasks = [];

    ['freq','term','phrase','grid','degen'].forEach(loadTerm);

    function loadTerm(type) {
        var cache = source._geocoder;
        tasks = tasks.concat(cache.list(type).map(loadShard));

        function loadShard(shard) {
            var ids = cache.list(type, shard);
            for (var i = 0; i < ids.length; i++) {
                var id = ids[i];
                switch (type) {
                    case 'term':
                    case 'grid':
                    case 'degen':
                        var data = source._geocoder.get(type, id) || [];
                        data.sort();
                        source._geocoder.set(type, id, _(data).uniq(true));
                        break;
                }
            }
            return [type, shard];
        }
    }
    var q = queue(10);
    tasks.forEach(function (task) {
        q.defer(function(task, callback) {
            var type = task[0];
            var shard = task[1];
            var cache = source._geocoder;
            source.putGeocoderData(type, shard, cache.pack(type, shard), callback);
        }, task);
    });
    q.awaitAll(callback);
}
<commit_msg>Allow shardlevel as option to be passed in.<commit_after>var termops = require('./util/termops'),
    feature = require('./util/feature'),
    ops = require('./util/ops'),
    _ = require('underscore'),
    queue = require('queue-async'),
    DEBUG = process.env.DEBUG;

module.exports = index;
module.exports.update = update;
module.exports.loadDoc = loadDoc;
module.exports.generateFrequency = generateFrequency;
module.exports.store = store;

function index(geocoder, from, to, options, callback) {
    options = options || {};

    to.startWriting(function(err) {
        if (err) return callback(err);
        if (options.shardlevel) {
            to.putInfo({shardlevel:options.shardlevel}, function(err) {
                if (err) return callback(err);
                to._geocoder.shardlevel = options.shardlevel;
                process({});
            });
        } else {
            process({});
        }
        function process(pointer) {
            from.getIndexableDocs(pointer, function(err, docs, pointer) {
                if (err) return callback(err);
                if (!docs.length) {
                    geocoder.emit('store');
                    store(to, function(err) {
                        if (err) return callback(err);
                        to.stopWriting(callback);
                    });
                } else {
                    geocoder.emit('index', docs.length);
                    update(to, docs, function(err) {
                        if (err) return callback(err);
                        process(pointer);
                    });
                }
            });
        };
    });
}

// # Update
//
// Updates the source's index with provided docs.
//
// @param {Object} source - a Carmen source
// @param {Array} docs - an array of documents
// @param {Function} callback
function update(source, docs, callback) {
    source._geocoder._known = source._geocoder._known || { term:{} };
    var known = source._geocoder._known;

    // First pass over docs.
    // - Creates termsets (one or more arrays of termids) from document text.
    // - Tallies frequency of termids against current frequencies compiling a
    //   final in-memory frequency count of all terms involved with this set of
    //   documents to be indexed.
    // - Stores new frequencies.
    var freq = generateFrequency(docs);
    var getter = source.getGeocoderData.bind(source);

    // Ensures all shards are loaded.
    var ids = Object.keys(freq).map(function(v) { return parseInt(v, 10); });
    source._geocoder.getall(getter, 'freq', ids, function(err) {
        if (err) return callback(err);
        for (var i = 0; i < ids.length; i++) {
            var id = ids[i];
            freq[id][0] = (source._geocoder.get('freq', id) || [0])[0] + freq[id][0];
            source._geocoder.set('freq', id, freq[id]);
        }
        indexDocs(freq[0], freq, callback);
    });

    // Second pass over docs.
    // - Create term => docid index. Uses calculated frequencies to index only
    //   significant terms for each document.
    // - Create id => grid zxy index.
    function indexDocs(approxdocs, freq, callback) {
        var patch = { grid: {}, term: {}, phrase: {}, degen: {} };
        var features = {};

        for (var i = 0; i < docs.length; i++) loadDoc(docs[i], freq, patch, known);

        var q = queue(500);
        q.defer(function(features, callback) {
            feature.putFeatures(source, docs, function(err) {
                if (err) return callback(err);
                // @TODO manually calls _commit on MBTiles sources.
                // This ensures features are persisted to the store for the
                // next run which would not necessarily be the case without.
                // Given that this is a very performant pattern, commit may
                // be worth making a public function in node-mbtiles (?).
                return source._commit ? source._commit(callback) : callback();
            });
        }, features);
        _(patch).each(setParts);
        q.awaitAll(callback);

        function setParts(data, type) {
            q.defer(function(data, type, callback) {
                var ids = Object.keys(data);
                var cache = source._geocoder;
                cache.getall(getter, type, ids, function(err) {
                    if (err) return callback(err);
                    for (var i = 0; i < ids.length; i++) {
                        var id = ids[i];
                        // This merges new entries on top of old ones.
                        switch (type) {
                        case 'term':
                            var current = cache.get(type, id);
                            if (current) {
                                current.push.apply(current, data[id]);
                                if (current.length > 2000) {
                                    current.sort();
                                    current = _(current).uniq(true);
                                }
                            } else {
                                current = data[id];
                            }
                            cache.set(type, id, current);
                            break;
                        case 'grid':
                        case 'degen':
                            var current = cache.get(type, id);
                            if (current) {
                                current.push.apply(current, data[id]);
                            } else {
                                current = data[id];
                            }
                            cache.set(type, id, current);
                            break;
                        case 'phrase':
                            cache.set(type, id, data[id]);
                            break;
                        }
                    }
                    callback();
                });
            }, data, type);
        }
    }
};

function loadDoc(doc, freq, patch, known) {
    doc._hash = termops.feature(doc._id.toString());
    doc._grid = doc._grid || [];
    if (doc._zxy) for (var i = 0; i < doc._zxy.length; i++) {
        doc._grid.push(ops.zxy(doc._hash, doc._zxy[i]));
    }

    var texts = doc._text.split(',');
    var termsets = [];
    var termsmaps = [];
    var tokensets = [];
    for (var x = 0; x < texts.length; x++) {
        var tokens = termops.tokenize(texts[x]);
        if (!tokens.length) continue;
        termsets.push(termops.termsWeighted(tokens, freq));
        termsmaps.push(termops.termsMap(tokens));
        tokensets.push(tokens);
    }

    for (var x = 0; x < termsets.length; x++) {
        var terms = termsets[x];
        var sigid = null;
        var sigweight = 0;
        var termsmap = termsmaps[x];

        for (var i = 0; i < terms.length; i++) {
            // Decode the term id, weight from weighted terms.
            var id = terms[i] >>> 4 << 4 >>> 0;
            var weight = terms[i] % 16;
            if (weight > sigweight) {
                sigid = id;
                sigweight = weight;
            }

            // This check avoids doing redundant work for a term once
            // it is known to be indexed. @TODO known issue, this prevents
            // degens from being used as an approach to avoiding fnv1a term
            // collisions.
            if (known.term[id]) continue;
            known.term[id] = true;

            // Degenerate terms are indexed for all terms
            // (not just significant ones).
            var degens = termops.degens(termsmap[id]);
            for (var j = 0; j < degens.length; j = j+2) {
                var d = degens[j];
                patch.degen[d] = patch.degen[d] || [];
                patch.degen[d].push(degens[j+1]);
            }
        }

        // Generate phrase, clustered by most significant term.
        var phrase = termops.phrase(tokensets[x], termsmap[sigid]);
        patch.phrase[phrase] = patch.phrase[phrase] || terms;
        patch.term[sigid] = patch.term[sigid] || [];
        patch.term[sigid].push(phrase);
        patch.grid[phrase] = patch.grid[phrase] || [];
        patch.grid[phrase].push.apply(patch.grid[phrase], doc._grid);

        // Debug significant term selection.
        if (DEBUG) {
            var debug = termsmap;
            var oldtext = terms.map(function(id) {
                id = id >>> 4 << 4 >>> 0;
                return debug[id];
            }).join(' ');
            var sigtext = debug[sigid];
            if (oldtext !== sigtext)  console.log('%s => %s', oldtext, sigtext);
        }
    }
}

function generateFrequency(docs) {
    var freq = {};

    // Uses freq[0] as a convention for storing total # of docs.
    // @TODO determine whether 0 can really ever be a relevant term
    // when using fnv1a.
    freq[0] = [0];
    for (var i = 0; i < docs.length; i++) {
        var texts = docs[i]._text.split(',');
        for (var x = 0; x < texts.length; x++) {
            var tokens = termops.tokenize(texts[x]);
            var terms = termops.terms(tokens);
            for (var k = 0; k < terms.length; k++) {
                var id = terms[k];
                freq[id] = freq[id] || [0];
                freq[id][0]++;
                freq[0][0]++;
            }
        }
    }

    return freq;
}

// ## Store
//
// Serialize and make permanent the index currently in memory for a source.
function store(source, callback) {
    if (source._geocoder._known) delete source._geocoder._known;

    var tasks = [];

    ['freq','term','phrase','grid','degen'].forEach(loadTerm);

    function loadTerm(type) {
        var cache = source._geocoder;
        tasks = tasks.concat(cache.list(type).map(loadShard));

        function loadShard(shard) {
            var ids = cache.list(type, shard);
            for (var i = 0; i < ids.length; i++) {
                var id = ids[i];
                switch (type) {
                    case 'term':
                    case 'grid':
                    case 'degen':
                        var data = source._geocoder.get(type, id) || [];
                        data.sort();
                        source._geocoder.set(type, id, _(data).uniq(true));
                        break;
                }
            }
            return [type, shard];
        }
    }
    var q = queue(10);
    tasks.forEach(function (task) {
        q.defer(function(task, callback) {
            var type = task[0];
            var shard = task[1];
            var cache = source._geocoder;
            source.putGeocoderData(type, shard, cache.pack(type, shard), callback);
        }, task);
    });
    q.awaitAll(callback);
}
<|endoftext|>


<commit_before>/*
 * ////////////////////////// JSREMEMBER ///////////////////////////////////
 * Simple local storage plugin to save forms data before submitting to the
 * server. It support all major browsers including: IE6+, FF2+, Safari4+,
 * Chrome4+, Android 2+ and Opera 10.5+ thanks to jStorage. And it don't
 * depend on the html element's Name, Id or any CSS Class to be applied.
 *
 * jsRemember requires jStorage from Andris Reinman - www.jstorage.info
 *
 * Copyright(c) 2013 Celso Junior, celsojrfull@gmail.com
 * Plugin demo page: http://celsojr.com/demos/jsRemember/
 *
 * NOTE: The file-input element will not be supported for security reasons.
 *
 * Licensed under MIT-style license:
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

$(function() {
	var sItens = $.jStorage.index();
	if(sItens.length > 0){ //something stored?
	//Get values
		$(':text,:password,textarea').each(function(index, element) {
			var id = 'text'+index;
			if ($.jStorage.get(id.toString()))
				$(element).val($.jStorage.get(id.toString()))
		});

		$('select').each(function(index, element) {
			var id = 'select'+index;
			if ($.jStorage.get(id.toString()))
				$(element).val($.jStorage.get(id.toString()));
		});

		$(':radio,:checkbox').each(function(index, element) {
			var id = 'non-text'+index;
			if ($.jStorage.get(id.toString()))
				$(element).attr('checked','checked');
		});
	}

	//Set values
	$(':text,:password,textarea').change(function() {
		$(':text,:password,textarea').each(function(index, element) {
			var id = 'text'+index;
			if($(element).val() != '')$.jStorage.set(id.toString(),$(element).val());
		});
	});

	$('select').change(function() {
		$('select').each(function(index, element) {
			var id = 'select'+index;
			var sValue = $(element).val();
			if(sValue != '')$.jStorage.set(id.toString(),sValue);
		});

	});

	$(':radio,:checkbox').click(function() {
		$(':radio,:checkbox').each(function(index, element) {
			var id = 'non-text'+index;
			if($(element).is(':checked'))$.jStorage.set(id.toString(),true);
		});
	});

	$('form').submit(function() {
		$.jStorage.flush()
	});
});
<commit_msg>Delete jsRemember.js<commit_after><|endoftext|>


<commit_before>import React from 'react'
import styled from 'styled-components'

const FooterLinks = styled.ul`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  margin: 0;

  list-style-type: none;
`

const Footer = props => (

    <FooterLinks>

          Code de conduite

          2018

          2017

          2016

    </FooterLinks>

)

export default Footer
<commit_msg>add link to 2019<commit_after>import React from 'react'
import styled from 'styled-components'

const FooterLinks = styled.ul`
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  margin: 0;

  list-style-type: none;
`

const Footer = props => (

    <FooterLinks>

          Code de conduite

          2019

          2018

          2017

          2016

    </FooterLinks>

)

export default Footer
<|endoftext|>


<commit_before>import arrayEach from 'lodash/_arrayEach';
import isArray from 'lodash/isArray';
import noop from 'lodash/noop';

import onlyOnce from './onlyOnce';
import setImmediate from './setImmediate';
import DLL from './DoublyLinkedList';

export default function queue(worker, concurrency, payload) {
    if (concurrency == null) {
        concurrency = 1;
    }
    else if(concurrency === 0) {
        throw new Error('Concurrency must not be zero');
    }

    function _insert(data, pos, callback) {
        if (callback != null && typeof callback !== 'function') {
            throw new Error('task callback must be a function');
        }
        q.started = true;
        if (!isArray(data)) {
            data = [data];
        }
        if(data.length === 0 && q.idle()) {
            // call drain immediately if there are no tasks
            return setImmediate(function() {
                q.drain();
            });
        }
        arrayEach(data, function(task) {
            var item = {
                data: task,
                callback: callback || noop
            };

            if (pos) {
                q._tasks.unshift(item);
            } else {
                q._tasks.push(item);
            }

        });
        setImmediate(q.process);
    }

    function _next(tasks) {
        return function(){
            workers -= 1;

            var removed = false;
            var args = arguments;
            arrayEach(tasks, function (task) {
                arrayEach(workersList, function (worker, index) {
                    if (worker === task && !removed) {
                        workersList.splice(index, 1);
                        removed = true;
                    }
                });

                task.callback.apply(task, args);

                if (args[0] != null) {
                    q.error(args[0], task.data);
                }
            });

            if (workers <= (q.concurrency - q.buffer) ) {
                q.unsaturated();
            }

            if (q._tasks.length + workers === 0) {
                q.drain();
            }
            q.process();
        };
    }

    var workers = 0;
    var workersList = [];
    var q = {
        _tasks: new DLL(),
        concurrency: concurrency,
        payload: payload,
        saturated: noop,
        unsaturated:noop,
        buffer: concurrency / 4,
        empty: noop,
        drain: noop,
        error: noop,
        started: false,
        paused: false,
        push: function (data, callback) {
            _insert(data, false, callback);
        },
        kill: function () {
            q.drain = noop;
            q._tasks.empty();
        },
        unshift: function (data, callback) {
            _insert(data, true, callback);
        },
        process: function () {
            while(!q.paused && workers < q.concurrency && q._tasks.length){
                var tasks = [], data = [];
                var l = q._tasks.length;
                if (q.payload) l = Math.min(l, q.payload);
                for (var i = 0; i < l; i++) {
                    var node = q._tasks.shift();
                    tasks.push(node);
                    data.push(node.data);
                }

                if (q._tasks.length === 0) {
                    q.empty();
                }
                workers += 1;
                workersList.push(tasks[0]);

                if (workers === q.concurrency) {
                    q.saturated();
                }

                var cb = onlyOnce(_next(tasks));
                worker(data, cb);
            }
        },
        length: function () {
            return q._tasks.length;
        },
        running: function () {
            return workers;
        },
        workersList: function () {
            return workersList;
        },
        idle: function() {
            return q._tasks.length + workers === 0;
        },
        pause: function () {
            q.paused = true;
        },
        resume: function () {
            if (q.paused === false) { return; }
            q.paused = false;
            var resumeCount = Math.min(q.concurrency, q._tasks.length);
            // Need to call q.process once per concurrent
            // worker to preserve full concurrency after pause
            for (var w = 1; w <= resumeCount; w++) {
                setImmediate(q.process);
            }
        }
    };
    return q;
}
<commit_msg>Optimize queue processing<commit_after>import arrayEach from 'lodash/_arrayEach';
import arraySome from 'lodash/_arraySome';
import isArray from 'lodash/isArray';
import noop from 'lodash/noop';
import rest from 'lodash/rest';

import onlyOnce from './onlyOnce';
import setImmediate from './setImmediate';
import DLL from './DoublyLinkedList';

export default function queue(worker, concurrency, payload) {
    if (concurrency == null) {
        concurrency = 1;
    }
    else if(concurrency === 0) {
        throw new Error('Concurrency must not be zero');
    }

    function _insert(data, pos, callback) {
        if (callback != null && typeof callback !== 'function') {
            throw new Error('task callback must be a function');
        }
        q.started = true;
        if (!isArray(data)) {
            data = [data];
        }
        if(data.length === 0 && q.idle()) {
            // call drain immediately if there are no tasks
            return setImmediate(function() {
                q.drain();
            });
        }
        arrayEach(data, function(task) {
            var item = {
                data: task,
                callback: callback || noop
            };

            if (pos) {
                q._tasks.unshift(item);
            } else {
                q._tasks.push(item);
            }

        });
        setImmediate(q.process);
    }

    function _next(tasks) {
        return rest(function(args){
            workers -= 1;

            arrayEach(tasks, function (task) {
                arraySome(workersList, function (worker, index) {
                    if (worker === task) {
                        workersList.splice(index, 1);
                        return true;
                    }
                });

                task.callback.apply(task, args);

                if (args[0] != null) {
                    q.error(args[0], task.data);
                }
            });

            if (workers <= (q.concurrency - q.buffer) ) {
                q.unsaturated();
            }

            if (q.idle()) {
                q.drain();
            }
            q.process();
        });
    }

    var workers = 0;
    var workersList = [];
    var q = {
        _tasks: new DLL(),
        concurrency: concurrency,
        payload: payload,
        saturated: noop,
        unsaturated:noop,
        buffer: concurrency / 4,
        empty: noop,
        drain: noop,
        error: noop,
        started: false,
        paused: false,
        push: function (data, callback) {
            _insert(data, false, callback);
        },
        kill: function () {
            q.drain = noop;
            q._tasks.empty();
        },
        unshift: function (data, callback) {
            _insert(data, true, callback);
        },
        process: function () {
            while(!q.paused && workers < q.concurrency && q._tasks.length){
                var tasks = [], data = [];
                var l = q._tasks.length;
                if (q.payload) l = Math.min(l, q.payload);
                for (var i = 0; i < l; i++) {
                    var node = q._tasks.shift();
                    tasks.push(node);
                    data.push(node.data);
                }

                if (q._tasks.length === 0) {
                    q.empty();
                }
                workers += 1;
                workersList.push(tasks[0]);

                if (workers === q.concurrency) {
                    q.saturated();
                }

                var cb = onlyOnce(_next(tasks));
                worker(data, cb);
            }
        },
        length: function () {
            return q._tasks.length;
        },
        running: function () {
            return workers;
        },
        workersList: function () {
            return workersList;
        },
        idle: function() {
            return q._tasks.length + workers === 0;
        },
        pause: function () {
            q.paused = true;
        },
        resume: function () {
            if (q.paused === false) { return; }
            q.paused = false;
            var resumeCount = Math.min(q.concurrency, q._tasks.length);
            // Need to call q.process once per concurrent
            // worker to preserve full concurrency after pause
            for (var w = 1; w <= resumeCount; w++) {
                setImmediate(q.process);
            }
        }
    };
    return q;
}
<|endoftext|>


<commit_before>//Global variable initialization

   var s = chrome.storage.sync;

   var bucketNames = [] //bucketNames act as keys to buckets Objects
	//array of Buckets
	var arrBucket = [];

	//test
	/*
	addBucket("tom", "red", "bat");
	 addBucket("Jerry", "Blue", "cat")
	var testList = arrBucket;
	*/

   //load list of existing buckets.
   s.get('BucketList', function(data){
		arrBucket = data.BucketList;
		var n = arrBucket.length;

	});

   //****************************************************************

	/**Adds a bucket to the list of buckets
	   @param {String} bucketName - name of Bucket
	   @param {color} color - color of bucket
	   @param {icon} icon - icon that represents bucket
	   @return {bool} returns true if bucket is successfully made
	*/
	function addBucket(bucketName, color, icon){

		//declares Bucket object
		var Bucket = new Object(),
			name,
			color,
			icon,
			products;

	    Bucket.name = bucketName;
		Bucket.color = color;
		Bucket.icon = icon;
		Bucket.products = [];

		arrBucket.push(Bucket);

		return true;
	}
	/**Returns a list of buckets

	*/
	function getBuckets(){
		return arrBucket;
	}

<commit_msg>Products can now be added to the buckets. Slight issue with Product.getProduct(product); this function doesn't work at the moment, but can be worked around by using Product.getProductList() for the mean time. Storage works for the duration that the app is running, but permanent storage is still not completely functional.<commit_after>//Prototype Product Object
	function Product(url, price, quantity, productName, comment){
			this.url = url;
			this.price = price;
			this.quantity = quantity;
			this.productName = productName;
			this.comment = comment;

			//object behavior
			this.getUrl = function(){
				return this.url;
			};
			this.setUrl = function(url){
				this.url = url;
			};
			this.getPrice = function(){
				return this.price;
			};
			this.setPrice = function(price){
				this.price = price;
			};
			this.getComment = function(){
				return this.comment;
			};
			this.setComment = function(comment){
				this.comment = comment;
			};
			this.getQuantity = function(){
				return this.quantity;
			};
			this.setQuantity = function(quantity){
				this.quantity = quantity;
			};
			this.getProductName = function(){
				return this.productName;
			};
			this.setProductName = function(productName){
				this.productName = productName;
			};
	}

//Prototype Buckey Object
	function Bucket(name, color, icon){
		this.name = name;
		this.color = color;
		this.icon = icon;
		this.products = new Array;

		//Bucket Behavior
		this.getName = function(){
			return 	this.name;
		};
		this.setName = function(name){
			this.name = name;
		};
		this.getColor = function(){
			return this.color;
		};
		this.setColor = function(color){
			this.color = color;
		};
		this.getIcon = function(){
			return this.icon;
		};
		this.setIcon = function(icon){
			this.icon = icon;
		};
		this.getProductList = function(){
			return this.products;
		};
		/**Add a product to the bucket
			@param {String} url - product URL
			@param {Float} price - price of object
			@param {Int} quantity - quantity of object
			@param {String} productName - Name of product
			@param {String} comment - Description of product

			@return {Product} - product created
		*/
		this.addProduct = function(url, price, quantity, productName, comment){
			var temp = new Product(url, price, quantity, productName, comment)
			this.products.push(temp);
			return temp;
		};

		this.getProduct = function(product){
			return this.products.find(product);
		};
		this.removeProduct = function(product){

		};

	}

//Global variable initialization
   var s = chrome.storage.sync;

   var bucketNames = [] //bucketNames act as keys to buckets Objects
	//array of Buckets
	var arrBucket = [];

	//test code
	//addBucket("tom", "red", "bat");
	//addBucket("Jerry", "Blue", "cat");
	var testList = arrBucket;

   //load list of existing buckets.
   s.get('BucketList', function(data){
		arrBucket = data.BucketList;
		var n = arrBucket.length;
		console.log(arrBucket);
	});

	//more test code
	//console.log(arrBucket);
	//arrBucket[0].addProduct("url", 280.57, 2, "couch", "its alright");
	//var testProduct = arrBucket[0].getProductList();
	//console.log(testProduct);
	//console.log(arrBucket[0].getProduct(testProduct[0]));
	//s.set({'BucketList': arrBucket}, function(){})

   //****************************************************************

	/**Adds a bucket to the list of buckets
	   @param {String} bucketName - name of Bucket
	   @param {color} color - color of bucket
	   @param {icon} icon - icon that represents bucket
	*/
	function addBucket(bucketName, color, icon){

		//declares Bucket object
		var bucket = new Bucket(bucketName, color, icon)
		arrBucket.push(bucket);

	}
	/**Returns a list of buckets

	*/
	function getBuckets(){
		return arrBucket;
	}

<|endoftext|>


<commit_before>var sssheet = (function() {
	var style = document.createElement("style");
	style.appendChild(document.createTextNode(""));
	document.head.appendChild(style);
	return style.sheet;
})();

sssheet.insertRule(".adsbygoogle { display: none !important; }", 0);
sssheet.insertRule("iframe { display: none !important; }", 0);
sssheet.insertRule(".pop-ad-cover { display: none !important; }", 0);
<commit_msg>remove fb ad<commit_after>var sssheet = (function() {
	var style = document.createElement("style");
	style.appendChild(document.createTextNode(""));
	document.head.appendChild(style);
	return style.sheet;
})();

sssheet.insertRule(".adsbygoogle { display: none !important; }", 0);
sssheet.insertRule("iframe { display: none !important; }", 0);
sssheet.insertRule(".pop-ad-cover { display: none !important; }", 0);
sssheet.insertRule(".fbAdLink { display: none !important; }", 0);
<|endoftext|>


<commit_before>var ical = require('./ical')
  , request = require('request')
  , fs = require('fs')

exports.fromURL = function(url, opts, cb){
  if (!cb)
    return;
  request(url, opts, function(err, r, data){
    if (err)
      return cb(err, null);
    cb(undefined, ical.parseICS(data));
  })
}

exports.parseFile = function(filename){
  return ical.parseICS(fs.readFileSync(filename, 'utf8'))
}

var rrule = require('rrule').RRule

ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
  curr.rrule = line;
  return curr
}
var originalEnd = ical.objectHandlers['END'];
ical.objectHandlers['END'] = function(val, params, curr, stack){
  if (curr.rrule) {
    var rule = curr.rrule.replace('RRULE:', '');
    if (rule.indexOf('DTSTART') === -1) {
      rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, '');
      rule = rule.replace(/\.[0-9]{3}/, '');
    }
    curr.rrule = rrule.fromString(rule);
  }
  return originalEnd.call(this, val, params, curr, stack);
}
<commit_msg>fix #223<commit_after>var ical = require('./ical')
  , request = require('request')
  , fs = require('fs')

exports.fromURL = function(url, opts, cb){
  if (!cb)
    return;
  request(url, opts, function(err, r, data){
    if (err)
      return cb(err, null);
    cb(undefined, ical.parseICS(data));
  })
}

exports.parseFile = function(filename){
  return ical.parseICS(fs.readFileSync(filename, 'utf8'))
}

var rrule = require('rrule').RRule

ical.objectHandlers['RRULE'] = function(val, params, curr, stack, line){
  curr.rrule = line;
  return curr
}
var originalEnd = ical.objectHandlers['END'];
ical.objectHandlers['END'] = function(val, params, curr, stack){
  if (curr.rrule) {
    var rule = curr.rrule.replace('RRULE:', '');
    if (rule.indexOf('DTSTART') === -1) {

      if (curr.start.length === 8) {
        var comps = /^(\d{4})(\d{2})(\d{2})$/.exec(curr.start);
        if (comps) {
         curr.start = new Date (comps[1], comps[2], comps[3]);
        }
      }

      rule += ';DTSTART=' + curr.start.toISOString().replace(/[-:]/g, '');
      rule = rule.replace(/\.[0-9]{3}/, '');
    }
    curr.rrule = rrule.fromString(rule);
  }
  return originalEnd.call(this, val, params, curr, stack);
}
<|endoftext|>


<commit_before>'use strict';

const { ArrayCache, ArrayCacheBySymbolById } = require ('../../base/Cache')
const assert = require ('assert');

function equals (a, b) {
    if (a.length !== b.length) {
        return false
    }
    for (const prop in a) {
        if (Array.isArray (a[prop]) || typeof a[prop] === 'object') {
            if (!equals (a[prop], b[prop])) {
                return false
            }
        }
        else if (a[prop] !== b[prop]) {
            return false
        }
    }
    return true
}

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

let cache = new ArrayCache (3);

cache.append (1);
cache.append (2);
cache.append (3);
cache.append (4);

assert (equals (cache, [2, 3, 4]));

cache.append (5);
cache.append (6);
cache.append (7);
cache.append (8);

assert (equals (cache, [6, 7, 8]));

cache.clear ();

assert (equals (cache, []));

cache.append (1);

assert (equals (cache, [1]));

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

cache = new ArrayCache (1);

cache.append (1);
cache.append (2);

assert (equals (cache, [2]));

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

cache = new ArrayCacheBySymbolById ();

const object1 = { 'symbol': 'BTC/USDT', 'id': 'abcdef', 'i': 1 };
const object2 = { 'symbol': 'ETH/USDT', 'id': 'qwerty', 'i': 2 };
const object3 = { 'symbol': 'BTC/USDT', 'id': 'abcdef', 'i': 3 };

cache.append (object1);
cache.append (object2);
cache.append (object3); // should update index 0

assert (equals (cache, [ object3, object2 ]));

const maxLength = 5;

cache = new ArrayCacheBySymbolById (maxLength);

for (let i = 1; i <= 10; i++) {
    cache.append ({
        'symbol': 'BTC/USDT',
        'id': i.toString (),
        'i': i,
    });
}

assert (equals (cache, [
    { 'symbol': 'BTC/USDT', 'id': '6', 'i': 6 },
    { 'symbol': 'BTC/USDT', 'id': '7', 'i': 7 },
    { 'symbol': 'BTC/USDT', 'id': '8', 'i': 8 },
    { 'symbol': 'BTC/USDT', 'id': '9', 'i': 9 },
    { 'symbol': 'BTC/USDT', 'id': '10', 'i': 10 },
]));

for (let i = 1; i <= 10; i++) {
    cache.append ({
        'symbol': 'BTC/USDT',
        'id': i.toString (),
        'i': i + 10,
    });
}

assert (equals (cache, [
    { 'symbol': 'BTC/USDT', 'id': '6', 'i': 16 },
    { 'symbol': 'BTC/USDT', 'id': '7', 'i': 17 },
    { 'symbol': 'BTC/USDT', 'id': '8', 'i': 18 },
    { 'symbol': 'BTC/USDT', 'id': '9', 'i': 19 },
    { 'symbol': 'BTC/USDT', 'id': '10', 'i': 20 },
]));
<commit_msg>test.Cache.js minor fix for python range<commit_after>'use strict';

const { ArrayCache, ArrayCacheBySymbolById } = require ('../../base/Cache')
const assert = require ('assert');

function equals (a, b) {
    if (a.length !== b.length) {
        return false
    }
    for (const prop in a) {
        if (Array.isArray (a[prop]) || typeof a[prop] === 'object') {
            if (!equals (a[prop], b[prop])) {
                return false
            }
        }
        else if (a[prop] !== b[prop]) {
            return false
        }
    }
    return true
}

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

let cache = new ArrayCache (3);

cache.append (1);
cache.append (2);
cache.append (3);
cache.append (4);

assert (equals (cache, [2, 3, 4]));

cache.append (5);
cache.append (6);
cache.append (7);
cache.append (8);

assert (equals (cache, [6, 7, 8]));

cache.clear ();

assert (equals (cache, []));

cache.append (1);

assert (equals (cache, [1]));

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

cache = new ArrayCache (1);

cache.append (1);
cache.append (2);

assert (equals (cache, [2]));

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

cache = new ArrayCacheBySymbolById ();

const object1 = { 'symbol': 'BTC/USDT', 'id': 'abcdef', 'i': 1 };
const object2 = { 'symbol': 'ETH/USDT', 'id': 'qwerty', 'i': 2 };
const object3 = { 'symbol': 'BTC/USDT', 'id': 'abcdef', 'i': 3 };

cache.append (object1);
cache.append (object2);
cache.append (object3); // should update index 0

assert (equals (cache, [ object3, object2 ]));

const maxLength = 5;

cache = new ArrayCacheBySymbolById (maxLength);

for (let i = 1; i < 11; i++) {
    cache.append ({
        'symbol': 'BTC/USDT',
        'id': i.toString (),
        'i': i,
    });
}

assert (equals (cache, [
    { 'symbol': 'BTC/USDT', 'id': '6', 'i': 6 },
    { 'symbol': 'BTC/USDT', 'id': '7', 'i': 7 },
    { 'symbol': 'BTC/USDT', 'id': '8', 'i': 8 },
    { 'symbol': 'BTC/USDT', 'id': '9', 'i': 9 },
    { 'symbol': 'BTC/USDT', 'id': '10', 'i': 10 },
]));

for (let i = 1; i < 11; i++) {
    cache.append ({
        'symbol': 'BTC/USDT',
        'id': i.toString (),
        'i': i + 10,
    });
}

assert (equals (cache, [
    { 'symbol': 'BTC/USDT', 'id': '6', 'i': 16 },
    { 'symbol': 'BTC/USDT', 'id': '7', 'i': 17 },
    { 'symbol': 'BTC/USDT', 'id': '8', 'i': 18 },
    { 'symbol': 'BTC/USDT', 'id': '9', 'i': 19 },
    { 'symbol': 'BTC/USDT', 'id': '10', 'i': 20 },
]));
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');

function Representation(data) {
  this.content = data || {};
}

Representation.prototype._putLinksIfAbsent = function () {
  _.defaults(this.content, { _links: {} });
  return this.content._links;
};

Representation.prototype._doAddLink = function (rel, link) {
  this._putLinksIfAbsent();
  this.content._links[rel] = link;
};

Representation.prototype.link = function (rel, href) {
  this._doAddLink(rel, { href: href });
  return this;
};

Representation.prototype.toJSON = function () {
  return this.content;
};

module.exports = {
  representation: function (data) {
    return new Representation(data);
  }
};
<commit_msg>Moving reused JSON Object literal into a separate variable to avoid recreation of the literal object on every method call.<commit_after>'use strict';

var _ = require('lodash');

var DEFAULT_LINKS = { _links: {} };

function Representation(data) {
  this.content = data || {};
}

Representation.prototype._putLinksIfAbsent = function () {
  _.defaults(this.content, DEFAULT_LINKS);
  return this.content._links;
};

Representation.prototype._doAddLink = function (rel, link) {
  this._putLinksIfAbsent();
  this.content._links[rel] = link;
};

Representation.prototype.link = function (rel, href) {
  this._doAddLink(rel, { href: href });
  return this;
};

Representation.prototype.toJSON = function () {
  return this.content;
};

module.exports = {
  representation: function (data) {
    return new Representation(data);
  }
};
<|endoftext|>


<commit_before>/* @flow */
import React, { createFactory, createClass, DOM as e, PropTypes as t } from 'react'
import { calcBounds } from './utils'
import Debug from 'debug'
import addResizeListener from 'element-resize-event'

let log = Debug('react-popover')

let bodyStyle = {
  display: 'inline-flex',
  flexDirection: 'column',
  padding: '4rem'
}

let containerStyle = {
  position: 'absolute',
  top: 0,
  left: 0,
  display: 'flex',
  'WebkitTransitionProperty': 'top, left',
  'WebkitTransitionDuration': '200ms',
  'WebkitTransitionEasingFunction': 'ease-in'
}

let tipTranslateFuncDict = { row: 'translateY', column: 'translateX' }

let axes = {}

axes.row = {
  main: { start: 'x', end: 'x2', size: 'w' },
  cross: { start: 'y', end: 'y2', size: 'h' }
}

axes.column = {
  main: axes.row.cross,
  cross: axes.row.main
}

export default createClass({
  name: 'popover',
  propTypes: {
    lockPoint: t.string.isRequired,
    children: t.element.isRequired,
    tipSize: t.number.isOptional
  },
  getInitialState() {
    return {
      standing: 'above'
    }
  },
  getDefaultProps() {
    return {
      tipSize: 7,
      offset: 4
    }
  },
  checkForLockPositionChange() {
    if (this.measureLockBounds()) this.resolvePopoverLayout()
  },
  resolvePopoverLayout() {

    let pos = { }

    /* Find the optimal zone to position self. Measure the size of each zone and use the one with
    the greatest area. */

    let zone = resolveZoneChoice(this.p2, this.frameBounds)

    this.setState({
      standing: zone.standing
    })

    let flexFlow = zone.orientation === 'landscape' ? 'row' : 'column'
    let isBefore = zone.order === 'before'
    let dir = isBefore ? -1 : 1
    let axis = axes[flexFlow]

    log('zone', zone)
    log('axes', axis)

    let mainLength = this.size[axis.main.size]
    let lockMainStart = this.p2[axis.main.start]
    let lockMainEnd = this.p2[axis.main.end]

    /* When positioning self on the cross-axis do not exceed frame bounds. The strategy to achieve
    this is thus: First position cross-axis self to the cross-axis-center of the the lock. Then,
    offset self by the amount that self is past the boundaries of frame. */

    pos[axis.main.start] = (

      /* Unfortunately we cannot seem to get around the base fact that layout expands in a direction
      which happens to be leftward, and thus in the before-case requires an extra step of offsetting
      all the leftware-expansion (AKA the length of the element). TODO: We could potentially have
      helper functions like positionBefore / after etc. to abstract this complexity away. */
      (isBefore ? lockMainStart - mainLength : lockMainEnd)

      // offset allows users to control the distance betweent the tip and the lock.
      + (this.props.offset * dir)
    )
    pos[axis.cross.start] = center('cross', flexFlow, this.p2) - (this.size[axis.cross.size] / 2)
    pos.x2 = pos.x + this.size.w
    pos.y2 = pos.y + this.size.h

    /* Constrain containerEl Position */

    if (pos[axis.cross.start] < this.frameBounds[axis.cross.start]) {
      pos[axis.cross.start] = this.frameBounds[axis.cross.start]
    } else if (pos[axis.cross.end] > this.frameBounds[axis.cross.end]) {
      pos[axis.cross.start] = pos[axis.cross.start] - (pos[axis.cross.end] - this.frameBounds[axis.cross.end])
    }

    /* Apply Layout */

    this.containerEl.style.top = pos.y
    this.containerEl.style.left = pos.x

    this.bodyEl.style.order = isBefore ? 0 : 1
    this.containerEl.style.flexFlow = flexFlow

    /* Calculate Tip Center */

    let tipCrossPos = (
      center('cross', flexFlow, this.p2)

      /* We do not have to calcualte half-of-tip-size since tip-size specifies
      the length from base to tip which is half of total length already. */

      - this.props.tipSize

      /* Because tip is positioned by the browser relative containerEl but
      targeting absolute center of lock we need to cancel the containerEl
      positioning so as to hit our intended position. */

      - pos[axis.cross.start]
    )

    this.tipEl.style.transform = `${tipTranslateFuncDict[flexFlow]}(${tipCrossPos}px)`

    /* Record fact that repaint is synced. */

    this.p1 = this.p2
  },
  measurePopoverSize() {
    this.size = {
      w: this.containerEl.offsetWidth,
      h: this.containerEl.offsetHeight
    }
  },
  measureLockBounds() {
    let np = calcBounds(this.lockEl)
    // log('doCheck: Current position:', p, this.p1, this.p2)
    if (!this.p1) {
      this.p2 = np
    } else if (!equalCoords(np, this.p2)) {
      this.p1 = this.p2
      this.p2 = np
    }
    return this.p1 !== this.p2
  },
  componentDidMount() {
    this.containerEl = React.findDOMNode(this)
    this.bodyEl = this.containerEl.querySelector('.Popover-body')
    this.tipEl = this.containerEl.querySelector('.Popover-tip')
    this.lockEl = React.findDOMNode(this.props.children).querySelector(this.props.lockPoint)
    this.frameEl = window

    this.measurePopoverSize()
    this.measureFrameBounds()
    this.measureLockBounds()
    this.resolvePopoverLayout()

    this.checkLayoutInterval = setInterval(this.checkForLockPositionChange, 200)
    addResizeListener(this.containerEl, this.onPopoverSizeChange)
    addResizeListener(this.lockEl, this.onLockSizeChange)
    // TODO: When frame is not window use element-resize function instead.
    window.addEventListener('resize', this.onFrameBoundsChange)
  },
  onLockSizeChange() {
    log('Recalculating layout because _lock_ size changed!')
    this.measureLockBounds()
    this.resolvePopoverLayout()
  },
  onPopoverSizeChange() {
    log('Recalculating layout because _popover_ size changed!')
    this.measurePopoverSize()
    this.resolvePopoverLayout()
  },
  onFrameBoundsChange() {
    log('Recalculating layout because _frame_ bounds changed!')
    this.measureFrameBounds()
    this.resolvePopoverLayout()
  },
  measureFrameBounds() {
    this.frameBounds = calcBounds(this.frameEl)
  },
  componentWillUnmount() {
    clearInterval(this.checkLayoutInterval)
  },
  render() {
    log('React render.')
    let { standing } = this.state
    let faces = { above: 'down', right: 'left', below: 'up', left: 'right' }
    let tipDirection = faces[standing]

    return (
      e.div({
        className: 'Popover',
        style: containerStyle
        },
          e.div({
            className: 'Popover-body',
            style: bodyStyle
            },
            e.h1({}, 'Popover Title'),
            e.div({}, 'Popover contents.')
          ),
          PopoverTip({
            size: this.props.tipSize,
            style: {
            },
            direction: tipDirection
          })
      )
    )
  }
})

let PopoverTip = createFactory(createClass({
  name: 'tip',
  render() {

    let { direction } = this.props
    let size = this.props.size || 24
    let isPortrait = direction === 'up' || direction === 'down'
    let mainLength = size
    let crossLength = size * 2
    let points = (
      direction === 'up' ? `0,${mainLength} ${mainLength},0, ${crossLength},${mainLength}`
      : direction === 'down' ? `0,0 ${mainLength},${mainLength}, ${crossLength},0`
      : direction === 'left' ? `${mainLength},0 0,${mainLength}, ${mainLength},${crossLength}`
      : `0,0 ${mainLength},${mainLength}, 0,${crossLength}`
     )

    let triangle = e.svg({
      className: 'Popover-tip',
      width: isPortrait ? crossLength : mainLength,
      height: isPortrait ? mainLength : crossLength,
      style: {
        transition: 'transform 150ms ease-in'
      }
    },
      e.polygon({
        className: 'Popover-tipShape',
        points
      })
    )

    return (
      triangle
    )
  }
}))

/* Private */

function resolveZoneChoice (lockBounds, frameBounds) {
  let l = lockBounds, f = frameBounds
  return [
    { standing: 'above', orientation: 'portrait', order: 'before', w: f.x2, h: l.y },
    { standing: 'right', orientation: 'landscape', order: 'after', w: (f.x2 - l.x2), h: f.y2 },
    { standing: 'below', orientation: 'portrait', order: 'after', w: f.x2, h: (f.y2 - l.y2) },
    { standing: 'left', orientation: 'landscape', order: 'before', w: l.x, h: f.y2 }
  ]
  .reduce((b1, b2) => area(b1) > area(b2) ? b1 : b2)
}

function area (bounds) {
  return bounds.w * bounds.h
}

function equalCoords(c1, c2) {
  for (var key in c1) if (c1[key] !== c2[key]) return false
  return true
}

function center(axis, flowDirection, bounds) {
  return bounds[axes[flowDirection][axis].start] + (bounds[axes[flowDirection][axis].size] / 2)
}

<commit_msg>Fix setting of top/left styles<commit_after>/* @flow */
import React, { createFactory, createClass, DOM as e, PropTypes as t } from 'react'
import { calcBounds } from './utils'
import Debug from 'debug'
import addResizeListener from 'element-resize-event'

let log = Debug('react-popover')

let bodyStyle = {
  display: 'inline-flex',
  flexDirection: 'column',
  padding: '4rem'
}

let containerStyle = {
  position: 'absolute',
  top: 0,
  left: 0,
  display: 'flex',
  'WebkitTransitionProperty': 'top, left',
  'WebkitTransitionDuration': '200ms',
  'WebkitTransitionEasingFunction': 'ease-in'
}

let tipTranslateFuncDict = { row: 'translateY', column: 'translateX' }

let axes = {}

axes.row = {
  main: { start: 'x', end: 'x2', size: 'w' },
  cross: { start: 'y', end: 'y2', size: 'h' }
}

axes.column = {
  main: axes.row.cross,
  cross: axes.row.main
}

export default createClass({
  name: 'popover',
  propTypes: {
    lockPoint: t.string.isRequired,
    children: t.element.isRequired,
    tipSize: t.number.isOptional
  },
  getInitialState() {
    return {
      standing: 'above'
    }
  },
  getDefaultProps() {
    return {
      tipSize: 7,
      offset: 4
    }
  },
  checkForLockPositionChange() {
    if (this.measureLockBounds()) this.resolvePopoverLayout()
  },
  resolvePopoverLayout() {

    let pos = { }

    /* Find the optimal zone to position self. Measure the size of each zone and use the one with
    the greatest area. */

    let zone = resolveZoneChoice(this.p2, this.frameBounds)

    this.setState({
      standing: zone.standing
    })

    let flexFlow = zone.orientation === 'landscape' ? 'row' : 'column'
    let isBefore = zone.order === 'before'
    let dir = isBefore ? -1 : 1
    let axis = axes[flexFlow]

    log('zone', zone)
    log('axes', axis)

    let mainLength = this.size[axis.main.size]
    let lockMainStart = this.p2[axis.main.start]
    let lockMainEnd = this.p2[axis.main.end]

    /* When positioning self on the cross-axis do not exceed frame bounds. The strategy to achieve
    this is thus: First position cross-axis self to the cross-axis-center of the the lock. Then,
    offset self by the amount that self is past the boundaries of frame. */

    pos[axis.main.start] = (

      /* Unfortunately we cannot seem to get around the base fact that layout expands in a direction
      which happens to be leftward, and thus in the before-case requires an extra step of offsetting
      all the leftware-expansion (AKA the length of the element). TODO: We could potentially have
      helper functions like positionBefore / after etc. to abstract this complexity away. */
      (isBefore ? lockMainStart - mainLength : lockMainEnd)

      // offset allows users to control the distance betweent the tip and the lock.
      + (this.props.offset * dir)
    )
    pos[axis.cross.start] = center('cross', flexFlow, this.p2) - (this.size[axis.cross.size] / 2)
    pos.x2 = pos.x + this.size.w
    pos.y2 = pos.y + this.size.h

    /* Constrain containerEl Position */

    if (pos[axis.cross.start] < this.frameBounds[axis.cross.start]) {
      pos[axis.cross.start] = this.frameBounds[axis.cross.start]
    } else if (pos[axis.cross.end] > this.frameBounds[axis.cross.end]) {
      pos[axis.cross.start] = pos[axis.cross.start] - (pos[axis.cross.end] - this.frameBounds[axis.cross.end])
    }

    /* Apply Layout */

    log('pos', pos)
    this.containerEl.style.top = `${pos.y}px`
    this.containerEl.style.left = `${pos.x}px`
    this.bodyEl.style.order = isBefore ? 0 : 1
    this.containerEl.style.flexFlow = flexFlow

    /* Calculate Tip Center */

    let tipCrossPos = (
      center('cross', flexFlow, this.p2)

      /* We do not have to calcualte half-of-tip-size since tip-size specifies
      the length from base to tip which is half of total length already. */

      - this.props.tipSize

      /* Because tip is positioned by the browser relative containerEl but
      targeting absolute center of lock we need to cancel the containerEl
      positioning so as to hit our intended position. */

      - pos[axis.cross.start]
    )

    this.tipEl.style.transform = `${tipTranslateFuncDict[flexFlow]}(${tipCrossPos}px)`

    /* Record fact that repaint is synced. */

    this.p1 = this.p2
  },
  measurePopoverSize() {
    this.size = {
      w: this.containerEl.offsetWidth,
      h: this.containerEl.offsetHeight
    }
  },
  measureLockBounds() {
    let np = calcBounds(this.lockEl)
    // log('doCheck: Current position:', p, this.p1, this.p2)
    if (!this.p1) {
      this.p2 = np
    } else if (!equalCoords(np, this.p2)) {
      this.p1 = this.p2
      this.p2 = np
    }
    return this.p1 !== this.p2
  },
  componentDidMount() {
    this.containerEl = React.findDOMNode(this)
    this.bodyEl = this.containerEl.querySelector('.Popover-body')
    this.tipEl = this.containerEl.querySelector('.Popover-tip')
    this.lockEl = React.findDOMNode(this.props.children).querySelector(this.props.lockPoint)
    this.frameEl = window

    this.measurePopoverSize()
    this.measureFrameBounds()
    this.measureLockBounds()
    this.resolvePopoverLayout()

    this.checkLayoutInterval = setInterval(this.checkForLockPositionChange, 200)
    addResizeListener(this.containerEl, this.onPopoverSizeChange)
    addResizeListener(this.lockEl, this.onLockSizeChange)
    // TODO: When frame is not window use element-resize function instead.
    window.addEventListener('resize', this.onFrameBoundsChange)
  },
  onLockSizeChange() {
    log('Recalculating layout because _lock_ size changed!')
    this.measureLockBounds()
    this.resolvePopoverLayout()
  },
  onPopoverSizeChange() {
    log('Recalculating layout because _popover_ size changed!')
    this.measurePopoverSize()
    this.resolvePopoverLayout()
  },
  onFrameBoundsChange() {
    log('Recalculating layout because _frame_ bounds changed!')
    this.measureFrameBounds()
    this.resolvePopoverLayout()
  },
  measureFrameBounds() {
    this.frameBounds = calcBounds(this.frameEl)
  },
  componentWillUnmount() {
    clearInterval(this.checkLayoutInterval)
  },
  render() {
    log('React render.')
    let { standing } = this.state
    let faces = { above: 'down', right: 'left', below: 'up', left: 'right' }
    let tipDirection = faces[standing]

    return (
      e.div({
        className: 'Popover',
        style: containerStyle
        },
          e.div({
            className: 'Popover-body',
            style: bodyStyle
            },
            e.h1({}, 'Popover Title'),
            e.div({}, 'Popover contents.')
          ),
          PopoverTip({
            size: this.props.tipSize,
            style: {
            },
            direction: tipDirection
          })
      )
    )
  }
})

let PopoverTip = createFactory(createClass({
  name: 'tip',
  render() {

    let { direction } = this.props
    let size = this.props.size || 24
    let isPortrait = direction === 'up' || direction === 'down'
    let mainLength = size
    let crossLength = size * 2
    let points = (
      direction === 'up' ? `0,${mainLength} ${mainLength},0, ${crossLength},${mainLength}`
      : direction === 'down' ? `0,0 ${mainLength},${mainLength}, ${crossLength},0`
      : direction === 'left' ? `${mainLength},0 0,${mainLength}, ${mainLength},${crossLength}`
      : `0,0 ${mainLength},${mainLength}, 0,${crossLength}`
     )

    let triangle = e.svg({
      className: 'Popover-tip',
      width: isPortrait ? crossLength : mainLength,
      height: isPortrait ? mainLength : crossLength,
      style: {
        transition: 'transform 150ms ease-in'
      }
    },
      e.polygon({
        className: 'Popover-tipShape',
        points
      })
    )

    return (
      triangle
    )
  }
}))

/* Private */

function resolveZoneChoice (lockBounds, frameBounds) {
  let l = lockBounds, f = frameBounds
  return [
    { standing: 'above', orientation: 'portrait', order: 'before', w: f.x2, h: l.y },
    { standing: 'right', orientation: 'landscape', order: 'after', w: (f.x2 - l.x2), h: f.y2 },
    { standing: 'below', orientation: 'portrait', order: 'after', w: f.x2, h: (f.y2 - l.y2) },
    { standing: 'left', orientation: 'landscape', order: 'before', w: l.x, h: f.y2 }
  ]
  .reduce((b1, b2) => area(b1) > area(b2) ? b1 : b2)
}

function area (bounds) {
  return bounds.w * bounds.h
}

function equalCoords(c1, c2) {
  for (var key in c1) if (c1[key] !== c2[key]) return false
  return true
}

function center(axis, flowDirection, bounds) {
  return bounds[axes[flowDirection][axis].start] + (bounds[axes[flowDirection][axis].size] / 2)
}

<|endoftext|>


<commit_before>var map = {
    map_hash: null,
    fow_hash: null,
    unit_hash: null,
    note_hash: null,
    maps: [],
    showing: false,
    loading: {
        features: true,
        types: true,
        units: true,
        fow: true
    }
};

map.init = function(){
    cursed.modules.live.push(map);
    cursed.modules.interactive.push(map);
    cursed.modules.text_display.push(map);

    cursed.client.subscribe("get.map", (data)=>{
        data = data.payload;
        cursed.viewport.updateBounds(data["max_x"], data["max_y"]);
        cursed.viewport.updateFeatures(data["features"]);
        map.loading.features = false;
        if(!map.is_loading()){
            viewport.dirty = true;
            viewport.clear();
            viewport.draw();
        }
    })

    cursed.client.subscribe("get.map.fow", (data)=>{
        cursed.viewport.updateFow(data.payload);
        map.loading.fow = false;
        if(!map.is_loading()){
            viewport.dirty = true;
            viewport.clear();
            viewport.draw();
        }
    })

    cursed.client.subscribe("get.map.units", (data)=>{
        cursed.viewport.updateUnits(data.payload);
        map.loading.units = false;
        if(!cursed.viewer.animation_running){
            //prevents a bug where the animation is still
            //running when the sl is drawn
            cursed.status_line.draw();
            if(!map.is_loading()){
                viewport.dirty = true;
                viewport.clear();
                viewport.draw();
            }
        }
    })

    cursed.client.subscribe("list.maps", (data)=>{
        map.maps = data.payload;
        if(map.showing){
            map.show_maps();
        }
    });

    cursed.client.ack("move.user", (data)=>{
        map.load_map();
    });

    cursed.client.subscribe("add.map.unit", (data)=>{
        cursed.viewport.units.push(data.details);

        cursed.viewport.dirty = true;
        cursed.viewport.draw();

        cursed.status_line.dirty = true;
        cursed.status_line.draw();

        cursed.command_window.dirty = true;
        cursed.command_window.draw();
    });

    cursed.client.subscribe("modify.map.unit", (data)=>{
        var mod_unit = data.details;
        for(var i=0; i<cursed.viewport.units.length; i++){
            var unit = cursed.viewport.units[i];
            if(unit["id"] == mod_unit.id){
                cursed.viewport.units[i] = mod_unit;

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();
            }
        }
    });

    cursed.client.subscribe("remove.map.unit", (data)=>{
        var unit_id = data.details.id;
        for(var i=0; i<cursed.viewport.units.length; i++){
            var unit = cursed.viewport.units[i];
            if(unit["id"] == unit_id){
                cursed.viewport.units.splice(i, 1);

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();

                cursed.command_window.dirty = true;
                cursed.command_window.draw();
                break;
            }
        }
    })

    cursed.client.subscribe("modify.map.unit", (data)=>{
        var units = cursed.viewport.units;
        for(var unit of units){
            if(unit.id == data.details.id){
                unit.x = data.details.x;
                unit.y = data.details.y;
                unit.max_health = data.details.max_health;
                unit.current_health = data.details.current_health;
                unit.controller = data.details.controller;
                unit.type = data.details.type;
                unit.name = data.details.name;

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();
            }
        }
    });

    cursed.client.subscribe("add.map.feature", (data)=>{
        var features = cursed.viewport.features;
        features.push(data.details);
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.subscribe("remove.map.feature", (data)=>{
        var features = cursed.viewport.features;
        for(feature of features){
            if(feature.x === data.details.x &&
                feature.y === data.details.y){

                var index = features.indexOf(feature);
                cursed.viewport.features.splice(index, 1);

                cursed.viewport.dirty = true;
                cursed.viewport.draw();
                return;
            }
        }
    });

    cursed.client.subscribe("add.map.fow", (data)=>{
        var new_fow = data.details;
        cursed.viewport.fow[new_fow.x][new_fow.y] = true;
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.subscribe("remove.map.fow", (data)=>{
        var new_fow = data.details;
        cursed.viewport.fow[new_fow.x][new_fow.y] = false;
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.registerInitHook(()=>{
        map.load_map();
        map.get_maps();
    });

}

map.update = function(hashes){
}

map.handle = function(e){
    if(cursed.state.role === "gm"){
        if(e.key === "m" && cursed.command_window.mode === cursed.command_window.command_modes.default){
            map.show();
        }
    }
};

map.handle_help = function(buff){

}

map.load_map = function(){

    map.loading = {
        features: true,
        types: true,
        units: true,
        fow: true
    }

    cursed.client.send({
        type: "command",
        key: "get.map.feature.types"
    });

    cursed.client.send({
        type: "command",
        key: "get.map"
    });

    cursed.client.send({
        type: "command",
        key: "get.map.fow"
    });

    cursed.client.send({
        type: "command",
        key: "get.map.units"
    });

    cursed.client.send({
        type: "command",
        key: "get.users"
    });
}

map.handle_combo = function(buff){
    buff = buff.split(" ");

    if(buff[0] === "map" || buff[0] === "m" || buff[0] === "maps"){

        if(buff.length == 5 && (buff[1] === "add" || buff[1] === "a")){
            var name = buff[2];
            var height = parseInt(buff[3]);
            var width = parseInt(buff[4]);

        }
        else if(buff.length == 4 && (buff[1] === "move" || buff[1] === "m")){
            user_to_switch = buff[2];
            map_to_switch = buff[3];

            var valid_map = false;
            for(var map_name of map.maps){
                if(map_name == map_to_switch){
                    valid_map = true;
                    break
                }
            }

            if(valid_map){
                usernames = cursed.users.users.map((user)=>{
                    return user.username;
                });

                if(user_to_switch === "*"){
                    var frames = []
                    for(var username of usernames){
                        frames.push({
                            type: "command",
                            key: "move.user",
                            details: {
                                username: username,
                                map_name: map_to_switch
                            }
                        });
                    }
                    cursed.client.sendBulk(frames);
                }
                else if(usernames.indexOf(user_to_switch) > -1){
                    cursed.client.send({
                        type: "command",
                        key: "move.user",
                        details: {
                            username: user_to_switch,
                            map_name: map_to_switch
                        }
                    });
                }
            }
        }

        else if (buff.length == 2 && (buff[1] === "features" || buff[1] === "f")){
            var el = $("#features_menu");
            el.dialog({
                resizable: true,
                height: "auto",
                width: "75%",
                modal: true,
                open: function(){
                    // pause keypress handling
                    cursed.viewer.editor_open = true;
                    $("#features_menu").blur();
                },
                close: function(){
                    // allow client to handle keypresses again
                    cursed.viewer.editor_open = false;
                },
                buttons: {
                    "Close": function(){
                        $(this).dialog("close");
                    }
                }
            });
        }
        else if(buff.length > 3
            && (buff[1] == "feature" || buff[1] === "f")
            && (buff[2] == "pack" || buff[2] === "p")){

            if(buff[3] == "list" || buff[3] == "l"){
                var el = $("#feature_pack_menu");

                var ul = $("#feature_pack_menu_list");

                ul.empty();

                for(var pack of cursed.features.packs){
                    var node = $("

")
                        .append(
                            $("
" + pack.name + "
")
                        );

                    var node_ul = $("

");
                    for(var t of pack.types){
                        node_ul.append($("
").text(t.name));
                    }

                    node.append(node_ul);
                    ul.append(node);
                }

                el.dialog({
                    resizable: true,
                    height: "auto",
                    width: "75%",
                    modal: true,
                    open: function(){
                        // pause keypress handling
                        cursed.viewer.editor_open = true;
                        $("#feature_pack_menu").blur();
                    },
                    close: function(){
                        // allow client to handle keypresses again
                        cursed.viewer.editor_open = false;
                    },
                    buttons: {
                        "Close": function(){
                            $(this).dialog("close");
                        }
                    }
                });
            }
            else if(buff[3] === "apply" || buff[3] === "a"){

                var pack = null;

                for(var i=0; i<cursed.features.packs.length; i++){
                    if(buff[4].toUpperCase() == cursed.features.packs[i].name.toUpperCase()){
                        pack = cursed.features.packs[i];
                        break;
                    }
                }
                if(pack !== null){

                    var payloads = [];
                    for(var feature_type of pack.types){
                        payloads.push({
                            "type": "command",
                            "key": "add.map.feature.type",
                            "details": feature_type
                        });
                    }

                    cursed.client.sendBulk(payloads, true);
                }
            }
            else if(buff[3] === "remove" || buff[3] === "r"){ /* Remove Feature Pack Types */
                for(var i=0; i<cursed.features.packs.length; i++){
                    if(buff[4].toUpperCase() == cursed.features.packs[i].name.toUpperCase()){
                        pack = cursed.features.packs[i];
                        break;
                    }
                }
                if(pack !== null){
                    var payloads = [];
                    for(var feature_type of pack.types){
                        payloads.push({
                            "type": "command",
                            "key": "remove.map.feature.type",
                            "details": {
                                "name": feature_type.name
                            }
                        });
                    }

                    cursed.client.sendBulk(payloads, true);
                }
            }
        }
        else if(buff.length === 4
            && (buff[1] === "feature" || buff[1] === "f")
            && (buff[2] === "rm" || buff[2] === "r")){
            for(var feature of features.objects){
                if(feature.name === buff[3]){
                    cursed.client.send({
                        type: "command",
                        key: "remove.map.feature.type",
                        details:{
                            "name": name
                        }
                    }, true);

                    break;
                }
            }
        }
        else if(buff.length === 3
            && (buff[1] === "feature" || buff[1] === "f")
            && (buff[2] === "add" || buff[2] === "a")){

            var el = $("#features_add_menu");
            el.dialog({
                resizable: true,
                height: "auto",
                width: "auto",
                modal: true,
                open: function(){
                    // pause keypress handling
                    cursed.viewer.editor_open = true;
                    $("#features_add_menu").blur();
                },
                close: function(){
                    // allow client to handle keypresses again
                    cursed.viewer.editor_open = false;
                },
                buttons: {
                    "Add": function(){

                        var feature_name = $("#feature_name");
                        name = feature_name.val();
                        if(name === "" || name === null || name === undefined){
                            return;
                        }

                        var feature_key = $("#feature_key");
                        key = feature_key.val();
                        if(key === "" || key === null || key === undefined){
                            return;
                        }

                        var feature_character = $("#feature_character");
                        character = feature_character.val();
                        if(character === "" || character === null || character === undefined){
                            return;
                        }

                        var feature_color = $("#feature_color");
                        color = feature_color.val();
                        if(color === "" || color === null || color === undefined){
                            return;
                        }

                        cursed.client.send({
                            type: "command",
                            key: "add.map.feature.type",
                            details:{
                                "name": name,
                                "key": key,
                                "character": character,
                                "color": color
                            }
                        }, true);

                        $(this).dialog("close");
                    },
                    "Close": function(){
                        $(this).dialog("close");
                    }
                }
            });

        }
    }
}

map.show = function(){
    cursed.modules.text_display.map((e)=>{e.hide()});
    map.showing = true;
    map.get_maps()
    map.show_maps()
}

map.hide = function(){
    map.showing = false;
}

map.get_maps = function(){
    cursed.client.send({
        type: "command",
        key: "list.maps"
    });
};

map.show_maps = function(){
    lines = [ [{
        text: "Maps",
        color: "Gold"
    }] ];

    for(var map_name of map.maps){
        lines.push([{
            text: map_name,
            color: "Light Blue"
        }]);

        for(var user of cursed.users.users){
            if(user.current_map === map_name){
                lines.push([{
                    text: "- " + user.username,
                    color: null
                }]);
            }
        }
    }

    cursed.text_box.set(lines);
}

map.is_loading = function(){
    return (
        map.loading.fow ||
        map.loading.features ||
        map.loading.types ||
        map.loading.units );
}
<commit_msg>closes #16<commit_after>var map = {
    map_hash: null,
    fow_hash: null,
    unit_hash: null,
    note_hash: null,
    maps: [],
    showing: false,
    loading: {
        features: true,
        types: true,
        units: true,
        fow: true
    }
};

map.init = function(){
    cursed.modules.live.push(map);
    cursed.modules.interactive.push(map);
    cursed.modules.text_display.push(map);

    cursed.client.subscribe("get.map", (data)=>{
        data = data.payload;
        cursed.viewport.updateBounds(data["max_x"], data["max_y"]);
        cursed.viewport.updateFeatures(data["features"]);
        map.loading.features = false;
        if(!map.is_loading()){
            viewport.dirty = true;
            viewport.clear();
            viewport.draw();
        }
    })

    cursed.client.subscribe("get.map.fow", (data)=>{
        cursed.viewport.updateFow(data.payload);
        map.loading.fow = false;
        if(!map.is_loading()){
            viewport.dirty = true;
            viewport.clear();
            viewport.draw();
        }
    })

    cursed.client.subscribe("get.map.units", (data)=>{
        cursed.viewport.updateUnits(data.payload);
        map.loading.units = false;
        if(!cursed.viewer.animation_running){
            //prevents a bug where the animation is still
            //running when the sl is drawn
            cursed.status_line.draw();
            if(!map.is_loading()){
                viewport.dirty = true;
                viewport.clear();
                viewport.draw();
            }
        }
    })

    cursed.client.subscribe("list.maps", (data)=>{
        map.maps = data.payload;
        if(map.showing){
            map.show_maps();
        }
    });

    cursed.client.ack("move.user", (data)=>{
        map.load_map();
    });

    cursed.client.subscribe("add.map.unit", (data)=>{
        cursed.viewport.units.push(data.details);

        cursed.viewport.dirty = true;
        cursed.viewport.draw();

        cursed.status_line.dirty = true;
        cursed.status_line.draw();

        cursed.command_window.dirty = true;
        cursed.command_window.draw();
    });

    cursed.client.subscribe("modify.map.unit", (data)=>{
        var mod_unit = data.details;
        for(var i=0; i<cursed.viewport.units.length; i++){
            var unit = cursed.viewport.units[i];
            if(unit["id"] == mod_unit.id){
                cursed.viewport.units[i] = mod_unit;

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();
            }
        }
    });

    cursed.client.subscribe("remove.map.unit", (data)=>{
        var unit_id = data.details.id;
        for(var i=0; i<cursed.viewport.units.length; i++){
            var unit = cursed.viewport.units[i];
            if(unit["id"] == unit_id){
                cursed.viewport.units.splice(i, 1);

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();

                cursed.command_window.dirty = true;
                cursed.command_window.draw();
                break;
            }
        }
    })

    cursed.client.subscribe("modify.map.unit", (data)=>{
        var units = cursed.viewport.units;
        for(var unit of units){
            if(unit.id == data.details.id){
                unit.x = data.details.x;
                unit.y = data.details.y;
                unit.max_health = data.details.max_health;
                unit.current_health = data.details.current_health;
                unit.controller = data.details.controller;
                unit.type = data.details.type;
                unit.name = data.details.name;

                cursed.viewport.dirty = true;
                cursed.viewport.draw();

                cursed.status_line.dirty = true;
                cursed.status_line.draw();
            }
        }
    });

    cursed.client.subscribe("add.map.feature", (data)=>{
        var features = cursed.viewport.features;
        features.push(data.details);
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.subscribe("remove.map.feature", (data)=>{
        var features = cursed.viewport.features;
        for(feature of features){
            if(feature.x === data.details.x &&
                feature.y === data.details.y){

                var index = features.indexOf(feature);
                cursed.viewport.features.splice(index, 1);

                cursed.viewport.dirty = true;
                cursed.viewport.draw();
                return;
            }
        }
    });

    cursed.client.subscribe("add.map.fow", (data)=>{
        var new_fow = data.details;
        cursed.viewport.fow[new_fow.x][new_fow.y] = true;
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.subscribe("remove.map.fow", (data)=>{
        var new_fow = data.details;
        cursed.viewport.fow[new_fow.x][new_fow.y] = false;
        cursed.viewport.dirty = true;
        cursed.viewport.draw();
    });

    cursed.client.registerInitHook(()=>{
        map.load_map();
        map.get_maps();
    });

}

map.update = function(hashes){
}

map.handle = function(e){
    if(cursed.state.role === "gm"){
        if(e.key === "m" && cursed.command_window.mode === cursed.command_window.command_modes.default){
            map.show();
        }
    }
};

map.handle_help = function(buff){

}

map.load_map = function(){

    map.loading = {
        features: true,
        types: true,
        units: true,
        fow: true
    }

    cursed.client.send({
        type: "command",
        key: "get.map.feature.types"
    });

    cursed.client.send({
        type: "command",
        key: "get.map"
    });

    cursed.client.send({
        type: "command",
        key: "get.map.fow"
    });

    cursed.client.send({
        type: "command",
        key: "get.map.units"
    });

    cursed.client.send({
        type: "command",
        key: "get.users"
    });
}

map.handle_combo = function(buff){
    buff = buff.split(" ");

    if(buff[0] === "map" || buff[0] === "m" || buff[0] === "maps"){

        if(buff.length >= 5 && (buff[1] === "add" || buff[1] === "a")){
            var height = parseInt(buff[2]);
            var width = parseInt(buff[3]);
            var name = buff.slice(4, buff.length).join(" ");

            if(height > 0 && width > 0){
                cursed.client.send({
                    type: "command",
                    key: "add.map",
                    details: {
                        "width": width,
                        "height": height,
                        "name": name
                    }
                });
            }

        }
        else if(buff.length == 4 && (buff[1] === "move" || buff[1] === "m")){
            user_to_switch = buff[2];
            map_to_switch = buff.slice(3, buff.length).join(" ");

            var valid_map = false;
            for(var map_name of map.maps){
                if(map_name == map_to_switch){
                    valid_map = true;
                    break
                }
            }

            if(valid_map){
                usernames = cursed.users.users.map((user)=>{
                    return user.username;
                });

                if(user_to_switch === "*"){
                    var frames = []
                    for(var username of usernames){
                        frames.push({
                            type: "command",
                            key: "move.user",
                            details: {
                                username: username,
                                map_name: map_to_switch
                            }
                        });
                    }
                    cursed.client.sendBulk(frames);
                }
                else if(usernames.indexOf(user_to_switch) > -1){
                    cursed.client.send({
                        type: "command",
                        key: "move.user",
                        details: {
                            username: user_to_switch,
                            map_name: map_to_switch
                        }
                    });
                }
            }
        }

        else if (buff.length == 2 && (buff[1] === "features" || buff[1] === "f")){
            var el = $("#features_menu");
            el.dialog({
                resizable: true,
                height: "auto",
                width: "75%",
                modal: true,
                open: function(){
                    // pause keypress handling
                    cursed.viewer.editor_open = true;
                    $("#features_menu").blur();
                },
                close: function(){
                    // allow client to handle keypresses again
                    cursed.viewer.editor_open = false;
                },
                buttons: {
                    "Close": function(){
                        $(this).dialog("close");
                    }
                }
            });
        }
        else if(buff.length > 3
            && (buff[1] == "feature" || buff[1] === "f")
            && (buff[2] == "pack" || buff[2] === "p")){

            if(buff[3] == "list" || buff[3] == "l"){
                var el = $("#feature_pack_menu");

                var ul = $("#feature_pack_menu_list");

                ul.empty();

                for(var pack of cursed.features.packs){
                    var node = $("

")
                        .append(
                            $("
" + pack.name + "
")
                        );

                    var node_ul = $("

");
                    for(var t of pack.types){
                        node_ul.append($("
").text(t.name));
                    }

                    node.append(node_ul);
                    ul.append(node);
                }

                el.dialog({
                    resizable: true,
                    height: "auto",
                    width: "75%",
                    modal: true,
                    open: function(){
                        // pause keypress handling
                        cursed.viewer.editor_open = true;
                        $("#feature_pack_menu").blur();
                    },
                    close: function(){
                        // allow client to handle keypresses again
                        cursed.viewer.editor_open = false;
                    },
                    buttons: {
                        "Close": function(){
                            $(this).dialog("close");
                        }
                    }
                });
            }
            else if(buff[3] === "apply" || buff[3] === "a"){

                var pack = null;

                for(var i=0; i<cursed.features.packs.length; i++){
                    if(buff[4].toUpperCase() == cursed.features.packs[i].name.toUpperCase()){
                        pack = cursed.features.packs[i];
                        break;
                    }
                }
                if(pack !== null){

                    var payloads = [];
                    for(var feature_type of pack.types){
                        payloads.push({
                            "type": "command",
                            "key": "add.map.feature.type",
                            "details": feature_type
                        });
                    }

                    cursed.client.sendBulk(payloads, true);
                }
            }
            else if(buff[3] === "remove" || buff[3] === "r"){ /* Remove Feature Pack Types */
                for(var i=0; i<cursed.features.packs.length; i++){
                    if(buff[4].toUpperCase() == cursed.features.packs[i].name.toUpperCase()){
                        pack = cursed.features.packs[i];
                        break;
                    }
                }
                if(pack !== null){
                    var payloads = [];
                    for(var feature_type of pack.types){
                        payloads.push({
                            "type": "command",
                            "key": "remove.map.feature.type",
                            "details": {
                                "name": feature_type.name
                            }
                        });
                    }

                    cursed.client.sendBulk(payloads, true);
                }
            }
        }
        else if(buff.length === 4
            && (buff[1] === "feature" || buff[1] === "f")
            && (buff[2] === "rm" || buff[2] === "r")){
            for(var feature of features.objects){
                if(feature.name === buff[3]){
                    cursed.client.send({
                        type: "command",
                        key: "remove.map.feature.type",
                        details:{
                            "name": name
                        }
                    }, true);

                    break;
                }
            }
        }
        else if(buff.length === 3
            && (buff[1] === "feature" || buff[1] === "f")
            && (buff[2] === "add" || buff[2] === "a")){

            var el = $("#features_add_menu");
            el.dialog({
                resizable: true,
                height: "auto",
                width: "auto",
                modal: true,
                open: function(){
                    // pause keypress handling
                    cursed.viewer.editor_open = true;
                    $("#features_add_menu").blur();
                },
                close: function(){
                    // allow client to handle keypresses again
                    cursed.viewer.editor_open = false;
                },
                buttons: {
                    "Add": function(){

                        var feature_name = $("#feature_name");
                        name = feature_name.val();
                        if(name === "" || name === null || name === undefined){
                            return;
                        }

                        var feature_key = $("#feature_key");
                        key = feature_key.val();
                        if(key === "" || key === null || key === undefined){
                            return;
                        }

                        var feature_character = $("#feature_character");
                        character = feature_character.val();
                        if(character === "" || character === null || character === undefined){
                            return;
                        }

                        var feature_color = $("#feature_color");
                        color = feature_color.val();
                        if(color === "" || color === null || color === undefined){
                            return;
                        }

                        cursed.client.send({
                            type: "command",
                            key: "add.map.feature.type",
                            details:{
                                "name": name,
                                "key": key,
                                "character": character,
                                "color": color
                            }
                        }, true);

                        $(this).dialog("close");
                    },
                    "Close": function(){
                        $(this).dialog("close");
                    }
                }
            });

        }
    }
}

map.show = function(){
    cursed.modules.text_display.map((e)=>{e.hide()});
    map.showing = true;
    map.get_maps()
    map.show_maps()
}

map.hide = function(){
    map.showing = false;
}

map.get_maps = function(){
    cursed.client.send({
        type: "command",
        key: "list.maps"
    });
};

map.show_maps = function(){
    lines = [ [{
        text: "Maps",
        color: "Gold"
    }] ];

    for(var map_name of map.maps){
        lines.push([{
            text: map_name,
            color: "Light Blue"
        }]);

        for(var user of cursed.users.users){
            if(user.current_map === map_name){
                lines.push([{
                    text: "- " + user.username,
                    color: null
                }]);
            }
        }
    }

    cursed.text_box.set(lines);
}

map.is_loading = function(){
    return (
        map.loading.fow ||
        map.loading.features ||
        map.loading.types ||
        map.loading.units );
}
<|endoftext|>