<commit_before>var http = require('http'),
    url = require('url'),
    fs = require('fs'),
    ejs = require('ejs'),
    path = require('path'),
    exec = require('child_process').exec,
    fork = require('child_process').fork,
    util = require('util'),
    zlib = require('zlib');
//var querystring = require('querystring');
//===================================================
var Ys = exports.Ys = function(url_regexp) {

	var ys_inst = this;
	if(!this._html)
			ys_inst = Ys;

	if(typeof(ys_inst.routes)!='object')
			ys_inst.routes = [];

    var matched = ys_inst.routes.filter(function(route){
        return route["regexp"] === url_regexp;
	});

	return matched[0] || (function(){
		var route = {"regexp":url_regexp,"get":{},"post":{}};
		route.get.html =this._html_template(route.get);
		route.post.html =this._html_template(route.post);
    	this.routes.push(route);
    	return route;
	}).call(ys_inst);

}
//===================================================
Ys.mime_types = {}
//===================================================
Ys.send_stream = function(fs_path,req,res,headers) {

        fs.stat(fs_path,function(err,stats){

            if(err){
                res.writeHead(404);
                res.end("404 - not found.");
                return;

            }
            var ins = util.inspect(stats);
            var size = ins.match(/size:\s*(\d+)/)[1];
            headers['Content-Length'] = size;

           var flags = {};
           //console.log(req.headers);
           range = req.headers['range'];

           if(range){
                var match = range.match(/^bytes\s*=\s*(\d+)-(\d*)/);
                if(match){
                    var start = parseInt(match[1]);
                    var end = match[2].length > 0 ? parseInt(match[2]) : parseInt(size) -1;
                    headers["Content-Range"] =  'bytes %start-%end/%length'.replace("%start",start).replace("%end",end).replace("%length",size);
                    res.writeHead(206,headers);
                    flags["start"] = start;
                    flags["end"] = end;
                }
                else{
                    res.writeHead(416,headers);
                }
            }
            else
                res.writeHead(200,headers);

            var fstream = fs.createReadStream(fs_path,flags);
            fstream.pipe(res);

        });

};
//===================================================
Ys.send_ogg= function(path,req,res,headers,flags){

	var child = exec("ogginfo %s | grep 'Playback length'".replace("%s",path),
		  function (error, stdout, stderr) {
			  if(error)
					throw error;

			var lens = stdout.trim().split(":");
			var MINUTES = 1, SECONDS = 2;
			var duration = parseFloat(lens[MINUTES])*60 + parseFloat(lens[SECONDS]);
			headers['X-Content-Duration'] = String(duration);
			send_stream(path,req,res,headers);
	});
}
//===================================================
Ys.send_static = function(base_dir,file_path,req,res){

	var ext = path.extname(file_path).substring(1),
		mime_type = this.mime_types[ext],
		fs_path = path.join(path.resolve(base_dir),file_path),
		headers = {'Accept-Ranges': 'bytes','Content-Type':mime_type};

	if(Ys.ogg_header_support && headers["Content-Type"] && headers["Content-Type"].match(/^\w+\/ogg$/)){
			send_ogg(fs_path,req,res,headers);
			return;
	}

	this.send_stream(fs_path,req,res,headers);
}
//===================================================
Ys.send_gzip = function(base_dir,file_path,req,res){

	var ext = path.extname(file_path).substring(1),
	mime_type = this.mime_types[ext],
	fs_path = path.join(path.resolve(base_dir),file_path),
	headers = {'Content-Type':mime_type};

	var raw = fs.createReadStream(fs_path);
	stream_gzip(raw,req,res,headers);

}

//===================================================
Ys._html_template = function(parent){

	return function(template_path){
		parent.html_template = function(object){
		 	//load template
			var res = this;
		 	fs.readFile(template_path, function (err, data) {
				if (err) throw err;
				var compiled = ejs.compile(String(data));
				res.end(compiled(object));
		 	});
		};

		return parent.html_template;
	};
}
//--------------------------------------------------
exports.client = require('./client');
//--------------------------------------------------
Ys.stream_gzip = function(input,req,res,headers){

        var acceptEncoding = req.headers['accept-encoding'];
        if (!acceptEncoding)
            acceptEncoding = '';

        var stream = null,compression_stream = null;

        // Note: this is not a conformant accept-encoding parser.
        // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
        if (acceptEncoding.match(/\bdeflate\b/) && req.headers['user-agent'].indexOf("MSIE") < 0) { //IE does not seem to support deflate very well
            headers['content-encoding'] = 'deflate';
            compression_stream = zlib.createDeflate();
        } else if (acceptEncoding.match(/\bgzip\b/)) {
            headers['content-encoding'] = 'gzip';
            compression_stream = zlib.createGzip();
        }

       res.writeHead(200,headers);

       if(typeof(input.pipe) === "function"){
			   if(compression_stream)
					stream = input.pipe(compression_stream);
			   else
					stream = input;

			   stream.pipe(res);
			   return;
       }

       if(compression_stream){
	    compression_stream.pipe(res);
	    compression_stream.write(input);
	    compression_stream.end();
	    return;
       }

       res.end(input);

}
//--------------------------------------------------
Ys.handlers = [

	function redirect(route,req,res){

		if(typeof(route.redirect) != "string") return false;

     	redirect_to = route["redirect"].replace("$1",req.$1);
		var statusCode = 301;

		if(req.method == "post") statusCode = 307;
		res.writeHead(statusCode,{"Location":redirect_to});
		res.end();
        return true;
	},

	function rewrite(route,req,res){
		if(typeof(route.rewrite) != "string") return false;

        req.pathname = route.rewrite.replace("$1",req.$1);
		return false;//so we continue processing the request with the re-written pathname!
	},

	function gzip(route,req,res){
		if(typeof(route[req.method].gzip) != "string") return false;
		var base_dir = route[req.method].gzip;
		this.send_gzip(base_dir,req.pathname,req,res);
		return true;
	},

	function static(route,req,res){
		if(typeof(route[req.method].static) != "string") return false;
		var base_dir = route[req.method].static;
		this.send_static(base_dir,req.pathname,req,res);
		return true;
	},

	function raw(route,req,res){

		if(typeof(route[req.method]) != "function") return false;

		res.writeHead(200, {'Content-Type': 'text/html'});

		route[req.method](req,res);

		return true;

	},

	function json(route,req,res){
		if(typeof(route[req.method].json) != "function") return false;

		res.writeHead(200, {'Content-Type': 'application/json'});
		res.returnObject = function(object){
    		this.end(JSON.stringify(object));
		};
		route[req.method].json(req,res);
		return true;

	},

	function html(route,req,res){
		if(typeof(route[req.method].json) != "function") return false;
		res.writeHead(200, {'Content-Type': 'text/html'});
		route[req.method].html(req,res);
		return true;

	},

	function html_template(route,req,res){

		if(typeof(route[req.method].html_template) != "function") return false;

		res.writeHead(200, {'Content-Type': 'text/html'});

		res.returnObject = function(object){

			route[req.method].html_template.call(this,object);
		};

		route[req.method].html_template.args(req,res);
		return true;
	}

];

//--------------------------------------------------
Ys.handle_request = function(req,res){

    req.pathname = url.parse(req.url).pathname;
	req.method = req.method.toLowerCase();

	var route = null;
	//find matching route
	this.routes.every(function(r){
        var regexp = RegExp(r.regexp);
		var match = regexp.exec(req.pathname);
        if(match){
			route = r;
			if(match.length > 1) req.$1 = match[1];
			return false;
		}
		return true;
	 });

	if(!route)
		throw new Error(pathname +" >> No mapping for this path");

	var ys_inst = this;
	var request_handled = !ys_inst.handlers.every(function(handler){
		return !handler.call(ys_inst,route,req,res);//reverse return value so every will work correctly
	});

    if(!request_handled)
    	throw new Error(req.pathname +" >> No mapping for this path");
}
//--------------------------------------------------
var run_debug_parent = function(options){

    var dir = path.dirname(module.parent.filename);
    var module_file = module.parent.filename;
    var prev_stat = fs.statSync(module_file);
    var child = null;

	var fork_child = function () {
		child = fork(module_file,[],{env:{is_child:true}});
		child.on('exit',fork_child);
	};

    fork_child();

    var tid = null;
    fs.watch(dir, function(event,filename){
        clearTimeout(tid);
        tid = setTimeout(function(){
            stat = fs.statSync(module_file);
            if(stat.mtime.getTime() > prev_stat.mtime.getTime()){
                console.log("restarting server...");
                prev_stat = stat;
                child.kill();
            }
        }, 1000);
    });
 };
//--------------------------------------------------
Ys.run = function(options){

	if(!options)
	    options = {};

    if(options.debug && !process.env.is_child){
        run_debug_parent(options);
        return;
    }

    if(!options.port)
        options.port = 8780;

    if(!options.host)
        options.host= "localhost";

    var mimes_raw  = fs.readFileSync('/etc/mime.types','utf-8').split('\n')
    for(var i=0; i<mimes_raw.length; i++){
        var next = mimes_raw[i].split(/\s+/g)
        if(next && next.length >= 2)
            for(var j=1; j < next.length; j++)
                this.mime_types[next[j]] = next[0];
    }

    //correction for some types
    //mime_types["mp3"] = "audio/mp3";
    process.on('uncaughtException', function (err) {
            var str = err.stack;
            console.log(str);
            //res.end(str);
    });

	var ys_inst = this;
    var server = Ys.server =http.createServer(function (req, res) {

        try{
            ys_inst.handle_request(req,res);
        }
        catch(e){
            var str = e.stack;
            console.log(str);
            res.end(str);
        }

    });

    if(options.user){
        server.once("listening",function(){
            process.setuid(options.user);
            //process.setgid(user);
        });
    }

    server.listen(options.port,options.host);

    console.log('Server running at '+options.host+':'+options.port+'/');
}
//--------------------------------------------------
Ys.stop = function(){
    console.log("shutting down ...");
    Ys.server.close();
}
//--------------------------------------------------
<commit_msg>internal changes to enable proxy<commit_after>var http = require('http'),
    url = require('url'),
    fs = require('fs'),
    ejs = require('ejs'),
    path = require('path'),
    exec = require('child_process').exec,
    fork = require('child_process').fork,
    util = require('util'),
    zlib = require('zlib');
//var querystring = require('querystring');
//===================================================
var Router = function(){};
//===================================================
Router.prototype.routes = [];
//===================================================
Router.prototype.mime_types = {}
//===================================================
Router.prototype.send_stream = function(fs_path,req,res,headers) {

        fs.stat(fs_path,function(err,stats){

            if(err){
                res.writeHead(404);
                res.end("404 - not found.");
                return;

            }
            var ins = util.inspect(stats);
            var size = ins.match(/size:\s*(\d+)/)[1];
            headers['Content-Length'] = size;

           var flags = {};
           //console.log(req.headers);
           range = req.headers['range'];

           if(range){
                var match = range.match(/^bytes\s*=\s*(\d+)-(\d*)/);
                if(match){
                    var start = parseInt(match[1]);
                    var end = match[2].length > 0 ? parseInt(match[2]) : parseInt(size) -1;
                    headers["Content-Range"] =  'bytes %start-%end/%length'.replace("%start",start).replace("%end",end).replace("%length",size);
                    res.writeHead(206,headers);
                    flags["start"] = start;
                    flags["end"] = end;
                }
                else{
                    res.writeHead(416,headers);
                }
            }
            else
                res.writeHead(200,headers);

            var fstream = fs.createReadStream(fs_path,flags);
            fstream.pipe(res);

        });

};
//===================================================
Router.prototype.send_ogg= function(path,req,res,headers,flags){

	var child = exec("ogginfo %s | grep 'Playback length'".replace("%s",path),
		  function (error, stdout, stderr) {
			  if(error)
					throw error;

			var lens = stdout.trim().split(":");
			var MINUTES = 1, SECONDS = 2;
			var duration = parseFloat(lens[MINUTES])*60 + parseFloat(lens[SECONDS]);
			headers['X-Content-Duration'] = String(duration);
			send_stream(path,req,res,headers);
	});
}
//===================================================
Router.prototype.send_static = function(base_dir,file_path,req,res){

	var ext = path.extname(file_path).substring(1),
		mime_type = this.mime_types[ext],
		fs_path = path.join(path.resolve(base_dir),file_path),
		headers = {'Accept-Ranges': 'bytes','Content-Type':mime_type};

	if(Ys.ogg_header_support && headers["Content-Type"] && headers["Content-Type"].match(/^\w+\/ogg$/)){
			send_ogg(fs_path,req,res,headers);
			return;
	}

	this.send_stream(fs_path,req,res,headers);
}
//===================================================
Router.prototype.send_gzip = function(base_dir,file_path,req,res){

	var ext = path.extname(file_path).substring(1),
	mime_type = this.mime_types[ext],
	fs_path = path.join(path.resolve(base_dir),file_path),
	headers = {'Content-Type':mime_type};

	var raw = fs.createReadStream(fs_path);
	stream_gzip(raw,req,res,headers);

}

//===================================================
Router.prototype._html_template = function(parent){

	return function(template_path){
		parent.html_template = function(object){
		 	//load template
			var res = this;
		 	fs.readFile(template_path, function (err, data) {
				if (err) throw err;
				var compiled = ejs.compile(String(data));
				res.end(compiled(object));
		 	});
		};

		return parent.html_template;
	};
}
//--------------------------------------------------
exports.client = require('./client');
//--------------------------------------------------
Router.prototype.stream_gzip = function(input,req,res,headers){

        var acceptEncoding = req.headers['accept-encoding'];
        if (!acceptEncoding)
            acceptEncoding = '';

        var stream = null,compression_stream = null;

        // Note: this is not a conformant accept-encoding parser.
        // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3
        if (acceptEncoding.match(/\bdeflate\b/) && req.headers['user-agent'].indexOf("MSIE") < 0) { //IE does not seem to support deflate very well
            headers['content-encoding'] = 'deflate';
            compression_stream = zlib.createDeflate();
        } else if (acceptEncoding.match(/\bgzip\b/)) {
            headers['content-encoding'] = 'gzip';
            compression_stream = zlib.createGzip();
        }

       res.writeHead(200,headers);

       if(typeof(input.pipe) === "function"){
			   if(compression_stream)
					stream = input.pipe(compression_stream);
			   else
					stream = input;

			   stream.pipe(res);
			   return;
       }

       if(compression_stream){
	    compression_stream.pipe(res);
	    compression_stream.write(input);
	    compression_stream.end();
	    return;
       }

       res.end(input);

}
//--------------------------------------------------
Router.prototype.handlers = [

	function redirect(route,req,res){

		if(typeof(route.redirect) != "string") return false;

     	redirect_to = route["redirect"].replace("$1",req.$1);
		var statusCode = 301;

		if(req.method == "post") statusCode = 307;
		res.writeHead(statusCode,{"Location":redirect_to});
		res.end();
        return true;
	},

	function rewrite(route,req,res){
		if(typeof(route.rewrite) != "string") return false;

        req.pathname = route.rewrite.replace("$1",req.$1);
		return false;//so we continue processing the request with the re-written pathname!
	},

	function gzip(route,req,res){
		if(typeof(route[req.method].gzip) != "string") return false;
		var base_dir = route[req.method].gzip;
		this.send_gzip(base_dir,req.pathname,req,res);
		return true;
	},

	function static(route,req,res){
		if(typeof(route[req.method].static) != "string") return false;
		var base_dir = route[req.method].static;
		this.send_static(base_dir,req.pathname,req,res);
		return true;
	},

	function raw(route,req,res){

		if(typeof(route[req.method]) != "function") return false;

		res.writeHead(200, {'Content-Type': 'text/html'});

		route[req.method](req,res);

		return true;

	},

	function json(route,req,res){
		if(typeof(route[req.method].json) != "function") return false;

		res.writeHead(200, {'Content-Type': 'application/json'});
		res.returnObject = function(object){
    		this.end(JSON.stringify(object));
		};
		route[req.method].json(req,res);
		return true;

	},

	function html(route,req,res){
		if(typeof(route[req.method].json) != "function") return false;
		res.writeHead(200, {'Content-Type': 'text/html'});
		route[req.method].html(req,res);
		return true;

	},

	function html_template(route,req,res){

		if(typeof(route[req.method].html_template) != "function") return false;

		res.writeHead(200, {'Content-Type': 'text/html'});

		res.returnObject = function(object){

			route[req.method].html_template.call(this,object);
		};

		route[req.method].html_template.args(req,res);
		return true;
	}

];

//--------------------------------------------------
Router.prototype.handle_request = function(req,res){

    req.pathname = url.parse(req.url).pathname;
	req.method = req.method.toLowerCase();

	var route = null;
	//find matching route
	this.routes.every(function(r){
        var regexp = RegExp(r.regexp);
		var match = regexp.exec(req.pathname);
        if(match){
			route = r;
			if(match.length > 1) req.$1 = match[1];
			return false;
		}
		return true;
	 });

	if(!route)
		throw new Error(pathname +" >> No mapping for this path");

	var ys_inst = this;
	var request_handled = !ys_inst.handlers.every(function(handler){
		return !handler.call(ys_inst,route,req,res);//reverse return value so every will work correctly
	});

    if(!request_handled)
    	throw new Error(req.pathname +" >> No mapping for this path");
}
//--------------------------------------------------
var Ys = exports.Ys = function(url_regexp) {

	var router = this;
   	if(router === global){
		router = Ys.router = new Router();
	}

    var matched = router.routes.filter(function(route){
        return route["regexp"] === url_regexp;
	});

	return matched[0] || (function(){
		var route = {"regexp":url_regexp,"get":{},"post":{}};
		route.get.html = router._html_template(route.get);
		route.post.html = router._html_template(route.post);
    	this.routes.push(route);
    	return route;
	}).call(router);

}
//--------------------------------------------------
var run_debug_parent = function(options){

    var dir = path.dirname(module.parent.filename);
    var module_file = module.parent.filename;
    var prev_stat = fs.statSync(module_file);
    var child = null;

	var fork_child = function () {
		child = fork(module_file,[],{env:{is_child:true}});
		child.on('exit',fork_child);
	};

    fork_child();

    var tid = null;
    fs.watch(dir, function(event,filename){
        clearTimeout(tid);
        tid = setTimeout(function(){
            stat = fs.statSync(module_file);
            if(stat.mtime.getTime() > prev_stat.mtime.getTime()){
                console.log("restarting server...");
                prev_stat = stat;
                child.kill();
            }
        }, 1000);
    });
 };
//--------------------------------------------------
Ys.run = function(options){

	if(!options)
	    options = {};

    if(options.debug && !process.env.is_child){
        run_debug_parent(options);
        return;
    }

    if(!options.port)
        options.port = 8780;

    if(!options.host)
        options.host= "localhost";

	var router = this;
	if(router === Ys)
		router = Ys.router;

    var mimes_raw  = fs.readFileSync('/etc/mime.types','utf-8').split('\n')
    for(var i=0; i<mimes_raw.length; i++){
        var next = mimes_raw[i].split(/\s+/g)
        if(next && next.length >= 2)
            for(var j=1; j < next.length; j++)
                router.mime_types[next[j]] = next[0];
    }

    //correction for some types
    //mime_types["mp3"] = "audio/mp3";
    process.on('uncaughtException', function (err) {
            var str = err.stack;
            console.log(str);
            //res.end(str);
    });

    var server = router.server = http.createServer(function (req, res) {

        try{
            router.handle_request(req,res);
        }
        catch(e){
            var str = e.stack;
            console.log(str);
            res.end(str);
        }

    });

    if(options.user){
        server.once("listening",function(){
            process.setuid(options.user);
            //process.setgid(user);
        });
    }

    server.listen(options.port,options.host);

    console.log('Server running at '+options.host+':'+options.port+'/');
}
//--------------------------------------------------
Ys.stop = function(){
    console.log("shutting down ...");

	var router = this;
	if(router === Ys)
		router = Ys.router;
    router.server.close();
}
//--------------------------------------------------
Ys.instance = function(){

	var router = new Router();

	var func = function(){};
	func.prototype = Ys;
	var inst = new func();
	var bound = router.bind(inst);
	bound.__proto__ = Ys;
	return bound;
}
//--------------------------------------------------
<|endoftext|>


<commit_before>PostModeMenu = {
  mode: "view",

  init: function(pool_id) {
    try {	/* This part doesn't work on IE7; for now, let's allow execution to continue so at least some initialization is run */

    /* If pool_id isn't null, it's the pool that we're currently searching for. */
    this.pool_id = pool_id;

    var color_element = $("mode-box")
    this.original_style = { border: color_element.getStyle("border") }

    if (Cookie.get("mode") == "") {
      Cookie.put("mode", "view")
      $("mode").value = "view"
    } else {
      $("mode").value = Cookie.get("mode")
    }

    } catch (e) {}

    this.vote_score = Cookie.get("vote")
    if (this.vote_score == "") {
      this.vote_score = 1
      Cookie.put("vote", this.vote_score)
    } else {
      this.vote_score == +this.vote_score
    }

    Post.posts.each(function(p) {
      var post_id = p[0]
      var post = p[1]

      var span = $("p" + post.id);
      if(span == null)
        return;

      /* Use post_id here, not post, since the post object can be replaced later after updates. */
      span.down("A").observe("click", function(e) { PostModeMenu.click(e, post_id); });
      span.down("A").observe("mousedown", function(e) { PostModeMenu.post_mousedown(e, post_id); });
      span.down("A").observe("mouseover", function(e) { PostModeMenu.post_mouseover(e, post_id); });
      span.down("A").observe("mouseout", function(e) { PostModeMenu.post_mouseout(e, post_id); });
      span.down("A").observe("mouseup", function(e) { PostModeMenu.post_mouseup(e, post_id); });
    });

    document.observe("mouseup", function(e) { PostModeMenu.post_mouseup(e, null); });
    Event.observe(window, "pagehide", function(e) { PostModeMenu.post_end_drag(); });

    this.change()
  },

  set_vote: function(score) {
    this.vote_score = score
    Cookie.put("vote", this.vote_score)
    Post.update_vote_widget('vote-menu', this.vote_score);
  },

  get_style_for_mode: function(s)
  {
    if (s == "view") {
      return {background: ""};
    } else if (s == "edit") {
      return {background: "#3A3"}
    } else if (s == "rating-q") {
      return {background: "#AAA"}
    } else if (s == "rating-s") {
      return {background: "#6F6"}
    } else if (s == "rating-e") {
      return {background: "#F66"}
    } else if (s == "vote") {
      return {background: "#FAA"}
    } else if (s == "lock-rating") {
      return {background: "#AA3"}
    } else if (s == "lock-note") {
      return {background: "#3AA"}
    } else if (s == "approve") {
      return {background: "#26A"}
    } else if (s == "flag") {
      return {background: "#F66"}
    } else if (s == "add-to-pool") {
      return {background: "#26A"}
    } else if (s == "apply-tag-script") {
      return {background: "#A3A"}
    } else if (s == "reparent-quick") {
      return {background: "#CCA"}
    } else if (s == "remove-from-pool") {
      return {background: "#CCA"}
    } else if (s == 'reparent') {
      return {background: "#0C0"}
    } else if (s == 'dupe') {
      return {background: "#0C0"}
    } else {
      return {background: "#AFA"}
    }
  },

  change: function() {
    if(!$("mode"))
      return;
    var s = $F("mode")
    Cookie.put("mode", s, 7)

    PostModeMenu.mode = s

    if (s.value != "edit") {
      $("quick-edit").hide()
    }
    if (s.value != "apply-tag-script") {
      $("edit-tag-script").hide()
      Post.reset_tag_script_applied()
    }

    if (s == "vote") {
      Post.update_vote_widget('vote-menu', this.vote_score);
      $("vote-score").show()
    } else if (s == "apply-tag-script") {
      $("edit-tag-script").show()
      $("edit-tag-script").focus()
    }
  },

  click: function(event, post_id) {
    var s = $("mode")
    if(!s)
      return;

    if (s.value == "view") {
      return true
    }

    if (s.value == "edit") {
      post_quick_edit.show(post_id);
    } else if (s.value == 'vote') {
      Post.vote(this.vote_score, post_id, {})
    } else if (s.value == 'rating-q') {
      Post.update(post_id, {"post[rating]": "questionable"})
    } else if (s.value == 'rating-s') {
      Post.update(post_id, {"post[rating]": "safe"})
    } else if (s.value == 'rating-e') {
      Post.update(post_id, {"post[rating]": "explicit"})
    } else if (s.value == 'reparent') {
      if(post_id == id)
       return false;
      TagScript.run(post_id, "parent:" + id)
    } else if (s.value == 'dupe') {
      if(post_id == id)
       return false;
      TagScript.run(post_id, "duplicate parent:" + id)
    } else if (s.value == 'lock-rating') {
      Post.update(post_id, {"post[is_rating_locked]": "1"})
    } else if (s.value == 'lock-note') {
      Post.update(post_id, {"post[is_note_locked]": "1"})
    } else if (s.value == 'flag') {
      Post.flag(post_id)
    } else if (s.value == "approve") {
      Post.approve(post_id)
    } else if (s.value == 'add-to-pool') {
      Pool.add_post(post_id, 0)
    } else if (s.value == "remove-from-pool") {
      Pool.remove_post(post_id, PostModeMenu.pool_id);
    }

    event.stopPropagation();
    event.preventDefault();
  },

  dragging_from_post: null,
  dragging_active: false,
  dragging_list: null,
  dragging_hash: null,

  post_add_to_hovered_list: function(post_id)
  {
    var element = element = $$("#p" + post_id + " > .directlink");
    if(element.length > 0)
    {
      element[0].addClassName("tag-script-applied");
      Post.applied_list.push(element[0]);
    }

    if(!PostModeMenu.dragging_hash.get(post_id))
    {
      PostModeMenu.dragging_hash.set(post_id, true);
      PostModeMenu.dragging_list.push(post_id);
    }
  },

  post_mousedown: function(event, post_id)
  {
    if(event.button != 0)
      return;

    if(PostModeMenu.mode == "reparent-quick")
    {
      PostModeMenu.dragging_from_post = post_id;
      PostModeMenu.post_begin_drag();
    }
    else if(PostModeMenu.mode == "apply-tag-script")
    {
      Post.reset_tag_script_applied();
      PostModeMenu.dragging_from_post = post_id;
      PostModeMenu.dragging_list = new Array;
      PostModeMenu.dragging_hash = new Hash;
      PostModeMenu.post_add_to_hovered_list(post_id);
    }
    else
      return;

    /* Prevent the mousedown from being processed; this keeps it from turning into
     * a real drag action, which will suppress our mouseover/mouseout messages.  We
     * only do this when the tag script is enabled, so we don't mess with regular
     * clicks. */
    event.preventDefault();
    event.stopPropagation();
  },

  post_begin_drag: function(type)
  {
    document.body.addClassName("dragging-to-post");
  },

  post_end_drag: function()
  {
    document.body.removeClassName("dragging-to-post");
    PostModeMenu.dragging_from_post = null;
  },

  post_mouseup: function(event, post_id)
  {
    if(event.button != 0)
      return;
    if(!PostModeMenu.dragging_from_post)
      return;

    if(PostModeMenu.mode == "reparent-quick")
    {
      if(post_id)
      {
        notice("Updating post");
        Post.update_batch([{ id: PostModeMenu.dragging_from_post, parent_id: post_id}]);
      }

      PostModeMenu.post_end_drag();
      return;
    }
    else if(PostModeMenu.mode == "apply-tag-script")
    {
      if(post_id)
        return;

      /* We clicked or dragged some posts to apply a tag script; process it. */
      var tag_script = TagScript.TagEditArea.value;
      TagScript.run(PostModeMenu.dragging_list, tag_script);

      PostModeMenu.dragging_from_post = null;
      PostModeMenu.dragging_active = false;
      PostModeMenu.dragging_list = null;
      PostModeMenu.dragging_hash = null;
    }
  },

  post_mouseover: function(event, post_id)
  {
    var post = $("p" + post_id);
    var style = PostModeMenu.get_style_for_mode(PostModeMenu.mode)
    post.down("span").setStyle(style)

    if(PostModeMenu.mode != "apply-tag-script")
      return;

    if(!PostModeMenu.dragging_from_post)
      return;

    if(post_id != PostModeMenu.dragging_from_post)
      PostModeMenu.dragging_active = true;

    PostModeMenu.post_add_to_hovered_list(post_id);
  },

  post_mouseout: function(event, post_id)
  {
    var post = $("p" + post_id);
    post.down("span").setStyle({background: ""});
  },

  apply_tag_script_to_all_posts: function()
  {
    var tag_script = TagScript.TagEditArea.value;
    var post_ids = Post.posts.inject([], function(list, pair) {
      list.push(pair[0]);
      return list;
    });

    TagScript.run(post_ids, tag_script);
  }
}

TagScript = {
  TagEditArea: null,

  load: function() {
    this.TagEditArea.value = Cookie.get("tag-script")
  },
  save: function() {
    Cookie.put("tag-script", encodeURIComponent(this.TagEditArea.value))
  },

  init: function(element, x) {
    this.TagEditArea = element

    TagScript.load()

    this.TagEditArea.observe("change", function(e) { TagScript.save() })
    this.TagEditArea.observe("focus", function(e) { Post.reset_tag_script_applied() })

    /* This mostly keeps the tag script field in sync between windows, but it
     * doesn't work in Opera, which sends focus events before blur events. */
    document.observe("blur", function(e) { TagScript.save() })
    document.observe("focus", function(e) { TagScript.load() })
  },

  parse: function(script) {
    return script.match(/\[.+?\]|\S+/g)
  },

  test: function(tags, predicate) {
    var split_pred = predicate.match(/\S+/g)
    var is_true = true

    split_pred.each(function(x) {
      if (x[0] == "-") {
        if (tags.include(x.substr(1, 100))) {
          is_true = false
          throw $break
        }
      } else {
        if (!tags.include(x)) {
          is_true = false
          throw $break
        }
      }
    })

    return is_true
  },

  process: function(tags, command) {
    if (command.match(/^\[if/)) {
      var match = command.match(/\[if\s+(.+?)\s*,\s*(.+?)\]/)
      if (TagScript.test(tags, match[1])) {
        return TagScript.process(tags, match[2])
      } else {
        return tags
      }
    } else if (command == "[reset]") {
      return []
    } else if (command[0] == "-" && command.indexOf(":") == -1) {
      return tags.reject(function(x) {return x == command.substr(1, 100)})
    } else {
      tags.push(command)
      return tags
    }
  },

  run: function(post_ids, tag_script, finished) {
    if(!Object.isArray(post_ids))
      post_ids = $A([post_ids]);

    var commands = TagScript.parse(tag_script) || []

    var posts = new Array;
    post_ids.each(function(post_id) {
      var post = Post.posts.get(post_id)
      var old_tags = post.tags.join(" ")

      commands.each(function(x) {
        post.tags = TagScript.process(post.tags, x)
      })

      posts.push({
        id: post_id,
        old_tags: old_tags,
        tags: post.tags.join(" ")
      });
    });

    notice("Updating " + posts.length + (post_ids.length == 1? " post": " posts") );
    Post.update_batch(posts, finished);
  }
}

function PostQuickEdit(container)
{
  this.container = container;
  this.submit_event = this.submit_event.bindAsEventListener(this);

  this.container.down("form").observe("submit", this.submit_event);
  this.container.down(".cancel").observe("click", function(e) {
    e.preventDefault();
    this.hide();
  }.bindAsEventListener(this));
  this.container.down("#post_tags").observe("keydown", function(e) {
    if(e.keyCode == Event.KEY_ESC)
    {
      e.stop();
      this.hide();
      return;
    }

    if(e.keyCode != Event.KEY_RETURN)
      return;
    this.submit_event(e);
  }.bindAsEventListener(this));
}

PostQuickEdit.prototype.show = function(post_id)
{
  Post.hover_info_pin(post_id);

  var post = Post.posts.get(post_id);
  this.container.down("#id").value = post_id;
  this.container.down("#post_old_tags").value = post.tags.join(" ");
  this.container.down("#post_tags").value = post.tags.join(" ") + " rating:" + post.rating.substr(0, 1) + " ";
  this.container.show();
  this.container.down("#post_tags").focus();
}

PostQuickEdit.prototype.hide = function()
{
  this.container.hide();
  Post.hover_info_pin(null);
}

PostQuickEdit.prototype.submit_event = function(e)
{
  e.stop();
  this.hide();

  new Ajax.Request("/post/update.json", {
    parameters: this.container.down("form").serialize(),
    onSuccess: function(resp) {
      var resp = resp.responseJSON;
      notice("Post updated");
      this.hide();
      Post.register(resp.post);
      Post.register_tags(resp.tags);
    }.bindAsEventListener(this),
    onFailure: function(resp) {
      var resp = resp.responseJSON;
      notice("Error: " + resp.reason);
    }.bindAsEventListener(this)
  });
}

<commit_msg>fix cookies double-escaped<commit_after>PostModeMenu = {
  mode: "view",

  init: function(pool_id) {
    try {	/* This part doesn't work on IE7; for now, let's allow execution to continue so at least some initialization is run */

    /* If pool_id isn't null, it's the pool that we're currently searching for. */
    this.pool_id = pool_id;

    var color_element = $("mode-box")
    this.original_style = { border: color_element.getStyle("border") }

    if (Cookie.get("mode") == "") {
      Cookie.put("mode", "view")
      $("mode").value = "view"
    } else {
      $("mode").value = Cookie.get("mode")
    }

    } catch (e) {}

    this.vote_score = Cookie.get("vote")
    if (this.vote_score == "") {
      this.vote_score = 1
      Cookie.put("vote", this.vote_score)
    } else {
      this.vote_score == +this.vote_score
    }

    Post.posts.each(function(p) {
      var post_id = p[0]
      var post = p[1]

      var span = $("p" + post.id);
      if(span == null)
        return;

      /* Use post_id here, not post, since the post object can be replaced later after updates. */
      span.down("A").observe("click", function(e) { PostModeMenu.click(e, post_id); });
      span.down("A").observe("mousedown", function(e) { PostModeMenu.post_mousedown(e, post_id); });
      span.down("A").observe("mouseover", function(e) { PostModeMenu.post_mouseover(e, post_id); });
      span.down("A").observe("mouseout", function(e) { PostModeMenu.post_mouseout(e, post_id); });
      span.down("A").observe("mouseup", function(e) { PostModeMenu.post_mouseup(e, post_id); });
    });

    document.observe("mouseup", function(e) { PostModeMenu.post_mouseup(e, null); });
    Event.observe(window, "pagehide", function(e) { PostModeMenu.post_end_drag(); });

    this.change()
  },

  set_vote: function(score) {
    this.vote_score = score
    Cookie.put("vote", this.vote_score)
    Post.update_vote_widget('vote-menu', this.vote_score);
  },

  get_style_for_mode: function(s)
  {
    if (s == "view") {
      return {background: ""};
    } else if (s == "edit") {
      return {background: "#3A3"}
    } else if (s == "rating-q") {
      return {background: "#AAA"}
    } else if (s == "rating-s") {
      return {background: "#6F6"}
    } else if (s == "rating-e") {
      return {background: "#F66"}
    } else if (s == "vote") {
      return {background: "#FAA"}
    } else if (s == "lock-rating") {
      return {background: "#AA3"}
    } else if (s == "lock-note") {
      return {background: "#3AA"}
    } else if (s == "approve") {
      return {background: "#26A"}
    } else if (s == "flag") {
      return {background: "#F66"}
    } else if (s == "add-to-pool") {
      return {background: "#26A"}
    } else if (s == "apply-tag-script") {
      return {background: "#A3A"}
    } else if (s == "reparent-quick") {
      return {background: "#CCA"}
    } else if (s == "remove-from-pool") {
      return {background: "#CCA"}
    } else if (s == 'reparent') {
      return {background: "#0C0"}
    } else if (s == 'dupe') {
      return {background: "#0C0"}
    } else {
      return {background: "#AFA"}
    }
  },

  change: function() {
    if(!$("mode"))
      return;
    var s = $F("mode")
    Cookie.put("mode", s, 7)

    PostModeMenu.mode = s

    if (s.value != "edit") {
      $("quick-edit").hide()
    }
    if (s.value != "apply-tag-script") {
      $("edit-tag-script").hide()
      Post.reset_tag_script_applied()
    }

    if (s == "vote") {
      Post.update_vote_widget('vote-menu', this.vote_score);
      $("vote-score").show()
    } else if (s == "apply-tag-script") {
      $("edit-tag-script").show()
      $("edit-tag-script").focus()
    }
  },

  click: function(event, post_id) {
    var s = $("mode")
    if(!s)
      return;

    if (s.value == "view") {
      return true
    }

    if (s.value == "edit") {
      post_quick_edit.show(post_id);
    } else if (s.value == 'vote') {
      Post.vote(this.vote_score, post_id, {})
    } else if (s.value == 'rating-q') {
      Post.update(post_id, {"post[rating]": "questionable"})
    } else if (s.value == 'rating-s') {
      Post.update(post_id, {"post[rating]": "safe"})
    } else if (s.value == 'rating-e') {
      Post.update(post_id, {"post[rating]": "explicit"})
    } else if (s.value == 'reparent') {
      if(post_id == id)
       return false;
      TagScript.run(post_id, "parent:" + id)
    } else if (s.value == 'dupe') {
      if(post_id == id)
       return false;
      TagScript.run(post_id, "duplicate parent:" + id)
    } else if (s.value == 'lock-rating') {
      Post.update(post_id, {"post[is_rating_locked]": "1"})
    } else if (s.value == 'lock-note') {
      Post.update(post_id, {"post[is_note_locked]": "1"})
    } else if (s.value == 'flag') {
      Post.flag(post_id)
    } else if (s.value == "approve") {
      Post.approve(post_id)
    } else if (s.value == 'add-to-pool') {
      Pool.add_post(post_id, 0)
    } else if (s.value == "remove-from-pool") {
      Pool.remove_post(post_id, PostModeMenu.pool_id);
    }

    event.stopPropagation();
    event.preventDefault();
  },

  dragging_from_post: null,
  dragging_active: false,
  dragging_list: null,
  dragging_hash: null,

  post_add_to_hovered_list: function(post_id)
  {
    var element = element = $$("#p" + post_id + " > .directlink");
    if(element.length > 0)
    {
      element[0].addClassName("tag-script-applied");
      Post.applied_list.push(element[0]);
    }

    if(!PostModeMenu.dragging_hash.get(post_id))
    {
      PostModeMenu.dragging_hash.set(post_id, true);
      PostModeMenu.dragging_list.push(post_id);
    }
  },

  post_mousedown: function(event, post_id)
  {
    if(event.button != 0)
      return;

    if(PostModeMenu.mode == "reparent-quick")
    {
      PostModeMenu.dragging_from_post = post_id;
      PostModeMenu.post_begin_drag();
    }
    else if(PostModeMenu.mode == "apply-tag-script")
    {
      Post.reset_tag_script_applied();
      PostModeMenu.dragging_from_post = post_id;
      PostModeMenu.dragging_list = new Array;
      PostModeMenu.dragging_hash = new Hash;
      PostModeMenu.post_add_to_hovered_list(post_id);
    }
    else
      return;

    /* Prevent the mousedown from being processed; this keeps it from turning into
     * a real drag action, which will suppress our mouseover/mouseout messages.  We
     * only do this when the tag script is enabled, so we don't mess with regular
     * clicks. */
    event.preventDefault();
    event.stopPropagation();
  },

  post_begin_drag: function(type)
  {
    document.body.addClassName("dragging-to-post");
  },

  post_end_drag: function()
  {
    document.body.removeClassName("dragging-to-post");
    PostModeMenu.dragging_from_post = null;
  },

  post_mouseup: function(event, post_id)
  {
    if(event.button != 0)
      return;
    if(!PostModeMenu.dragging_from_post)
      return;

    if(PostModeMenu.mode == "reparent-quick")
    {
      if(post_id)
      {
        notice("Updating post");
        Post.update_batch([{ id: PostModeMenu.dragging_from_post, parent_id: post_id}]);
      }

      PostModeMenu.post_end_drag();
      return;
    }
    else if(PostModeMenu.mode == "apply-tag-script")
    {
      if(post_id)
        return;

      /* We clicked or dragged some posts to apply a tag script; process it. */
      var tag_script = TagScript.TagEditArea.value;
      TagScript.run(PostModeMenu.dragging_list, tag_script);

      PostModeMenu.dragging_from_post = null;
      PostModeMenu.dragging_active = false;
      PostModeMenu.dragging_list = null;
      PostModeMenu.dragging_hash = null;
    }
  },

  post_mouseover: function(event, post_id)
  {
    var post = $("p" + post_id);
    var style = PostModeMenu.get_style_for_mode(PostModeMenu.mode)
    post.down("span").setStyle(style)

    if(PostModeMenu.mode != "apply-tag-script")
      return;

    if(!PostModeMenu.dragging_from_post)
      return;

    if(post_id != PostModeMenu.dragging_from_post)
      PostModeMenu.dragging_active = true;

    PostModeMenu.post_add_to_hovered_list(post_id);
  },

  post_mouseout: function(event, post_id)
  {
    var post = $("p" + post_id);
    post.down("span").setStyle({background: ""});
  },

  apply_tag_script_to_all_posts: function()
  {
    var tag_script = TagScript.TagEditArea.value;
    var post_ids = Post.posts.inject([], function(list, pair) {
      list.push(pair[0]);
      return list;
    });

    TagScript.run(post_ids, tag_script);
  }
}

TagScript = {
  TagEditArea: null,

  load: function() {
    this.TagEditArea.value = Cookie.get("tag-script")
  },
  save: function() {
    Cookie.put("tag-script", this.TagEditArea.value)
  },

  init: function(element, x) {
    this.TagEditArea = element

    TagScript.load()

    this.TagEditArea.observe("change", function(e) { TagScript.save() })
    this.TagEditArea.observe("focus", function(e) { Post.reset_tag_script_applied() })

    /* This mostly keeps the tag script field in sync between windows, but it
     * doesn't work in Opera, which sends focus events before blur events. */
    document.observe("blur", function(e) { TagScript.save() })
    document.observe("focus", function(e) { TagScript.load() })
  },

  parse: function(script) {
    return script.match(/\[.+?\]|\S+/g)
  },

  test: function(tags, predicate) {
    var split_pred = predicate.match(/\S+/g)
    var is_true = true

    split_pred.each(function(x) {
      if (x[0] == "-") {
        if (tags.include(x.substr(1, 100))) {
          is_true = false
          throw $break
        }
      } else {
        if (!tags.include(x)) {
          is_true = false
          throw $break
        }
      }
    })

    return is_true
  },

  process: function(tags, command) {
    if (command.match(/^\[if/)) {
      var match = command.match(/\[if\s+(.+?)\s*,\s*(.+?)\]/)
      if (TagScript.test(tags, match[1])) {
        return TagScript.process(tags, match[2])
      } else {
        return tags
      }
    } else if (command == "[reset]") {
      return []
    } else if (command[0] == "-" && command.indexOf(":") == -1) {
      return tags.reject(function(x) {return x == command.substr(1, 100)})
    } else {
      tags.push(command)
      return tags
    }
  },

  run: function(post_ids, tag_script, finished) {
    if(!Object.isArray(post_ids))
      post_ids = $A([post_ids]);

    var commands = TagScript.parse(tag_script) || []

    var posts = new Array;
    post_ids.each(function(post_id) {
      var post = Post.posts.get(post_id)
      var old_tags = post.tags.join(" ")

      commands.each(function(x) {
        post.tags = TagScript.process(post.tags, x)
      })

      posts.push({
        id: post_id,
        old_tags: old_tags,
        tags: post.tags.join(" ")
      });
    });

    notice("Updating " + posts.length + (post_ids.length == 1? " post": " posts") );
    Post.update_batch(posts, finished);
  }
}

function PostQuickEdit(container)
{
  this.container = container;
  this.submit_event = this.submit_event.bindAsEventListener(this);

  this.container.down("form").observe("submit", this.submit_event);
  this.container.down(".cancel").observe("click", function(e) {
    e.preventDefault();
    this.hide();
  }.bindAsEventListener(this));
  this.container.down("#post_tags").observe("keydown", function(e) {
    if(e.keyCode == Event.KEY_ESC)
    {
      e.stop();
      this.hide();
      return;
    }

    if(e.keyCode != Event.KEY_RETURN)
      return;
    this.submit_event(e);
  }.bindAsEventListener(this));
}

PostQuickEdit.prototype.show = function(post_id)
{
  Post.hover_info_pin(post_id);

  var post = Post.posts.get(post_id);
  this.container.down("#id").value = post_id;
  this.container.down("#post_old_tags").value = post.tags.join(" ");
  this.container.down("#post_tags").value = post.tags.join(" ") + " rating:" + post.rating.substr(0, 1) + " ";
  this.container.show();
  this.container.down("#post_tags").focus();
}

PostQuickEdit.prototype.hide = function()
{
  this.container.hide();
  Post.hover_info_pin(null);
}

PostQuickEdit.prototype.submit_event = function(e)
{
  e.stop();
  this.hide();

  new Ajax.Request("/post/update.json", {
    parameters: this.container.down("form").serialize(),
    onSuccess: function(resp) {
      var resp = resp.responseJSON;
      notice("Post updated");
      this.hide();
      Post.register(resp.post);
      Post.register_tags(resp.tags);
    }.bindAsEventListener(this),
    onFailure: function(resp) {
      var resp = resp.responseJSON;
      notice("Error: " + resp.reason);
    }.bindAsEventListener(this)
  });
}

<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');

function read(filename) {
  return fs.readFileSync(filename, {'encoding': 'utf8'});
}

exports.revision = {
  'build': (test) => {
    const actual = read('tmp/index.html');
    const expected = read('test/expected/index.html');

    test.equal(actual, expected, 'should revision static files');

    test.done();
  },
};
<commit_msg>Refactor test<commit_after>'use strict';

const fs = require('fs');
const path = require('path');

function read(filename) {
  return fs.readFileSync(filename, 'utf8');
}

exports.revision = {
  'build': (test) => {
    const actual = read(path.join(__dirname, '..', 'tmp', 'index.html'));
    const expected = read(path.join(__dirname, 'expected', 'index.html'));

    test.equal(actual, expected, 'should revision static files');

    test.done();
  },
};
<|endoftext|>


<commit_before>self.addEventListener('push', event => {
  const data = event.data.json();
  event.waitUntil(self.registration.getNotifications().then(notifications => {
    notifications.forEach(notification => {
      if (notification.tag === data.stack) notification.close();
    });
    return self.registration.showNotification(data.title, {
      badge: 'https://lichess1.org/assets/images/logo.256.png',
      icon: 'https://lichess1.org/assets/images/logo.256.png',
      body: data.body,
      tag: data.stack,
      data: data.payload
    });
  }));
});

self.addEventListener('notificationclick', event => {
  event.waitUntil(self.registration.getNotifications().then(notifications => {
    notifications.forEach(notification => notification.close());
    return clients.matchAll({
      type: 'window',
      includeUncontrolled: true
    });
  }).then(windowClients => {
    // determine url
    const data = event.notification.data.userData;
    let url = '/';
    if (data.fullId) url = '/' + data.fullId;
    else if (data.threadId) url = '/inbox/' + data.threadId + '#bottom';
    else if (data.challengeId) url = '/' + data.challengeId;

    // focus open window with same url
    for (let client of windowClients) {
      var clientUrl = new URL(client.url, self.location.href);
      if (clientUrl.pathname === url && 'focus' in client) return client.focus();
    }

    // navigate from open homepage to url
    for (let client of windowClients) {
      var clientUrl = new URL(client.url, self.location.href);
      if (clientUrl.pathname === '/') return client.navigate(url);
    }

    // open new window
    clients.openWindow(url);
  }));
});
<commit_msg>wait for user to dismiss push notification<commit_after>self.addEventListener('push', event => {
  const data = event.data.json();
  event.waitUntil(self.registration.getNotifications().then(notifications => {
    notifications.forEach(notification => {
      if (notification.tag === data.stack) notification.close();
    });
    return self.registration.showNotification(data.title, {
      badge: 'https://lichess1.org/assets/images/logo.256.png',
      icon: 'https://lichess1.org/assets/images/logo.256.png',
      body: data.body,
      tag: data.stack,
      data: data.payload,
      requireInteraction: true
    });
  }));
});

self.addEventListener('notificationclick', event => {
  event.waitUntil(self.registration.getNotifications().then(notifications => {
    notifications.forEach(notification => notification.close());
    return clients.matchAll({
      type: 'window',
      includeUncontrolled: true
    });
  }).then(windowClients => {
    // determine url
    const data = event.notification.data.userData;
    let url = '/';
    if (data.fullId) url = '/' + data.fullId;
    else if (data.threadId) url = '/inbox/' + data.threadId + '#bottom';
    else if (data.challengeId) url = '/' + data.challengeId;

    // focus open window with same url
    for (let client of windowClients) {
      var clientUrl = new URL(client.url, self.location.href);
      if (clientUrl.pathname === url && 'focus' in client) return client.focus();
    }

    // navigate from open homepage to url
    for (let client of windowClients) {
      var clientUrl = new URL(client.url, self.location.href);
      if (clientUrl.pathname === '/') return client.navigate(url);
    }

    // open new window
    clients.openWindow(url);
  }));
});
<|endoftext|>


<commit_before>/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('flowableApp')
  .controller('TasksController', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', '$popover', 'appResourceRoot', 'CommentService', 'TaskService', '$routeParams', 'AppDefinitionService',
        function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal, $popover, appResourceRoot, CommentService, TaskService, $routeParams, AppDefinitionService) {

            // Ensure correct main page is set
            $rootScope.setMainPageById('tasks');

            $scope.model = {
                page: 0,
                initialLoad: false
            };

            // Init sort options
            $scope.model.sorts = [
                { id: 'created-desc', title: 'TASK.FILTER.CREATED-DESC' },
                { id: 'created-asc', title: 'TASK.FILTER.CREATED-ASC' },
                { id: 'due-desc', title: 'TASK.FILTER.DUE-DESC' },
                { id: 'due-asc', title: 'TASK.FILTER.DUE-ASC' }
            ];

            // Init state options
            $scope.model.stateFilterOptions  = [
                { id: 'open', title: 'TASK.FILTER.STATE-OPEN' },
                { id: 'completed', title: 'TASK.FILTER.STATE-COMPLETED' }
            ];

            // Init assignment options
            $scope.model.assignmentOptions = [
                { id: 'involved', title: $translate.instant('TASK.FILTER.ASSIGNMENT-INVOLVED') },
                { id: 'assignee', title: $translate.instant('TASK.FILTER.ASSIGNMENT-ASSIGNEE') },
                { id: 'candidate', title: $translate.instant('TASK.FILTER.ASSIGNMENT-CANDIDATE') }
            ];

            if ($scope.account && $scope.account.groups && $scope.account.groups.length > 0) {
                for (var i=0; i < $scope.account.groups.length; i++) {
                    if ($scope.account.groups[i].type == 1) {
                        $scope.model.assignmentOptions.push({ id: 'group_' + $scope.account.groups[i].id, title: $translate.instant('TASK.FILTER.ASSIGNMENT-GROUP', $scope.account.groups[i]) });
                    }
                }
            }

            // Sets defaults for all filters
            $scope.resetFilters = function(forcePropagateToRootScope) {

                $scope.model.page = 0;

                // Init empty filter model
                $scope.model.filter = {
                    loading: false,
                    expanded: false,
                    param: {
                        state: $scope.model.stateFilterOptions[0],
                        nonDefaultFilter: false
                    }
                };

                // Defaults
                $scope.model.filter.param.sort = $scope.model.sorts[0].id;
                $scope.model.filter.param.assignment = $scope.model.assignmentOptions[0].id;
                $scope.model.filter.param.processDefinitionId = 'default';

                $scope.deploymentKey = $routeParams.deploymentKey;
                $scope.missingAppdefinition = $scope.deploymentKey === false;

                // In case of viewing tasks in an app-context, need to make filter aware of this
                $scope.model.filter.param.deploymentKey = $scope.deploymentKey;

                // Propagate to root filter
                if (forcePropagateToRootScope === true || ($rootScope.taskFilter === null && $rootScope.taskFilter === undefined)) {
                    $rootScope.taskFilter = { param: $scope.model.filter.param };
                }
            };

            $scope.resetFilters(); // First time: init defaults

            // The filter is stored on the rootScope, which allows the user to switch back and forth without losing the filter.
            if ($rootScope.taskFilter !== null && $rootScope.taskFilter !== undefined) {
                $scope.model.filter.param = $rootScope.taskFilter.param;
            } else {
                $rootScope.taskFilter = { param: $scope.model.filter.param };
            }

            // Update app on rootScope. If app id present, it will fetch definition if not already fetched to update view and navigation accordingly
            AppDefinitionService.setActiveDeploymentKey($scope.deploymentKey);

            // Callback for state selection widget
            $scope.selectStateFilter = function (state) {
                if(state !== $scope.model.filter.param.state) {
                    $scope.model.filter.param.state = state;
                    $scope.collapseFilter();
                    $scope.resetPaging();
                    $scope.refreshFilter();
                }
            };

            $scope.sortChanged = function() {
                $scope.resetPaging();
                $scope.refreshFilter();
            };

            $scope.nextPage = function() {
                $scope.model.page = $scope.model.page +1;
                $scope.refreshFilter();
            };

            $scope.resetPaging = function() {
                $scope.model.page = 0;
            };

            $scope.refreshFilter = function () {
                $scope.model.filter.loading = true;

                var params = $scope.model.filter.param;
                params.nonDefaultFilter = false;

                // Assignee
                var data = {
                    assignee: $rootScope.account.id,
                    page: $scope.model.page
                };

                // Text filter
                if (params.text) {
                    data.text = params.text;
                    params.nonDefaultFilter = true;
                }

                // State folder
                if (params.state) {
                    data.state = params.state.id;

                    if (params.state.id !== 'open') {
                        params.nonDefaultFilter = true;
                    }
                }

                // Assignment
                if (params.assignment) {
                    data.assignment = params.assignment;

                    if (params.assignment !== 'involved') {
                        params.nonDefaultFilter = true;
                    }
                }

                // Process definition
                if (params.processDefinitionId && params.processDefinitionId !== 'default') { // default = empty choice
                    data.processDefinitionId = params.processDefinitionId;
                    params.nonDefaultFilter = true;
                }

                // App definition
                if (params.deploymentKey) {
                    data.deploymentKey = params.deploymentKey;
                }

                // Sort order
                data.sort = params.sort;

                TaskService.queryTasks(data).then(function (response) {
                    $scope.model.filter.loading = false;

                    if (response.start === 0) {
                        $scope.model.tasks = response.data;

                        if((!response.data || response.data.length == 0) && !params.nonDefaultFilter) {
                            $scope.state= {noOwnTasks: true};
                        } else {
                            $scope.state= {noOwnTasks: false};
                        }

                        $scope.selectedTask = $scope.model.tasks[0];

                    } else {
                        for (var taskIndex=0; taskIndex<response.data.length; taskIndex++) {
                            $scope.model.tasks.push(response.data[taskIndex]);
                        }
                        $scope.state= {noOwnTasks: false};

                        if (!$scope.selectedTask) {
                            $scope.selectedTask = response.data[0];
                        }
                    }
                    $scope.model.hasNextPage = (response.start + response.size < response.total);

                    // If coming from the process page the root.selectedTaskId will be set
                    var taskIsPartOfCurrentList = false;
                    if ($rootScope.root.selectedTaskId) {
                        for (var i = 0; i < response.data.length; i++) {
                            if (response.data[i].id == $rootScope.root.selectedTaskId) {
                                taskIsPartOfCurrentList = true;
                                $scope.selectedTask = response.data[i];
                                break;
                            }
                        }

                        // if the task isn't part of the currently showed list,
                        // the details are still shown
                        if (!taskIsPartOfCurrentList) {
                            // This is kinda cheating, but this way no additional fetch is needed
                            $scope.selectedTask = { id:  $rootScope.root.selectedTaskId};
                        }

                        // Always remove the root task id when it was used (otherwise will screw up next calls)
                        $rootScope.root.selectedTaskId = undefined;

                    } else {
                        if (!$scope.selectedTask) {
                            $scope.selectedTask = $scope.model.tasks[0];
                        }
                    }

                });
            };

            $scope.selectTask = function (task) {
                $scope.selectedTask = task;
            };

            $scope.createTask = function () {
                // Create popover
                if (!$scope.createTaskPopover) {
                    $scope.newTask = {
                        name: 'New task'
                    };

                    $scope.createTaskPopover = $popover(angular.element('#toggle-create-task'), {
                        template: appResourceRoot+ 'views/popover/create-task-popover.html',
                        placement: 'bottom-left',
                        show: true,
                        scope: $scope
                    });

                    $scope.createTaskPopover.$scope.$on('tooltip.hide', function () {
                        $scope.createTaskPopover.$scope.$destroy();
                        $scope.createTaskPopover.destroy();
                        $scope.createTaskPopover = undefined;

                        $scope.newTask = undefined;
                    });
                }
            };

            $scope.setTaskAssignee = function (user) {
                $scope.newTask.assignee = user;
            };

            $scope.confirmTaskCreation = function (newTask) {

                if(!newTask) {
                    newTask = $scope.newTask;
                }
                if (newTask && newTask.name) {
                    var taskData = {
                        name: newTask.name,
                        description: newTask.description
                    };

                    if (newTask.assignee && newTask.assignee.id) {
                        taskData.assignee= newTask.assignee.id;
                    }

                    if ($rootScope.activeAppDefinition) {
                        taskData.category = '' + $rootScope.activeAppDefinition.id;
                    }

                    newTask.loading = true;
                    TaskService.createTask(taskData).then(function (task) {
                        newTask.loading = false;

                        if($scope.createTaskPopover) {
                            $scope.createTaskPopover.$scope.$destroy();
                            $scope.createTaskPopover.destroy();
                            $scope.createTaskPopover = undefined;
                        }

                        $rootScope.addAlertPromise($translate('TASK.ALERT.CREATED', task));
                    });
                }
            };

            $scope.expandFilter = function () {
                $scope.model.filter.expanded = true;
            };

            $scope.collapseFilter = function () {
                $scope.model.filter.expanded = false;
            };

            $scope.$on('task-completed', function (event, data) {
                $rootScope.addAlertPromise($translate('TASK.ALERT.COMPLETED', data));
                if (data && data.taskId == $scope.selectedTask.id) {
                    // Current task has been completed. Refresh the filter
                    $scope.selectedTask = undefined;
                    $scope.resetPaging();
                    $scope.refreshFilter();
                }
            });

            $scope.$on('task-saved', function (event, data) {
                $rootScope.addAlertPromise($translate('TASK.ALERT.SAVED', data));
            });

            $scope.$on('task-completed-error', function (event, data) {
                $rootScope.addAlert(data.error.message, 'error');
            });

            $scope.$on('task-save-error', function (event, data) {
                $rootScope.addAlert(data.error.message, 'error');
            });

            // Refreshing list when new task is created
            $scope.$on("new-task-created", function (event, task) {
                // New tasks should always be on top, hence the sorting is set to last created
                $scope.resetFilters(true);
                $scope.resetPaging();
                $scope.refreshFilter();
            });

            // Load process definitions for the filter dropdown
            $rootScope.loadProcessDefinitions($scope.deploymentKey);

            // Add a watch, so we can
            var unregisterWatch = $scope.$watch('root.processDefinitions', function(data) {
                if (data !== null && data != undefined && data.length > 0) {
                    var processDefinitions = data;
                    processDefinitions.splice(0, 0, {id:'default', name: ''});
                    $scope.model.processDefinitions = processDefinitions;
                    unregisterWatch();
                }
            });

            // Initial load
            if (!$rootScope.account) {
           		$http.get(FLOWABLE.CONFIG.contextRoot + '/app/rest/account')
                	.success(function (data, status, headers, config) {
                      	$rootScope.account = data;
                       	$rootScope.invalidCredentials = false;
         				$scope.refreshFilter();

                  	});

            } else {
            	$scope.refreshFilter();
            }

  }]);
<commit_msg>Dodgy character - Filip!<commit_after>/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
'use strict';

angular.module('flowableApp')
  .controller('TasksController', ['$rootScope', '$scope', '$translate', '$http', '$timeout','$location', '$modal', '$popover', 'appResourceRoot', 'CommentService', 'TaskService', '$routeParams', 'AppDefinitionService',
        function ($rootScope, $scope, $translate, $http, $timeout, $location, $modal, $popover, appResourceRoot, CommentService, TaskService, $routeParams, AppDefinitionService) {

            // Ensure correct main page is set
            $rootScope.setMainPageById('tasks');

            $scope.model = {
                page: 0,
                initialLoad: false
            };

            // Init sort options
            $scope.model.sorts = [
                { id: 'created-desc', title: 'TASK.FILTER.CREATED-DESC' },
                { id: 'created-asc', title: 'TASK.FILTER.CREATED-ASC' },
                { id: 'due-desc', title: 'TASK.FILTER.DUE-DESC' },
                { id: 'due-asc', title: 'TASK.FILTER.DUE-ASC' }
            ];

            // Init state options
            $scope.model.stateFilterOptions = [
                { id: 'open', title: 'TASK.FILTER.STATE-OPEN' },
                { id: 'completed', title: 'TASK.FILTER.STATE-COMPLETED' }
            ];

            // Init assignment options
            $scope.model.assignmentOptions = [
                { id: 'involved', title: $translate.instant('TASK.FILTER.ASSIGNMENT-INVOLVED') },
                { id: 'assignee', title: $translate.instant('TASK.FILTER.ASSIGNMENT-ASSIGNEE') },
                { id: 'candidate', title: $translate.instant('TASK.FILTER.ASSIGNMENT-CANDIDATE') }
            ];

            if ($scope.account && $scope.account.groups && $scope.account.groups.length > 0) {
                for (var i=0; i < $scope.account.groups.length; i++) {
                    if ($scope.account.groups[i].type == 1) {
                        $scope.model.assignmentOptions.push({ id: 'group_' + $scope.account.groups[i].id, title: $translate.instant('TASK.FILTER.ASSIGNMENT-GROUP', $scope.account.groups[i]) });
                    }
                }
            }

            // Sets defaults for all filters
            $scope.resetFilters = function(forcePropagateToRootScope) {

                $scope.model.page = 0;

                // Init empty filter model
                $scope.model.filter = {
                    loading: false,
                    expanded: false,
                    param: {
                        state: $scope.model.stateFilterOptions[0],
                        nonDefaultFilter: false
                    }
                };

                // Defaults
                $scope.model.filter.param.sort = $scope.model.sorts[0].id;
                $scope.model.filter.param.assignment = $scope.model.assignmentOptions[0].id;
                $scope.model.filter.param.processDefinitionId = 'default';

                $scope.deploymentKey = $routeParams.deploymentKey;
                $scope.missingAppdefinition = $scope.deploymentKey === false;

                // In case of viewing tasks in an app-context, need to make filter aware of this
                $scope.model.filter.param.deploymentKey = $scope.deploymentKey;

                // Propagate to root filter
                if (forcePropagateToRootScope === true || ($rootScope.taskFilter === null && $rootScope.taskFilter === undefined)) {
                    $rootScope.taskFilter = { param: $scope.model.filter.param };
                }
            };

            $scope.resetFilters(); // First time: init defaults

            // The filter is stored on the rootScope, which allows the user to switch back and forth without losing the filter.
            if ($rootScope.taskFilter !== null && $rootScope.taskFilter !== undefined) {
                $scope.model.filter.param = $rootScope.taskFilter.param;
            } else {
                $rootScope.taskFilter = { param: $scope.model.filter.param };
            }

            // Update app on rootScope. If app id present, it will fetch definition if not already fetched to update view and navigation accordingly
            AppDefinitionService.setActiveDeploymentKey($scope.deploymentKey);

            // Callback for state selection widget
            $scope.selectStateFilter = function (state) {
                if(state !== $scope.model.filter.param.state) {
                    $scope.model.filter.param.state = state;
                    $scope.collapseFilter();
                    $scope.resetPaging();
                    $scope.refreshFilter();
                }
            };

            $scope.sortChanged = function() {
                $scope.resetPaging();
                $scope.refreshFilter();
            };

            $scope.nextPage = function() {
                $scope.model.page = $scope.model.page +1;
                $scope.refreshFilter();
            };

            $scope.resetPaging = function() {
                $scope.model.page = 0;
            };

            $scope.refreshFilter = function () {
                $scope.model.filter.loading = true;

                var params = $scope.model.filter.param;
                params.nonDefaultFilter = false;

                // Assignee
                var data = {
                    assignee: $rootScope.account.id,
                    page: $scope.model.page
                };

                // Text filter
                if (params.text) {
                    data.text = params.text;
                    params.nonDefaultFilter = true;
                }

                // State folder
                if (params.state) {
                    data.state = params.state.id;

                    if (params.state.id !== 'open') {
                        params.nonDefaultFilter = true;
                    }
                }

                // Assignment
                if (params.assignment) {
                    data.assignment = params.assignment;

                    if (params.assignment !== 'involved') {
                        params.nonDefaultFilter = true;
                    }
                }

                // Process definition
                if (params.processDefinitionId && params.processDefinitionId !== 'default') { // default = empty choice
                    data.processDefinitionId = params.processDefinitionId;
                    params.nonDefaultFilter = true;
                }

                // App definition
                if (params.deploymentKey) {
                    data.deploymentKey = params.deploymentKey;
                }

                // Sort order
                data.sort = params.sort;

                TaskService.queryTasks(data).then(function (response) {
                    $scope.model.filter.loading = false;

                    if (response.start === 0) {
                        $scope.model.tasks = response.data;

                        if((!response.data || response.data.length == 0) && !params.nonDefaultFilter) {
                            $scope.state= {noOwnTasks: true};
                        } else {
                            $scope.state= {noOwnTasks: false};
                        }

                        $scope.selectedTask = $scope.model.tasks[0];

                    } else {
                        for (var taskIndex=0; taskIndex<response.data.length; taskIndex++) {
                            $scope.model.tasks.push(response.data[taskIndex]);
                        }
                        $scope.state= {noOwnTasks: false};

                        if (!$scope.selectedTask) {
                            $scope.selectedTask = response.data[0];
                        }
                    }
                    $scope.model.hasNextPage = (response.start + response.size < response.total);

                    // If coming from the process page the root.selectedTaskId will be set
                    var taskIsPartOfCurrentList = false;
                    if ($rootScope.root.selectedTaskId) {
                        for (var i = 0; i < response.data.length; i++) {
                            if (response.data[i].id == $rootScope.root.selectedTaskId) {
                                taskIsPartOfCurrentList = true;
                                $scope.selectedTask = response.data[i];
                                break;
                            }
                        }

                        // if the task isn't part of the currently showed list,
                        // the details are still shown
                        if (!taskIsPartOfCurrentList) {
                            // This is kinda cheating, but this way no additional fetch is needed
                            $scope.selectedTask = { id:  $rootScope.root.selectedTaskId};
                        }

                        // Always remove the root task id when it was used (otherwise will screw up next calls)
                        $rootScope.root.selectedTaskId = undefined;

                    } else {
                        if (!$scope.selectedTask) {
                            $scope.selectedTask = $scope.model.tasks[0];
                        }
                    }

                });
            };

            $scope.selectTask = function (task) {
                $scope.selectedTask = task;
            };

            $scope.createTask = function () {
                // Create popover
                if (!$scope.createTaskPopover) {
                    $scope.newTask = {
                        name: 'New task'
                    };

                    $scope.createTaskPopover = $popover(angular.element('#toggle-create-task'), {
                        template: appResourceRoot+ 'views/popover/create-task-popover.html',
                        placement: 'bottom-left',
                        show: true,
                        scope: $scope
                    });

                    $scope.createTaskPopover.$scope.$on('tooltip.hide', function () {
                        $scope.createTaskPopover.$scope.$destroy();
                        $scope.createTaskPopover.destroy();
                        $scope.createTaskPopover = undefined;

                        $scope.newTask = undefined;
                    });
                }
            };

            $scope.setTaskAssignee = function (user) {
                $scope.newTask.assignee = user;
            };

            $scope.confirmTaskCreation = function (newTask) {

                if(!newTask) {
                    newTask = $scope.newTask;
                }
                if (newTask && newTask.name) {
                    var taskData = {
                        name: newTask.name,
                        description: newTask.description
                    };

                    if (newTask.assignee && newTask.assignee.id) {
                        taskData.assignee= newTask.assignee.id;
                    }

                    if ($rootScope.activeAppDefinition) {
                        taskData.category = '' + $rootScope.activeAppDefinition.id;
                    }

                    newTask.loading = true;
                    TaskService.createTask(taskData).then(function (task) {
                        newTask.loading = false;

                        if($scope.createTaskPopover) {
                            $scope.createTaskPopover.$scope.$destroy();
                            $scope.createTaskPopover.destroy();
                            $scope.createTaskPopover = undefined;
                        }

                        $rootScope.addAlertPromise($translate('TASK.ALERT.CREATED', task));
                    });
                }
            };

            $scope.expandFilter = function () {
                $scope.model.filter.expanded = true;
            };

            $scope.collapseFilter = function () {
                $scope.model.filter.expanded = false;
            };

            $scope.$on('task-completed', function (event, data) {
                $rootScope.addAlertPromise($translate('TASK.ALERT.COMPLETED', data));
                if (data && data.taskId == $scope.selectedTask.id) {
                    // Current task has been completed. Refresh the filter
                    $scope.selectedTask = undefined;
                    $scope.resetPaging();
                    $scope.refreshFilter();
                }
            });

            $scope.$on('task-saved', function (event, data) {
                $rootScope.addAlertPromise($translate('TASK.ALERT.SAVED', data));
            });

            $scope.$on('task-completed-error', function (event, data) {
                $rootScope.addAlert(data.error.message, 'error');
            });

            $scope.$on('task-save-error', function (event, data) {
                $rootScope.addAlert(data.error.message, 'error');
            });

            // Refreshing list when new task is created
            $scope.$on("new-task-created", function (event, task) {
                // New tasks should always be on top, hence the sorting is set to last created
                $scope.resetFilters(true);
                $scope.resetPaging();
                $scope.refreshFilter();
            });

            // Load process definitions for the filter dropdown
            $rootScope.loadProcessDefinitions($scope.deploymentKey);

            // Add a watch, so we can
            var unregisterWatch = $scope.$watch('root.processDefinitions', function(data) {
                if (data !== null && data != undefined && data.length > 0) {
                    var processDefinitions = data;
                    processDefinitions.splice(0, 0, {id:'default', name: ''});
                    $scope.model.processDefinitions = processDefinitions;
                    unregisterWatch();
                }
            });

            // Initial load
            if (!$rootScope.account) {
           		$http.get(FLOWABLE.CONFIG.contextRoot + '/app/rest/account')
                	.success(function (data, status, headers, config) {
                      	$rootScope.account = data;
                       	$rootScope.invalidCredentials = false;
         				$scope.refreshFilter();

                  	});

            } else {
            	$scope.refreshFilter();
            }

  }]);
<|endoftext|>


<commit_before>"use strict";

"use strict";

var kue = require('kue');
var async = require('async');
var request = require('supertest');
var app = require('../../app.js');

describe("POST /update", function() {
  beforeEach(function eraseAndPopulateHashs(done) {
    this.store = app.get('keyValueStore');
    async.series([
      function delTokens(cb) {
        this.store.del('tokens', cb);
      }.bind(this),
      function delCursors(cb) {
        this.store.del('cursors', cb);
      }.bind(this),
      function delStatus(cb) {
        this.store.del('status', cb);
      }.bind(this),
      function delLastUpdates(cb) {
        this.store.del('lastUpdates', cb);
      }.bind(this)
    ], done);
  });

  it('should not update if the provider token is not set', function(done) {
    request(app)
      .post('/update')
      .query({ access_token: 'tok' })
      .expect(500)
      .expect(/not initialized/)
      .end(done);
  });

  it('should update if the provider token not set', function(done) {
    async.waterfall([
      function addProviderToken(cb) {
        this.store.hset('tokens', 'tok', 'toktok', cb);
      }.bind(this),
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(204)
          .end(cb);
      }
    ], done);
  });

  it('should not let update the provider if it is alredy updating', function(done) {
    async.waterfall([
      function addProviderToken(cb) {
        this.store.hset('tokens', 'tok', 'toktok', cb);
      }.bind(this),
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(204)
          .end(cb);
      },
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(500)
          .expect(/already processing/)
          .end(cb);
      }
    ], done);
  });
});
<commit_msg>aesthetical fixes<commit_after>"use strict";

var async = require('async');
var request = require('supertest');
var app = require('../../app.js');

describe("POST /update", function() {
  beforeEach(function eraseAndPopulateHashs(done) {
    this.store = app.get('keyValueStore');
    async.series([
      function delTokens(cb) {
        this.store.del('tokens', cb);
      }.bind(this),
      function delCursors(cb) {
        this.store.del('cursors', cb);
      }.bind(this),
      function delStatus(cb) {
        this.store.del('status', cb);
      }.bind(this),
      function delLastUpdates(cb) {
        this.store.del('lastUpdates', cb);
      }.bind(this)
    ], done);
  });

  it('should not update if the provider token is not set', function(done) {
    request(app)
      .post('/update')
      .query({ access_token: 'tok' })
      .expect(500)
      .expect(/not initialized/)
      .end(done);
  });

  it('should update if the provider token is set', function(done) {
    async.waterfall([
      function addProviderToken(cb) {
        this.store.hset('tokens', 'tok', 'toktok', cb);
      }.bind(this),
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(204)
          .end(cb);
      }
    ], done);
  });

  it('should not let update the provider if it is already updating', function(done) {
    async.waterfall([
      function addProviderToken(cb) {
        this.store.hset('tokens', 'tok', 'toktok', cb);
      }.bind(this),
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(204)
          .end(cb);
      },
      function requestUpdate(status, cb) {
        request(app)
          .post('/update')
          .query({ access_token: 'tok' })
          .expect(500)
          .expect(/already processing/)
          .end(cb);
      }
    ], done);
  });
});
<|endoftext|>