<commit_before>// Cool debugger written in Perl.
// perl -E "use Regexp::Debugger; 'ababc' =~ / (a|b) b+ c /x"

function Node(type, from, to) {
    this.type = type;
    this.from = from;
    this.to = to;
}

Node.prototype.patch = function (nextA, nextB) {
    this.nextA = nextA;
    this.nextB = nextB;
};

Node.prototype.match = function (state) {
    if (this.match(state)) {
        if (this.isEnd) {
            return true;
        }
        var newState = state.nextChar();
        var left = this.nextA.match(newState);
        if (!left && this.nextB) {
            return this.nextB.match(newState);
        }
    }
    return false;
};

Node.EMPTY = 'EMPTY';
Node.CHAR = 'CHAR';
Node.CHARSET = 'CHARSET';
Node.ALTR = 'ALTR';
Node.JOIN = 'JOIN';
Node.GROUP_BEGIN = 'GROUP_BEGIN';
Node.GROUP_END = 'GROUP_END';
Node.REPEAT = 'REPEAT';

// node types:
// - character
// - group
// - repeat
// - lookback

function State(str) {
    this.str = str;
    this.idx = 0;
    this.matches = {};
    this.data = {};
    this.counts = {};
}

State.prototype.incr = function() {
    this.idx += 1;
};

State.prototype.finished = function() {
    return this.idx >= this.str.length;
};

State.prototype.getCurrentChar = function() {
    return this.str[this.idx];
};

State.prototype.clone = function() {
    return clone(this);
};

State.prototype.recordMatch = function(idx, from, to) {
    this.matches[idx] = this.str.substring(from, to);
};

State.prototype.set = function(key, value) {
    this.data[key] = value;
};

State.prototype.get = function(key) {
    return this.data[key];
};

State.prototype.incCounts = function(idx) {
    return this.counts[idx] = (this.counts[idx] || -1) + 1;
};

function match(state, node) {
    var res;
    while (node) {
        var nextChar = state.getCurrentChar();

        switch (node.type) {
            case Node.REPEAT:
                // StateCounters start at -1 -> first inc makes the counter
                // be zero.
                var counter = state.incCounts(node.id);
                if (counter < node.from) {
                    // Haven't matched the minimum number yet
                    // -> match one more time.
                    res = match(state.clone(), node.child);
                } else if (counter === node.to) {
                    // Have matched the maximum number
                    // -> nothing to change.
                    res = state;
                } else {
                    // match \in {from, to}
                    if (node.greedy) {
                        res = match(state.clone(), node.child);
                        if (!res) {
                            res = match(state.clone(), node.next);
                        }
                    } else {
                        res = match(state.clone(), node.next);
                        if (!res) {
                            res = match(state.clone(), node.child);
                        }
                    }
                }
                return res;

            case Node.CHARSET:
                if (state.finished()) {
                    return false;
                }

                res = node.children.some(function(f) {
                    return f(nextChar);
                });

                if (node.not) {
                    res = !res;
                }

                if (res) {
                    state.incr();
                    node = node.next;
                } else {
                    return false;
                }
                break;

            case Node.CHAR:
                if (state.finished()) {
                    return false;
                }

                if (node.data === nextChar) {
                    state.incr();
                    node = node.next;
                } else {
                    return false;
                }
                break;
            case Node.ALTR:
                res = match(state.clone(), node.left);
                if (!res) {
                    return match(state.clone(), node.right);
                } else {
                    return res;
                }
                break;
            case Node.EMPTY:
            case Node.JOIN:
                node = node.next;
                break;

            case Node.GROUP_BEGIN:
                if (node.data !== 0) {
                    state.set(node.data, state.idx);
                }

                node = node.next;
                break;

            case Node.GROUP_END:
                if (node.data !== 0) {
                    var beginState = state.get(node.data);
                    state.recordMatch(node.data, beginState, state.idx);
                }

                node = node.next;
                break;

        }
    }

    if (node) {
        return false;
    }

    return state;
}

function retArr(nodes) {
    return [nodes[0], nodes[nodes.length - 1]];
}

function bText(str) {
    var nodeA, nodeB;

    if (str === '') {
        nodeA = new Node(Node.EMPTY);
        nodeB = new Node(Node.EMPTY);
        nodeA.next = nodeB;
        return [nodeA, nodeB];
    } else if (str.length === 1) {
        nodeA = new Node(Node.CHAR);
        nodeA.data = str;
        nodeB = new Node(Node.EMPTY);

        nodeA.next = nodeB;
        return [nodeA, nodeB];
    }

    var nodes = str.split('').map(function(ch, idx) {
        var node = new Node(Node.CHAR, idx, idx + 1);
        node.data = ch;
        return node;
    });
    for (var i = 0; i < nodes.length - 1; i++) {
        nodes[i].next = nodes[i + 1];
    }

    return retArr(nodes);
}

// If group should be a not-remember-group like `(?:x)`, then set
// `idx=0`.
function bGroup(idx, children) {
    var begin = new Node(Node.GROUP_BEGIN);
    var end = new Node(Node.GROUP_END);

    begin.data = end.data = idx;

    begin.next = children[0];
    children[1].next = end;

    return [begin, end];
}

function bCharSet(isNot, str) {
    var nodeA = new Node(Node.CHARSET);
    nodeA.not = isNot;

    // TODO: Add proper parsing of charSet here.
    nodeA.children = str.split('').map(function(matchChar) {
        return function(inputChar) {
            return inputChar === matchChar;
        };
    });

    var nodeB = new Node(Node.EMPTY);
    nodeA.next = nodeB;

    return [nodeA, nodeB];
}

// BuildDot is just a shorthand for a charSet excluding all newlines.
function bDot() {
    return bCharSet(true, '\n\r\u2028\u2029');
}

function bAlt(left, right) {
    var altr = new Node(Node.ALTR);
    var join = new Node(Node.JOIN);

    altr.left = left[0];
    altr.right = right[0];

    left[1].next = join;
    right[1].next = join;

    return [altr, join];
}

function bJoin() {
    var args = arguments;

    for (var i = 0; i < args.length - 1; i++) {
        args[i][1].next = args[i + 1][0];
    }

    return [args[0][0], args[args.length - 1][1]];
}

var idCounter = 0;

function bRepeat(greedy, from, to, children) {
    var node = new Node(Node.REPEAT);
    var nodeEmpty =  new Node(Node.EMPTY);

    node.id = idCounter++;
    node.greedy = greedy;
    node.from = from;
    node.to = to;

    // Create a loop.
    node.child = children[0];
    children[1].next = node;

    node.next = nodeEmpty;

    return [node, nodeEmpty];
}

function run(value) {

    // var str = 'dabc';

    test('dabc', bJoin(
        bDot(),
        bGroup(
            1,
            bAlt(
                bText(''),
                bText('a')
            )
        ),
        bText('bc')
    ), 4, {1: 'a'});

    test('abab', bJoin(
        bRepeat(true, 0, 100, bDot()),
        bGroup(
            1,
            bText('b')
        )
    ), 4, {1: 'b'});

    test('abab', bJoin(
        bRepeat(false, 0, 100, bDot()),
        bGroup(
            1,
            bText('b')
        )
    ), 2, {1: 'b'});

    test('abcabd', bJoin(
        bGroup(
            1,
            bText('abd')
        )
    ), 6, {1: 'abd'});

}

function test(str, nodes, lastIdx, matches) {
    function fail(msg) {
        console.error(msg);
    }

    function pass() {
        console.log('PASSED TEST');
    }

    // Note: Add to each start node the /(.)*?/ pattern to make the match
    // work also from not only the beginning
    var startNode = bJoin(
        bRepeat(false, 0, str.length + 1, bDot()),
        nodes//,
    )[0];

    var state = new State(str);
    var endState = match(state, startNode);

    if (endState) {
        if (lastIdx === -1) {
            return fail('Got match but did not expect one');
        }

        if (endState.idx !== lastIdx) {
            return fail('State lastIdx does not match expected one');
        }

        if (Object.keys(endState.matches).length !== Object.keys(matches).length) {
            return fail('Matches number does not match');
        }

        for (var i in endState.matches) {
            if (matches[i] !== endState.matches[i]) {
                return fail('Expected match does not match');
            }
        }
    } else {
        if (lastIdx !== -1) {
            return fail('Did not match but expected to do so');
        }
    }

    pass();
}

<commit_msg>Add x(?=y) functionality<commit_after>// Cool debugger written in Perl.
// perl -E "use Regexp::Debugger; 'ababc' =~ / (a|b) b+ c /x"

function Node(type, from, to) {
    this.type = type;
    this.from = from;
    this.to = to;
}

Node.prototype.patch = function (nextA, nextB) {
    this.nextA = nextA;
    this.nextB = nextB;
};

Node.prototype.match = function (state) {
    if (this.match(state)) {
        if (this.isEnd) {
            return true;
        }
        var newState = state.nextChar();
        var left = this.nextA.match(newState);
        if (!left && this.nextB) {
            return this.nextB.match(newState);
        }
    }
    return false;
};

Node.EMPTY = 'EMPTY';
Node.CHAR = 'CHAR';
Node.CHARSET = 'CHARSET';
Node.ALTR = 'ALTR';
Node.JOIN = 'JOIN';
Node.GROUP_BEGIN = 'GROUP_BEGIN';
Node.GROUP_END = 'GROUP_END';
Node.REPEAT = 'REPEAT';

// node types:
// - character
// - group
// - repeat
// - lookback

function State(str) {
    this.str = str;
    this.idx = 0;
    this.matches = {};
    this.data = {};
    this.counts = {};
}

State.prototype.incr = function() {
    this.idx += 1;
};

State.prototype.finished = function() {
    return this.idx >= this.str.length;
};

State.prototype.getCurrentChar = function() {
    return this.str[this.idx];
};

State.prototype.clone = function() {
    return clone(this);
};

State.prototype.recordMatch = function(idx, from, to) {
    this.matches[idx] = this.str.substring(from, to);
};

State.prototype.set = function(key, value) {
    this.data[key] = value;
};

State.prototype.get = function(key) {
    return this.data[key];
};

State.prototype.incCounts = function(idx) {
    return this.counts[idx] = (this.counts[idx] || -1) + 1;
};

function match(state, node) {
    var res;
    while (node) {
        var nextChar = state.getCurrentChar();

        switch (node.type) {
            case Node.REPEAT:
                // TODO: Reset values of groups.

                // StateCounters start at -1 -> first inc makes the counter
                // be zero.
                var counter = state.incCounts(node.id);
                if (counter < node.from) {
                    // Haven't matched the minimum number yet
                    // -> match one more time.
                    res = match(state.clone(), node.child);
                } else if (counter === node.to) {
                    // Have matched the maximum number
                    // -> nothing to change.
                    res = state;
                } else {
                    // match \in {from, to}
                    if (node.greedy) {
                        res = match(state.clone(), node.child);
                        if (!res) {
                            res = match(state.clone(), node.next);
                        }
                    } else {
                        res = match(state.clone(), node.next);
                        if (!res) {
                            res = match(state.clone(), node.child);
                        }
                    }
                }
                return res;

            case Node.CHARSET:
                if (state.finished()) {
                    return false;
                }

                res = node.children.some(function(f) {
                    return f(nextChar);
                });

                if (node.not) {
                    res = !res;
                }

                if (res) {
                    state.incr();
                    node = node.next;
                } else {
                    return false;
                }
                break;

            case Node.CHAR:
                if (state.finished()) {
                    return false;
                }

                if (node.data === nextChar) {
                    state.incr();
                    node = node.next;
                } else {
                    return false;
                }
                break;
            case Node.ALTR:
                res = match(state.clone(), node.left);
                if (!res) {
                    return match(state.clone(), node.right);
                } else {
                    return res;
                }
                break;
            case Node.EMPTY:
            case Node.JOIN:
                node = node.next;
                break;

            case Node.GROUP_BEGIN:
                state.set(node.data, state.idx);

                node = node.next;
                break;

            case Node.GROUP_END:
                // If node.idx > 0, then it's a group to store the match.
                if (node.data > 0) {
                    var beginState = state.get(node.data);
                    state.recordMatch(node.data, beginState, state.idx);
                }

                // Case of: x(?=y)
                if (node.data < 0) {
                    state.idx = state.get(node.data);
                }

                node = node.next;
                break;

        }
    }

    if (node) {
        return false;
    }

    return state;
}

function retArr(nodes) {
    return [nodes[0], nodes[nodes.length - 1]];
}

function bText(str) {
    var nodeA, nodeB;

    if (str === '') {
        nodeA = new Node(Node.EMPTY);
        nodeB = new Node(Node.EMPTY);
        nodeA.next = nodeB;
        return [nodeA, nodeB];
    } else if (str.length === 1) {
        nodeA = new Node(Node.CHAR);
        nodeA.data = str;
        nodeB = new Node(Node.EMPTY);

        nodeA.next = nodeB;
        return [nodeA, nodeB];
    }

    var nodes = str.split('').map(function(ch, idx) {
        var node = new Node(Node.CHAR, idx, idx + 1);
        node.data = ch;
        return node;
    });
    for (var i = 0; i < nodes.length - 1; i++) {
        nodes[i].next = nodes[i + 1];
    }

    return retArr(nodes);
}

// If group should be a not-remember-group like `(?:x)`, then set
// `idx=0`.
function bGroup(idx, children) {
    var begin = new Node(Node.GROUP_BEGIN);
    var end = new Node(Node.GROUP_END);

    begin.data = end.data = idx;

    begin.next = children[0];
    children[1].next = end;

    return [begin, end];
}

function bFollowMatch(children) {
    var id = idCounter++;
    return bGroup(-id, children);
}

function bCharSet(isNot, str) {
    var nodeA = new Node(Node.CHARSET);
    nodeA.not = isNot;

    // TODO: Add proper parsing of charSet here.
    nodeA.children = str.split('').map(function(matchChar) {
        return function(inputChar) {
            return inputChar === matchChar;
        };
    });

    var nodeB = new Node(Node.EMPTY);
    nodeA.next = nodeB;

    return [nodeA, nodeB];
}

// BuildDot is just a shorthand for a charSet excluding all newlines.
function bDot() {
    return bCharSet(true, '\n\r\u2028\u2029');
}

function bAlt(left, right) {
    var altr = new Node(Node.ALTR);
    var join = new Node(Node.JOIN);

    altr.left = left[0];
    altr.right = right[0];

    left[1].next = join;
    right[1].next = join;

    return [altr, join];
}

function bJoin() {
    var args = arguments;

    for (var i = 0; i < args.length - 1; i++) {
        args[i][1].next = args[i + 1][0];
    }

    return [args[0][0], args[args.length - 1][1]];
}

var idCounter = 0;

function bRepeat(greedy, from, to, children) {
    var node = new Node(Node.REPEAT);
    var nodeEmpty =  new Node(Node.EMPTY);

    node.id = idCounter++;
    node.greedy = greedy;
    node.from = from;
    node.to = to;

    // Create a loop.
    node.child = children[0];
    children[1].next = node;

    node.next = nodeEmpty;

    return [node, nodeEmpty];
}

function run(value) {

    // var str = 'dabc';

    test('dabc', bJoin(
        bDot(),
        bGroup(
            1,
            bAlt(
                bText(''),
                bText('a')
            )
        ),
        bText('bc')
    ), 4, {1: 'a'});

    test('abab', bJoin(
        bRepeat(true, 0, 100, bDot()),
        bGroup(
            1,
            bText('b')
        )
    ), 4, {1: 'b'});

    test('abab', bJoin(
        bRepeat(false, 0, 100, bDot()),
        bGroup(
            1,
            bText('b')
        )
    ), 2, {1: 'b'});

    test('abcabd', bJoin(
        bGroup(
            1,
            bText('abd')
        )
    ), 6, {1: 'abd'});

    test('abcabd', bGroup(1, bJoin(
        bText('b'),
        bFollowMatch(
            bText('d')
        )
    )), 5, { 1: 'b' });

}

function test(str, nodes, lastIdx, matches) {
    function fail(msg) {
        console.error(msg);
    }

    function pass() {
        console.log('PASSED TEST');
    }

    // Note: Add to each start node the /(.)*?/ pattern to make the match
    // work also from not only the beginning
    var startNode = bJoin(
        bRepeat(false, 0, str.length + 1, bDot()),
        nodes//,
    )[0];

    var state = new State(str);
    var endState = match(state, startNode);

    if (endState) {
        if (lastIdx === -1) {
            return fail('Got match but did not expect one');
        }

        if (endState.idx !== lastIdx) {
            return fail('State lastIdx does not match expected one');
        }

        if (Object.keys(endState.matches).length !== Object.keys(matches).length) {
            return fail('Matches number does not match');
        }

        for (var i in endState.matches) {
            if (matches[i] !== endState.matches[i]) {
                return fail('Expected match does not match');
            }
        }
    } else {
        if (lastIdx !== -1) {
            return fail('Did not match but expected to do so');
        }
    }

    pass();
}

<|endoftext|>


<commit_before>var request = require("request");
var cheerio = require("cheerio");

var Crawler = (function(){
    function Crawler(config) {
        this.config = config || {};
        this.cache = null;
    }

    Crawler.prototype.fetchTrendPage = function(lang) {
        var target = {
            url: "https://github.com/trending"
        };
        if (lang) {
            target.url += "?l=" + lang;
        }
        return new Promise(function (resolve, reject){
            request(target, function(err, res, body){
                if (err) {
                    reject(err);
                    return;
                }
                if (res.statusCode != 200) {
                    reject(new Error("Request Page Error, Status Code: " + res,statusCode));
                    return;
                }
                resolve(body);
            });
        });
    };

    Crawler.prototype.fetchTrendRepos = function (lang) {
        return this.fetchTrendPage(lang).then(function (html){
            var dom = cheerio.load(html);
            // TODO: Refactor all the code below for better crawler result
            var res1 = dom(".repo-list-item .repo-list-name a")
                .toArray()
                .map(function(a){
                    var href = a.attribs['href'];
                    var match = href.match(/^\/([^\/]+)\/([^\/]+)$/);
                    return match;
                });
            var res2 = dom('.repo-list-item .repo-list-description')
                .toArray()
                .map(function(a){
                    var desc = a.children[0].data;
                    if (desc){
                        desc = desc.replace(/\s+/g, ' ');
                    }
                    else{
                        desc = "";
                    }
                    return desc;
                });
            var res3 = dom('.repo-list-item .repo-list-meta')
                .toArray()
                .map(function(a){
                    var meta = a.children[0].data;
                    meta = meta.replace(/\s+/g, ' ');
                    meta = meta.split('•');
                    return meta;
                });
            result = []
            for (var idx = 0; idx < res1.length; idx++){
                var item = [];
                item.owner = res1[idx][1];
                item.name = res1[idx][2];
                item.desc = res2[idx];
                item.lang = res3[idx][0];
                item.stars = res3[idx][1];
                result[idx] = item;
            };
            return result;
        });
    };

    return Crawler;

})();

exports.Crawler = Crawler;<commit_msg>Refactor the code<commit_after>var request = require("request");
var cheerio = require("cheerio");

var Crawler = (function(){
    function Crawler(config) {
        this.config = config || {};
        this.cache = null;
    }

    Crawler.prototype.fetchTrendPage = function(lang) {
        var target = {
            url: "https://github.com/trending"
        };
        if (lang) {
            target.url += "?l=" + lang;
        }
        return new Promise(function (resolve, reject){
            request(target, function(err, res, body){
                if (err) {
                    reject(err);
                    return;
                }
                if (res.statusCode != 200) {
                    reject(new Error("Request Page Error, Status Code: " + res,statusCode));
                    return;
                }
                resolve(body);
            });
        });
    };

    Crawler.prototype.fetchTrendRepos = function (lang, callback) {
        return this.fetchTrendPage(lang).then(function (html){
            var $ = cheerio.load(html);
            // TODO: Refactor all the code below for better crawler result
            var result = [];
            $(".repo-list-item").each(function(i, elem){
                var res = []
                var after_link = $(this).children(".repo-list-name").children("a").attr("href");
                res.link = "https://github.com" + after_link;
                res.name = $(this).children(".repo-list-name").text().replace(/\s+/g, "");
                res.desc = $(this).children(".repo-list-description").text().replace(/\s+/g, " ");
                var meta = $(this).children(".repo-list-meta").text().replace(/\s+/g, '').split("•");
                res.lang = meta[0];
                res.stars = meta[1].replace("starstoday", "");
                result.push(res)
            });
            callback(result);
        });
    };

    return Crawler;

})();

exports.Crawler = Crawler;<|endoftext|>


<commit_before>/*eslint no-unused-vars:0*/
import 'core-js';
import * as TheLogManager from 'aurelia-logging';
import {Container} from 'aurelia-dependency-injection';
import {Loader} from 'aurelia-loader';
import {BindingLanguage, ViewEngine, ViewSlot, ViewResources, CompositionEngine, Animator} from 'aurelia-templating';
import {DOM, PLATFORM} from 'aurelia-pal';
import {bindingSystem} from 'aurelia-binding';
import {FrameworkConfiguration} from './framework-configuration';

function preventActionlessFormSubmit() {
  DOM.addEventListener('submit', evt => {
    const target = evt.target;
    const action = target.action;

    if (target.tagName.toLowerCase() === 'form' && !action) {
      evt.preventDefault();
    }
  });
}

/**
 * The framework core that provides the main Aurelia object.
 * @param loader The loader for this Aurelia instance to use. If a loader is not specified, Aurelia will use a defaultLoader.
 * @param container The dependency injection container for this Aurelia instance to use. If a container is not specified, Aurelia will create an empty container.
 * @param resources The resource registry for this Aurelia instance to use. If a resource registry is not specified, Aurelia will create an empty registry.
 */
export class Aurelia {
  loader: Loader;
  container: Container;
  resources: ViewResources;
  use: FrameworkConfiguration;

  constructor(loader?: Loader, container?: Container, resources?: ViewResources) {
    this.loader = loader || new PLATFORM.Loader();
    this.container = container || new Container();
    this.resources = resources || new ViewResources();
    this.use = new FrameworkConfiguration(this);
    this.logger = TheLogManager.getLogger('aurelia');
    this.hostConfigured = false;
    this.host = null;

    this.use.instance(Aurelia, this);
    this.use.instance(Loader, this.loader);
    this.use.instance(ViewResources, this.resources);
    this.container.makeGlobal();
  }

  /**
   * Loads plugins, then resources, and then starts the Aurelia instance.
   * @return Returns the started Aurelia instance.
   */
  start(): Promise<Aurelia> {
    if (this.started) {
      return Promise.resolve(this);
    }

    this.started = true;
    this.logger.info('Aurelia Starting');

    return this.use.apply().then(() => {
      preventActionlessFormSubmit();

      if (!this.container.hasResolver(BindingLanguage)) {
        let message = 'You must configure Aurelia with a BindingLanguage implementation.';
        this.logger.error(message);
        throw new Error(message);
      }

      if (!this.container.hasResolver(Animator)) {
        Animator.configureDefault(this.container);
      }

      bindingSystem.initialize(this.container);

      this.logger.info('Aurelia Started');
      let evt = DOM.createCustomEvent('aurelia-started', { bubbles: true, cancelable: true });
      DOM.dispatchEvent(evt);
      return this;
    });
  }

  /**
   * Enhances the host's existing elements with behaviors and bindings.
   * @param bindingContext A binding context for the enhanced elements.
   * @param applicationHost The DOM object that Aurelia will enhance.
   * @return Returns the current Aurelia instance.
   */
  enhance(bindingContext: Object = {}, applicationHost = null): Promise<Aurelia> {
    this._configureHost(applicationHost);

    return new Promise(resolve => {
      let viewEngine = this.container.get(ViewEngine);
      this.root = viewEngine.enhance(this.container, this.host, this.resources, bindingContext);
      this.root.attached();
      this._onAureliaComposed();
      return this;
    });
  }

  /**
   * Instantiates the root view-model and view and add them to the DOM.
   * @param root The root view-model to load upon bootstrap.
   * @param applicationHost The DOM object that Aurelia will attach to.
   * @return Returns the current Aurelia instance.
   */
  setRoot(root: string = 'app', applicationHost = null): Promise<Aurelia> {
    let compositionEngine;
    let instruction = {};

    this._configureHost(applicationHost);

    compositionEngine = this.container.get(CompositionEngine);
    instruction.viewModel = root;
    instruction.container = instruction.childContainer = this.container;
    instruction.viewSlot = this.hostSlot;
    instruction.host = this.host;

    return compositionEngine.compose(instruction).then(r => {
      this.root = r;
      instruction.viewSlot.attached();
      this._onAureliaComposed();
      return this;
    });
  }

  _configureHost(applicationHost) {
    if (this.hostConfigured) {
      return;
    }

    applicationHost = applicationHost || this.host;

    if (!applicationHost || typeof applicationHost === 'string') {
      this.host = DOM.getElementById(applicationHost || 'applicationHost');
    } else {
      this.host = applicationHost;
    }

    if (!this.host) {
      throw new Error('No applicationHost was specified.');
    }

    this.hostConfigured = true;
    this.host.aurelia = this;
    this.hostSlot = new ViewSlot(this.host, true);
    this.hostSlot.transformChildNodesIntoView();
    this.container.registerInstance(DOM.boundary, this.host);
  }

  _onAureliaComposed() {
    let evt = DOM.createCustomEvent('aurelia-composed', { bubbles: true, cancelable: true });
    setTimeout(() => DOM.dispatchEvent(evt), 1);
  }
}
<commit_msg>chore(bindingSystem): bindingSystem renamed bindingEngine<commit_after>/*eslint no-unused-vars:0*/
import 'core-js';
import * as TheLogManager from 'aurelia-logging';
import {Container} from 'aurelia-dependency-injection';
import {Loader} from 'aurelia-loader';
import {BindingLanguage, ViewEngine, ViewSlot, ViewResources, CompositionEngine, Animator} from 'aurelia-templating';
import {DOM, PLATFORM} from 'aurelia-pal';
import {bindingEngine} from 'aurelia-binding';
import {FrameworkConfiguration} from './framework-configuration';

function preventActionlessFormSubmit() {
  DOM.addEventListener('submit', evt => {
    const target = evt.target;
    const action = target.action;

    if (target.tagName.toLowerCase() === 'form' && !action) {
      evt.preventDefault();
    }
  });
}

/**
 * The framework core that provides the main Aurelia object.
 * @param loader The loader for this Aurelia instance to use. If a loader is not specified, Aurelia will use a defaultLoader.
 * @param container The dependency injection container for this Aurelia instance to use. If a container is not specified, Aurelia will create an empty container.
 * @param resources The resource registry for this Aurelia instance to use. If a resource registry is not specified, Aurelia will create an empty registry.
 */
export class Aurelia {
  loader: Loader;
  container: Container;
  resources: ViewResources;
  use: FrameworkConfiguration;

  constructor(loader?: Loader, container?: Container, resources?: ViewResources) {
    this.loader = loader || new PLATFORM.Loader();
    this.container = container || new Container();
    this.resources = resources || new ViewResources();
    this.use = new FrameworkConfiguration(this);
    this.logger = TheLogManager.getLogger('aurelia');
    this.hostConfigured = false;
    this.host = null;

    this.use.instance(Aurelia, this);
    this.use.instance(Loader, this.loader);
    this.use.instance(ViewResources, this.resources);
    this.container.makeGlobal();
  }

  /**
   * Loads plugins, then resources, and then starts the Aurelia instance.
   * @return Returns the started Aurelia instance.
   */
  start(): Promise<Aurelia> {
    if (this.started) {
      return Promise.resolve(this);
    }

    this.started = true;
    this.logger.info('Aurelia Starting');

    return this.use.apply().then(() => {
      preventActionlessFormSubmit();

      if (!this.container.hasResolver(BindingLanguage)) {
        let message = 'You must configure Aurelia with a BindingLanguage implementation.';
        this.logger.error(message);
        throw new Error(message);
      }

      if (!this.container.hasResolver(Animator)) {
        Animator.configureDefault(this.container);
      }

      bindingEngine.initialize(this.container);

      this.logger.info('Aurelia Started');
      let evt = DOM.createCustomEvent('aurelia-started', { bubbles: true, cancelable: true });
      DOM.dispatchEvent(evt);
      return this;
    });
  }

  /**
   * Enhances the host's existing elements with behaviors and bindings.
   * @param bindingContext A binding context for the enhanced elements.
   * @param applicationHost The DOM object that Aurelia will enhance.
   * @return Returns the current Aurelia instance.
   */
  enhance(bindingContext: Object = {}, applicationHost = null): Promise<Aurelia> {
    this._configureHost(applicationHost);

    return new Promise(resolve => {
      let viewEngine = this.container.get(ViewEngine);
      this.root = viewEngine.enhance(this.container, this.host, this.resources, bindingContext);
      this.root.attached();
      this._onAureliaComposed();
      return this;
    });
  }

  /**
   * Instantiates the root view-model and view and add them to the DOM.
   * @param root The root view-model to load upon bootstrap.
   * @param applicationHost The DOM object that Aurelia will attach to.
   * @return Returns the current Aurelia instance.
   */
  setRoot(root: string = 'app', applicationHost = null): Promise<Aurelia> {
    let compositionEngine;
    let instruction = {};

    this._configureHost(applicationHost);

    compositionEngine = this.container.get(CompositionEngine);
    instruction.viewModel = root;
    instruction.container = instruction.childContainer = this.container;
    instruction.viewSlot = this.hostSlot;
    instruction.host = this.host;

    return compositionEngine.compose(instruction).then(r => {
      this.root = r;
      instruction.viewSlot.attached();
      this._onAureliaComposed();
      return this;
    });
  }

  _configureHost(applicationHost) {
    if (this.hostConfigured) {
      return;
    }

    applicationHost = applicationHost || this.host;

    if (!applicationHost || typeof applicationHost === 'string') {
      this.host = DOM.getElementById(applicationHost || 'applicationHost');
    } else {
      this.host = applicationHost;
    }

    if (!this.host) {
      throw new Error('No applicationHost was specified.');
    }

    this.hostConfigured = true;
    this.host.aurelia = this;
    this.hostSlot = new ViewSlot(this.host, true);
    this.hostSlot.transformChildNodesIntoView();
    this.container.registerInstance(DOM.boundary, this.host);
  }

  _onAureliaComposed() {
    let evt = DOM.createCustomEvent('aurelia-composed', { bubbles: true, cancelable: true });
    setTimeout(() => DOM.dispatchEvent(evt), 1);
  }
}
<|endoftext|>


<commit_before>var bot = require('./lib/utils/bot');
const spawn = require('child_process').spawn;
const commands = require('./commands');
const config = require('./config');
var pm2 = false;

/*
    These functions are bootstrapping the bot process
    so we can manage it with a parent process
    cool, i guess...
*/

function startlog() {

    if (pm2 !== false) {
        console.log('pm2 logs process already started...');
        return;
    }
    start = false;

    pm2 = spawn('pm2', ['logs']);
    pm2.on('exit', (code, signal) => {
        console.log('PM2 EXIT');
    })

    pm2.stderr.on('data', (data) => {
        // console.error(data.toString());
        bot.channels.get(config.log_channel).sendCode("shell", 'Log-' + error + '\n' + stderr, { split: true });
    });

    pm2.stdout.on('data', (data) => {
        //console.log(data.toString());
        bot.channels.get(config.log_channel).sendCode("shell", 'ERROR -' + error + '\n' + stderr, { split: true });
    });

    return pm2;
};

/*
    Initialize our bot bootstrapper
    listen for restart/stop here
*/

bot.on('ready', () => {
    startlog();
    bot.channels.get(config.log_channel).sendMessage('Bot Ready (pid: ' + process.pid + ')');
})

bot.on('message', message => {
    if (message.author.bot) return;
    var guild = bot.guilds.get('256299642180861953');

    if (message.guild != guild) {
        return;
    }
    if (!message.member.roles.find('name', 'dev')) return;
    var msg = message.content.toLowerCase();

    if (msg == '~restart') {
        bot.channels.get(config.log_channel).sendMessage('Exiting with code 0');
        process.exit();
    }
    else if (msg == '~status') {
        message.reply(':) (pid: ' + process.pid + ')');
    } else if (msg.startsWith('~cmd')) {
        const exec = require('child_process').exec;
        exec(msg.substring(5), (error, stdout, stderr) => {
            if (error) {
                console.error(`exec error: ${error}`);
                message.channel.sendCode("shell", '**ERROR** Shell-' + error + '\n' + stderr, { split: true });
                return;
            }
            console.log(`stdout: ${stdout}`);
            message.channel.sendCode("shell", stdout + { split: true });
            console.log(`stderr: ${stderr}`);
        });
    }
});
process.on('exit', (code) => {
    console.log(`About to exit with code: ${code}`);
});
process.on('uncaughtException', (err) => {
    console.log('Uncaught Exception:' + err);
    console.log(err.stack);
});
<commit_msg>Hopefully fixed<commit_after>var bot = require('./lib/utils/bot');
const spawn = require('child_process').spawn;
const commands = require('./commands');
const config = require('./config');
var pm2 = false;

/*
    These functions are bootstrapping the bot process
    so we can manage it with a parent process
    cool, i guess...
*/

function startlog() {

    if (pm2 !== false) {
        console.log('pm2 logs process already started...');
        return;
    }
    start = false;

    pm2 = spawn('pm2', ['logs']);
    pm2.on('exit', (code, signal) => {
        console.log('PM2 EXIT');
    })

    pm2.stderr.on('data', (data) => {
        // console.error(data.toString());
        bot.channels.get(config.log_channel).sendCode("shell", 'Log- ' + stderr, { split: true });
    });

    pm2.stdout.on('data', (data) => {
        //console.log(data.toString());
        bot.channels.get(config.log_channel).sendCode("shell", 'ERROR - ' + stderr, { split: true });
    });

    return pm2;
};

/*
    Initialize our bot bootstrapper
    listen for restart/stop here
*/

bot.on('ready', () => {
    startlog();
    console.log("logs ready");
    bot.channels.get(config.log_channel).sendMessage('Bot Ready (pid: ' + process.pid + ')');
})

bot.on('message', message => {
    if (message.author.bot) return;
    var guild = bot.guilds.get('256299642180861953');

    if (message.guild != guild) {
        return;
    }
    if (!message.member.roles.find('name', 'dev')) return;
    var msg = message.content.toLowerCase();

    if (msg == '~restart') {
        bot.channels.get(config.log_channel).sendMessage('Exiting with code 0');
        process.exit();
    }
    else if (msg == '~status') {
        message.reply(':) (pid: ' + process.pid + ')');
    } else if (msg.startsWith('~cmd')) {
        const exec = require('child_process').exec;
        exec(msg.substring(5), (error, stdout, stderr) => {
            if (error) {
                console.error(`exec error: ${error}`);
                message.channel.sendCode("shell", '**ERROR** Shell-' + error + '\n' + stderr, { split: true });
                return;
            }
            console.log(`stdout: ${stdout}`);
            message.channel.sendCode("shell", stdout + { split: true });
            console.log(`stderr: ${stderr}`);
        });
    }
});
process.on('exit', (code) => {
    console.log(`About to exit with code: ${code}`);
});
process.on('uncaughtException', (err) => {
    console.log('Uncaught Exception:' + err);
    console.log(err.stack);
});
<|endoftext|>


<commit_before>(function (window) {
  console.log('Hello from Cuddly Telegram');

  var comment_canvas = document.getElementById('comment-canvas');
  comment_canvas.width = document.documentElement.clientWidth;
  comment_canvas.height = document.documentElement.clientHeight;
  var comment_draw_ctx = comment_canvas.getContext('2d');

  var comment_types = { TOP_SLIDE: 0, TOP_STICK: 1, BOTTOM_STICK: 2 };
  var comment_type_names = { 'top': 0, 'bottom': 2 }; // Workaround = =

  var comment_onboard_bullets = [];
  var comment_add_bullet = function (id, message, color, top, left, xspeed, life) {
    var i;
    for (i = 0; comment_onboard_bullets[i]; ++i);
    comment_onboard_bullets[i] = {
      id: id, message: message, color: color,
      y: top, x: left, xspeed: xspeed,
      expiry: Date.now() + life
    };
  };
  var comment_update_bullets = function () {
    var now = Date.now();
    comment_draw_ctx.clearRect(0, 0, document.documentElement.clientWidth, document.documentElement.clientHeight);
    comment_draw_ctx.font = '54px Lucida Grande';
    comment_draw_ctx.textBaseline = 'top';
    comment_draw_ctx.shadowColor = '#666666';
    comment_draw_ctx.shadowBlur = 0;  // 6;
    comment_draw_ctx.shadowOffsetX = 2;
    comment_draw_ctx.shadowOffsetY = 2;
    var cur_bullet;
    for (var i = 0; i < comment_onboard_bullets.length; ++i) {
      cur_bullet = comment_onboard_bullets[i];
      if (!cur_bullet) continue;
      cur_bullet.x += cur_bullet.xspeed * 40;
      if (now > cur_bullet.expiry) comment_onboard_bullets[i] = undefined;
      else {
        // Draw the bullet
        comment_draw_ctx.fillStyle = cur_bullet.color;
        comment_draw_ctx.fillText(cur_bullet.message, cur_bullet.x, cur_bullet.y);
      }
    }
  };
  setInterval(comment_update_bullets, 40);

  var comment_v_chunk_height = 5;
  var CommentBoardTopSlide = function (width, height) {
    this.width = width;
    this.height = height;
    this.next_unblock = [];
    this.next_clear = [];
    for (var i = 0; i <= Math.ceil(height / comment_v_chunk_height); ++i) {
      this.next_unblock[i] = 0;
      this.next_clear[i] = 0;
    }
  };
  CommentBoardTopSlide.prototype.fire = function (id, message, color) {
    var now = Date.now();
    // Measure size
    var cmt_w = comment_draw_ctx.measureText(message).width;
    var cmt_h = 64;
    // Animations
    var duration = Math.random() * 8000 + 3000;
    var speed = (this.width + cmt_w) / duration;
    // Allocate space
    var positioning_data = null;
    var unblock_time = now + cmt_w / speed;
    var border_touch_time = now + this.width / speed;
    var fully_out_time = now + duration;
    var v_chunks = Math.ceil(cmt_h / comment_v_chunk_height);
    for (var i = 0; i <= this.next_unblock.length - v_chunks; ++i) {
      if (this.next_unblock[i] <= now && this.next_clear[i] <= border_touch_time) {
        var j, valid = true;
        for (j = i; j < i + v_chunks; ++j)
          if (this.next_unblock[i] > now || this.next_clear[i] > border_touch_time) {
            valid = false; break;
          }
        if (valid) {
          for (j = i; j < i + v_chunks; ++j) {
            this.next_unblock[j] = unblock_time;
            this.next_clear[j] = fully_out_time;
          }
          positioning_data = {top: comment_v_chunk_height * i, speed: speed};
          break;
        } else {
          i = j;
        }
      }
    }
    if (positioning_data === null) return false;
    comment_add_bullet(id, message, color, positioning_data.top, this.width, -speed, duration);
    return true;
  };

  var comment_board_cnt = 4;
  var comment_board_topslide = [];
  for (var i = 0; i < comment_board_cnt; ++i) {
    comment_board_topslide[i] = new CommentBoardTopSlide(document.documentElement.clientWidth, document.documentElement.clientHeight);
  }
  var comment_board_fire = function (c) {
    if (comment_type_names[c.position] === comment_types.TOP_SLIDE) {
      comment_board_topslide[0].fire(c.id, c.message, c.color);
    }
  };

  //var socket = new WebSocket('<?php $channel = new SaeChannel();echo $channel -> createChannel('danmaku',18000);?>');
  // Workaround x2...
  // Workaround x3: http://stackoverflow.com/questions/17088609/disable-firefox-same-origin-policy/29096229#29096229
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://app.efzer.org/live/2016newyear/danmaku.php', false);
  xhr.send();
  var socket = new WebSocket(xhr.response.substr(4299, 115));
  socket.onmessage = function (msg) {
    comment_board_fire(JSON.parse(msg.data));
    //var c = JSON.parse(msg.data);
    //comment_board_topslide[0].fire(c.id, c.message, c.color);
  };
  window.___socket = socket;
  window.f = comment_board_fire;
  // f({id: 233, message: 'xaxx', type: 'top', color: '#c0c0ff'})
}(window));
<commit_msg>Fix timing issues<commit_after>(function (window) {
  console.log('Hello from Cuddly Telegram');

  var comment_canvas = document.getElementById('comment-canvas');
  comment_canvas.width = document.documentElement.clientWidth;
  comment_canvas.height = document.documentElement.clientHeight;
  var comment_draw_ctx = comment_canvas.getContext('2d');

  var comment_types = { TOP_SLIDE: 0, TOP_STICK: 1, BOTTOM_STICK: 2 };
  var comment_type_names = { 'top': 0, 'bottom': 2 }; // Workaround = =

  var comment_onboard_bullets = [];
  var comment_add_bullet = function (id, message, color, top, left, xspeed, life) {
    var i;
    for (i = 0; comment_onboard_bullets[i]; ++i);
    comment_onboard_bullets[i] = {
      id: id, message: message, color: color,
      y: top, x: left, xspeed: xspeed,
      expiry: Date.now() + life
    };
  };
  var comment_update_last_time = -1;
  var comment_update_bullets = function () {
    var now = Date.now();
    var delta = now - comment_update_last_time;
    comment_draw_ctx.clearRect(0, 0, document.documentElement.clientWidth, document.documentElement.clientHeight);
    comment_draw_ctx.font = '54px Lucida Grande';
    comment_draw_ctx.textBaseline = 'top';
    comment_draw_ctx.shadowColor = '#666666';
    comment_draw_ctx.shadowBlur = 0;  // 6;
    comment_draw_ctx.shadowOffsetX = 2;
    comment_draw_ctx.shadowOffsetY = 2;
    var cur_bullet;
    for (var i = 0; i < comment_onboard_bullets.length; ++i) {
      cur_bullet = comment_onboard_bullets[i];
      if (!cur_bullet) continue;
      cur_bullet.x += cur_bullet.xspeed * delta;
      if (now > cur_bullet.expiry) comment_onboard_bullets[i] = undefined;
      else {
        // Draw the bullet
        comment_draw_ctx.fillStyle = cur_bullet.color;
        comment_draw_ctx.fillText(cur_bullet.message, cur_bullet.x, cur_bullet.y);
      }
    }
    comment_update_last_time = now;
  };
  setInterval(comment_update_bullets, 40);
  comment_update_last_time = Date.now();

  var comment_v_chunk_height = 5;
  var CommentBoardTopSlide = function (width, height) {
    this.width = width;
    this.height = height;
    this.next_unblock = [];
    this.next_clear = [];
    for (var i = 0; i <= Math.ceil(height / comment_v_chunk_height); ++i) {
      this.next_unblock[i] = 0;
      this.next_clear[i] = 0;
    }
  };
  CommentBoardTopSlide.prototype.fire = function (id, message, color) {
    var now = Date.now();
    // Measure size
    var cmt_w = comment_draw_ctx.measureText(message).width;
    var cmt_h = 64;
    // Animations
    var duration = Math.random() * 3000 + 8000;
    var speed = (this.width + cmt_w) / duration;
    // Allocate space
    var positioning_data = null;
    var unblock_time = now + cmt_w / speed;
    var border_touch_time = now + this.width / speed;
    var fully_out_time = now + duration;
    var v_chunks = Math.ceil(cmt_h / comment_v_chunk_height);
    for (var i = 0; i <= this.next_unblock.length - v_chunks; ++i) {
      if (this.next_unblock[i] <= now && this.next_clear[i] <= border_touch_time) {
        var j, valid = true;
        for (j = i; j < i + v_chunks; ++j)
          if (this.next_unblock[i] > now || this.next_clear[i] > border_touch_time) {
            valid = false; break;
          }
        if (valid) {
          for (j = i; j < i + v_chunks; ++j) {
            this.next_unblock[j] = unblock_time;
            this.next_clear[j] = fully_out_time;
          }
          positioning_data = {top: comment_v_chunk_height * i, speed: speed};
          break;
        } else {
          i = j;
        }
      }
    }
    if (positioning_data === null) return false;
    comment_add_bullet(id, message, color, positioning_data.top, this.width, -speed, duration);
    return true;
  };

  var comment_board_cnt = 4;
  var comment_board_topslide = [];
  for (var i = 0; i < comment_board_cnt; ++i) {
    comment_board_topslide[i] = new CommentBoardTopSlide(document.documentElement.clientWidth, document.documentElement.clientHeight);
  }
  var comment_board_fire = function (c) {
    if (comment_type_names[c.position] === comment_types.TOP_SLIDE) {
      comment_board_topslide[0].fire(c.id, c.message, c.color);
    }
  };

  //var socket = new WebSocket('<?php $channel = new SaeChannel();echo $channel -> createChannel('danmaku',18000);?>');
  // Workaround x2...
  // Workaround x3: http://stackoverflow.com/questions/17088609/disable-firefox-same-origin-policy/29096229#29096229
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'http://app.efzer.org/live/2016newyear/danmaku.php', false);
  xhr.send();
  var socket = new WebSocket(xhr.response.substr(4299, 115));
  socket.onmessage = function (msg) {
    comment_board_fire(JSON.parse(msg.data));
    //var c = JSON.parse(msg.data);
    //comment_board_topslide[0].fire(c.id, c.message, c.color);
  };
  window.___socket = socket;
  window.f = comment_board_fire;
  // f({id: 233, message: 'xaxx', type: 'top', color: '#c0c0ff'})
}(window));
<|endoftext|>


<commit_before>var sortColumns = {
  'fulfilled-last': 'fulfilled_at',
  'top': 'post.num_votes',
  'recent': 'post.updated_at',
  'suggested': 'suggested',
  'start_time': ['post.start_time', 'asc']
}

var findPosts = function (req, res, opts) {
  var params = _.merge(
    _.pick(req.allParams(), [
      'sort', 'limit', 'offset', 'type', 'start_time', 'end_time', 'filter'
    ]),
    _.pick(opts, 'sort')
  )

  // using Promise.props here allows us to pass queries as attributes,
  // e.g. when looking up communities in PostController.findForUser
  Promise.props(_.merge(
    {
      sort: sortColumns[params.sort || 'recent'],
      forUser: req.session.userId
    },
    _.pick(params, 'type', 'limit', 'offset', 'start_time', 'end_time', 'filter'),
    _.pick(opts, 'communities', 'project', 'users', 'visibility')
  ))
  .then(args => Search.forPosts(args).fetchAll({
    withRelated: PostPresenter.relations(req.session.userId, opts.relationsOpts)
  }))
  .then(PostPresenter.mapPresentWithTotal)
  .then(res.ok, res.serverError)
}

var newPostAttrs = function (userId, params) {
  return _.merge(Post.newPostAttrs(), {
    name: RichText.sanitize(params.name),
    description: RichText.sanitize(params.description),
    user_id: userId,
    visibility: params.public ? Post.Visibility.PUBLIC_READABLE : Post.Visibility.DEFAULT
  }, _.pick(params, 'type', 'start_time', 'end_time'))
}

var afterSavingPost = function (post, opts) {
  var userId = post.get('user_id')
  var mentioned = RichText.getUserMentions(post.get('description'))
  var followerIds = _.uniq(mentioned.concat(userId))

  return Promise.all(_.flatten([
    // Attach post to communities
    opts.communities.map(id => new Community({id: id}).posts().attach(post.id, _.pick(opts, 'transacting'))),

    // Add mentioned users and creator as followers
    post.addFollowers(followerIds, userId, _.pick(opts, 'transacting')),

    // create activity and send notification to all mentioned users except the creator
    Promise.map(_.without(mentioned, userId), mentionedUserId => Post.notifyAboutMention(post, mentionedUserId, _.pick(opts, 'transacting'))),

    // Add image, if any
    (opts.imageUrl && Media.createImage(post.id, opts.imageUrl, opts.transacting)),

    (opts.docs && Promise.map(opts.docs, doc => Media.createDoc(post.id, doc, opts.transacting))),

    Queue.classMethod('Post', 'sendPushNotifications', {postId: post.id})

  ])).then(() => mentioned)
}

module.exports = {
  findOne: function (req, res) {
    res.locals.post.load(PostPresenter.relations(req.session.userId))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  findForUser: function (req, res) {
    findPosts(req, res, {
      users: [req.param('userId')],
      communities: Membership.activeCommunityIds(req.session.userId),
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForCommunity: function (req, res) {
    if (TokenAuth.isAuthenticated(res)) {
      if (!RequestValidation.requireTimeRange(req, res)) return
    }

    findPosts(req, res, {
      communities: [req.param('communityId')],
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForProject: function (req, res) {
    findPosts(req, res, {
      project: req.param('projectId'),
      relationsOpts: {fromProject: true},
      sort: 'fulfilled-last'
    })
  },

  findForNetwork: function (req, res) {
    Community.where({network_id: req.param('networkId')}).fetchAll()
    .then(communities => {
      findPosts(req, res, {
        communities: communities.map(c => c.id),
        visibility: [Post.Visibility.DEFAULT, Post.Visibility.PUBLIC_READABLE]
      })
    })
  },

  findFollowed: function (req, res) {
    Search.forPosts({
      follower: req.session.userId,
      limit: req.param('limit') || 10,
      offset: req.param('offset'),
      sort: 'post.updated_at',
      type: 'all+welcome'
    }).fetchAll({
      withRelated: PostPresenter.relations(req.session.userId)
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  findAllForUser: function (req, res) {
    Membership.activeCommunityIds(req.session.userId)
    .then(function (communityIds) {
      return Search.forPosts({
        communities: communityIds,
        limit: req.param('limit') || 10,
        offset: req.param('offset'),
        sort: sortColumns[req.param('sort') || 'recent'],
        type: req.param('type') || 'all+welcome',
        forUser: req.session.userId
      }).fetchAll({
        withRelated: PostPresenter.relations(req.session.userId)
      })
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  create: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: req.param('communities'),
        imageUrl: req.param('imageUrl'),
        docs: req.param('docs'),
        transacting: trx
      }))
    })
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  createFromEmail: function (req, res) {
    try {
      var replyData = Email.decodePostCreationAddress(req.param('To'))
    } catch (e) {
      return res.serverError(new Error('Invalid reply address: ' + req.param('To')))
    }

    var allParams = _.assign(req.allParams(), {'type': replyData.type})
    var attrs = newPostAttrs(replyData.userId, allParams)

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: [replyData.communityId],
        imageUrl: req.param('imageUrl'),
        docs: req.param('docs'),
        transacting: trx
      }))
    })
    .then(() => res.ok({}), res.serverError)
  },

  createForProject: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())
    var projectId = req.param('projectId')

    return Project.find(projectId)
    .tap(project => {
      if (project.isDraft()) attrs.visibility = Post.Visibility.DRAFT_PROJECT
    })
    .then(project => bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => PostProjectMembership.create(post.id, projectId, {transacting: trx}))
      .tap(post => afterSavingPost(post, {
        communities: [req.param('communityId')],
        imageUrl: req.param('imageUrl'),
        transacting: trx
      })
      .then(mentioned => {
        // Send notifications to project contributors
        Queue.classMethod('Project', 'notifyAboutNewPost', {
          projectId: projectId,
          postId: post.id,
          exclude: mentioned
        })
      }))
    }))
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  follow: function (req, res) {
    var userId = req.session.userId
    var post = res.locals.post
    Follower.query().where({user_id: userId, post_id: post.id}).count()
    .then(function (rows) {
      if (Number(rows[0].count) > 0 || req.param('force') === 'unfollow') {
        return post.removeFollower(userId, {createActivity: true})
        .then(() => res.ok({}))
      }

      return post.addFollowers([userId], userId, {createActivity: true}).then(function (follows) {
        return User.find(req.session.userId)
      }).then(function (user) {
        res.ok(_.pick(user.attributes, 'id', 'name', 'avatar_url'))
      })
    })
    .catch(res.serverError.bind(res))
  },

  update: function (req, res) {
    var post = res.locals.post
    var params = req.allParams()
    var attrs = _.extend(
      _.pick(params, 'name', 'description', 'type', 'start_time', 'end_time'),
      {
        edited: true,
        edited_timestamp: new Date(),
        visibility: params.public ? Post.Visibility.PUBLIC_READABLE : Post.Visibility.DEFAULT
      }
    )

    return bookshelf.transaction(function (trx) {
      return post.save(attrs, {patch: true, transacting: trx})
      .tap(() => {
        var newIds = req.param('communities').sort()
        var oldIds = post.relations.communities.pluck('id').sort()
        if (newIds !== oldIds) {
          return Promise.join(
            Promise.map(_.difference(newIds, oldIds), id =>
              post.communities().attach(id, {transacting: trx})),
            Promise.map(_.difference(oldIds, newIds), id =>
              post.communities().detach(id, {transacting: trx}))
          )
        }
      })
      .tap(() => {
        var mediaParams = ['docs', 'removedDocs', 'imageUrl', 'imageRemoved']
        var isSet = _.partial(_.has, params)
        if (_.any(mediaParams, isSet)) return post.load('media')
      })
      .tap(function () {
        if (!params.imageUrl && !params.imageRemoved) return
        var media = post.relations.media.find(m => m.get('type') === 'image')

        if (media && params.imageRemoved) { // remove media
          return media.destroy({transacting: trx})
        } else if (media) { // replace url in existing media
          if (media.get('url') !== params.imageUrl) {
            return media.save({url: params.imageUrl}, {patch: true, transacting: trx})
          }
        } else if (params.imageUrl) { // create new media
          return Media.createImage(post.id, params.imageUrl, trx)
        }
      })
      .tap(() => {
        if (!params.removedDocs) return
        return Promise.map(params.removedDocs, doc => {
          var media = post.relations.media.find(m => m.get('url') === doc.url)
          if (media) return media.destroy({transacting: trx})
        })
      })
      .tap(() => {
        if (!params.docs) return
        return Promise.map(params.docs, doc => {
          var media = post.relations.media.find(m => m.get('url') === doc.url)
          if (!media) return Media.createDoc(post.id, doc, trx)
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  fulfill: function (req, res) {
    bookshelf.transaction(function (trx) {
      return res.locals.post.save({
        fulfilled_at: new Date()
      }, {patch: true, transacting: trx})
      .tap(function () {
        return Promise.map(req.param('contributors'), function (userId) {
          return new Contribution({
            post_id: res.locals.post.id,
            user_id: userId,
            date_contributed: new Date()
          }).save(null, {transacting: trx})
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  vote: function (req, res) {
    var post = res.locals.post

    post.votes().query({where: {user_id: req.session.userId}}).fetchOne()
    .then(vote => bookshelf.transaction(trx => {
      var inc = delta => () => Post.query().where('id', post.id).increment('num_votes', delta).transacting(trx)

      return (vote
        ? vote.destroy({transacting: trx}).then(inc(-1))
        : new Vote({
          post_id: res.locals.post.id,
          user_id: req.session.userId
        }).save().then(inc(1)))
    }))
    .then(() => res.ok({}), res.serverError)
  },

  destroy: function (req, res) {
    return bookshelf.transaction(function (trx) {
      // FIXME this post will still be accessible via activity about a comment
      return Promise.join(
        Activity.where('post_id', res.locals.post.id).destroy({transacting: trx}),
        res.locals.post.save({active: false}, {patch: true, transacting: trx})
      )
    })
    .then(() => res.ok({}), res.serverError)
  },

  complain: function (req, res) {
    var post = res.locals.post
    var community = post.relations.communities.first()

    User.find(req.session.userId)
    .then(user => Email.sendRawEmail('hello@hylo.com', {
      subject: 'Objectionable content report',
      body: format(
        '%s <%s> has flagged %s as objectionable',
        user.get('name'), user.get('email'),
        Frontend.Route.post(post, community)
      )
    }))
    .then(() => res.ok({}), res.serverError)
  }

}
<commit_msg>reassign params from mailgun post<commit_after>var sortColumns = {
  'fulfilled-last': 'fulfilled_at',
  'top': 'post.num_votes',
  'recent': 'post.updated_at',
  'suggested': 'suggested',
  'start_time': ['post.start_time', 'asc']
}

var findPosts = function (req, res, opts) {
  var params = _.merge(
    _.pick(req.allParams(), [
      'sort', 'limit', 'offset', 'type', 'start_time', 'end_time', 'filter'
    ]),
    _.pick(opts, 'sort')
  )

  // using Promise.props here allows us to pass queries as attributes,
  // e.g. when looking up communities in PostController.findForUser
  Promise.props(_.merge(
    {
      sort: sortColumns[params.sort || 'recent'],
      forUser: req.session.userId
    },
    _.pick(params, 'type', 'limit', 'offset', 'start_time', 'end_time', 'filter'),
    _.pick(opts, 'communities', 'project', 'users', 'visibility')
  ))
  .then(args => Search.forPosts(args).fetchAll({
    withRelated: PostPresenter.relations(req.session.userId, opts.relationsOpts)
  }))
  .then(PostPresenter.mapPresentWithTotal)
  .then(res.ok, res.serverError)
}

var newPostAttrs = function (userId, params) {
  return _.merge(Post.newPostAttrs(), {
    name: RichText.sanitize(params.name),
    description: RichText.sanitize(params.description),
    user_id: userId,
    visibility: params.public ? Post.Visibility.PUBLIC_READABLE : Post.Visibility.DEFAULT
  }, _.pick(params, 'type', 'start_time', 'end_time'))
}

var afterSavingPost = function (post, opts) {
  var userId = post.get('user_id')
  var mentioned = RichText.getUserMentions(post.get('description'))
  var followerIds = _.uniq(mentioned.concat(userId))

  return Promise.all(_.flatten([
    // Attach post to communities
    opts.communities.map(id => new Community({id: id}).posts().attach(post.id, _.pick(opts, 'transacting'))),

    // Add mentioned users and creator as followers
    post.addFollowers(followerIds, userId, _.pick(opts, 'transacting')),

    // create activity and send notification to all mentioned users except the creator
    Promise.map(_.without(mentioned, userId), mentionedUserId => Post.notifyAboutMention(post, mentionedUserId, _.pick(opts, 'transacting'))),

    // Add image, if any
    (opts.imageUrl && Media.createImage(post.id, opts.imageUrl, opts.transacting)),

    (opts.docs && Promise.map(opts.docs, doc => Media.createDoc(post.id, doc, opts.transacting))),

    Queue.classMethod('Post', 'sendPushNotifications', {postId: post.id})

  ])).then(() => mentioned)
}

module.exports = {
  findOne: function (req, res) {
    res.locals.post.load(PostPresenter.relations(req.session.userId))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  findForUser: function (req, res) {
    findPosts(req, res, {
      users: [req.param('userId')],
      communities: Membership.activeCommunityIds(req.session.userId),
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForCommunity: function (req, res) {
    if (TokenAuth.isAuthenticated(res)) {
      if (!RequestValidation.requireTimeRange(req, res)) return
    }

    findPosts(req, res, {
      communities: [req.param('communityId')],
      visibility: (req.session.userId ? null : Post.Visibility.PUBLIC_READABLE)
    })
  },

  findForProject: function (req, res) {
    findPosts(req, res, {
      project: req.param('projectId'),
      relationsOpts: {fromProject: true},
      sort: 'fulfilled-last'
    })
  },

  findForNetwork: function (req, res) {
    Community.where({network_id: req.param('networkId')}).fetchAll()
    .then(communities => {
      findPosts(req, res, {
        communities: communities.map(c => c.id),
        visibility: [Post.Visibility.DEFAULT, Post.Visibility.PUBLIC_READABLE]
      })
    })
  },

  findFollowed: function (req, res) {
    Search.forPosts({
      follower: req.session.userId,
      limit: req.param('limit') || 10,
      offset: req.param('offset'),
      sort: 'post.updated_at',
      type: 'all+welcome'
    }).fetchAll({
      withRelated: PostPresenter.relations(req.session.userId)
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  findAllForUser: function (req, res) {
    Membership.activeCommunityIds(req.session.userId)
    .then(function (communityIds) {
      return Search.forPosts({
        communities: communityIds,
        limit: req.param('limit') || 10,
        offset: req.param('offset'),
        sort: sortColumns[req.param('sort') || 'recent'],
        type: req.param('type') || 'all+welcome',
        forUser: req.session.userId
      }).fetchAll({
        withRelated: PostPresenter.relations(req.session.userId)
      })
    })
    .then(PostPresenter.mapPresentWithTotal)
    .then(res.ok)
    .catch(res.serverError)
  },

  create: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: req.param('communities'),
        imageUrl: req.param('imageUrl'),
        docs: req.param('docs'),
        transacting: trx
      }))
    })
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  createFromEmail: function (req, res) {
    try {
      var replyData = Email.decodePostCreationAddress(req.param('To'))
    } catch (e) {
      return res.serverError(new Error('Invalid reply address: ' + req.param('To')))
    }

    var allParams = _.assign(req.allParams(), {'type': replyData.type})
    allParams.name = allParams['subject']
    allParams.description = allParams['stripped-text']
    var attrs = newPostAttrs(replyData.userId, allParams)

    return bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => afterSavingPost(post, {
        communities: [replyData.communityId],
        imageUrl: req.param('imageUrl'),
        docs: req.param('docs'),
        transacting: trx
      }))
    })
    .then(() => res.ok({}), res.serverError)
  },

  createForProject: function (req, res) {
    var attrs = newPostAttrs(req.session.userId, req.allParams())
    var projectId = req.param('projectId')

    return Project.find(projectId)
    .tap(project => {
      if (project.isDraft()) attrs.visibility = Post.Visibility.DRAFT_PROJECT
    })
    .then(project => bookshelf.transaction(trx => {
      return new Post(attrs).save(null, {transacting: trx})
      .tap(post => PostProjectMembership.create(post.id, projectId, {transacting: trx}))
      .tap(post => afterSavingPost(post, {
        communities: [req.param('communityId')],
        imageUrl: req.param('imageUrl'),
        transacting: trx
      })
      .then(mentioned => {
        // Send notifications to project contributors
        Queue.classMethod('Project', 'notifyAboutNewPost', {
          projectId: projectId,
          postId: post.id,
          exclude: mentioned
        })
      }))
    }))
    .then(post => post.load(PostPresenter.relations(req.session.userId)))
    .then(PostPresenter.present)
    .then(res.ok)
    .catch(res.serverError)
  },

  follow: function (req, res) {
    var userId = req.session.userId
    var post = res.locals.post
    Follower.query().where({user_id: userId, post_id: post.id}).count()
    .then(function (rows) {
      if (Number(rows[0].count) > 0 || req.param('force') === 'unfollow') {
        return post.removeFollower(userId, {createActivity: true})
        .then(() => res.ok({}))
      }

      return post.addFollowers([userId], userId, {createActivity: true}).then(function (follows) {
        return User.find(req.session.userId)
      }).then(function (user) {
        res.ok(_.pick(user.attributes, 'id', 'name', 'avatar_url'))
      })
    })
    .catch(res.serverError.bind(res))
  },

  update: function (req, res) {
    var post = res.locals.post
    var params = req.allParams()
    var attrs = _.extend(
      _.pick(params, 'name', 'description', 'type', 'start_time', 'end_time'),
      {
        edited: true,
        edited_timestamp: new Date(),
        visibility: params.public ? Post.Visibility.PUBLIC_READABLE : Post.Visibility.DEFAULT
      }
    )

    return bookshelf.transaction(function (trx) {
      return post.save(attrs, {patch: true, transacting: trx})
      .tap(() => {
        var newIds = req.param('communities').sort()
        var oldIds = post.relations.communities.pluck('id').sort()
        if (newIds !== oldIds) {
          return Promise.join(
            Promise.map(_.difference(newIds, oldIds), id =>
              post.communities().attach(id, {transacting: trx})),
            Promise.map(_.difference(oldIds, newIds), id =>
              post.communities().detach(id, {transacting: trx}))
          )
        }
      })
      .tap(() => {
        var mediaParams = ['docs', 'removedDocs', 'imageUrl', 'imageRemoved']
        var isSet = _.partial(_.has, params)
        if (_.any(mediaParams, isSet)) return post.load('media')
      })
      .tap(function () {
        if (!params.imageUrl && !params.imageRemoved) return
        var media = post.relations.media.find(m => m.get('type') === 'image')

        if (media && params.imageRemoved) { // remove media
          return media.destroy({transacting: trx})
        } else if (media) { // replace url in existing media
          if (media.get('url') !== params.imageUrl) {
            return media.save({url: params.imageUrl}, {patch: true, transacting: trx})
          }
        } else if (params.imageUrl) { // create new media
          return Media.createImage(post.id, params.imageUrl, trx)
        }
      })
      .tap(() => {
        if (!params.removedDocs) return
        return Promise.map(params.removedDocs, doc => {
          var media = post.relations.media.find(m => m.get('url') === doc.url)
          if (media) return media.destroy({transacting: trx})
        })
      })
      .tap(() => {
        if (!params.docs) return
        return Promise.map(params.docs, doc => {
          var media = post.relations.media.find(m => m.get('url') === doc.url)
          if (!media) return Media.createDoc(post.id, doc, trx)
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  fulfill: function (req, res) {
    bookshelf.transaction(function (trx) {
      return res.locals.post.save({
        fulfilled_at: new Date()
      }, {patch: true, transacting: trx})
      .tap(function () {
        return Promise.map(req.param('contributors'), function (userId) {
          return new Contribution({
            post_id: res.locals.post.id,
            user_id: userId,
            date_contributed: new Date()
          }).save(null, {transacting: trx})
        })
      })
    })
    .then(() => res.ok({}))
    .catch(res.serverError)
  },

  vote: function (req, res) {
    var post = res.locals.post

    post.votes().query({where: {user_id: req.session.userId}}).fetchOne()
    .then(vote => bookshelf.transaction(trx => {
      var inc = delta => () => Post.query().where('id', post.id).increment('num_votes', delta).transacting(trx)

      return (vote
        ? vote.destroy({transacting: trx}).then(inc(-1))
        : new Vote({
          post_id: res.locals.post.id,
          user_id: req.session.userId
        }).save().then(inc(1)))
    }))
    .then(() => res.ok({}), res.serverError)
  },

  destroy: function (req, res) {
    return bookshelf.transaction(function (trx) {
      // FIXME this post will still be accessible via activity about a comment
      return Promise.join(
        Activity.where('post_id', res.locals.post.id).destroy({transacting: trx}),
        res.locals.post.save({active: false}, {patch: true, transacting: trx})
      )
    })
    .then(() => res.ok({}), res.serverError)
  },

  complain: function (req, res) {
    var post = res.locals.post
    var community = post.relations.communities.first()

    User.find(req.session.userId)
    .then(user => Email.sendRawEmail('hello@hylo.com', {
      subject: 'Objectionable content report',
      body: format(
        '%s <%s> has flagged %s as objectionable',
        user.get('name'), user.get('email'),
        Frontend.Route.post(post, community)
      )
    }))
    .then(() => res.ok({}), res.serverError)
  }

}
<|endoftext|>


<commit_before>var app = angular.module('RadioBrowserApp');

app.directive('ngEnter', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if (event.which === 13) {
        scope.$apply(function() {
          scope.$eval(attrs.ngEnter);
        });

        event.preventDefault();
      }
    });
  };
});

app.controller('MainController', function($scope, $http, $sce, $httpParamSerializerJQLike) {
  $scope.bigTotalItems = 0;
  $scope.bigCurrentPage = 1;
  $scope.itemsPerPage = 20;
  $scope.countryList = [];
  $scope.playerItem = null;
  $scope.audioVolume = 1;
  $scope.editStation = null;
  var audio = null;
  $scope.tab = "home";
  const serverAdress = "http://www.radio-browser.info";

  $http.get(serverAdress+'/webservice/json/stats').then(function(data) {
    $scope.stats = data.data;
  }, function(err) {
    console.log("error:" + err);
  });

  $scope.changeItemsPerPage = function(items) {
    $scope.itemsPerPage = items;
    $scope.updateList();
  }

  $scope.addTag = function(tag) {
    $scope.editStation.tags_arr.splice(0, 0, tag);
    $scope.editStation.tag = "";
  }

  $scope.removeTag = function(tag) {
    var index = $scope.editStation.tags_arr.indexOf(tag);
    if (index !== -1) {
      $scope.editStation.tags_arr.splice(index, 1);
    }
  }

  $scope.edit = function(station) {
    console.log(JSON.stringify(station));
    $scope.setTab("editstation");
    $scope.editStation = station;
    if (station.tags.trim() === "") {
      $scope.editStation.tags_arr = [];
    } else {
      $scope.editStation.tags_arr = station.tags.split(',');
    }

  }

  function replaceStations(stationupdates) {
    for (var i = 0; i < stationupdates.length; i++) {
      var stationNew = stationupdates[i];
      replaceStation(stationNew);
    }
  }

  function replaceStation(stationNew) {
    for (var i = 0; i < $scope.resultListFull.length; i++) {
      var station = $scope.resultListFull[i];
      if (station.id === stationNew.id) {
        $scope.resultListFull.splice(i, 1, stationNew);
      }
    }
  }

  function getStationById(id) {
    for (var i = 0; i < $scope.resultListFull.length; i++) {
      var station = $scope.resultListFull[i];
      if (station.id === id) {
        return station;
      }
    }
    return null;
  }

  $scope.setTab = function(tab) {
    console.log("tab=" + tab);
    $scope.tab = tab;

    if (tab === "home") {
      $scope.clearList();
    }
    if (tab === "byclicks") {
      $scope.displayTopClick();
    }
    if (tab === "byvotes") {
      $scope.displayTopVote();
    }
    if (tab === "bycountry") {
      $scope.displayCountries();
    }
    if (tab === "bylanguage") {
      $scope.displayLanguages();
    }
    if (tab === "bytag") {
      $scope.displayTags();
    }
    if (tab === "latelychanged") {
      $scope.displayLastChanged();
    }
    if (tab === "latelyplayed") {
      $scope.displayLastPlayed();
    }
    if (tab === "editstation") {
      $scope.clearList();
      $scope.editStation = {};
      $scope.editStation.homepage = "";
      $scope.editStation.favicon = "";
      $scope.editStation.country = "";
      $scope.editStation.language = "";
      $scope.editStation.tags = "";
      $scope.editStation.subcountry = "";
      $scope.editStation.tags_arr = [];
    }
    if (tab === "api") {
      $scope.clearList();
    }
    if (tab !== 'editstation') {
      $scope.editStation = null;
    }
  }

  $scope.sendStation = function() {
    if ($scope.editStation !== null) {
      console.log("---" + $scope.editStation.id);
      $scope.editStation.tags = $scope.editStation.tags_arr.join(',');
      if (undefined === $scope.editStation.id) {
        url = serverAdress+'/webservice/add';
      } else {
        url = serverAdress+'/webservice/edit';
        $scope.editStation.stationid = $scope.editStation.id;
      }
      $http({
        url: url,
        method: 'POST',
        data: $httpParamSerializerJQLike($scope.editStation),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }).success(function(response) {
        console.log("ok:" + response);
        $scope.editStation = null;
        $scope.clearList();
      });
    }
  }

  $scope.vote = function(stationid) {
    $http.get(serverAdress+'/webservice/json/vote/' + stationid).then(function(data) {
      replaceStations(data.data);
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.clearList = function() {
    $scope.resultListFull = [];
    $scope.bigCurrentPage = 1;
    $scope.bigTotalItems = 0;
    $scope.updateList();
  }

  $scope.displayCountries = function() {
    $http.get(serverAdress+'/webservice/json/countries').then(function(data) {
      $scope.countryList = data.data;
      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLanguages = function() {
    $http.get(serverAdress+'/webservice/json/languages').then(function(data) {
      $scope.languageList = data.data;
      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTags = function() {
    $http.get(serverAdress+'/webservice/json/tags').then(function(data) {
      $scope.tagList = data.data;
      $scope.tagListVeryPopular = [];
      $scope.tagListPopular = [];
      $scope.tagListNotPopular = [];

      for (var i = 0; i < $scope.tagList.length; i++) {
        var tag = $scope.tagList[i];
        if (tag.stationcount >= 10) {
          $scope.tagListVeryPopular.push(tag);
        } else if (tag.stationcount > 1) {
          $scope.tagListPopular.push(tag);
        } else {
          $scope.tagListNotPopular.push(tag);
        }
      }

      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByCountry = function(country) {
    $http.get(serverAdress+'/webservice/json/stations/bycountryexact/' + encodeURIComponent(country)).then(function(data) {
      $scope.countryList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByState = function(state) {
    $http.get(serverAdress+'/webservice/json/stations/bystateexact/' + encodeURIComponent(state)).then(function(data) {
      $scope.countryList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByLanguage = function(language) {
    $http.get(serverAdress+'/webservice/json/stations/bylanguageexact/' + encodeURIComponent(language)).then(function(data) {
      $scope.languageList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByTag = function(tag) {
    $http.get(serverAdress+'/webservice/json/stations/bytagexact/' + encodeURIComponent(tag)).then(function(data) {
      $scope.tagList = [];
      $scope.tagListPopular = [];
      $scope.tagListNotPopular = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTopClick = function() {
    $http.get(serverAdress+'/webservice/json/stations/topclick/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTopVote = function() {
    $http.get(serverAdress+'/webservice/json/stations/topvote/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLastChanged = function() {
    $http.get(serverAdress+'/webservice/json/stations/lastchange/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLastPlayed = function() {
    $http.get(serverAdress+'/webservice/json/stations/lastclick/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.play = function(id) {
    // decode playlist
    var decodeUrl = "http://www.radio-browser.info/webservice/json/url/" + id;
    $http.get(decodeUrl).then(function(data) {
      if (data.data.length > 0) {
        var station = data.data[0];
        $scope.playerItem = station;
        PlayAudioStream(data.data[0].url);
      }
    }, function(err) {
      console.log("error:" + err);
      alert("could not find station");
    });
  }

  function PlayAudioStream(url) {
    // do play audio
    if (audio !== null) {
      audio.src = url;
      audio.play();
    } else {
      audio = new Audio(url);
      audio.volume = 1;
      audio.onplay = function() {
        console.log("play ok");
      };
      audio.onerror = function() {
        console.log("error on play");
        $scope.$apply(function() {
          $scope.playerItem = null;
          audio.pause();
        });
        alert("browser is not able to play station. please try with external player.");
      };
      audio.play();
    }
  }

  $scope.setVolume = function(volume) {
    if (audio) {
      audio.volume = volume;
    }
  }

  $scope.stopAudio = function() {
    if (audio) {
      $scope.playerItem = null;
      audio.pause();
    }
  }

  $scope.doSearch = function(term) {
    $http.get(serverAdress+'/webservice/json/stations/byname/' + encodeURIComponent(term)).then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
      $scope.setTab('search');
    }, function(err) {
      console.log("error:" + err);
    });
  };

  $scope.updateList = function() {
    $scope.resultList = $scope.resultListFull.slice(($scope.bigCurrentPage - 1) * $scope.itemsPerPage, ($scope.bigCurrentPage) * $scope.itemsPerPage);
  }

  $scope.getCodecs = function(term) {
    return $http.get(serverAdress+'/webservice/json/codecs/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getCountries = function(term) {
    return $http.get(serverAdress+'/webservice/json/countries/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getStates = function(term) {
    return $http.get(serverAdress+'/webservice/json/states/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getLanguages = function(term) {
    return $http.get(serverAdress+'/webservice/json/languages/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getTags = function(term) {
    return $http.get(serverAdress+'/webservice/json/tags/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getTagsArray = function(tags_string) {
    if (tags_string.trim() === "") {
      return [];
    }
    return tags_string.split(',');
  };
});
<commit_msg><commit_after>var app = angular.module('RadioBrowserApp');

app.directive('ngEnter', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if (event.which === 13) {
        scope.$apply(function() {
          scope.$eval(attrs.ngEnter);
        });

        event.preventDefault();
      }
    });
  };
});

app.controller('MainController', function($scope, $http, $sce, $httpParamSerializerJQLike) {
  $scope.bigTotalItems = 0;
  $scope.bigCurrentPage = 1;
  $scope.itemsPerPage = 20;
  $scope.countryList = [];
  $scope.playerItem = null;
  $scope.audioVolume = 1;
  $scope.editStation = null;
  var audio = null;
  $scope.tab = "home";
  const serverAdress = "http://www.radio-browser.info";

  $http.get(serverAdress+'/webservice/json/stats').then(function(data) {
    $scope.stats = data.data;
  }, function(err) {
    console.log("error:" + err);
  });

  $scope.changeItemsPerPage = function(items) {
    $scope.itemsPerPage = items;
    $scope.updateList();
  }

  $scope.addTag = function(tag) {
    $scope.editStation.tags_arr.splice(0, 0, tag);
    $scope.editStation.tag = "";
  }

  $scope.removeTag = function(tag) {
    var index = $scope.editStation.tags_arr.indexOf(tag);
    if (index !== -1) {
      $scope.editStation.tags_arr.splice(index, 1);
    }
  }

  $scope.edit = function(station) {
    console.log(JSON.stringify(station));
    $scope.setTab("editstation");
    $scope.editStation = station;
    if (station.tags.trim() === "") {
      $scope.editStation.tags_arr = [];
    } else {
      $scope.editStation.tags_arr = station.tags.split(',');
    }

  }

  function replaceStations(stationupdates) {
    for (var i = 0; i < stationupdates.length; i++) {
      var stationNew = stationupdates[i];
      replaceStation(stationNew);
    }
  }

  function replaceStation(stationNew) {
    for (var i = 0; i < $scope.resultListFull.length; i++) {
      var station = $scope.resultListFull[i];
      if (station.id === stationNew.id) {
        $scope.resultListFull.splice(i, 1, stationNew);
      }
    }
  }

  function getStationById(id) {
    for (var i = 0; i < $scope.resultListFull.length; i++) {
      var station = $scope.resultListFull[i];
      if (station.id === id) {
        return station;
      }
    }
    return null;
  }

  $scope.setTab = function(tab) {
    console.log("tab=" + tab);
    $scope.tab = tab;

    if (tab === "home") {
      $scope.clearList();
    }
    if (tab === "byclicks") {
      $scope.displayTopClick();
    }
    if (tab === "byvotes") {
      $scope.displayTopVote();
    }
    if (tab === "bycountry") {
      $scope.displayCountries();
    }
    if (tab === "bylanguage") {
      $scope.displayLanguages();
    }
    if (tab === "bytag") {
      $scope.displayTags();
    }
    if (tab === "latelychanged") {
      $scope.displayLastChanged();
    }
    if (tab === "latelyplayed") {
      $scope.displayLastPlayed();
    }
    if (tab === "editstation") {
      $scope.clearList();
      $scope.editStation = {};
      $scope.editStation.homepage = "";
      $scope.editStation.favicon = "";
      $scope.editStation.country = "";
      $scope.editStation.language = "";
      $scope.editStation.tags = "";
      $scope.editStation.subcountry = "";
      $scope.editStation.tags_arr = [];
    }
    if (tab === "api") {
      $scope.clearList();
    }
    if (tab !== 'editstation') {
      $scope.editStation = null;
    }
  }

  $scope.sendStation = function() {
    if ($scope.editStation !== null) {
      console.log("---" + $scope.editStation.id);
      $scope.editStation.tags = $scope.editStation.tags_arr.join(',');
      if (undefined === $scope.editStation.id) {
        url = serverAdress+'/webservice/add';
      } else {
        url = serverAdress+'/webservice/edit';
        $scope.editStation.stationid = $scope.editStation.id;
      }
      $http({
        url: url,
        method: 'POST',
        data: $httpParamSerializerJQLike($scope.editStation),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded'
        }
      }).success(function(response) {
        console.log("ok:" + response);
        $scope.editStation = null;
        $scope.clearList();
      });
    }
  }

  $scope.vote = function(stationid) {
    $http.get(serverAdress+'/webservice/json/vote/' + stationid).then(function(data) {
      replaceStations(data.data);
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.clearList = function() {
    $scope.resultListFull = [];
    $scope.bigCurrentPage = 1;
    $scope.bigTotalItems = 0;
    $scope.updateList();
  }

  $scope.displayCountries = function() {
    $http.get(serverAdress+'/webservice/json/countries').then(function(data) {
      $scope.countryList = data.data;
      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLanguages = function() {
    $http.get(serverAdress+'/webservice/json/languages').then(function(data) {
      $scope.languageList = data.data;
      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTags = function() {
    $http.get(serverAdress+'/webservice/json/tags').then(function(data) {
      $scope.tagList = data.data;
      $scope.tagListVeryPopular = [];
      $scope.tagListPopular = [];
      $scope.tagListNotPopular = [];

      for (var i = 0; i < $scope.tagList.length; i++) {
        var tag = $scope.tagList[i];
        if (tag.stationcount >= 10) {
          $scope.tagListVeryPopular.push(tag);
        } else if (tag.stationcount > 1) {
          $scope.tagListPopular.push(tag);
        } else {
          $scope.tagListNotPopular.push(tag);
        }
      }

      $scope.clearList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByCountry = function(country) {
    $http.get(serverAdress+'/webservice/json/stations/bycountryexact/' + encodeURIComponent(country)).then(function(data) {
      $scope.countryList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByState = function(state) {
    $http.get(serverAdress+'/webservice/json/stations/bystateexact/' + encodeURIComponent(state)).then(function(data) {
      $scope.countryList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByLanguage = function(language) {
    $http.get(serverAdress+'/webservice/json/stations/bylanguageexact/' + encodeURIComponent(language)).then(function(data) {
      $scope.languageList = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayByTag = function(tag) {
    $http.get(serverAdress+'/webservice/json/stations/bytagexact/' + encodeURIComponent(tag)).then(function(data) {
      $scope.tagList = [];
      $scope.tagListPopular = [];
      $scope.tagListNotPopular = [];
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTopClick = function() {
    $http.get(serverAdress+'/webservice/json/stations/topclick/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayTopVote = function() {
    $http.get(serverAdress+'/webservice/json/stations/topvote/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLastChanged = function() {
    $http.get(serverAdress+'/webservice/json/stations/lastchange/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.displayLastPlayed = function() {
    $http.get(serverAdress+'/webservice/json/stations/lastclick/100').then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
    }, function(err) {
      console.log("error:" + err);
    });
  }

  $scope.play = function(id) {
    // decode playlist
    var decodeUrl = serverAdress + "/webservice/json/url/" + id;
    $http.get(decodeUrl).then(function(data) {
      if (data.data.length > 0) {
        var station = data.data[0];
        $scope.playerItem = station;
        PlayAudioStream(data.data[0].url);
      }
    }, function(err) {
      console.log("error:" + err);
      alert("could not find station");
    });
  }

  function PlayAudioStream(url) {
    // do play audio
    if (audio !== null) {
      audio.src = url;
      audio.play();
    } else {
      audio = new Audio(url);
      audio.volume = 1;
      audio.onplay = function() {
        console.log("play ok");
      };
      audio.onerror = function() {
        console.log("error on play");
        $scope.$apply(function() {
          $scope.playerItem = null;
          audio.pause();
        });
        alert("browser is not able to play station. please try with external player.");
      };
      audio.play();
    }
  }

  $scope.setVolume = function(volume) {
    if (audio) {
      audio.volume = volume;
    }
  }

  $scope.stopAudio = function() {
    if (audio) {
      $scope.playerItem = null;
      audio.pause();
    }
  }

  $scope.doSearch = function(term) {
    $http.get(serverAdress+'/webservice/json/stations/byname/' + encodeURIComponent(term)).then(function(data) {
      $scope.resultListFull = data.data;
      $scope.bigCurrentPage = 1;
      $scope.bigTotalItems = data.data.length;
      $scope.updateList();
      $scope.setTab('search');
    }, function(err) {
      console.log("error:" + err);
    });
  };

  $scope.updateList = function() {
    $scope.resultList = $scope.resultListFull.slice(($scope.bigCurrentPage - 1) * $scope.itemsPerPage, ($scope.bigCurrentPage) * $scope.itemsPerPage);
  }

  $scope.getCodecs = function(term) {
    return $http.get(serverAdress+'/webservice/json/codecs/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getCountries = function(term) {
    return $http.get(serverAdress+'/webservice/json/countries/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getStates = function(term) {
    return $http.get(serverAdress+'/webservice/json/states/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getLanguages = function(term) {
    return $http.get(serverAdress+'/webservice/json/languages/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getTags = function(term) {
    return $http.get(serverAdress+'/webservice/json/tags/' + encodeURIComponent(term), {}).then(function(response) {
      return response.data.slice(0, 5);
    });
  };

  $scope.getTagsArray = function(tags_string) {
    if (tags_string.trim() === "") {
      return [];
    }
    return tags_string.split(',');
  };
});
<|endoftext|>