<commit_before>var Board = function (firebase, size, gameId) {
    this.templateCreate = _.template($('#template-game').html());
    this.fb = firebase;
    this.size = parseInt(size);
    this.gameId = gameId;
    this.stones = [];
    this.init(this.size);
};

Board.Goban = {small: 9, medium: 13, large: 19};

Board.prototype.init = function (size) {
    this.generateGoban(size);
    for (var x = 1; x <= this.size; x++) {
        this.stones[x] = [];
    }
};

Board.prototype.get = function (x, y) {
    var _ref;
    return (_ref = this.stones[x]) !== null ? _ref[y] : void 0;
};

Board.prototype.setStone = function (x, y, color) {
    if (this.isOkSetStone(x, y, color)) {
        this.setClassName(x, y, color);
        this.stones[x][y] = color;
    }
};

Board.prototype.setStoneFirebase = function (x, y, color, playerNum) {
    if (this.isOkSetStone(x, y, color) && !_.isNull(playerNum)) {

        var self = this;
        var token = 'games/' + this.gameId + '/players/token';

        this.fb.once(token, 'value').then(function (snap) {
            var value = snap.val();

            if (!_.isNull(value) && _.isEqual(playerNum, value)) {
                var oldValue = self.stones[x][y];
                self.setClassName(x, y, color);
                self.stones[x][y] = color;

                self.fb.setStone(x, y, color).then(function () {
                    self.fb.switchToken(playerNum);
                }, function () {
                    //delete self.stones[x][y];
                    self.stones[x][y] = oldValue;
                });
            } else {
                toastr.error('This is your opponent\'s turn ');
            }
        });
    }
};

Board.prototype.skipTurnFirebase = function (playerNum) {
    if (_.isNull(playerNum)) {
        console.error('skipTurnFirebase(), playerNum is null');
        return;
    }

    var self = this;
    this.fb.once('players/token', 'value').then(function (snap) {
        if (_.isEqual(snap.val(), playerNum)) {
            toastr.success('You skip your turn');
            self.fb.switchToken(playerNum);
        } else {
            toastr.error('This is your opponent\'s turn ');
        }
    });
};

Board.prototype.removeStone = function (x, y, playerNum) {
    if (!this.isCoordOnGoban(x, y)) {
        toastr.success('Outside goban');
        return false;
    }

    if (_.isUndefined(this.stones[x]) || _.isUndefined(this.stones[x][y])) {
        return;
    }

    this.removeClassName(x, y);
    if (_.isNumber(playerNum)) {
        this.fb.removeStone(x, y, playerNum);
    }
};

Board.prototype.isCoordOnGoban = function (x, y) {
    return 0 < x && x <= this.size && 0 < y && y <= this.size;
};

Board.prototype.isColorValid = function (color) {
    return (_.isEqual(color, Game.color.BLACK) || _.isEqual(color, Game.color.WHITE));
};

Board.prototype.isOkSetStone = function (x, y, color) {
    if (!this.isCoordOnGoban(x, y) || !this.isColorValid(color)) {
        toastr.error('Outside goban or color invalid');
        return false;
    }

    if (this.stones[x][y] !== undefined && this.stones[x][y] !== color) {
        toastr.error('A stone already exists');
        return false;
    }
    return true;
};

Board.prototype.convertStringToInt = function (str) {
    var start = "A".toUpperCase().charCodeAt(0);
    return str.charCodeAt(0) - start;
};

Board.prototype.convertIntToString = function (int) {
    var start = "A".toUpperCase().charCodeAt(0);
    return String.fromCharCode(start + int);
};

Board.prototype.setClassName = function (x, y, color) {
    var d = this.removeClassName(x, y);
    if (!_.isNull(d)) {
        d.className = d.className + " stone " + color.toLowerCase();
    }
};

Board.prototype.removeClassName = function (x, y) {
    var d = document.getElementById(x + "-" + y);
    if (_.isNull(d)) {
        console.error("getElementById is null for ", x, y);
        return false;
    }

    d.className = d.className.replace(" stone", "")
        .replace(Game.color.BLACK.toLowerCase(), "")
        .replace(Game.color.WHITE.toLowerCase(), "");
    delete this.stones[x][y];

    return d;
};

Board.prototype.generatorSVG = function (size) {
    var boardGo = document.getElementById('board');
    var canvasGoban = document.getElementById('canvasGoban');
    var svgns = 'http://www.w3.org/2000/svg';

    var svg = document.createElementNS(svgns, 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    svg.setAttribute('viewBox', '0 0 100 100');
    svg.setAttribute('preserveAspectRatio', 'none');
    svg.setAttribute('style', 'padding:' + (100 / ((size - 1) * 2)) + '%;');

    var defs = document.createElementNS(svgns, "defs");

    var path = document.createElementNS(svgns, "path");
    path.setAttribute('d', 'M 100 0 L 100 100 0 100');
    path.setAttribute('fill', 'none');
    path.setAttribute('stroke', 'black');
    path.setAttribute('stroke-width', '.4');

    var pattern = document.createElementNS(svgns, "pattern");
    pattern.setAttribute('id', 'grid');
    pattern.setAttribute('width', 100 / (size - 1) + '%');
    pattern.setAttribute('height', 100 / (size - 1) + '%');
    pattern.setAttribute('patternUnits', 'userSpaceOnUse');

    var patternPath = document.createElementNS(svgns, "path");
    patternPath.setAttribute('d', 'M 30 0 L 0 0 0 30');
    patternPath.setAttribute('fill', 'none');
    patternPath.setAttribute('stroke', 'black');
    patternPath.setAttribute('stroke-width', '.8');

    pattern.appendChild(patternPath);
    defs.appendChild(pattern);

    var hoshi9X = ['25.2%', '25.2%', '75.2%', '75.2%'];
    var hoshi9Y = ['25.2%', '75.2%', '25.2%', '75.2%'];
    var space1 = ((Math.round(((100 / (size - 1)) * 3) * 100) / 100) + 0.2) + '%';
    var space2 = ((Math.round(((100 / (size - 1)) * ((size - 1) / 2)) * 100) / 100) + 0.2) + '%';
    var space3 = ((Math.round(((100 / (size - 1)) * (size - 4)) * 100) / 100) + 0.2) + '%';
    var hoshiX = [space1, space1, space1, space2, space2, space2, space3, space3, space3];
    var hoshiY = [space1, space2, space3, space1, space2, space3, space1, space2, space3];

    for (var i = 0; i < ((size > 9) ? 9 : 4); ++i) {
        // Boucle 4x pour un goban de 9
        // Boucle 9x pour un goban > 9
        var circle = document.createElementNS(svgns, "circle");
        circle.setAttribute('fill', 'black');
        circle.setAttribute('stroke', 'none');
        circle.setAttribute('cx', (size > 9) ? hoshiX[i] : hoshi9X[i]);
        circle.setAttribute('cy', (size > 9) ? hoshiY[i] : hoshi9Y[i]);
        circle.setAttribute('r', '1');

        svg.appendChild(circle);
    }

    var rect = document.createElementNS(svgns, "rect");
    rect.setAttribute('width', '100%');
    rect.setAttribute('height', '100%');
    rect.setAttribute('fill', 'url(#grid)');

    svg.appendChild(defs);
    svg.appendChild(path);
    svg.appendChild(rect);
    boardGo.insertBefore(svg, canvasGoban);
};

Board.prototype.generateGoban = function (size) {
    $('#container-value').html('').addClass('is-visible').append(this.templateCreate);

    if (size != Board.Goban.small && size != Board.Goban.medium && size != Board.Goban.large) {
        this.size = Board.Goban.medium;
    }

    this.generatorSVG(this.size);

    var r = document.getElementById('canvasGoban');
    $(r).addClass('gb' + size);
    $('#gb' + size).show();
    for (var row = 1; row <= this.size; row++) {
        for (var col = 1; col <= this.size; col++) {
            var attrClass = [];
            if (col == 1) {
                attrClass.push("first_col");
            }
            if (col == this.size) {
                attrClass.push("last_col");
            }
            if (row == 1) {
                attrClass.push("first_row");
            }
            if (row == this.size) {
                attrClass.push("last_row");
            }

            if (this.size == Board.Goban.small) {
                if ((row == 3 && col == 3) ||
                    (row == 7 && col == 3) ||
                    (row == 3 && col == 7) ||
                    (row == 7 && col == 7)) {
                    attrClass.push("hoshi");
                }
            } else {
                var hoshiMax = this.size - 3;
                var hoshiMiddle = Math.round(this.size / 2);

                if ((row == 4 && col == 4) ||
                    (row == hoshiMax && col == 4) ||
                    (row == 4 && col == hoshiMax) ||
                    (row == hoshiMax && col == hoshiMax) ||
                    (row == 4 && col == hoshiMiddle) ||
                    (row == hoshiMiddle && col == 4) ||
                    (row == hoshiMiddle && col == 4) ||
                    (row == hoshiMiddle && col == hoshiMax) ||
                    (row == hoshiMax && col == hoshiMiddle) ||
                    (row == hoshiMiddle && col == hoshiMiddle)) {
                    attrClass.push("hoshi");
                }
            }
            r.innerHTML = r.innerHTML + '

';
        }
    }
};
<commit_msg>rename method<commit_after>var Board = function (firebase, size, gameId) {
    this.templateCreate = _.template($('#template-game').html());
    this.fb = firebase;
    this.size = parseInt(size);
    this.gameId = gameId;
    this.stones = [];
    this.init(this.size);
};

Board.Goban = {small: 9, medium: 13, large: 19};

Board.prototype.init = function (size) {
    this.generateGoban(size);
    for (var x = 1; x <= this.size; x++) {
        this.stones[x] = [];
    }
};

Board.prototype.get = function (x, y) {
    var _ref;
    return (_ref = this.stones[x]) !== null ? _ref[y] : void 0;
};

Board.prototype.setStone = function (x, y, color) {
    if (this.checkValueStone(x, y, color)) {
        this.setClassName(x, y, color);
        this.stones[x][y] = color;
    }
};

Board.prototype.setStoneFirebase = function (x, y, color, playerNum) {
    if (this.checkValueStone(x, y, color) && !_.isNull(playerNum)) {

        var self = this;
        var token = 'games/' + this.gameId + '/players/token';

        this.fb.once(token, 'value').then(function (snap) {
            var value = snap.val();

            if (!_.isNull(value) && _.isEqual(playerNum, value)) {
                var oldValue = self.stones[x][y];
                self.setClassName(x, y, color);
                self.stones[x][y] = color;

                self.fb.setStone(x, y, color).then(function () {
                    self.fb.switchToken(playerNum);
                }, function () {
                    //delete self.stones[x][y];
                    self.stones[x][y] = oldValue;
                });
            } else {
                toastr.error('This is your opponent\'s turn ');
            }
        });
    }
};

Board.prototype.skipTurnFirebase = function (playerNum) {
    if (_.isNull(playerNum)) {
        console.error('skipTurnFirebase(), playerNum is null');
        return;
    }

    var self = this;
    this.fb.once('players/token', 'value').then(function (snap) {
        if (_.isEqual(snap.val(), playerNum)) {
            toastr.success('You skip your turn');
            self.fb.switchToken(playerNum);
        } else {
            toastr.error('This is your opponent\'s turn ');
        }
    });
};

Board.prototype.removeStone = function (x, y, playerNum) {
    if (!this.checkCoord(x, y)) {
        toastr.success('Outside goban');
        return false;
    }

    if (_.isUndefined(this.stones[x]) || _.isUndefined(this.stones[x][y])) {
        return;
    }

    this.removeClassName(x, y);
    if (_.isNumber(playerNum)) {
        this.fb.removeStone(x, y, playerNum);
    }
};

Board.prototype.checkCoord = function (x, y) {
    return 0 < x && x <= this.size && 0 < y && y <= this.size;
};

Board.prototype.isColorValid = function (color) {
    return (_.isEqual(color, Game.color.BLACK) || _.isEqual(color, Game.color.WHITE));
};

Board.prototype.checkValueStone = function (x, y, color) {
    if (!this.checkCoord(x, y) || !this.isColorValid(color)) {
        toastr.error('Outside goban or color invalid');
        return false;
    }

    if (this.stones[x][y] !== undefined && this.stones[x][y] !== color) {
        toastr.error('A stone already exists');
        return false;
    }
    return true;
};

Board.prototype.convertStringToInt = function (str) {
    var start = "A".toUpperCase().charCodeAt(0);
    return str.charCodeAt(0) - start;
};

Board.prototype.convertIntToString = function (int) {
    var start = "A".toUpperCase().charCodeAt(0);
    return String.fromCharCode(start + int);
};

Board.prototype.setClassName = function (x, y, color) {
    var d = this.removeClassName(x, y);
    if (!_.isNull(d)) {
        d.className = d.className + " stone " + color.toLowerCase();
    }
};

Board.prototype.removeClassName = function (x, y) {
    var d = document.getElementById(x + "-" + y);
    if (_.isNull(d)) {
        console.error("getElementById is null for ", x, y);
        return false;
    }

    d.className = d.className.replace(" stone", "")
        .replace(Game.color.BLACK.toLowerCase(), "")
        .replace(Game.color.WHITE.toLowerCase(), "");
    delete this.stones[x][y];

    return d;
};

Board.prototype.generatorSVG = function (size) {
    var boardGo = document.getElementById('board');
    var canvasGoban = document.getElementById('canvasGoban');
    var svgns = 'http://www.w3.org/2000/svg';

    var svg = document.createElementNS(svgns, 'svg');
    svg.setAttribute('width', '100%');
    svg.setAttribute('height', '100%');
    svg.setAttribute('viewBox', '0 0 100 100');
    svg.setAttribute('preserveAspectRatio', 'none');
    svg.setAttribute('style', 'padding:' + (100 / ((size - 1) * 2)) + '%;');

    var defs = document.createElementNS(svgns, "defs");

    var path = document.createElementNS(svgns, "path");
    path.setAttribute('d', 'M 100 0 L 100 100 0 100');
    path.setAttribute('fill', 'none');
    path.setAttribute('stroke', 'black');
    path.setAttribute('stroke-width', '.4');

    var pattern = document.createElementNS(svgns, "pattern");
    pattern.setAttribute('id', 'grid');
    pattern.setAttribute('width', 100 / (size - 1) + '%');
    pattern.setAttribute('height', 100 / (size - 1) + '%');
    pattern.setAttribute('patternUnits', 'userSpaceOnUse');

    var patternPath = document.createElementNS(svgns, "path");
    patternPath.setAttribute('d', 'M 30 0 L 0 0 0 30');
    patternPath.setAttribute('fill', 'none');
    patternPath.setAttribute('stroke', 'black');
    patternPath.setAttribute('stroke-width', '.8');

    pattern.appendChild(patternPath);
    defs.appendChild(pattern);

    var hoshi9X = ['25.2%', '25.2%', '75.2%', '75.2%'];
    var hoshi9Y = ['25.2%', '75.2%', '25.2%', '75.2%'];
    var space1 = ((Math.round(((100 / (size - 1)) * 3) * 100) / 100) + 0.2) + '%';
    var space2 = ((Math.round(((100 / (size - 1)) * ((size - 1) / 2)) * 100) / 100) + 0.2) + '%';
    var space3 = ((Math.round(((100 / (size - 1)) * (size - 4)) * 100) / 100) + 0.2) + '%';
    var hoshiX = [space1, space1, space1, space2, space2, space2, space3, space3, space3];
    var hoshiY = [space1, space2, space3, space1, space2, space3, space1, space2, space3];

    for (var i = 0; i < ((size > 9) ? 9 : 4); ++i) {
        // Boucle 4x pour un goban de 9
        // Boucle 9x pour un goban > 9
        var circle = document.createElementNS(svgns, "circle");
        circle.setAttribute('fill', 'black');
        circle.setAttribute('stroke', 'none');
        circle.setAttribute('cx', (size > 9) ? hoshiX[i] : hoshi9X[i]);
        circle.setAttribute('cy', (size > 9) ? hoshiY[i] : hoshi9Y[i]);
        circle.setAttribute('r', '1');

        svg.appendChild(circle);
    }

    var rect = document.createElementNS(svgns, "rect");
    rect.setAttribute('width', '100%');
    rect.setAttribute('height', '100%');
    rect.setAttribute('fill', 'url(#grid)');

    svg.appendChild(defs);
    svg.appendChild(path);
    svg.appendChild(rect);
    boardGo.insertBefore(svg, canvasGoban);
};

Board.prototype.generateGoban = function (size) {
    $('#container-value').html('').addClass('is-visible').append(this.templateCreate);

    if (size != Board.Goban.small && size != Board.Goban.medium && size != Board.Goban.large) {
        this.size = Board.Goban.medium;
    }

    this.generatorSVG(this.size);

    var r = document.getElementById('canvasGoban');
    $(r).addClass('gb' + size);
    $('#gb' + size).show();
    for (var row = 1; row <= this.size; row++) {
        for (var col = 1; col <= this.size; col++) {
            var attrClass = [];
            if (col == 1) {
                attrClass.push("first_col");
            }
            if (col == this.size) {
                attrClass.push("last_col");
            }
            if (row == 1) {
                attrClass.push("first_row");
            }
            if (row == this.size) {
                attrClass.push("last_row");
            }

            if (this.size == Board.Goban.small) {
                if ((row == 3 && col == 3) ||
                    (row == 7 && col == 3) ||
                    (row == 3 && col == 7) ||
                    (row == 7 && col == 7)) {
                    attrClass.push("hoshi");
                }
            } else {
                var hoshiMax = this.size - 3;
                var hoshiMiddle = Math.round(this.size / 2);

                if ((row == 4 && col == 4) ||
                    (row == hoshiMax && col == 4) ||
                    (row == 4 && col == hoshiMax) ||
                    (row == hoshiMax && col == hoshiMax) ||
                    (row == 4 && col == hoshiMiddle) ||
                    (row == hoshiMiddle && col == 4) ||
                    (row == hoshiMiddle && col == 4) ||
                    (row == hoshiMiddle && col == hoshiMax) ||
                    (row == hoshiMax && col == hoshiMiddle) ||
                    (row == hoshiMiddle && col == hoshiMiddle)) {
                    attrClass.push("hoshi");
                }
            }
            r.innerHTML = r.innerHTML + '

';
        }
    }
};
<|endoftext|>


<commit_before>"use strict";
let env = process.env.NODE_ENV || "development";

///flickr/photos/smartbrother/?tags=[\"featured\"]
let routes = function(app) {
    app.get("/api/flickr/photos/:userid",
        function(req, res) {
            return res.status(200).send({"result": "ok"});
        });
};

module.exports = routes;<commit_msg>use modern router syntax<commit_after>var express = require('express');
var router = express.Router();

router.get("/api/flickr/photos/:userid", function(req, res, next) {
    res.status(200);
    res.send({"status":"ok"});
});

module.exports = router;
<|endoftext|>


<commit_before>var number = 0;
document.onkeydown = checkKey;
var images = ['america', 'france', 'portugal', 'spain', 'china', 'italian', 'russian'];
var text = ['Hello', 'Bonjour', 'Olá', 'Hola', '你好', 'Ciao', 'Здравствуйте'];
var audiosounds =['USA','France','Portugal','Spain','China','Italy','Russia'];

//Buttons
$(document).ready(function () {
	if(number == 0) {
		getData();
	}
});

function getData() {
  	if(number <= 6) {
		document.getElementById('img').src='../images/' + images[number]  + '.jpg';
		document.getElementById('imgcaption').innerHTML=text[number];
 		document.getElementById('audio').innerHTML = "<audio id='audiotag' controls autoplay><source src='../audio/" + audiosounds[number]+ ".mp3' type='audio/mpeg'></audio>";

		if(number == 0 || number == 6) {
			$("#image").removeClass("hidden");
			document.getElementById('enddiv').innerHTML= "";
		}

		if(number == 0) {
			$("#imgcaption").addClass("bold");
		} else {
			$("#imgcaption").removeClass("bold");
		}

	}
	else {
		//If last page then get rid of content and replace with the end
		clearPage();
		document.getElementById('enddiv').innerHTML= "The End";
	 }
}
function clearPage() {
	//Add open button and hide next and back
	$("#image").addClass("hidden");
}

function checkKey(e) {

    e = e || window.event;

    if (e.keyCode == '38') {
        // up arrow
        document.getElementById('audiotag').currentTime=0;
        document.getElementById('audiotag').play();

    }
    else if (e.keyCode == '40') {
        // down arrow
        var audio = document.getElementById('audiotag');
        if(audio.paused) {
        	audio.play();
        } else {
        	audio.pause();
        }

    }
    else if (e.keyCode == '37') {
       // left arrow
       if(number > 0) {
       	number--;
		getData();
	   }
    }
    else if (e.keyCode == '39' || e.keyCode == '32') {
       // right arrow
       if(number <= 6) {
       	number++;
       	getData();
       }
    }

}
<commit_msg>image text<commit_after>var number = 0;
document.onkeydown = checkKey;
var images = ['america', 'france', 'portugal', 'spain', 'china', 'italian', 'russian'];
var text = ['Hello', 'Bonjour', 'Olá', 'Hola', '你好', 'Ciao', 'Здравствуйте'];
var audiosounds =['USA','France','Portugal','Spain','China','Italy','Russia'];

//Buttons
$(document).ready(function () {
	if(number == 0) {
		getData();
	}
});

function getData() {
  	if(number <= 6) {
		var i = document.getElementById('img').
		i.src='../images/' + images[number]  + '.jpg';
		i.alt= "flag from " + images[number];
		document.getElementById('imgcaption').innerHTML=text[number];
 		document.getElementById('audio').innerHTML = "<audio id='audiotag' controls autoplay><source src='../audio/" + audiosounds[number]+ ".mp3' type='audio/mpeg'></audio>";

		if(number == 0 || number == 6) {
			$("#image").removeClass("hidden");
			document.getElementById('enddiv').innerHTML= "";
		}

		if(number == 0) {
			$("#imgcaption").addClass("bold");
		} else {
			$("#imgcaption").removeClass("bold");
		}

	}
	else {
		//If last page then get rid of content and replace with the end
		clearPage();
		document.getElementById('enddiv').innerHTML= "The End";
	 }
}
function clearPage() {
	//Add open button and hide next and back
	$("#image").addClass("hidden");
}

function checkKey(e) {

    e = e || window.event;

    if (e.keyCode == '38') {
        // up arrow
        document.getElementById('audiotag').currentTime=0;
        document.getElementById('audiotag').play();

    }
    else if (e.keyCode == '40') {
        // down arrow
        var audio = document.getElementById('audiotag');
        if(audio.paused) {
        	audio.play();
        } else {
        	audio.pause();
        }

    }
    else if (e.keyCode == '37') {
       // left arrow
       if(number > 0) {
       	number--;
		getData();
	   }
    }
    else if (e.keyCode == '39' || e.keyCode == '32') {
       // right arrow
       if(number <= 6) {
       	number++;
       	getData();
       }
    }

}
<|endoftext|>


<commit_before>const Sequelize = require("sequelize");
const Op = Sequelize.Op;

const Feed = require("../models/feed");
const Article = require("../models/article");

const autodiscover = require("../util/autodiscover");
const parseFeed = require("../util/parser");
const language = require("../util/language");
const cheerio = require("cheerio");

var striptags = require("striptags");

exports.getCrawl = async (req, res, next) => {
  try {
    const feeds = await Feed.findAll({
      where: {
        active: true
      }
    });

    if (feeds.length > 0) {
      feeds.forEach(async function(feed) {
        //discover rssUrl
        const url = await autodiscover.discover(feed.url);

        //do not process undefined URLs
        if(typeof url !== "undefined") {
          try {
            const feeditem = await parseFeed.process(url);
            if (feeditem) {
              //process all feed posts
              feeditem.posts.forEach(function(post) {
                processArticle(feed, post);
              });

              //reset the feed count
              feed.update({
                errorCount: 0
              });
            }
          } catch (err) {
            console.log(err.stack.split("\n", 1).join(""));
            //update the errorCount
            feed.update({
              errorCount: Sequelize.literal("errorCount + 1")
            });
          }
        }
      });
    }

    return res.status(200).json("Crawling started.");
  } catch (err) {
    console.log(err);
  }
};

async function processArticle(feed, post) {
  try {
    const article = await Article.findOne({
      where: {
        [Op.or]: [
          {
            url: post.link
          },
          {
            subject: post.title
          }
        ],
        [Op.and]: {
          feedId: feed.id
        }
      }
    });

    if (!article) {

      //remove any script tags
      const $ = cheerio.load(post.description);
      $('script').remove();

      //add article
      Article.create({
        feedId: feed.id,
        status: "unread",
        star_ind: 0,
        url: post.link,
        image_url: "",
        subject: post.title || 'No title',
        content: $.html(),
        contentStripped: striptags($.html(), ["a", "img", "strong"]),
        language: language.get($.html()),
        //contentSnippet: item.contentSnippet,
        //author: item.author,
        published: post.pubdate
      });
    }
  } catch (err) {
    console.log(err);
  }
}
<commit_msg>Update crawl.js<commit_after>const Sequelize = require("sequelize");
const Op = Sequelize.Op;

const Feed = require("../models/feed");
const Article = require("../models/article");

const autodiscover = require("../util/autodiscover");
const parseFeed = require("../util/parser");
const language = require("../util/language");
const cheerio = require("cheerio");

var striptags = require("striptags");

exports.getCrawl = async (req, res, next) => {
  try {
    const feeds = await Feed.findAll({
      where: {
        active: true
      }
    });

    if (feeds.length > 0) {
      feeds.forEach(async function(feed) {
        //discover rssUrl
        const url = await autodiscover.discover(feed.url);

        //do not process undefined URLs
        if(typeof url !== "undefined") {
          try {
            const feeditem = await parseFeed.process(url);
            if (feeditem) {
              //process all feed posts
              feeditem.posts.forEach(function(post) {
                processArticle(feed, post);
              });

              //reset the feed count
              feed.update({
                errorCount: 0
              });
            }
          } catch (err) {
            console.log(err.stack.split("\n", 1).join(""));
            //update the errorCount
            feed.update({
              errorCount: Sequelize.literal("errorCount + 1")
            });
          }
        }
      });
    }

    return res.status(200).json("Crawling started.");
  } catch (err) {
    console.log(url);
    console.log(err);
  }
};

async function processArticle(feed, post) {
  try {
    const article = await Article.findOne({
      where: {
        [Op.or]: [
          {
            url: post.link
          },
          {
            subject: post.title
          }
        ],
        [Op.and]: {
          feedId: feed.id
        }
      }
    });

    if (!article) {

      //remove any script tags
      const $ = cheerio.load(post.description);
      $('script').remove();

      //add article
      Article.create({
        feedId: feed.id,
        status: "unread",
        star_ind: 0,
        url: post.link,
        image_url: "",
        subject: post.title || 'No title',
        content: $.html(),
        contentStripped: striptags($.html(), ["a", "img", "strong"]),
        language: language.get($.html()),
        //contentSnippet: item.contentSnippet,
        //author: item.author,
        published: post.pubdate
      });
    }
  } catch (err) {
    console.log(err);
  }
}
<|endoftext|>


<commit_before>'use strict';

var mean = require('meanio');

exports.render = function(req, res) {

    var modules = [];

    // Preparing angular modules list with dependencies
    for (var name in mean.modules) {
        modules.push({
            name: name,
            module: 'mean.' + name,
            angularDependencies: mean.modules[name].angularDependencies
        });
    }

    function isAdmin() {
        return req.user && req.user.roles.indexOf('admin') !== -1;
    }

    // Send some basic starting info to the view
    res.render('index', {
        user: req.user ? {
            name: req.user.name,
            _id: req.user._id,
            username: req.user.username,
            roles: req.user.roles
        } : {},
        modules: modules,
        isAdmin: isAdmin,
        adminEnabled: isAdmin() && mean.moduleEnabled('mean-admin')
    });
};

exports.landing = function(req, res) {

    var modules = [];

    // Preparing angular modules list with dependencies
    for (var name in mean.modules) {
        modules.push({
            name: name,
            module: 'mean.' + name,
            angularDependencies: mean.modules[name].angularDependencies
        });
    }

    // Send some basic starting info to the view
    res.render('landing');
};
<commit_msg>automatically send to home if still active<commit_after>'use strict';

var mean = require('meanio');

exports.render = function(req, res) {

    var modules = [];

    // Preparing angular modules list with dependencies
    for (var name in mean.modules) {
        modules.push({
            name: name,
            module: 'mean.' + name,
            angularDependencies: mean.modules[name].angularDependencies
        });
    }

    function isAdmin() {
        return req.user && req.user.roles.indexOf('admin') !== -1;
    }

    // Send some basic starting info to the view
    res.render('index', {
        user: req.user ? {
            name: req.user.name,
            _id: req.user._id,
            username: req.user.username,
            roles: req.user.roles
        } : {},
        modules: modules,
        isAdmin: isAdmin,
        adminEnabled: isAdmin() && mean.moduleEnabled('mean-admin')
    });
};

exports.landing = function(req, res) {

    var modules = [];

    // Preparing angular modules list with dependencies
    for (var name in mean.modules) {
        modules.push({
            name: name,
            module: 'mean.' + name,
            angularDependencies: mean.modules[name].angularDependencies
        });
    }

    // Send some basic starting info to the view
    if(req.isAuthenticated()) {
        return res.redirect('/');
    }

    res.render('landing');
};
<|endoftext|>


<commit_before>import * as auth from '../lib/auth';
import userLib from '../lib/userlib';
import constants from '../constants/activities';
import emailLib from '../lib/email';
import models from '../models';
import errors from '../lib/errors';
import { isValidEmail } from '../lib/utils';

const { User, Activity } = models;

const { Unauthorized } = errors;

export const updatePaypalEmail = (req, res, next) => {
  const required = req.required || {};

  req.user.paypalEmail = required.paypalEmail;

  req.user
    .save()
    .then(user => res.send(user.info))
    .catch(next);
};

/*
 * End point for social media image lookup from the public donation page
 */
export const getSocialMediaAvatars = (req, res) => {
  const { userData } = req.body;
  userData.email = req.user.email;
  userData.ip = req.ip;

  userLib.resolveUserAvatars(userData, (err, results) => {
    res.send(results);
  });
};

// TODO: reenable asynchronously
// userLib.fetchInfo(user)
export const _create = user =>
  User.createUserWithCollective(user).tap(dbUser =>
    Activity.create({
      type: constants.USER_CREATED,
      UserId: dbUser.id,
      data: { user: dbUser.info },
    }),
  );

/**
 *
 * Public methods.
 *
 */

/**
 * Check existence of a user based on email
 */
export const exists = async (req, res) => {
  const email = req.query.email.toLowerCase();
  if (!isValidEmail(email)) {
    return res.send({ exists: false });
  } else {
    const user = await models.User.findOne({
      attributes: ['id'],
      where: { email },
    });
    return res.send({ exists: Boolean(user) });
  }
};

/**
 * Create a user.
 */
export const create = (req, res, next) => {
  const { user } = req.required;

  _create(user)
    .tap(user => res.send(user.info))
    .catch(next);
};

/**
 * For the case when a user has submitted an expired token,
 * we can automatically detect the email address and send a refreshed token.
 */
export const refreshTokenByEmail = (req, res, next) => {
  if (!req.jwtPayload || !req.remoteUser) {
    return next(new Unauthorized('Invalid payload'));
  }

  const { redirect, websiteUrl } = req.body;
  const user = req.remoteUser;

  return emailLib
    .send(
      'user.new.token',
      req.remoteUser.email,
      { loginLink: user.generateLoginLink(redirect || '/', websiteUrl) },
      { sendEvenIfNotProduction: true },
    )
    .then(() => res.send({ success: true }))
    .catch(next);
};

/**
 * Send an email with the new token #deprecated
 */
export const sendNewTokenByEmail = (req, res, next) => {
  const { redirect, websiteUrl } = req.body;
  return User.findOne({
    where: {
      email: req.required.email,
    },
  })
    .then(user => {
      // If you don't find a user, proceed without error
      // Otherwise, we can leak email addresses
      if (user) {
        return emailLib.send(
          'user.new.token',
          req.body.email,
          { loginLink: user.generateLoginLink(redirect || '/', websiteUrl) },
          { sendEvenIfNotProduction: true },
        );
      }
      return null;
    })
    .then(() => res.send({ success: true }))
    .catch(next);
};

/**
 * Login or create a new user
 */
export const signin = (req, res, next) => {
  const { user, redirect, websiteUrl } = req.body;
  let loginLink;
  return models.User.findOne({ where: { email: user.email.toLowerCase() } })
    .then(u => u || models.User.createUserWithCollective(user))
    .then(u => {
      loginLink = u.generateLoginLink(redirect || '/', websiteUrl);
      return emailLib.send('user.new.token', u.email, { loginLink }, { sendEvenIfNotProduction: true });
    })
    .then(() => {
      const response = { success: true };
      // For e2e testing, we enable testuser+(admin|member)@opencollective.com to automatically receive the login link
      if (process.env.NODE_ENV !== 'production' && user.email.match(/.*test.*@opencollective.com$/)) {
        response.redirect = loginLink;
      }
      return response;
    })
    .then(response => res.send(response))
    .catch(next);
};

/**
 * Receive a JWT and generate another one.
 *
 * This can be used right after the first login
 */
export const updateToken = async (req, res) => {
  const token = req.remoteUser.jwt({}, auth.TOKEN_EXPIRATION_SESSION);
  res.send({ token });
};

/**
 * Deprecated (for old website)
 */

/**
 * Show.
 */
export const show = (req, res, next) => {
  const userData = req.user.show;

  if (req.remoteUser && req.remoteUser.id === req.user.id) {
    Promise.all([
      models.Collective.findByPk(req.user.CollectiveId),
      models.ConnectedAccount.findOne({
        where: { service: 'stripe', CollectiveId: req.remoteUser.CollectiveId },
      }),
    ])
      .then(results => {
        const userExtendedData = {
          username: results[0].slug,
          name: results[0].name,
          avatar: results[0].image,
          stripeAccount: results[1],
        };
        const response = Object.assign(userData, req.user.info, userExtendedData);
        res.send(response);
      })
      .catch(next);
  } else {
    res.send(userData);
  }
};

/**
 * Token.
 */
export const token = async (req, res) => {
  const userId = req.remoteUser.id;
  const appId = req.clientApp.id;

  const sessionToken = auth.createJwt(userId, { app: appId, scope: 'session' }, auth.TOKEN_EXPIRATION_SESSION);

  res.send({ token: sessionToken });
};
<commit_msg>dx: add login link in console for developers<commit_after>import config from 'config';

import * as auth from '../lib/auth';
import userLib from '../lib/userlib';
import constants from '../constants/activities';
import emailLib from '../lib/email';
import models from '../models';
import errors from '../lib/errors';
import logger from '../lib/logger';
import { isValidEmail } from '../lib/utils';

const { User, Activity } = models;

const { Unauthorized } = errors;

export const updatePaypalEmail = (req, res, next) => {
  const required = req.required || {};

  req.user.paypalEmail = required.paypalEmail;

  req.user
    .save()
    .then(user => res.send(user.info))
    .catch(next);
};

/*
 * End point for social media image lookup from the public donation page
 */
export const getSocialMediaAvatars = (req, res) => {
  const { userData } = req.body;
  userData.email = req.user.email;
  userData.ip = req.ip;

  userLib.resolveUserAvatars(userData, (err, results) => {
    res.send(results);
  });
};

// TODO: reenable asynchronously
// userLib.fetchInfo(user)
export const _create = user =>
  User.createUserWithCollective(user).tap(dbUser =>
    Activity.create({
      type: constants.USER_CREATED,
      UserId: dbUser.id,
      data: { user: dbUser.info },
    }),
  );

/**
 *
 * Public methods.
 *
 */

/**
 * Check existence of a user based on email
 */
export const exists = async (req, res) => {
  const email = req.query.email.toLowerCase();
  if (!isValidEmail(email)) {
    return res.send({ exists: false });
  } else {
    const user = await models.User.findOne({
      attributes: ['id'],
      where: { email },
    });
    return res.send({ exists: Boolean(user) });
  }
};

/**
 * Create a user.
 */
export const create = (req, res, next) => {
  const { user } = req.required;

  _create(user)
    .tap(user => res.send(user.info))
    .catch(next);
};

/**
 * For the case when a user has submitted an expired token,
 * we can automatically detect the email address and send a refreshed token.
 */
export const refreshTokenByEmail = (req, res, next) => {
  if (!req.jwtPayload || !req.remoteUser) {
    return next(new Unauthorized('Invalid payload'));
  }

  const { redirect, websiteUrl } = req.body;
  const user = req.remoteUser;

  return emailLib
    .send(
      'user.new.token',
      req.remoteUser.email,
      { loginLink: user.generateLoginLink(redirect || '/', websiteUrl) },
      { sendEvenIfNotProduction: true },
    )
    .then(() => res.send({ success: true }))
    .catch(next);
};

/**
 * Send an email with the new token #deprecated
 */
export const sendNewTokenByEmail = (req, res, next) => {
  const { redirect, websiteUrl } = req.body;
  return User.findOne({
    where: {
      email: req.required.email,
    },
  })
    .then(user => {
      // If you don't find a user, proceed without error
      // Otherwise, we can leak email addresses
      if (user) {
        return emailLib.send(
          'user.new.token',
          req.body.email,
          { loginLink: user.generateLoginLink(redirect || '/', websiteUrl) },
          { sendEvenIfNotProduction: true },
        );
      }
      return null;
    })
    .then(() => res.send({ success: true }))
    .catch(next);
};

/**
 * Login or create a new user
 */
export const signin = (req, res, next) => {
  const { user, redirect, websiteUrl } = req.body;
  let loginLink;
  return models.User.findOne({ where: { email: user.email.toLowerCase() } })
    .then(u => u || models.User.createUserWithCollective(user))
    .then(u => {
      loginLink = u.generateLoginLink(redirect || '/', websiteUrl);
      if (config.env === 'development') {
        logger.info(`Login Link: ${loginLink}`);
      }
      return emailLib.send('user.new.token', u.email, { loginLink }, { sendEvenIfNotProduction: true });
    })
    .then(() => {
      const response = { success: true };
      // For e2e testing, we enable testuser+(admin|member)@opencollective.com to automatically receive the login link
      if (process.env.NODE_ENV !== 'production' && user.email.match(/.*test.*@opencollective.com$/)) {
        response.redirect = loginLink;
      }
      return response;
    })
    .then(response => res.send(response))
    .catch(next);
};

/**
 * Receive a JWT and generate another one.
 *
 * This can be used right after the first login
 */
export const updateToken = async (req, res) => {
  const token = req.remoteUser.jwt({}, auth.TOKEN_EXPIRATION_SESSION);
  res.send({ token });
};

/**
 * Deprecated (for old website)
 */

/**
 * Show.
 */
export const show = (req, res, next) => {
  const userData = req.user.show;

  if (req.remoteUser && req.remoteUser.id === req.user.id) {
    Promise.all([
      models.Collective.findByPk(req.user.CollectiveId),
      models.ConnectedAccount.findOne({
        where: { service: 'stripe', CollectiveId: req.remoteUser.CollectiveId },
      }),
    ])
      .then(results => {
        const userExtendedData = {
          username: results[0].slug,
          name: results[0].name,
          avatar: results[0].image,
          stripeAccount: results[1],
        };
        const response = Object.assign(userData, req.user.info, userExtendedData);
        res.send(response);
      })
      .catch(next);
  } else {
    res.send(userData);
  }
};

/**
 * Token.
 */
export const token = async (req, res) => {
  const userId = req.remoteUser.id;
  const appId = req.clientApp.id;

  const sessionToken = auth.createJwt(userId, { app: appId, scope: 'session' }, auth.TOKEN_EXPIRATION_SESSION);

  res.send({ token: sessionToken });
};
<|endoftext|>


<commit_before>// Copyright (c) 2018 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const fs = require("fs");
const makePromise = require("./make-promise");
const randomString = require("./random-string");
console.log(randomString);

const mkdir = makePromise(fs.mkdir);
const readdir = makePromise(fs.readdir);
const rename = makePromise(fs.rename);
const stat = makePromise(fs.stat);

module.exports = function(watchDir, runDirFormat, runFunction) {
  const task = {};

  task.find = () => new Promise(function(resolve, reject) {
    readdir(watchDir).then(files => {
      const result = [];

      for (const file of files)
        result.push(watchDir + "/" + file);

      resolve(result);
    }, reject);
  });

  task.move = listOfFiles => new Promise(function(resolve, reject) {
    const path = randomString(runDirFormat);

    stat(path).catch(() => {
      return mkdir(path);
    }).then(() => {
      const movements = [];

      for (const filename of listOfFiles)
        movements.push(rename(filename,
                              path + filename.slice(watchDir.length)));

      Promise.all(movements).then(() => {
        resolve(path);
      }, reject);
    }, reject);
  });

  task.run = runFunction;

  return task;
};
<commit_msg>:muscle: Remove debug log<commit_after>// Copyright (c) 2018 The Regents of the University of Michigan.
// All Rights Reserved. Licensed according to the terms of the Revised
// BSD License. See LICENSE.txt for details.

const fs = require("fs");
const makePromise = require("./make-promise");
const randomString = require("./random-string");

const mkdir = makePromise(fs.mkdir);
const readdir = makePromise(fs.readdir);
const rename = makePromise(fs.rename);
const stat = makePromise(fs.stat);

module.exports = function(watchDir, runDirFormat, runFunction) {
  const task = {};

  task.find = () => new Promise(function(resolve, reject) {
    readdir(watchDir).then(files => {
      const result = [];

      for (const file of files)
        result.push(watchDir + "/" + file);

      resolve(result);
    }, reject);
  });

  task.move = listOfFiles => new Promise(function(resolve, reject) {
    const path = randomString(runDirFormat);

    stat(path).catch(() => {
      return mkdir(path);
    }).then(() => {
      const movements = [];

      for (const filename of listOfFiles)
        movements.push(rename(filename,
                              path + filename.slice(watchDir.length)));

      Promise.all(movements).then(() => {
        resolve(path);
      }, reject);
    }, reject);
  });

  task.run = runFunction;

  return task;
};
<|endoftext|>


<commit_before>horizon.addInitFunction(function() {
  var allPanelGroupBodies = $('.nav_accordion > dd > div > ul');

  allPanelGroupBodies.each(function(index, value) {
    var activePanels = $(this).find('li > a.active');
    if(activePanels.length === 0) {
      $(this).slideUp(0);
    }
  });

  // mark the active panel group
  var activePanel = $('.nav_accordion > dd > div > ul > li > a.active');
  activePanel.closest('div').find('h4').addClass('active');

  // dashboard click
  $('.nav_accordion > dt').click(function() {
    var myDashHeader = $(this);
    var myDashWasActive = myDashHeader.hasClass("active");

    // mark the active dashboard
    var allDashboardHeaders = $('.nav_accordion > dt');
    allDashboardHeaders.removeClass("active");

    // collapse all dashboard contents
    var allDashboardBodies = $('.nav_accordion > dd');
    allDashboardBodies.slideUp();

    // if the current dashboard was active, leave it collapsed
    if(!myDashWasActive) {
      myDashHeader.addClass("active");

      // expand the active dashboard body
      var myDashBody = myDashHeader.next();
      myDashBody.slideDown();

      var activeDashPanel = myDashBody.find("div > ul > li > a.active");
      // if the active panel is not in the expanded dashboard
      if (activeDashPanel.length === 0) {
        // expand the active panel group
        var activePanel = myDashBody.find("div:first > ul");
        activePanel.slideDown();
        activePanel.closest('div').find("h4").addClass("active");

        // collapse the inactive panel groups
        var nonActivePanels = myDashBody.find("div:not(:first) > ul");
        nonActivePanels.slideUp();
      }
      // the expanded dashboard contains the active panel
      else
      {
        // collapse the inactive panel groups
        activeDashPanel.closest('div').find("h4").addClass("active");
        activeDashPanel.closest('ul').slideDown();
        allPanelGroupBodies.each(function(index, value) {
          var activePanels = $(value).find('li > a.active');
          if(activePanels.length === 0) {
            $(this).slideUp();
          }
        });
      }
    }
    return false;
  });

  // panel group click
  $('.nav_accordion > dd > div > h4').click(function() {
    var myPanelGroupHeader = $(this);
    myPanelGroupWasActive = myPanelGroupHeader.hasClass("active");

    // collapse all panel groups
    var allPanelGroupHeaders = $('.nav_accordion > dd > div > h4');
    allPanelGroupHeaders.removeClass("active");
    allPanelGroupBodies.slideUp();

    // expand the selected panel group if not already active
    if(!myPanelGroupWasActive) {
      myPanelGroupHeader.addClass("active");
      myPanelGroupHeader.closest('div').find('ul').slideDown();
    }
  });

  // panel selection
  $('.nav_accordion > dd > div > ul > li > a').click(function() {
    horizon.modals.modal_spinner(gettext("Loading"));
  });

});
<commit_msg>left side header panel doesn't expand but show up arrow<commit_after>horizon.addInitFunction(function() {
  var allPanelGroupBodies = $('.nav_accordion > dd > div > ul');

  allPanelGroupBodies.each(function(index, value) {
    var activePanels = $(this).find('li > a.active');
    if(activePanels.length === 0) {
      $(this).slideUp(0);
    }
  });

  // mark the active panel group
  var activePanel = $('.nav_accordion > dd > div > ul > li > a.active');
  activePanel.closest('div').find('h4').addClass('active');

  // dashboard click
  $('.nav_accordion > dt').click(function() {
    var myDashHeader = $(this);
    var myDashWasActive = myDashHeader.hasClass("active");

    // mark the active dashboard
    var allDashboardHeaders = $('.nav_accordion > dt');
    allDashboardHeaders.removeClass("active");

    // collapse all dashboard contents
    var allDashboardBodies = $('.nav_accordion > dd');
    allDashboardBodies.slideUp();

    // if the current dashboard was active, leave it collapsed
    if(!myDashWasActive) {
      myDashHeader.addClass("active");

      // expand the active dashboard body
      var myDashBody = myDashHeader.next();
      myDashBody.slideDown();

      var activeDashPanel = myDashBody.find("div > ul > li > a.active");
      // if the active panel is not in the expanded dashboard
      if (activeDashPanel.length === 0) {
        // expand the active panel group
        var activePanel = myDashBody.find("div:first > ul");
        activePanel.slideDown();
        activePanel.closest('div').find("h4").addClass("active");

        // collapse the inactive panel groups
        var nonActivePanels = myDashBody.find("div:not(:first) > ul");
        nonActivePanels.slideUp();
        nonActivePanels.closest('div').find("h4").removeClass("active");
      }
      // the expanded dashboard contains the active panel
      else
      {
        // collapse the inactive panel groups
        activeDashPanel.closest('div').find("h4").addClass("active");
        activeDashPanel.closest('ul').slideDown();
        allPanelGroupBodies.each(function(index, value) {
          var activePanels = $(value).find('li > a.active');
          if(activePanels.length === 0) {
            $(this).slideUp();
            $(this).closest('div').find("h4").removeClass("active");
          }
        });
      }
    }
    return false;
  });

  // panel group click
  $('.nav_accordion > dd > div > h4').click(function() {
    var myPanelGroupHeader = $(this);
    myPanelGroupWasActive = myPanelGroupHeader.hasClass("active");

    // collapse all panel groups
    var allPanelGroupHeaders = $('.nav_accordion > dd > div > h4');
    allPanelGroupHeaders.removeClass("active");
    allPanelGroupBodies.slideUp();

    // expand the selected panel group if not already active
    if(!myPanelGroupWasActive) {
      myPanelGroupHeader.addClass("active");
      myPanelGroupHeader.closest('div').find('ul').slideDown();
    }
  });

  // panel selection
  $('.nav_accordion > dd > div > ul > li > a').click(function() {
    horizon.modals.modal_spinner(gettext("Loading"));
  });

});
<|endoftext|>


<commit_before>/*
 * AstronomyLibs_Coordinates_float2Text(float_coordinate, decimals)
 *
 * get text representation of coordinates like 19.180277778
 * i.e. +19° 10' 49.0"; $decimals allows to set the number of
 * arcsecond decimals (1 being the default)
 *
 * param: float float_coordinate coordinates as float value  (e.g. -19.180277778)
 * param: string format which format to output
 * param: int decimals number of decimals to display for arcseconds; default is 1
 * author: Markus Popp <git@mpopp.net>
 * return: string   text representation (e.g. -19° 10' 49.0")
 */
function AstronomyLibs_Coordinates_float2Text(float_coordinate, format = "symbols", decimals = 1) {

    var degrees = parseInt(float_coordinate);

    var minutes_full = Math.abs(parseInt(float_coordinate) - float_coordinate) * 60;

    var minutes = parseInt(minutes_full);

    if (decimals === null) {
        var seconds = (minutes_full - minues) * 60;
    } else {
        var seconds = parseInt((minutes_full - minutes) * 60 * Math.pow(10, decimals) + .5) / Math.pow(10, decimals);
    }

    var text_coordinate = "";
    switch (format) {
        case "symbols" :
            text_coordinate = degrees + "° " + minutes + "' " + seconds + "\" ";
            break;
        case "spaces" :
            text_coordinate = degrees + " " + minutes + " " + seconds;
            break;
        case "dms" :
            text_coordinate = degrees + "d " + minutes + "m " + seconds + "s";
            break;
        case "hms" :
            text_coordinate = degrees + "h " + minutes + "m " + seconds + "s";
            break;
        default:
    }

    return text_coordinate;
}

/*
 * AstronomyLibs_Coordinates_text2Float(text_coordinate)
 *
 * get float representation of coordinates like +19° 10' 49.0"
 * i.e. 19.180277778
 * param: string textCoordinate text form of coordinates (e.g. +19° 10' 49.0")
 * param: int decimals number of decimals
 * author: Markus Popp <git@mpopp.net>
 * return: float    float representation (e.g. 19.180277778)
 */
function AstronomyLibs_Coordinates_text2Float(text_coordinate, decimals = null) {
    // regex to extract parts needed for calcuation
    var regex = new RegExp(/([NSEW+-]?)\s*(\d{1,3})\s*[°dh ]\s*(?:(\d{1,2})\s*['m ]\s*)?(?:(\d{1,2}(?:\.\d*)?)\s*[\"s]?\s*)?\s*([NSEW+-]?)/);

    var matches = regex.exec(text_coordinate);

    if (isNaN(matches[3])) {
        matches[3] = 0;
    }

    if (isNaN(matches[4])) {
        matches[4] = 0;
    }

    // ensure entry is valid
    if (matches[2] > 359 || matches[3] > 59 || matches[4] >= 60) {
        return NaN;
    }

    var float_coordinate = parseFloat(matches[2]) +
                           parseFloat(matches[3]) / 60 +
                           parseFloat(matches[4]) / 3600;

    // if coordinates are negative
    if (matches[1] == "-" ||
        matches[1] == "S" ||
        matches[1] == "W" ||
        matches[5] == "S" ||
        matches[5] == "W") {
        float_coordinate = -float_coordinate;
    }

    if (decimals != null) {
        float_coordinate = parseInt(float_coordinate * Math.pow(10, decimals) + .5) / Math.pow(10, decimals);
    }

    return float_coordinate;
}<commit_msg>Improve JS: AstronomyLibs_Coordinates_text2Float<commit_after>/*
 * AstronomyLibs_Coordinates_float2Text(float_coordinate, decimals)
 *
 * get text representation of coordinates like 19.180277778
 * i.e. +19° 10' 49.0"; $decimals allows to set the number of
 * arcsecond decimals (1 being the default)
 *
 * param: float float_coordinate coordinates as float value  (e.g. -19.180277778)
 * param: string format which format to output
 * param: int decimals number of decimals to display for arcseconds; default is 1
 * author: Markus Popp <git@mpopp.net>
 * return: string   text representation (e.g. -19° 10' 49.0")
 */
function AstronomyLibs_Coordinates_float2Text(float_coordinate, format = "symbols", decimals = 1) {

    var degrees = parseInt(float_coordinate);

    var minutes_full = Math.abs(parseInt(float_coordinate) - float_coordinate) * 60;

    var minutes = parseInt(minutes_full);

    if (decimals === null) {
        var seconds = (minutes_full - minues) * 60;
    } else {
        var seconds = parseInt((minutes_full - minutes) * 60 * Math.pow(10, decimals) + .5) / Math.pow(10, decimals);
    }

    var text_coordinate = "";
    switch (format) {
        case "symbols" :
            text_coordinate = degrees + "° " + minutes + "' " + seconds + "\" ";
            break;
        case "spaces" :
            text_coordinate = degrees + " " + minutes + " " + seconds;
            break;
        case "dms" :
            text_coordinate = degrees + "d " + minutes + "m " + seconds + "s";
            break;
        case "hms" :
            text_coordinate = degrees + "h " + minutes + "m " + seconds + "s";
            break;
        default:
    }

    return text_coordinate;
}

/*
 * AstronomyLibs_Coordinates_text2Float(text_coordinate)
 *
 * get float representation of coordinates like +19° 10' 49.0"
 * i.e. 19.180277778
 * param: string textCoordinate text form of coordinates (e.g. +19° 10' 49.0")
 * param: int decimals number of decimals
 * author: Markus Popp <git@mpopp.net>
 * return: float    float representation (e.g. 19.180277778)
 */
function AstronomyLibs_Coordinates_text2Float(text_coordinate, decimals = null) {
    var regex = new RegExp(/^([NSEW+-])?\s*([0-9.]+)$/);

    var matches = regex.exec(text_coordinate);

    if (matches) {
        // input is already a valid float value, return right away

        // if coordinates are negative
        if (matches[1] == "S" ||
            matches[1] == "W") {
            text_coordinate = -(parseFloat(matches[2]));
        }

        if (decimals === null) {
            return parseFloat(text_coordinate);
        } else {
            return parseInt(text_coordinate * Math.pow(10, decimals) + .5) / Math.pow(10, decimals);
        }
    }

    // regex to extract parts needed for calcuation
    regex = new RegExp(/([NSEW+-]?)\s*(\d{1,3})\s*[°dh ]\s*(?:(\d{1,2})\s*['m]?\s*)?(?:(\d{1,2}(?:\.\d*)?)\s*[\"s]?\s*)?\s*([NSEW+-]?)/);

    matches = regex.exec(text_coordinate);

    if (isNaN(matches[3])) {
        matches[3] = 0;
    }

    if (isNaN(matches[4])) {
        matches[4] = 0;
    }

    // ensure entry is valid
    if (matches[3] > 59 || matches[4] >= 60) {
        return NaN;
    }

    var float_coordinate = parseFloat(matches[2]) +
                           parseFloat(matches[3]) / 60 +
                           parseFloat(matches[4]) / 3600;

    // if coordinates are negative
    if (matches[1] == "-" ||
        matches[1] == "S" ||
        matches[1] == "W" ||
        matches[5] == "S" ||
        matches[5] == "W") {
        float_coordinate = -float_coordinate;
    }

    if (decimals != null) {
        float_coordinate = parseInt(float_coordinate * Math.pow(10, decimals) + .5) / Math.pow(10, decimals);
    }

    return float_coordinate;
}<|endoftext|>


<commit_before>/*

MiLight platform shim for Homebridge
Written by Sam Edwards (https://samedwards.ca/)

Uses the node-milight-promise library (https://github.com/mwittig/node-milight-promise) which features some code from
applamp.nl (http://www.applamp.nl/service/applamp-api/) and uses other details from (http://www.limitlessled.com/dev/)

Configure in config.json as follows:

"platforms": [
        {
            "platform":"MiLight",
            "name":"MiLight",
            "ip_address": "255.255.255.255",
            "port": 8899,
            "type": "rgbw",
            "delay": 30,
            "repeat": 3,
            "zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"]
        }
]

Where the parameters are:
 *platform (required): This must be "MiLight", and refers to the name of the accessory as exported from this file
 *name (optional): The display name used for logging output by Homebridge. Best to set to "MiLight"
 *ip_address (optional): The IP address of the WiFi Bridge. Default to the broadcast address of 255.255.255.255 if not specified
 *port (optional): Port of the WiFi bridge. Defaults to 8899 if not specified
 *type (optional): One of either "rgbw", "rgb", or "white", depending on the type of bulb being controlled. This applies to all zones. Defaults to rgbw.
 *delay (optional): Delay between commands sent over UDP. Default 30ms
 *repeat (optional): Number of times to repeat the UDP command for better reliability. Default 3
 *zones (required): An array of the names of the zones, in order, 1-4. Use null if a zone is skipped. RGB lamps can only have a single zone.

Tips and Tricks:
 *Setting the brightness of an rgbw or a white bulb will set it to "night mode", which is dimmer than the lowest brightness setting
 *White and rgb bulbs don't support absolute brightness setting, so we just send a brightness up/brightness down command depending
   if we got a percentage above/below 50% respectively
 *The only exception to the above is that white bulbs support a "maximum brightness" command, so we send that when we get 100%
 *Implemented warmer/cooler for white lamps in a similar way to brightnes, except this time above/below 180 degrees on the colour wheel
 *I welcome feedback on a better way to work the brightness/hue for white and rgb bulbs

Troubleshooting:
The node-milight-promise library provides additional debugging output when the MILIGHT_DEBUG environmental variable is set

TODO:
 *Possibly build in some sort of state logging and persistance so that we can answswer HomeKit status queries to the best of our ability

*/

var Service = require("HAP-NodeJS").Service;
var Characteristic = require("HAP-NodeJS").Characteristic;
var Milight = require('node-milight-promise').MilightController;
var commands = require('node-milight-promise').commands;

module.exports = {
  accessory: MiLightAccessory,
  platform: MiLightPlatform
}

function MiLightPlatform(log, config) {
  this.log = log;

  this.config = config;
}

MiLightPlatform.prototype = {
  accessories: function(callback) {
    var that = this;
    var zones = [];

    // Various error checking
    if (this.config.zones) {
      var zoneLength = this.config.zones.length;
    } else {
      this.log("ERROR: Could not read zones from configuration.");
      return;
    }

    if (!this.config["type"]) {
      this.log("INFO: Type not specified, defaulting to rgbw");
      this.config["type"] = "rgbw";
    }

    if (zoneLength == 0) {
      this.log("ERROR: No zones found in configuration.");
      return;
    } else if (this.config["type"] == "rgb" && zoneLength > 1) {
      this.log("WARNING: RGB lamps only have a single zone. Only the first defined zone will be used.");
      zoneLength = 1;
    } else if (zoneLength > 4) {
      this.log("WARNING: Only a maximum of 4 zones are supported per bridge. Only recognizing the first 4 zones.");
      zoneLength = 4;
    }

    // Create lamp accessories for all of the defined zones
    for (var i=0; i < zoneLength; i++) {
      if (!!this.config.zones[i]) {
        this.config["name"] = this.config.zones[i];
        this.config["zone"] = i+1;
        lamp = new MiLightAccessory(this.log, this.config);
        zones.push(lamp);
      }
    }
    if (zones.length > 0) {
      callback(zones);
    } else {
      this.log("ERROR: Unable to find any valid zones");
      return;
    }
  }
}

function MiLightAccessory(log, config) {
  this.log = log;

  // config info
  this.ip_address = config["ip_address"];
  this.port = config["port"];
  this.name = config["name"];
  this.zone = config["zone"];
  this.type = config["type"];
  this.delay = config["delay"];
  this.repeat = config["repeat"];

  this.light = new Milight({
    ip: this.ip_address,
    port: this.port,
    delayBetweenCommands: this.delay,
    commandRepeat: this.repeat
  });

}
MiLightAccessory.prototype = {

  setPowerState: function(powerOn, callback) {
    if (powerOn) {
      this.log("Setting power state to on");
      this.light.sendCommands(commands[this.type].on(this.zone));
    } else {
      this.log("Setting power state to off");
      this.light.sendCommands(commands[this.type].off(this.zone));
    }
    callback();
  },

  setBrightness: function(level, callback) {
    if (level == 0) {
      // If brightness is set to 0, turn off the lamp
      this.log("Setting brightness to 0 (off)");
      this.light.sendCommands(commands[this.type].off(this.zone));
    } else if (level <= 2 && (this.type == "rgbw" || this.type == "white")) {
      // If setting brightness to 2 or lower, instead set night mode for lamps that support it
      this.log("Setting night mode", level);

      this.light.sendCommands(commands[this.type].off(this.zone));
      // Ensure we're pausing for 100ms between these commands as per the spec
      this.light.pause(100);
      this.light.sendCommands(commands[this.type].nightMode(this.zone));

    } else {
      this.log("Setting brightness to %s", level);

      // Send on command to ensure we're addressing the right bulb
      this.light.sendCommands(commands[this.type].on(this.zone));

      // If this is an rgbw lamp, set the absolute brightness specified
      if (this.type == "rgbw") {
        this.light.sendCommands(commands.rgbw.brightness(level));
      } else {
        // If this is an rgb or a white lamp, they only support brightness up and down.
        // Set brightness up when value is >50 and down otherwise. Not sure how well this works real-world.
        if (level >= 50) {
          if (this.type == "white" && level == 100) {
            // But the white lamps do have a "maximum brightness" command
            this.light.sendCommands(commands.white.maxBright(this.zone));
          } else {
            this.light.sendCommands(commands[this.type].brightUp());
          }
        } else {
          this.light.sendCommands(commands[this.type].brightDown());
        }
      }
    }
    callback();
  },

  setHue: function(value, callback) {
    this.log("Setting hue to %s", value);

    var hue = Array(value, 0, 0);

    // Send on command to ensure we're addressing the right bulb
    this.light.sendCommands(commands[this.type].on(this.zone));

    if (this.type == "rgbw") {
      if (value == 0) {
        this.light.sendCommands(commands.rgbw.whiteMode(this.zone));
      } else {
        this.light.sendCommands(commands.rgbw.hue(commands.rgbw.hsvToMilightColor(hue)));
      }
    } else if (this.type == "rgb") {
      this.light.sendCommands(commands.rgb.hue(commands.rgbw.hsvToMilightColor(hue)));
    } else if (this.type == "white") {
      // Again, white lamps don't support setting an absolue colour temp, so trying to do warmer/cooler step at a time based on colour
      if (value >= 180) {
        this.light.sendCommands(commands.white.warmer());
      } else {
        this.light.sendCommands(commands.white.cooler());
      }
    }

  },

  identify: function(callback) {
    this.log("Identify requested!");
    callback(); // success
  },

  getServices: function() {
    var informationService = new Service.AccessoryInformation();

    informationService
      .setCharacteristic(Characteristic.Manufacturer, "MiLight")
      .setCharacteristic(Characteristic.Model, this.type)
      .setCharacteristic(Characteristic.SerialNumber, "MILIGHT12345");

    var lightbulbService = new Service.Lightbulb();

    lightbulbService
      .getCharacteristic(Characteristic.On)
      .on('set', this.setPowerState.bind(this));

    lightbulbService
      .addCharacteristic(new Characteristic.Brightness())
      .on('set', this.setBrightness.bind(this));

    lightbulbService
      .addCharacteristic(new Characteristic.Hue())
      .on('set', this.setHue.bind(this));

    return [informationService, lightbulbService];
  }
};
<commit_msg>[MiLight] Modify logging to show the zone name when used as a platform accessory<commit_after>/*

MiLight platform shim for Homebridge
Written by Sam Edwards (https://samedwards.ca/)

Uses the node-milight-promise library (https://github.com/mwittig/node-milight-promise) which features some code from
applamp.nl (http://www.applamp.nl/service/applamp-api/) and uses other details from (http://www.limitlessled.com/dev/)

Configure in config.json as follows:

"platforms": [
        {
            "platform":"MiLight",
            "name":"MiLight",
            "ip_address": "255.255.255.255",
            "port": 8899,
            "type": "rgbw",
            "delay": 30,
            "repeat": 3,
            "zones":["Kitchen Lamp","Bedroom Lamp","Living Room Lamp","Hallway Lamp"]
        }
]

Where the parameters are:
 *platform (required): This must be "MiLight", and refers to the name of the accessory as exported from this file
 *name (optional): The display name used for logging output by Homebridge. Best to set to "MiLight"
 *ip_address (optional): The IP address of the WiFi Bridge. Default to the broadcast address of 255.255.255.255 if not specified
 *port (optional): Port of the WiFi bridge. Defaults to 8899 if not specified
 *type (optional): One of either "rgbw", "rgb", or "white", depending on the type of bulb being controlled. This applies to all zones. Defaults to rgbw.
 *delay (optional): Delay between commands sent over UDP. Default 30ms
 *repeat (optional): Number of times to repeat the UDP command for better reliability. Default 3
 *zones (required): An array of the names of the zones, in order, 1-4. Use null if a zone is skipped. RGB lamps can only have a single zone.

Tips and Tricks:
 *Setting the brightness of an rgbw or a white bulb will set it to "night mode", which is dimmer than the lowest brightness setting
 *White and rgb bulbs don't support absolute brightness setting, so we just send a brightness up/brightness down command depending
   if we got a percentage above/below 50% respectively
 *The only exception to the above is that white bulbs support a "maximum brightness" command, so we send that when we get 100%
 *Implemented warmer/cooler for white lamps in a similar way to brightnes, except this time above/below 180 degrees on the colour wheel
 *I welcome feedback on a better way to work the brightness/hue for white and rgb bulbs

Troubleshooting:
The node-milight-promise library provides additional debugging output when the MILIGHT_DEBUG environmental variable is set

TODO:
 *Possibly build in some sort of state logging and persistance so that we can answswer HomeKit status queries to the best of our ability

*/

var Service = require("HAP-NodeJS").Service;
var Characteristic = require("HAP-NodeJS").Characteristic;
var Milight = require('node-milight-promise').MilightController;
var commands = require('node-milight-promise').commands;

module.exports = {
  accessory: MiLightAccessory,
  platform: MiLightPlatform
}

function MiLightPlatform(log, config) {
  this.log = log;

  this.config = config;
}

MiLightPlatform.prototype = {
  accessories: function(callback) {
    var that = this;
    var zones = [];

    // Various error checking
    if (this.config.zones) {
      var zoneLength = this.config.zones.length;
    } else {
      this.log("ERROR: Could not read zones from configuration.");
      return;
    }

    if (!this.config["type"]) {
      this.log("INFO: Type not specified, defaulting to rgbw");
      this.config["type"] = "rgbw";
    }

    if (zoneLength == 0) {
      this.log("ERROR: No zones found in configuration.");
      return;
    } else if (this.config["type"] == "rgb" && zoneLength > 1) {
      this.log("WARNING: RGB lamps only have a single zone. Only the first defined zone will be used.");
      zoneLength = 1;
    } else if (zoneLength > 4) {
      this.log("WARNING: Only a maximum of 4 zones are supported per bridge. Only recognizing the first 4 zones.");
      zoneLength = 4;
    }

    // Create lamp accessories for all of the defined zones
    for (var i=0; i < zoneLength; i++) {
      if (!!this.config.zones[i]) {
        this.config["name"] = this.config.zones[i];
        this.config["zone"] = i+1;
        lamp = new MiLightAccessory(this.log, this.config);
        zones.push(lamp);
      }
    }
    if (zones.length > 0) {
      callback(zones);
    } else {
      this.log("ERROR: Unable to find any valid zones");
      return;
    }
  }
}

function MiLightAccessory(log, config) {
  this.log = log;

  // config info
  this.ip_address = config["ip_address"];
  this.port = config["port"];
  this.name = config["name"];
  this.zone = config["zone"];
  this.type = config["type"];
  this.delay = config["delay"];
  this.repeat = config["repeat"];

  this.light = new Milight({
    ip: this.ip_address,
    port: this.port,
    delayBetweenCommands: this.delay,
    commandRepeat: this.repeat
  });

}
MiLightAccessory.prototype = {

  setPowerState: function(powerOn, callback) {
    if (powerOn) {
      this.log("["+this.name+"] Setting power state to on");
      this.light.sendCommands(commands[this.type].on(this.zone));
    } else {
      this.log("["+this.name+"] Setting power state to off");
      this.light.sendCommands(commands[this.type].off(this.zone));
    }
    callback();
  },

  setBrightness: function(level, callback) {
    if (level == 0) {
      // If brightness is set to 0, turn off the lamp
      this.log("["+this.name+"] Setting brightness to 0 (off)");
      this.light.sendCommands(commands[this.type].off(this.zone));
    } else if (level <= 2 && (this.type == "rgbw" || this.type == "white")) {
      // If setting brightness to 2 or lower, instead set night mode for lamps that support it
      this.log("["+this.name+"] Setting night mode", level);

      this.light.sendCommands(commands[this.type].off(this.zone));
      // Ensure we're pausing for 100ms between these commands as per the spec
      this.light.pause(100);
      this.light.sendCommands(commands[this.type].nightMode(this.zone));

    } else {
      this.log("["+this.name+"] Setting brightness to %s", level);

      // Send on command to ensure we're addressing the right bulb
      this.light.sendCommands(commands[this.type].on(this.zone));

      // If this is an rgbw lamp, set the absolute brightness specified
      if (this.type == "rgbw") {
        this.light.sendCommands(commands.rgbw.brightness(level));
      } else {
        // If this is an rgb or a white lamp, they only support brightness up and down.
        // Set brightness up when value is >50 and down otherwise. Not sure how well this works real-world.
        if (level >= 50) {
          if (this.type == "white" && level == 100) {
            // But the white lamps do have a "maximum brightness" command
            this.light.sendCommands(commands.white.maxBright(this.zone));
          } else {
            this.light.sendCommands(commands[this.type].brightUp());
          }
        } else {
          this.light.sendCommands(commands[this.type].brightDown());
        }
      }
    }
    callback();
  },

  setHue: function(value, callback) {
    this.log("["+this.name+"] Setting hue to %s", value);

    var hue = Array(value, 0, 0);

    // Send on command to ensure we're addressing the right bulb
    this.light.sendCommands(commands[this.type].on(this.zone));

    if (this.type == "rgbw") {
      if (value == 0) {
        this.light.sendCommands(commands.rgbw.whiteMode(this.zone));
      } else {
        this.light.sendCommands(commands.rgbw.hue(commands.rgbw.hsvToMilightColor(hue)));
      }
    } else if (this.type == "rgb") {
      this.light.sendCommands(commands.rgb.hue(commands.rgbw.hsvToMilightColor(hue)));
    } else if (this.type == "white") {
      // Again, white lamps don't support setting an absolue colour temp, so trying to do warmer/cooler step at a time based on colour
      if (value >= 180) {
        this.light.sendCommands(commands.white.warmer());
      } else {
        this.light.sendCommands(commands.white.cooler());
      }
    }

  },

  identify: function(callback) {
    this.log("["+this.name+"] Identify requested!");
    callback(); // success
  },

  getServices: function() {
    var informationService = new Service.AccessoryInformation();

    informationService
      .setCharacteristic(Characteristic.Manufacturer, "MiLight")
      .setCharacteristic(Characteristic.Model, this.type)
      .setCharacteristic(Characteristic.SerialNumber, "MILIGHT12345");

    var lightbulbService = new Service.Lightbulb();

    lightbulbService
      .getCharacteristic(Characteristic.On)
      .on('set', this.setPowerState.bind(this));

    lightbulbService
      .addCharacteristic(new Characteristic.Brightness())
      .on('set', this.setBrightness.bind(this));

    lightbulbService
      .addCharacteristic(new Characteristic.Hue())
      .on('set', this.setHue.bind(this));

    return [informationService, lightbulbService];
  }
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name         pr0gramm-comments
// @namespace    http://pr0gramm.com/user/Mopsalarm
// @version      1.0.18
// @description  Adds a function to favorite comments
// @author       Mopsalarm
// @match        http://pr0gramm.com/*
// @match        https://pr0gramm.com/*
// @grant        none
// @require      https://mopsalarm.github.io/pr0gramm-comments-userscript/md5.js?1
// @require      https://mopsalarm.github.io/pr0gramm-comments-userscript/pr0gramm-commons.js?2
// @run-at       document-end
// ==/UserScript==

function setupCommentFavoriteScript (userHash) {
  var FAVCACHE = {};
  var ALL = 7;

  CommentFavorites.list(userHash, ALL).then(function (comments) {
    comments.forEach(function (c) {
      FAVCACHE[c.id] = true;
    });

    updateAfterFavCacheChanged();
  });

  jQuery("


<commit_before>"use strict";

var mongoose = require("mongoose");
var Promise = require("bluebird");
var constants = require("./constants");

var Schema = mongoose.Schema;
var INITIAL = constants.INITIAL;
var PENDING= constants.PENDING;
var DONE = constants.DONE;
var SAVE = constants.SAVE;
var UPDATE = constants.UPDATE;
var REMOVE = constants.REMOVE;
var utils;
var updateState;
var modelCache;
var setModel;
var getModel;
var TaskMdl;
var Roller;

var TaskProvider = function (mongoose, _TaskMdl) {
  TaskMdl = _TaskMdl;
  Roller = require("./roller")(mongoose, _TaskMdl);
  utils = require("./utils")(mongoose);
  updateState = utils.updateState;
  modelCache = utils.modelCache;
  setModel = utils.setModel;
  getModel = utils.getModel;

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.initModel = function(modelName, schema){
    // if(!(modelName && schema)) throw new Error("Missing Args: modelName, Schema, or both");
    if(modelCache[modelName]) throw new Error("The schema for this model has already been set");
    if (schema && !isObject(schema)) throw new Error("Invalid Schema");

    var DEFAULT_SCHEMA = new Schema({}, {strict: false});

    setModel(modelName, schema ? new Schema(schema, {strict: true}) : DEFAULT_SCHEMA);

    return task;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    handle$Token(condition);
    handle$Token(data);

    var updateObj = {
      index: index
      , type: UPDATE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;

    return task;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model.toObject();
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      index: index
      , type: SAVE
      , state: INITIAL
      , name: getModelName(model)
      , data: doc
    };

    steps.push(saveObj);
    index++;

    return task;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    handle$Token(condition);

    var removeObj = {
      index: index
      , type: REMOVE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;

    return task;
  };

  task.options = function(options){
    if(!steps.length) throw new Error("Can't set options on non-existing task");
    if(!isObject(options)) throw new Error("Invalid Options");

    var obj = steps[steps.length - 1];

    if(obj.type !== UPDATE){
      throw new Error("the " + obj.type + " function does not accept options");
    }

    obj.options = options;
    return task
  };

  task.run = function(){
    var chain = Promise.resolve();
    var dbTask = new TaskMdl({steps: steps});
    steps = [];
    index = 0;

    return dbTask.save().then(function (_task) {
      _task.steps.forEach(function (step) {
        chain = chain.then(function () {
          return getResolveFunc(step)(step, _task);
        });
      });

      return chain.then(function () {
        return _task.remove();
      }).catch(function (err) {
        return Roller.rollOne(_task).then(function () {
          throw err;
        });
      });
    });
  };
};

function getResolveFunc(step){
  switch(step.type){
    case UPDATE: return performUpdate;
    case SAVE: return performSave;
    case REMOVE: return performRemove;
  }
}

function performUpdate(update, task) {
  var Collection = getModel(update.name);

  handle$Token(update.condition, true);
  handle$Token(update.data, true);

  // console.log(update);
  return storeOldData(update, task)
    .then(function(){
      return updateState(task, update.index, PENDING)
        .then(function(_task){
          return Collection.update(update.condition, update.data, update.options)
            .exec()
            .then(function(){
              return updateState(_task, update.index, DONE);
            });
        });
    });
}

//todo: store id of saved doc
function performSave(save, task){
  var Collection = getModel(save.name);
  var doc = new Collection(save.data);
  var dataStore = [];

  doc._id = doc._id || utils.generateId();
  dataStore.push({_id: doc._id});

  task.steps[save.index].dataStore = dataStore;
  task.steps[save.index].markModified("dataStore");

  return updateState(task, save.index, PENDING).then(function (_task) {
    return doc.save().then(function () {
      return updateState(_task, save.index, DONE);
    });
  });
}

function performRemove(remove, task){
  var Collection = getModel(remove.name);

  handle$Token(remove.condition, true);

  return storeOldData(remove, task).then(function () {
    return updateState(task, remove.index, PENDING).then(function (_task) {
      return Collection.remove(remove.condition).exec().then(function () {
        return updateState(_task, remove.index, DONE);
      });
    });
  });
}

function storeOldData(step, task){
  var Collection = getModel(step.name);
  var options = step.options || step.type === REMOVE ? {multi: true} : null;
  var query = Collection.find(step.condition);
  var searchQuery = options && options.multi === true
    ? query
    : query.limit(1);

  return searchQuery
    .exec()
    .then(function(result) {
      var oldData = [];

      for (var i = 0; i < result.length; i++) {
        oldData.push(result[i].toObject());
      }

      task.steps[step.index].dataStore = oldData;
      task.steps[step.index].markModified("dataStore");
    });
}

function validModel(model){
  if(typeof model === "string") return true;

  return typeof model === "function" && model.modelName;
}

function validDoc(doc){
  return doc && doc._doc && doc.constructor && doc.constructor.modelName
}

function isObject(obj){
  return obj && typeof obj === "object";
}

function getModelName(model){
  return typeof model === "string" ? model : model.modelName;
}

function handle$Token(obj, fix){
  var ESCAPE_PREFIX = constants.ESCAPE_PREFIX;
  var escapeLength = ESCAPE_PREFIX.length;
  var keys = Object.keys(obj);
  var key;
  var $key;

  for(var i = 0; i < keys.length; i++){
    key = keys[i];

    if(!fix && key[0] === "$"){
      obj[ESCAPE_PREFIX + key] = obj[key];
      delete obj[key];
    }

    if(fix && key.length >= escapeLength && key.substring(0, escapeLength) === ESCAPE_PREFIX){
      $key = key.replace(ESCAPE_PREFIX, "");
      obj[$key] = obj[key];
      delete obj[key];
    }
  }
}

module.exports = TaskProvider;
<commit_msg>Fix task escaping<commit_after>"use strict";

var mongoose = require("mongoose");
var Promise = require("bluebird");
var constants = require("./constants");

var Schema = mongoose.Schema;
var INITIAL = constants.INITIAL;
var PENDING= constants.PENDING;
var DONE = constants.DONE;
var SAVE = constants.SAVE;
var UPDATE = constants.UPDATE;
var REMOVE = constants.REMOVE;
var utils;
var updateState;
var modelCache;
var setModel;
var getModel;
var TaskMdl;
var Roller;

var TaskProvider = function (mongoose, _TaskMdl) {
  TaskMdl = _TaskMdl;
  Roller = require("./roller")(mongoose, _TaskMdl);
  utils = require("./utils")(mongoose);
  updateState = utils.updateState;
  modelCache = utils.modelCache;
  setModel = utils.setModel;
  getModel = utils.getModel;

  return Task;
};

var Task = function(){
  var task = this;
  var index = 0;
  var steps = [];

  task.getTaskCollection = function(){
    return TaskMdl;
  };

  task.initModel = function(modelName, schema){
    // if(!(modelName && schema)) throw new Error("Missing Args: modelName, Schema, or both");
    if(modelCache[modelName]) throw new Error("The schema for this model has already been set");
    if (schema && !isObject(schema)) throw new Error("Invalid Schema");

    var DEFAULT_SCHEMA = new Schema({}, {strict: false});

    setModel(modelName, schema ? new Schema(schema, {strict: true}) : DEFAULT_SCHEMA);

    return task;
  };

  task.update = function(model, condition, data){
    if(!data){
      if(!validDoc(model)) throw new Error("Invalid doc");

      data = condition;
      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid model");
    if(!isObject(condition)) throw new Error("Invalid Condition");
    if(!isObject(data)) throw new Error("Invalid data");

    handle$Token(condition);
    handle$Token(data);

    var updateObj = {
      index: index
      , type: UPDATE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
      , data: data
    };

    steps.push(updateObj);
    index++;

    return task;
  };

  task.save = function(model, doc){
    if(!doc){
      if(!validDoc(model)) throw new Error("Invalid doc");

      doc = model.toObject();
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(doc)) throw new Error("Invalid doc");

    var saveObj = {
      index: index
      , type: SAVE
      , state: INITIAL
      , name: getModelName(model)
      , data: doc
    };

    steps.push(saveObj);
    index++;

    return task;
  };

  task.remove = function(model, condition){
    if(!condition){
      if(!validDoc(model)) throw new Error("Invalid doc");

      condition = {_id: model._id};
      model = model.constructor;
    }
    if(!validModel(model)) throw new Error("Invalid Model");
    if(!isObject(condition)) throw new Error("Invalid Condition");

    handle$Token(condition);

    var removeObj = {
      index: index
      , type: REMOVE
      , state: INITIAL
      , name: getModelName(model)
      , condition: condition
    };

    steps.push(removeObj);
    index++;

    return task;
  };

  task.options = function(options){
    if(!steps.length) throw new Error("Can't set options on non-existing task");
    if(!isObject(options)) throw new Error("Invalid Options");

    var obj = steps[steps.length - 1];

    if(obj.type !== UPDATE){
      throw new Error("the " + obj.type + " function does not accept options");
    }

    obj.options = options;
    return task
  };

  task.run = function(){
    var chain = Promise.resolve();
    var dbTask = new TaskMdl({steps: steps});
    steps = [];
    index = 0;

    return dbTask.save().then(function (_task) {
      _task.steps.forEach(function (step) {
        chain = chain.then(function () {
          return getResolveFunc(step)(step, _task);
        });
      });

      return chain.then(function () {
        return _task.remove();
      }).catch(function (err) {
        return Roller.rollOne(_task).then(function () {
          throw err;
        });
      });
    });
  };
};

function getResolveFunc(step){
  switch(step.type){
    case UPDATE: return performUpdate;
    case SAVE: return performSave;
    case REMOVE: return performRemove;
  }
}

function performUpdate(update, task) {
  var Collection = getModel(update.name);

  handle$Token(update.condition, true);
  handle$Token(update.data, true);

  // console.log(update);
  return storeOldData(update, task)
    .then(function(){
      return updateState(task, update.index, PENDING)
        .then(function(_task){
          return Collection.update(update.condition, update.data, update.options)
            .exec()
            .then(function(){
              return updateState(_task, update.index, DONE);
            });
        });
    });
}

//todo: store id of saved doc
function performSave(save, task){
  var Collection = getModel(save.name);
  var doc = new Collection(save.data);
  var dataStore = [];

  doc._id = doc._id || utils.generateId();
  dataStore.push({_id: doc._id});

  task.steps[save.index].dataStore = dataStore;
  task.steps[save.index].markModified("dataStore");

  return updateState(task, save.index, PENDING).then(function (_task) {
    return doc.save().then(function () {
      return updateState(_task, save.index, DONE);
    });
  });
}

function performRemove(remove, task){
  var Collection = getModel(remove.name);

  handle$Token(remove.condition, true);

  return storeOldData(remove, task).then(function () {
    return updateState(task, remove.index, PENDING).then(function (_task) {
      return Collection.remove(remove.condition).exec().then(function () {
        return updateState(_task, remove.index, DONE);
      });
    });
  });
}

function storeOldData(step, task){
  var Collection = getModel(step.name);
  var options = step.options || step.type === REMOVE ? {multi: true} : null;
  var query = Collection.find(step.condition);
  var searchQuery = options && options.multi === true
    ? query
    : query.limit(1);

  return searchQuery
    .exec()
    .then(function(result) {
      var oldData = [];

      for (var i = 0; i < result.length; i++) {
        oldData.push(result[i].toObject());
      }

      task.steps[step.index].dataStore = oldData;
      task.steps[step.index].markModified("dataStore");
    });
}

function validModel(model){
  if(typeof model === "string") return true;

  return typeof model === "function" && model.modelName;
}

function validDoc(doc){
  return doc && doc._doc && doc.constructor && doc.constructor.modelName
}

function isObject(obj){
  return obj && typeof obj === "object";
}

function getModelName(model){
  return typeof model === "string" ? model : model.modelName;
}

function handle$Token(obj, fix){
  var ESCAPE_PREFIX = constants.ESCAPE_PREFIX;
  var escapeLength = ESCAPE_PREFIX.length;
  var keys = Object.keys(obj);
  var key;
  var $key;

  for(var i = 0; i < keys.length; i++){
    key = keys[i];

    if(!fix && key[0] === "$"){
      obj[ESCAPE_PREFIX + key] = obj[key];
      delete obj[key];
    }

    if (!fix && typeof obj[key] === 'object') {
      var childKeys = Object.keys(obj[key]);
      for(var j = 0; j < childKeys.length; j++){
        var current = childKeys[j];
        if(current[0] === "$"){
          obj[key][ESCAPE_PREFIX + current] = obj[key][current];
          delete obj[key][current];
        }
      }
    }

    if(fix && key.length >= escapeLength && key.substring(0, escapeLength) === ESCAPE_PREFIX){
      $key = key.replace(ESCAPE_PREFIX, "");
      obj[$key] = obj[key];
      delete obj[key];
    }

    if(fix && typeof obj[key] === 'object'){
      var childKeys = Object.keys(obj[key]);
      for(var j = 0; j < childKeys.length; j++){
        var current = childKeys[j];
        if(current.indexOf(ESCAPE_PREFIX) === 0){
          var $current = current.replace(ESCAPE_PREFIX, "");
          obj[key][$current] = obj[key][current];
          delete obj[key][current];
        }
      }
    }
  }
}

module.exports = TaskProvider;
<|endoftext|>