<commit_before>jQuery(document).ready(function($) {
    $.extend(octopus, {

        sherpafact: {

            proxy: function (params) {
                var journal_or_issn = params.journal_or_issn;               // a string of some kind
                var funders = params.funders;                               // a list of juliet ids
                var success_callback = params.success ? params.success : function () {};
                var complete_callback = params.complete ? params.complete : function () {};
                var error_callback = params.error ? params.error : function () {};

                var url = octopus.config.fact_proxy_endpoint;

                // make the call to the fact proxy web service
                $.ajax({
                    type: "get",
                    url: url,
                    data: {journal_or_issn: journal_or_issn, funders: funders.join(",")},
                    dataType: "jsonp",
                    success: success_callback,
                    complete: complete_callback,
                    error: error_callback
               });
            },

            newFact : function(params) {
                var F = function() {};
                F.prototype = octopus.sherpafact.Fact;
                var fact = new F();
                if (params.raw) {
                    fact.raw = params.raw;
                }
                return fact;
            },

            Fact : {
                raw : {},
                result_count : function() {
                    if (this.raw.control) {
                        return this.raw.control.val_num_journals;
                    }
                },
                journal_name: function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.val_title;
                    } else {
                        return "";
                    }
                },
                funder_names: function() {
                    if (this.raw.funder && this.raw.funder.length > 0) {
                        var names = [];
                        for (var i = 0; i < this.raw.funder.length; i++) {
                            var f = this.raw.funder[i];
                            if (f.val_name) {
                                names.push(f.val_name);
                            }
                        }
                        return names;
                    }
                    return [];
                },
                issn : function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.arg_issn;
                    }
                    return "";
                },
                publisher : function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.val_publisher;
                    }
                    return "";
                },
                overall_compliance : function() {
                    if (this.raw.overall && this.raw.overall.arg_overallcompliancecode) {
                        return this.raw.overall.arg_overallcompliancecode;
                    }
                    return "no";
                },
                overall_compliance_msg : function() {
                    if (this.raw.overall && this.raw.overall.val_overallcompliance) {
                        return this.raw.overall.val_overallcompliance;
                    }
                    return "";
                },
                overall_recommendation : function() {
                    if (this.raw.overall && this.raw.overall.val_overallrecommendation) {
                        return this.raw.overall.val_overallrecommendation
                    }
                    return "";
                },
                overall_advice : function() {
                    var oas = [];
                    if (this.raw.overall && this.raw.overall.overalladvice && this.raw.overall.overalladvice.length > 0) {
                        for (var i = 0; i < this.raw.overall.overalladvice.length; i++) {
                            var oa = this.raw.overall.overalladvice[i];
                            if (oa.val_overalladvice) {
                                oas.push(oa.val_overalladvice);
                            }
                        }
                    }
                    return oas;
                },
                gold_compliance : function() {
                    if (this.raw.gold && this.raw.gold.arg_goldcompliancecode) {
                        return this.raw.gold.arg_goldcompliancecode;
                    }
                    return "no";
                },
                gold_compliance_msg : function() {
                    if (this.raw.gold && this.raw.gold.val_goldcompliance) {
                        return this.raw.gold.val_goldcompliance;
                    }
                    return "";
                },
                gold_compliance_reason : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var reason = "";
                    if (this.raw.gold && this.raw.gold.val_goldreason) {
                        reason = this.raw.gold.val_goldreason;
                    }

                    if (link && this.raw.gold && this.raw.gold.arg_goldreasonlinktext && this.raw.gold.arg_goldreasonlinkurl) {
                        var text = this.raw.gold.arg_goldreasonlinktext;
                        var url = this.raw.gold.arg_goldreasonlinkurl;
                        reason = this._linkify({
                            text : text,
                            url : url,
                            source : reason,
                            linkclass : linkclass
                        });
                    }

                    return reason;
                },
                gold_fee : function() {
                    if (this.raw.gold && this.raw.gold.val_goldfee) {
                        return this.raw.gold.val_goldfee;
                    }
                    return "";
                },
                gold_advice : function() {
                    var gas = [];
                    if (this.raw.gold && this.raw.gold.goldadvice && this.raw.gold.goldadvice.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldadvice.length; i++) {
                            var ga = this.raw.gold.goldadvice[i];
                            if (ga.val_goldadvice) {
                                gas.push(ga.val_goldadvice);
                            }
                        }
                    }
                    return gas;
                },
                gold_confirm : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var gcs = [];
                    if (this.raw.gold && this.raw.gold.goldconfirm && this.raw.gold.goldconfirm.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldconfirm.length; i++) {
                            var gc = this.raw.gold.goldconfirm[i];
                            if (gc.val_goldconfirm) {
                                var conf = gc.val_goldconfirm;
                                if (link && gc.arg_goldconfirmlinktext && gc.arg_goldconfirmlinkurl) {
                                    conf = this._linkify({
                                        text : gc.arg_goldconfirmlinktext,
                                        url : gc.arg_goldconfirmlinkurl,
                                        source : conf,
                                        linkclass : linkclass
                                    });
                                }
                                gcs.push(conf);
                            }
                        }
                    }
                    return gcs;
                },
                gold_process: function() {
                    var gps = [];
                    if (this.raw.gold && this.raw.gold.goldprocess && this.raw.gold.goldprocess.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldprocess.length; i++) {
                            var gp = this.raw.gold.goldprocess[i];
                            if (gp.val_goldprocess) {
                                gps.push(gp.val_goldprocess);
                            }
                        }
                    }
                    return gps;
                },
                green_compliance : function() {
                    if (this.raw.green && this.raw.green.arg_greencompliancecode) {
                        return this.raw.green.arg_greencompliancecode;
                    }
                    return "no";
                },
                green_compliance_msg : function() {
                    if (this.raw.green && this.raw.green.val_greencompliance) {
                        return this.raw.green.val_greencompliance;
                    }
                    return "";
                },
                green_compliance_reason : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var reason = "";
                    if (this.raw.green && this.raw.green.val_greenreason) {
                        reason = this.raw.green.val_greenreason;
                    }

                    if (link && this.raw.green && this.raw.green.arg_greenreasonlinktext && this.raw.green.arg_greenreasonlinkurl) {
                        var text = this.raw.green.arg_greenreasonlinktext;
                        var url = this.raw.green.arg_greenreasonlinkurl;
                        reason = this._linkify({
                            text : text,
                            url : url,
                            source : reason,
                            linkclass : linkclass
                        });
                    }

                    return reason;
                },
                green_advice : function() {
                    var gas = [];
                    if (this.raw.green && this.raw.green.greenadvice && this.raw.green.greenadvice.length > 0) {
                        for (var i = 0; i < this.raw.green.greenadvice.length; i++) {
                            var ga = this.raw.green.greenadvice[i];
                            if (ga.val_greenadvice) {
                                gas.push(ga.val_greenadvice);
                            }
                        }
                    }
                    return gas;
                },
                green_confirm : function() {
                    var gcs = [];
                    if (this.raw.green && this.raw.green.greenconfirm && this.raw.green.greenconfirm.length > 0) {
                        for (var i = 0; i < this.raw.green.greenconfirm.length; i++) {
                            var gc = this.raw.green.greenconfirm[i];
                            if (gc.val_greenconfirm) {
                                gcs.push(gc.val_greenconfirm);
                            }
                        }
                    }
                    return gcs;
                },
                _linkify : function(params) {
                    var text = params.text;
                    var url = params.url;
                    var source = params.source;
                    var linkclass = params.linkclass;

                    var start = source.indexOf(text);
                    var end = start + text.length;
                    return source.substring(0, start) + "" +
                                source.substring(start, end) + "" + source.substring(end);
                }
            }

        }
    });
});

<commit_msg>further implementation of fact data object - now complete<commit_after>jQuery(document).ready(function($) {
    $.extend(octopus, {

        sherpafact: {

            proxy: function (params) {
                var journal_or_issn = params.journal_or_issn;               // a string of some kind
                var funders = params.funders;                               // a list of juliet ids
                var success_callback = params.success ? params.success : function () {};
                var complete_callback = params.complete ? params.complete : function () {};
                var error_callback = params.error ? params.error : function () {};

                var url = octopus.config.fact_proxy_endpoint;

                // make the call to the fact proxy web service
                $.ajax({
                    type: "get",
                    url: url,
                    data: {journal_or_issn: journal_or_issn, funders: funders.join(",")},
                    dataType: "jsonp",
                    success: success_callback,
                    complete: complete_callback,
                    error: error_callback
               });
            },

            newFact : function(params) {
                var F = function() {};
                F.prototype = octopus.sherpafact.Fact;
                var fact = new F();
                if (params.raw) {
                    fact.raw = params.raw;
                }
                return fact;
            },

            Fact : {
                raw : {},

                result_count : function() {
                    if (this.raw.control) {
                        return this.raw.control.val_num_journals;
                    }
                },

                journal_name: function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.val_title;
                    } else {
                        return "";
                    }
                },
                funder_names: function() {
                    if (this.raw.funder && this.raw.funder.length > 0) {
                        var names = [];
                        for (var i = 0; i < this.raw.funder.length; i++) {
                            var f = this.raw.funder[i];
                            if (f.val_name) {
                                names.push(f.val_name);
                            }
                        }
                        return names;
                    }
                    return [];
                },
                issn : function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.arg_issn;
                    }
                    return "";
                },
                publisher : function() {
                    if (this.raw.journal && this.raw.journal.length > 0) {
                        var j = this.raw.journal[0];
                        return j.val_publisher;
                    }
                    return "";
                },
                juliet_ids : function() {
                    var jids = [];
                    if (this.raw.funder && this.raw.funder.length > 0) {
                        for (var i = 0; i < this.raw.funder.length; i++) {
                            var f = this.raw.funder[i];
                            if (f.arg_juliet_id) {
                                jids.push(f.arg_juliet_id);
                            }
                        }
                    }
                    return jids;
                },

                overall_compliance : function() {
                    if (this.raw.overall && this.raw.overall.arg_overallcompliancecode) {
                        return this.raw.overall.arg_overallcompliancecode;
                    }
                    return "no";
                },
                overall_compliance_msg : function() {
                    if (this.raw.overall && this.raw.overall.val_overallcompliance) {
                        return this.raw.overall.val_overallcompliance;
                    }
                    return "";
                },
                overall_recommendation : function() {
                    if (this.raw.overall && this.raw.overall.val_overallrecommendation) {
                        return this.raw.overall.val_overallrecommendation
                    }
                    return "";
                },
                overall_advice : function() {
                    var oas = [];
                    if (this.raw.overall && this.raw.overall.overalladvice && this.raw.overall.overalladvice.length > 0) {
                        for (var i = 0; i < this.raw.overall.overalladvice.length; i++) {
                            var oa = this.raw.overall.overalladvice[i];
                            if (oa.val_overalladvice) {
                                oas.push(oa.val_overalladvice);
                            }
                        }
                    }
                    return oas;
                },

                gold_compliance : function() {
                    if (this.raw.gold && this.raw.gold.arg_goldcompliancecode) {
                        return this.raw.gold.arg_goldcompliancecode;
                    }
                    return "no";
                },
                gold_compliance_msg : function() {
                    if (this.raw.gold && this.raw.gold.val_goldcompliance) {
                        return this.raw.gold.val_goldcompliance;
                    }
                    return "";
                },
                gold_compliance_reason : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var reason = "";
                    if (this.raw.gold && this.raw.gold.val_goldreason) {
                        reason = this.raw.gold.val_goldreason;
                    }

                    if (link && this.raw.gold && this.raw.gold.arg_goldreasonlinktext && this.raw.gold.arg_goldreasonlinkurl) {
                        var text = this.raw.gold.arg_goldreasonlinktext;
                        var url = this.raw.gold.arg_goldreasonlinkurl;
                        reason = this._linkify({
                            text : text,
                            url : url,
                            source : reason,
                            linkclass : linkclass
                        });
                    }

                    return reason;
                },
                gold_fee : function() {
                    if (this.raw.gold && this.raw.gold.val_goldfee) {
                        return this.raw.gold.val_goldfee;
                    }
                    return "";
                },
                gold_advice : function() {
                    var gas = [];
                    if (this.raw.gold && this.raw.gold.goldadvice && this.raw.gold.goldadvice.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldadvice.length; i++) {
                            var ga = this.raw.gold.goldadvice[i];
                            if (ga.val_goldadvice) {
                                gas.push(ga.val_goldadvice);
                            }
                        }
                    }
                    return gas;
                },
                gold_confirm : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var gcs = [];
                    if (this.raw.gold && this.raw.gold.goldconfirm && this.raw.gold.goldconfirm.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldconfirm.length; i++) {
                            var gc = this.raw.gold.goldconfirm[i];
                            if (gc.val_goldconfirm) {
                                var conf = gc.val_goldconfirm;
                                if (link && gc.arg_goldconfirmlinktext && gc.arg_goldconfirmlinkurl) {
                                    conf = this._linkify({
                                        text : gc.arg_goldconfirmlinktext,
                                        url : gc.arg_goldconfirmlinkurl,
                                        source : conf,
                                        linkclass : linkclass
                                    });
                                }
                                gcs.push(conf);
                            }
                        }
                    }
                    return gcs;
                },
                gold_process: function() {
                    var gps = [];
                    if (this.raw.gold && this.raw.gold.goldprocess && this.raw.gold.goldprocess.length > 0) {
                        for (var i = 0; i < this.raw.gold.goldprocess.length; i++) {
                            var gp = this.raw.gold.goldprocess[i];
                            if (gp.val_goldprocess) {
                                gps.push(gp.val_goldprocess);
                            }
                        }
                    }
                    return gps;
                },
                gold_decision : function() {
                    // This method is tricky, so will be carefully explained

                    // this is the parent decision trail object.  All decisions are stored in here, in the order
                    // that they were considered.  In turn each sub-decision has a set of children in the order that they
                    // occurred, and so on.
                    var decision = [];

                    // this is the stack, where we will keep track of the current deepest element in the tree.
                    // we start off with the parent decision trail object at the bottom of the stack
                    var stack = [];
                    stack.push(decision);

                    if (this.raw.gold && this.raw.gold.golddecision && this.raw.gold.golddecision.length > 0) {
                        for (var i = 0; i < this.raw.gold.golddecision.length; i++) {
                            // get the details that we care about for this decision, and make the decision
                            // object with a pre-prepared "children" field
                            var d = this.raw.gold.golddecision[i];
                            var al = d.arg_level;
                            var obj = {"text" : d.val_golddecision, "children" : []};

                            // pop the stack up to the point where the last element in the stack is the parent of
                            // the object's current level (al).  Because there is the parent decision trail object,
                            // the first element of the stack is untouchable, hence the + 1
                            while (al + 1 < stack.length) {
                                stack.pop()
                            }

                            // get the last element in the stack, which is the parent of the current element
                            var context = stack[stack.length - 1];

                            // push this object into the parent
                            context.push(obj);

                            // add the children array of the current object to the stack.  This means if the next
                            // element is a child of this one, it can push itself into that children array.  If it is
                            // not, then the stack will get popped back to the relevant level (see above).
                            stack.push(obj.children);
                        }
                    }
                    return decision;
                },

                green_compliance : function() {
                    if (this.raw.green && this.raw.green.arg_greencompliancecode) {
                        return this.raw.green.arg_greencompliancecode;
                    }
                    return "no";
                },
                green_compliance_msg : function() {
                    if (this.raw.green && this.raw.green.val_greencompliance) {
                        return this.raw.green.val_greencompliance;
                    }
                    return "";
                },
                green_compliance_reason : function(params) {
                    var link = params.link || false;
                    var linkclass = params.linkclass || "";

                    var reason = "";
                    if (this.raw.green && this.raw.green.val_greenreason) {
                        reason = this.raw.green.val_greenreason;
                    }

                    if (link && this.raw.green && this.raw.green.arg_greenreasonlinktext && this.raw.green.arg_greenreasonlinkurl) {
                        var text = this.raw.green.arg_greenreasonlinktext;
                        var url = this.raw.green.arg_greenreasonlinkurl;
                        reason = this._linkify({
                            text : text,
                            url : url,
                            source : reason,
                            linkclass : linkclass
                        });
                    }

                    return reason;
                },
                green_advice : function() {
                    var gas = [];
                    if (this.raw.green && this.raw.green.greenadvice && this.raw.green.greenadvice.length > 0) {
                        for (var i = 0; i < this.raw.green.greenadvice.length; i++) {
                            var ga = this.raw.green.greenadvice[i];
                            if (ga.val_greenadvice) {
                                gas.push(ga.val_greenadvice);
                            }
                        }
                    }
                    return gas;
                },
                green_confirm : function() {
                    var gcs = [];
                    if (this.raw.green && this.raw.green.greenconfirm && this.raw.green.greenconfirm.length > 0) {
                        for (var i = 0; i < this.raw.green.greenconfirm.length; i++) {
                            var gc = this.raw.green.greenconfirm[i];
                            if (gc.val_greenconfirm) {
                                gcs.push(gc.val_greenconfirm);
                            }
                        }
                    }
                    return gcs;
                },
                green_decision : function() {
                    // This method is tricky, so will be carefully explained

                    // this is the parent decision trail object.  All decisions are stored in here, in the order
                    // that they were considered.  In turn each sub-decision has a set of children in the order that they
                    // occurred, and so on.
                    var decision = [];

                    // this is the stack, where we will keep track of the current deepest element in the tree.
                    // we start off with the parent decision trail object at the bottom of the stack
                    var stack = [];
                    stack.push(decision);

                    if (this.raw.green && this.raw.green.greendecision && this.raw.green.greendecision.length > 0) {
                        for (var i = 0; i < this.raw.green.greendecision.length; i++) {
                            // get the details that we care about for this decision, and make the decision
                            // object with a pre-prepared "children" field
                            var d = this.raw.green.greendecision[i];
                            var al = d.arg_level;
                            var obj = {"text" : d.val_greendecision, "children" : []};

                            // pop the stack up to the point where the last element in the stack is the parent of
                            // the object's current level (al).  Because there is the parent decision trail object,
                            // the first element of the stack is untouchable, hence the + 1
                            while (al + 1 < stack.length) {
                                stack.pop()
                            }

                            // get the last element in the stack, which is the parent of the current element
                            var context = stack[stack.length - 1];

                            // push this object into the parent
                            context.push(obj);

                            // add the children array of the current object to the stack.  This means if the next
                            // element is a child of this one, it can push itself into that children array.  If it is
                            // not, then the stack will get popped back to the relevant level (see above).
                            stack.push(obj.children);
                        }
                    }
                    return decision;
                },

                _linkify : function(params) {
                    var text = params.text;
                    var url = params.url;
                    var source = params.source;
                    var linkclass = params.linkclass;

                    var start = source.indexOf(text);
                    var end = start + text.length;
                    return source.substring(0, start) + "" +
                                source.substring(start, end) + "" + source.substring(end);
                }
            }

        }
    });
});

<|endoftext|>


<commit_before>'use strict'

var http = require('http'),
    fileSystem = require('fs'),
    path = require('path');
var hummus=require('hummus');
var fillForm = require('./pdf-form-fill').fillForm;
function generatePDF(req, res){
  var outputName = new Date().getTime();
  var filePath = '/tmp/cultos_'+outputName+'.pdf');
  console.log('filePath: '+filePath);
  var writer = hummus.createWriterToModify('./public/pdf_templates/cultos.pdf', {
  			modifiedFilePath: filePath
  });

  var params = req.body;

  var data = params;
  console.log(data);

  fillForm(writer,data);
  writer.end();

  var stat = fileSystem.statSync(filePath);
  res.writeHead(200, {'Content-Type': 'application/pdf',
                      'Content-Length': stat.size});

  var readStream = fileSystem.createReadStream(filePath);
  // We replaced all the event handlers with a simple call to readStream.pipe()
  readStream.pipe(res);
  readStream.on('close', function(){
    fs.unlink(filePath);
  });
  res.end();
}

module.exports = {
  generatePDF
};
<commit_msg>Escribir fichero en /tmp<commit_after>'use strict'

var http = require('http'),
    fileSystem = require('fs'),
    path = require('path');
var hummus=require('hummus');
var fillForm = require('./pdf-form-fill').fillForm;
function generatePDF(req, res){
  var outputName = new Date().getTime();
  var filePath = '/tmp/cultos_'+outputName+'.pdf';
  console.log('filePath: '+filePath);
  var writer = hummus.createWriterToModify('./public/pdf_templates/cultos.pdf', {
  			modifiedFilePath: filePath
  });

  var params = req.body;

  var data = params;
  console.log(data);

  fillForm(writer,data);
  writer.end();

  var stat = fileSystem.statSync(filePath);
  res.writeHead(200, {'Content-Type': 'application/pdf',
                      'Content-Length': stat.size});

  var readStream = fileSystem.createReadStream(filePath);
  // We replaced all the event handlers with a simple call to readStream.pipe()
  readStream.pipe(res);
  readStream.on('close', function(){
    fs.unlink(filePath);
  });
  res.end();
}

module.exports = {
  generatePDF
};
<|endoftext|>


<commit_before>cdb.admin.models = cdb.admin.models || {};

//===================================================
// histogram filter
//===================================================
cdb.admin.models.Filter = cdb.core.Model.extend({

  DEFAULT_HIST_BUCKETS: 100,
  urlRoot: '/api/v1/filters',

  initialize: function() {

    this.table = this.get('table');

    if (!this.table) {
      throw "Filter needs a table";
    }

    this.unset('table');

    this.table.bind('data:saved', function() {
      this._fetchHist();
    }, this);

    if(this.table.has('name') && this.table.has('schema')) {
      this._fetchHist();
    }
    this.table.bind('change:name change:schema', this._fetchHist, this);
    this.bind('destroy', function() {
      this.table.unbind('change:name change:schema', this._fetchHist, this);
    });
  },

  _fetchHist: function() {

    if (this.get("column_type") == 'date') this._fetchDateHist();
    else this._fetchNumericHist();

  },

  _fetchNumericHist: function() {
    var self = this;

    this.table.originalData().histogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist, bounds) {

      if (hist) {
        self._changeHist(hist, bounds);
      } else {
        self.trigger('error', "numeric histogram couldn't be generated");
      }
    });

  },

  _fetchDateHist: function() {
    var self = this;

    this.table.originalData().date_histogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist, bounds) {

      if (hist) {
        self._changeDateHist(hist, bounds);
      } else {
        self.trigger('error', "date histogram couldn't be generated");
      }
    });

  },

  _changeDateHist: function(hist, bounds) {

    var previousBounds = this.get('bounds');

    this.set('hist',   hist,   { silent: true });
    this.set('bounds', bounds, { silent: true });

    if (!this.has('lower')) {

      var per_bar = bounds.bucket_size;

      bounds.lower = Math.round(bounds.lower * 1000);
      bounds.upper = Math.round(bounds.upper * 1000);

      this.set({
        'bucket_size': per_bar,
        'lower': bounds.lower,
        'upper': bounds.upper,
        'lower_limit': bounds.lower,
        'upper_limit': bounds.upper,
        'tz': bounds.tz
      });

    } else {

      var lower = Math.max(bounds.lower * 1000, this.get("lower"));
      var upper = Math.min(bounds.upper * 1000, this.get("upper"));

      // Bounds have changed
      if (previousBounds) {

        // Bounds have been expanded on the upper side
        if (bounds.upper > previousBounds.upper) {
          upper = bounds.upper * 1000;
        }

        // Bounds have been expanded on the lower side
        if (bounds.lower < previousBounds.lower) {
          lower = bounds.lower * 1000;
        }
      }

      if (_.isNaN(lower) || _.isNaN(upper)) {
        lower = bounds.lower * 1000;
        upper = bounds.upper * 1000;
      }

      this.set({
        'lower': lower,
        'upper': upper,
        'lower_limit': bounds.lower * 1000,
        'upper_limit': bounds.upper * 1000,
        'tz': bounds.tz
      });

    }

  },

  _changeHist: function(hist, bounds) {

    var previousBounds = this.get('bounds');

    this.set('hist', hist, { silent: true });
    this.set('bounds', bounds, { silent: true });

    if (!this.has('lower')) {

      // calculate limits based on partitions
      var per_bar  = bounds.bucket_size;

      bounds.upper = per_bar * Math.ceil(bounds.upper/per_bar);

        this.set({
          'bucket_size': per_bar,
          'lower': bounds.lower,
          'upper': bounds.upper,
          'lower_limit': bounds.lower,
          'upper_limit': bounds.upper
        });

    } else {

      var upper = Math.min(bounds.upper, this.get('upper'));
      var lower = Math.max(bounds.lower, this.get('lower'));

      // Bounds have changed
      if (previousBounds) {

        // Bounds have been expanded on the upper side
        if (bounds.upper > previousBounds.upper) {
          upper = bounds.upper;
        }

        // Bounds have been expanded on the lower side
        if (bounds.lower < previousBounds.lower) {
          lower = bounds.lower;
        }
      }

      this.set({
        'lower': lower,
        'upper': upper,
        'lower_limit': bounds.lower,
        'upper_limit': bounds.upper
      });

    }

  },

  _getDateFromTimestamp: function(timestamp) {
    return new Date(timestamp);
  },

  interpolate: function(t) {
    var a = this.get('lower_limit');
    var b = this.get('upper_limit');
    return (1 - t)*a + t*b;
  },

  fitToBucket: function(value) {
    var b = this.get('bucket_size');
    if (!b) return 0;
    return b*Math.floor(value/b);
  },

  getSQLCondition: function() {

    if (this.attributes.column_type == 'date') return this.getSQLConditionForDate();
    else return this.getSQLConditionForNumber();

  },

  /**
   *  Extracts the timezone from a date
   */
  _getTimeZone: function(date) {
    if (date) {
      return new Date(date).getTimezoneOffset()
    }
    return 0;
  },

  getSQLConditionForDate: function() {

    var lower = this.get("lower");
    var upper = this.get("upper");

    if (_.isNaN(lower) || _.isNaN(upper) || lower == undefined || upper == undefined) return null;

    var lowerDateWithOffset = moment(lower).format("YYYY-MM-DDTHH:mm:ssZ").toString();
    var upperDateWithOffset = moment(upper).format("YYYY-MM-DDTHH:mm:ssZ").toString();

    var attributes = _.clone(this.attributes);

    var options, sql;

    if (this.get('upper') >= this.get('upper_limit')) {
      options = _.extend(attributes, {
        lower: lowerDateWithOffset,
        upper: moment(this.get('upper_limit')).format("YYYY-MM-DDTHH:mm:ssZ").toString()
      });
      sql = _.template(" (<%= column %> >= ('<%= lower %>') AND <%= column %> <= ('<%= upper %>')) ")(options);

    } else {

      options = _.extend(attributes, { lower: lowerDateWithOffset, upper: upperDateWithOffset});
      sql = _.template(" (<%= column %> >= ('<%= lower %>') AND <%= column %> <= ('<%= upper %>')) ")(options);

    }

    return sql;

  },

  getSQLConditionForNumber: function() {
    if (!this.attributes.lower && !this.attributes.upper) return _.template(" (<%= column %> IS NULL) ")(this.attributes);
    if (this.attributes.upper >= this.attributes.upper_limit) return _.template(" (<%= column %> >= <%= lower %> AND <%= column %> <= <%= upper %>) ")(this.attributes);
    return _.template(" (<%= column %> >= <%= lower %> AND <%= column %> < <%= upper %>) ")(this.attributes);
  },

  toJSON: function() {

    return {
      column: this.get('column'),
      upper:  this.get('upper'),
      lower:  this.get('lower'),
      tz:     this.get('tz'),
      column_type:   this.get('column_type')
    }
  }

});

//===================================================
// discrete filter for text columns
//===================================================
cdb.admin.models.FilterDiscrete = cdb.core.Model.extend({

  DEFAULT_HIST_BUCKETS: 39,
  urlRoot: '/api/v1/filters',

  defaults: {
    list_view: true
  },

  initialize: function() {

    this.table = this.get('table');
    this.items = new Backbone.Collection();

    if (this.get('items')) {
      this.items.reset(this.get('items'));
    }

    if (!this.table) {
      throw "Filter needs a table";
    }

    this.unset('table');

    this.table.bind('data:saved', function() {
      this._fetchHist();
    }, this);

    this.items.bind('change', function() {
      this.trigger('change', this)
      this.trigger('change:items', this, this.items);
    }, this);

    this._fetchHist();

  },

  _fetchHist: function() {
    var self = this;

    this.table.originalData().discreteHistogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist) {

      if (hist) {
        self._updateHist(hist);
      } else {
        self.trigger('error', "Histogram couldn't be generated");
      }

    });
  },

  _updateHist: function(hist) {

    for (var i = 0; i < hist.rows.length; ++i) {

      var o = this.items.where({ bucket: hist.rows[i].bucket})

        if (o.length) {
          hist.rows[i].selected = o[0].get('selected')
        } else {
          hist.rows[i].selected = true;
        }

    }

    this.set("reached_limit", hist.reached_limit);
    this.items.reset(hist.rows);

  },

  _sanitize: function(s) {
    if (s) {
      return s.replace(/'/g, "''");
    }

    return s;
  },

  getSQLCondition: function() {

    if (this.get("column_type") == 'boolean') return this.getSQLConditionForBoolean();
    else return this.getSQLConditionForString();

  },

  getSQLConditionForBoolean: function() {

    if (this.items.size() === 0) { // if there aren't any items…
      return ' (true) ';
    }

    // Make some lists of values
    var selected_items            = this.items.filter(function(i) { return i.get('selected'); });
    var true_false_selected_items = this.items.filter(function(i) { return i.get('bucket') != null && i.get('selected'); });
    var null_selected_items       = this.items.filter(function(i) { return i.get('bucket') == null && i.get('selected'); });

    if (selected_items.length > 0 && null_selected_items.length == 0) { // there are just true or false values

      return _.template("<%= column %> IN (<%= opts %>) ")({
        column: this.get('column'),
             opts: true_false_selected_items.map(function(i) {
               return i.get("bucket");
             }).join(',')
      });

    }

    if (selected_items.length == 1 && null_selected_items.length == 1) { // only null values

      return _.template("<%= column %> IS NULL ")({
        column: this.get('column')
      });

    }

    if (selected_items.length > 0) {

      return _.template("<%= column %> IN (<%= opts %>) OR <%= column %> IS NULL ")({ // all kinds of values
        column: this.get('column'),
             opts: true_false_selected_items.map(function(i) {
               return i.get("bucket");
             }).join(',')
      });

    }

    return _.template("<%= column %> IN (NULL) ")({ // there aren't any selected values
      column: this.get('column'),
    });

  },

  _containsNull: function(items) {

    var containsNull = false;

    _.each(items, function(bucket) {
      if (!bucket.get("bucket")) containsNull = true;
    });

    return containsNull;

  },

  _nullIsNotSelected: function(origin, destiny) {
    var difference = _.difference(origin, destiny);

    var nullIsNotSelected = false;

    _.each(difference, function(bucket) {
      if (!bucket.get("selected") && !bucket.get("bucket")) nullIsNotSelected = true;
    });

    return nullIsNotSelected;

  },

  getSQLConditionForString: function() {

    var that = this;

    // If the user entered text…
    if (!this.get("list_view")) {

      if (this.get("free_text")) {

        var text = this._sanitize(this.get("free_text"));

        return _.template("<%= column %> ILIKE '%<%= t %>%' ")({
          column: this.get('column'),
               t: text
        });

      } else {
        return ' (true) ';
      }

    }

    // If there aren't any items…
    if (this.items.size() === 0) {
      return ' (true) ';
    }

    // If there are some items, first get the selected ones…
    var items = this.items.filter(function(i) {
      return i.get('selected');
    });

    // If there are selected items…
    if (items.length > 0) {

      var selected = items.filter(function(i) { return i.get('selected'); });

      if (items.length == this.items.length) {

        return _.template(" (true) ")({
          column: this.get('column'),
          opts: items.map(function(i) {
            var bucket = that._sanitize(i.get('bucket'));
            return "'" + bucket + "'";
          }).join(',')
        });

      } else {

        var query = "<%= column %> IN (<%= opts %>) ";

        if (this._nullIsNotSelected(this.items.models, items)) {
          query = "<%= column %> IN (<%= opts %>) AND <%= column %> IS NOT NULL "
        } else if (this._containsNull(items)) {
          query = "<%= column %> IN (<%= opts %>) OR <%= column %> IS NULL ";
        }

        return _.template(query)({
          column: this.get('column'),
          opts: items.map(function(i) {
            var bucket = that._sanitize(i.get('bucket'));

            return "'" + bucket + "'";
          }).join(',')
        });

      }

    } else { // if there's no selected element…

      return ' (true) '; // this will remove the 'WHERE' condition

    }

    // If there aren't selected items
    return "true ";

  },

  toJSON: function() {
    return {
      reached_limit: this.get("reached_limit"),
      column:        this.get('column'),
      items:         this.items.toJSON(),
      free_text:     this.get("free_text"),
      list_view:     this.get("list_view"),
      column_type:   this.get('column_type')
    }
  }
});

//===================================================
// filters collection
//===================================================
cdb.admin.models.Filters = Backbone.Collection.extend({

  model: function(attrs, options) {

    var self = options.collection;
    var col  = attrs.column;

    var schema = self.table.get("original_schema") ? "original_schema": "schema";

    var column_type = self.table.getColumnType(col, schema) || attrs.column_type;
    var FilterClass = self._getFilterModelforColumnType(column_type);

    return new FilterClass(_.extend(attrs, {
      column_type: column_type,
      table: self.table
    }));

  },

  initialize: function(m, options) {
    if (!options.table) {
      throw "Filters need a table";
    }
    this.table = options.table;
  },

  getSQLCondition: function() {

    var sqls = this.map(function(f) {
      return f.getSQLCondition();
    })

    var sql = _(sqls).compact().join(' AND ');

    return sql;
  },

  removeFilters: function() {
    while(this.size()) {
      this.at(0).destroy();
    }
  },

  _getFilterModelforColumnType: function(columnType) {
    if (columnType == 'number' || columnType == 'date') {
      return cdb.admin.models.Filter;
    } else {
      return cdb.admin.models.FilterDiscrete
    }
  }

});
<commit_msg>Changed default discrete buckets<commit_after>cdb.admin.models = cdb.admin.models || {};

//===================================================
// histogram filter
//===================================================
cdb.admin.models.Filter = cdb.core.Model.extend({

  DEFAULT_HIST_BUCKETS: 100,
  urlRoot: '/api/v1/filters',

  initialize: function() {

    this.table = this.get('table');

    if (!this.table) {
      throw "Filter needs a table";
    }

    this.unset('table');

    this.table.bind('data:saved', function() {
      this._fetchHist();
    }, this);

    if(this.table.has('name') && this.table.has('schema')) {
      this._fetchHist();
    }
    this.table.bind('change:name change:schema', this._fetchHist, this);
    this.bind('destroy', function() {
      this.table.unbind('change:name change:schema', this._fetchHist, this);
    });
  },

  _fetchHist: function() {

    if (this.get("column_type") == 'date') this._fetchDateHist();
    else this._fetchNumericHist();

  },

  _fetchNumericHist: function() {
    var self = this;

    this.table.originalData().histogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist, bounds) {

      if (hist) {
        self._changeHist(hist, bounds);
      } else {
        self.trigger('error', "numeric histogram couldn't be generated");
      }
    });

  },

  _fetchDateHist: function() {
    var self = this;

    this.table.originalData().date_histogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist, bounds) {

      if (hist) {
        self._changeDateHist(hist, bounds);
      } else {
        self.trigger('error', "date histogram couldn't be generated");
      }
    });

  },

  _changeDateHist: function(hist, bounds) {

    var previousBounds = this.get('bounds');

    this.set('hist',   hist,   { silent: true });
    this.set('bounds', bounds, { silent: true });

    if (!this.has('lower')) {

      var per_bar = bounds.bucket_size;

      bounds.lower = Math.round(bounds.lower * 1000);
      bounds.upper = Math.round(bounds.upper * 1000);

      this.set({
        'bucket_size': per_bar,
        'lower': bounds.lower,
        'upper': bounds.upper,
        'lower_limit': bounds.lower,
        'upper_limit': bounds.upper,
        'tz': bounds.tz
      });

    } else {

      var lower = Math.max(bounds.lower * 1000, this.get("lower"));
      var upper = Math.min(bounds.upper * 1000, this.get("upper"));

      // Bounds have changed
      if (previousBounds) {

        // Bounds have been expanded on the upper side
        if (bounds.upper > previousBounds.upper) {
          upper = bounds.upper * 1000;
        }

        // Bounds have been expanded on the lower side
        if (bounds.lower < previousBounds.lower) {
          lower = bounds.lower * 1000;
        }
      }

      if (_.isNaN(lower) || _.isNaN(upper)) {
        lower = bounds.lower * 1000;
        upper = bounds.upper * 1000;
      }

      this.set({
        'lower': lower,
        'upper': upper,
        'lower_limit': bounds.lower * 1000,
        'upper_limit': bounds.upper * 1000,
        'tz': bounds.tz
      });

    }

  },

  _changeHist: function(hist, bounds) {

    var previousBounds = this.get('bounds');

    this.set('hist', hist, { silent: true });
    this.set('bounds', bounds, { silent: true });

    if (!this.has('lower')) {

      // calculate limits based on partitions
      var per_bar  = bounds.bucket_size;

      bounds.upper = per_bar * Math.ceil(bounds.upper/per_bar);

        this.set({
          'bucket_size': per_bar,
          'lower': bounds.lower,
          'upper': bounds.upper,
          'lower_limit': bounds.lower,
          'upper_limit': bounds.upper
        });

    } else {

      var upper = Math.min(bounds.upper, this.get('upper'));
      var lower = Math.max(bounds.lower, this.get('lower'));

      // Bounds have changed
      if (previousBounds) {

        // Bounds have been expanded on the upper side
        if (bounds.upper > previousBounds.upper) {
          upper = bounds.upper;
        }

        // Bounds have been expanded on the lower side
        if (bounds.lower < previousBounds.lower) {
          lower = bounds.lower;
        }
      }

      this.set({
        'lower': lower,
        'upper': upper,
        'lower_limit': bounds.lower,
        'upper_limit': bounds.upper
      });

    }

  },

  _getDateFromTimestamp: function(timestamp) {
    return new Date(timestamp);
  },

  interpolate: function(t) {
    var a = this.get('lower_limit');
    var b = this.get('upper_limit');
    return (1 - t)*a + t*b;
  },

  fitToBucket: function(value) {
    var b = this.get('bucket_size');
    if (!b) return 0;
    return b*Math.floor(value/b);
  },

  getSQLCondition: function() {

    if (this.attributes.column_type == 'date') return this.getSQLConditionForDate();
    else return this.getSQLConditionForNumber();

  },

  /**
   *  Extracts the timezone from a date
   */
  _getTimeZone: function(date) {
    if (date) {
      return new Date(date).getTimezoneOffset()
    }
    return 0;
  },

  getSQLConditionForDate: function() {

    var lower = this.get("lower");
    var upper = this.get("upper");

    if (_.isNaN(lower) || _.isNaN(upper) || lower == undefined || upper == undefined) return null;

    var lowerDateWithOffset = moment(lower).format("YYYY-MM-DDTHH:mm:ssZ").toString();
    var upperDateWithOffset = moment(upper).format("YYYY-MM-DDTHH:mm:ssZ").toString();

    var attributes = _.clone(this.attributes);

    var options, sql;

    if (this.get('upper') >= this.get('upper_limit')) {
      options = _.extend(attributes, {
        lower: lowerDateWithOffset,
        upper: moment(this.get('upper_limit')).format("YYYY-MM-DDTHH:mm:ssZ").toString()
      });
      sql = _.template(" (<%= column %> >= ('<%= lower %>') AND <%= column %> <= ('<%= upper %>')) ")(options);

    } else {

      options = _.extend(attributes, { lower: lowerDateWithOffset, upper: upperDateWithOffset});
      sql = _.template(" (<%= column %> >= ('<%= lower %>') AND <%= column %> <= ('<%= upper %>')) ")(options);

    }

    return sql;

  },

  getSQLConditionForNumber: function() {
    if (!this.attributes.lower && !this.attributes.upper) return _.template(" (<%= column %> IS NULL) ")(this.attributes);
    if (this.attributes.upper >= this.attributes.upper_limit) return _.template(" (<%= column %> >= <%= lower %> AND <%= column %> <= <%= upper %>) ")(this.attributes);
    return _.template(" (<%= column %> >= <%= lower %> AND <%= column %> < <%= upper %>) ")(this.attributes);
  },

  toJSON: function() {

    return {
      column: this.get('column'),
      upper:  this.get('upper'),
      lower:  this.get('lower'),
      tz:     this.get('tz'),
      column_type:   this.get('column_type')
    }
  }

});

//===================================================
// discrete filter for text columns
//===================================================
cdb.admin.models.FilterDiscrete = cdb.core.Model.extend({

  DEFAULT_HIST_BUCKETS: 65,
  urlRoot: '/api/v1/filters',

  defaults: {
    list_view: true
  },

  initialize: function() {

    this.table = this.get('table');
    this.items = new Backbone.Collection();

    if (this.get('items')) {
      this.items.reset(this.get('items'));
    }

    if (!this.table) {
      throw "Filter needs a table";
    }

    this.unset('table');

    this.table.bind('data:saved', function() {
      this._fetchHist();
    }, this);

    this.items.bind('change', function() {
      this.trigger('change', this)
      this.trigger('change:items', this, this.items);
    }, this);

    this._fetchHist();

  },

  _fetchHist: function() {
    var self = this;

    this.table.originalData().discreteHistogram(this.DEFAULT_HIST_BUCKETS, this.get('column'), function(hist) {

      if (hist) {
        self._updateHist(hist);
      } else {
        self.trigger('error', "Histogram couldn't be generated");
      }

    });
  },

  _updateHist: function(hist) {

    for (var i = 0; i < hist.rows.length; ++i) {

      var o = this.items.where({ bucket: hist.rows[i].bucket})

        if (o.length) {
          hist.rows[i].selected = o[0].get('selected')
        } else {
          hist.rows[i].selected = true;
        }

    }

    this.set("reached_limit", hist.reached_limit);
    this.items.reset(hist.rows);

  },

  _sanitize: function(s) {
    if (s) {
      return s.replace(/'/g, "''");
    }

    return s;
  },

  getSQLCondition: function() {

    if (this.get("column_type") == 'boolean') return this.getSQLConditionForBoolean();
    else return this.getSQLConditionForString();

  },

  getSQLConditionForBoolean: function() {

    if (this.items.size() === 0) { // if there aren't any items…
      return ' (true) ';
    }

    // Make some lists of values
    var selected_items            = this.items.filter(function(i) { return i.get('selected'); });
    var true_false_selected_items = this.items.filter(function(i) { return i.get('bucket') != null && i.get('selected'); });
    var null_selected_items       = this.items.filter(function(i) { return i.get('bucket') == null && i.get('selected'); });

    if (selected_items.length > 0 && null_selected_items.length == 0) { // there are just true or false values

      return _.template("<%= column %> IN (<%= opts %>) ")({
        column: this.get('column'),
             opts: true_false_selected_items.map(function(i) {
               return i.get("bucket");
             }).join(',')
      });

    }

    if (selected_items.length == 1 && null_selected_items.length == 1) { // only null values

      return _.template("<%= column %> IS NULL ")({
        column: this.get('column')
      });

    }

    if (selected_items.length > 0) {

      return _.template("<%= column %> IN (<%= opts %>) OR <%= column %> IS NULL ")({ // all kinds of values
        column: this.get('column'),
             opts: true_false_selected_items.map(function(i) {
               return i.get("bucket");
             }).join(',')
      });

    }

    return _.template("<%= column %> IN (NULL) ")({ // there aren't any selected values
      column: this.get('column'),
    });

  },

  _containsNull: function(items) {

    var containsNull = false;

    _.each(items, function(bucket) {
      if (!bucket.get("bucket")) containsNull = true;
    });

    return containsNull;

  },

  _nullIsNotSelected: function(origin, destiny) {
    var difference = _.difference(origin, destiny);

    var nullIsNotSelected = false;

    _.each(difference, function(bucket) {
      if (!bucket.get("selected") && !bucket.get("bucket")) nullIsNotSelected = true;
    });

    return nullIsNotSelected;

  },

  getSQLConditionForString: function() {

    var that = this;

    // If the user entered text…
    if (!this.get("list_view")) {

      if (this.get("free_text")) {

        var text = this._sanitize(this.get("free_text"));

        return _.template("<%= column %> ILIKE '%<%= t %>%' ")({
          column: this.get('column'),
               t: text
        });

      } else {
        return ' (true) ';
      }

    }

    // If there aren't any items…
    if (this.items.size() === 0) {
      return ' (true) ';
    }

    // If there are some items, first get the selected ones…
    var items = this.items.filter(function(i) {
      return i.get('selected');
    });

    // If there are selected items…
    if (items.length > 0) {

      var selected = items.filter(function(i) { return i.get('selected'); });

      if (items.length == this.items.length) {

        return _.template(" (true) ")({
          column: this.get('column'),
          opts: items.map(function(i) {
            var bucket = that._sanitize(i.get('bucket'));
            return "'" + bucket + "'";
          }).join(',')
        });

      } else {

        var query = "<%= column %> IN (<%= opts %>) ";

        if (this._nullIsNotSelected(this.items.models, items)) {
          query = "<%= column %> IN (<%= opts %>) AND <%= column %> IS NOT NULL "
        } else if (this._containsNull(items)) {
          query = "<%= column %> IN (<%= opts %>) OR <%= column %> IS NULL ";
        }

        return _.template(query)({
          column: this.get('column'),
          opts: items.map(function(i) {
            var bucket = that._sanitize(i.get('bucket'));

            return "'" + bucket + "'";
          }).join(',')
        });

      }

    } else { // if there's no selected element…

      return ' (true) '; // this will remove the 'WHERE' condition

    }

    // If there aren't selected items
    return "true ";

  },

  toJSON: function() {
    return {
      reached_limit: this.get("reached_limit"),
      column:        this.get('column'),
      items:         this.items.toJSON(),
      free_text:     this.get("free_text"),
      list_view:     this.get("list_view"),
      column_type:   this.get('column_type')
    }
  }
});

//===================================================
// filters collection
//===================================================
cdb.admin.models.Filters = Backbone.Collection.extend({

  model: function(attrs, options) {

    var self = options.collection;
    var col  = attrs.column;

    var schema = self.table.get("original_schema") ? "original_schema": "schema";

    var column_type = self.table.getColumnType(col, schema) || attrs.column_type;
    var FilterClass = self._getFilterModelforColumnType(column_type);

    return new FilterClass(_.extend(attrs, {
      column_type: column_type,
      table: self.table
    }));

  },

  initialize: function(m, options) {
    if (!options.table) {
      throw "Filters need a table";
    }
    this.table = options.table;
  },

  getSQLCondition: function() {

    var sqls = this.map(function(f) {
      return f.getSQLCondition();
    })

    var sql = _(sqls).compact().join(' AND ');

    return sql;
  },

  removeFilters: function() {
    while(this.size()) {
      this.at(0).destroy();
    }
  },

  _getFilterModelforColumnType: function(columnType) {
    if (columnType == 'number' || columnType == 'date') {
      return cdb.admin.models.Filter;
    } else {
      return cdb.admin.models.FilterDiscrete
    }
  }

});
<|endoftext|>


<commit_before>var http = require('http'),
    isRegExp = require('util').isRegExp,
    isError = require('util').isError,
    inspect = require('util').inspect,
    resolve = require('path').resolve;

var qsparse = require('./querystring').parse,
    urljoin = require('./path-join'),
    Request = require('./Request'),
    Response = require('./Response'),
    View = require('./View');

var pathRegex = require('path-to-regexp');

var EMPTY_PARAMS = {},
    STATUS_CODES = http.STATUS_CODES,
    ROUTES_MATCH_ALL = [ '' ],
    NO_KEYS = [],
    METHODS;

if (http.METHODS)
  METHODS = http.METHODS;
else {
  METHODS = [
    'DELETE',
    'GET',
    'HEAD',
    'POST',
    'PUT',
    /* PATHOLOGICAL */
    'CONNECT',
    'OPTIONS',
    'TRACE',
    /* WEBDAV */
    'COPY',
    'LOCK',
    'MKCOL',
    'MOVE',
    'PROPFIND',
    'PROPPATCH',
    'SEARCH',
    'UNLOCK',
    /* SUBVERSION */
    'REPORT',
    'MKACTIVITY',
    'CHECKOUT',
    'MERGE',
    /* UPNP */
    'MSEARCH',
    'NOTIFY',
    'SUBSCRIBE',
    'UNSUBSCRIBE',
    /* RFC-5789 */
    'PATCH',
    'PURGE'
  ].sort();
}
METHODS = METHODS.map(function(method) { return method.toLowerCase(); });

function Router(opts) {
  var self = this,
      env = process.env.NODE_ENV || 'development';

  // the route handler stack contains two possible kinds of objects:
  //   1. A normal route handler containing:
  //
  //          id - This is an integer that uniquely identifies this handler in
  //               this router instance. This value allows us to efficiently
  //               check if a particular route handler's path was previously
  //               matched.
  //          re - This is a regular expression to use in matching the request's
  //               url.
  //        keys - This is an array containing the names of placeholders in
  //               the handler's regular expression. This is used to help
  //               populate req.$.params.
  //     handler - This is a function in the case of a non-method-specific
  //               middleware function, or an object containing the method
  //               and the handler function.
  //
  //   2. A "pointer" route handler. This is a special route handler that shares
  //      a path regexp and param keys with another route handler. It contains:
  //
  //      parent - This is a reference to the previous route handler with whom
  //               the regexp and param keys are shared.
  //     handler - This is the same format as normal route handlers.
  this._stack = [];

  this._paramFuncs = {};
  this._mounted = false;
  this._rc = 0;

  this.path = '/';
  this.locals = Object.create(null);
  this.config = {
    'etag': 'weak',
    'env': env,
    'query parser': 'extended',
    'subdomain offset': 2,
    'trust proxy': false,
    'view': View,
    'views': resolve('views'),
    'jsonp callback name': 'callback',
    'view cache': (env === 'production')
  };
  this.handler = function routerHandlerWrap(req, res, cb) {
    self.handle(req, res, cb);
  };
}

Router.prototype.handle = function handle(req, res, cb) {
  var stack = this._stack,
      stacklen = stack.length,
      paramFuncs = this._paramFuncs,
      paramsCache = {},
      paramPtr = 0,
      matched = {},
      ptr = 0,
      req$,
      res$;

  req$ = req.$;
  res$ = res.$;

  // req/res decorations
  if (!req$) {
    var urlpath = req.url,
        qm = urlpath.indexOf('?'),
        path_,
        query_;
    if (qm > -1) {
      path_ = urlpath.substring(0, qm);
      if (qm === urlpath.length - 1)
        query_ = {};
      else
        query_ = qsparse(urlpath.substring(qm + 1));
    } else {
      path_ = urlpath;
      query_ = {};
    }

    req$ = req.$ = new Request(this, req, res, next, path_, query_);
    res$ = res.$ = new Response(this, req, res);
  }
  // end req/res decorations

  var pathname = req$.path;

  function next(err) {
    var method = req.method,
        handler,
        parent,
        curmw,
        error,
        keys,
        ret,
        m;

    if (err !== undefined && err !== null) {
      if (isError(err))
        error = err;
      else if (typeof err === 'string') {
        error = new Error(err);
        error.code = err.code || this.DEFAULT_ERROR_STATUS;
      } else if (typeof err === 'number' && err >= 400 && err < 600) {
        error = new Error(method
                          + ' '
                          + pathname
                          + ' '
                          + (STATUS_CODES[err] || 'Unknown Error'));
        error.code = err;
      } else {
        error = new Error(method + ' ' + pathname + ' Unknown Error');
        error.code = this.DEFAULT_ERROR_STATUS;
      }
    }

    if (error)
      ptr = stacklen;

    for (; ptr < stacklen; ++ptr, paramPtr = 0) {
      curmw = stack[ptr];
      handler = curmw.handler;
      if (curmw.parent
          && (typeof handler === 'function' || handler.method === method)) {
        // optimized route handler
        parent = curmw.parent;
        if ((m = matched[parent.id]) === undefined)
          continue;
        curmw = parent;
      } else if (m = curmw.re.exec(pathname)) {
        // normal route handler
        matched[curmw.id] = m;
      } else
        continue;

      if (method === handler.method)
        handler = handler.handler;

      var handlerLen = handler.length;
      if (!((error === undefined && handlerLen < 4)
            || (error !== undefined && handlerLen === 4)))
        continue;

      // check for `router.param('foo', cb);` calls before calling route handler
      if ((keys = curmw.keys).length) {
        var resrc = curmw.re.toString(),
            hadError = false,
            paramKeys,
            params,
            lenk,
            pk,
            fn;

        req$.params = params = (paramsCache[resrc]
                                || (paramsCache[resrc] = makeParams(m, keys)));
        paramKeys = Object.keys(params);
        for (lenk = paramKeys.length, fn; paramPtr < lenk; ++paramPtr) {
          pk = paramKeys[paramPtr];
          if (fn = paramFuncs[pk]) {
            ++paramPtr;
            ret = execParam(fn, req, res, next, params[pk]);
            if (ret && isError(ret)) {
              if (!error)
                hadError = true;
              error = ret;
              break;
            }
            return;
          }
        }
        if (hadError) {
          ptr = stacklen;
          break;
        }
      } else
        req$.params = EMPTY_PARAMS;

      ++ptr;

      ret = execHandler(handler, req, res, next, error);
      if (ret && isError(ret)) {
        ptr = stacklen;
        error = ret;
        break;
      }
      return;
    }

    if (ptr === stacklen) {
      if (cb)
        cb(error);
      else if (error === undefined)
        res.end(method + ' ' + pathname + ' ' + STATUS_CODES[res.statusCode = 404]);
      else {
        res.statusCode = error.code;
        res.end(error.message);
      }
    }
  }

  next();
};

Router.prototype.DEFAULT_ERROR_STATUS = 500;

function execHandler(handler, req, res, next, error) {
  try {
    if (error === undefined)
      handler(req, res, next);
    else
      handler(error, req, res, next);
  } catch (ex) {
    console.error('!!! route handler exception:');
    console.error(ex.stack);
    ex.code = 500;
    return ex;
  }
}

function execParam(fn, req, res, next, val) {
  try {
    fn(req, res, next, val);
  } catch (ex) {
    console.error('!!! route param exception:');
    console.error(ex.stack);
    ex.code = 500;
    return ex;
  }
}

Router.prototype.use = function use(route) {
  var handlersEnd = arguments.length,
      handlersStart = 1,
      routes = route,
      method;

  // check for method-specific route, this is only the case when use() is called
  // internally from router.VERB()
  if (arguments.length > 2
      && typeof arguments[arguments.length - 1] === 'string') {
    method = arguments[arguments.length - 1].toUpperCase();
    --handlersEnd;
  }

  // match all routes by default when no path is given as the first argument
  if (typeof route === 'function' || route instanceof Router) {
    handlersStart = 0;
    routes = ROUTES_MATCH_ALL;
  }

  if (!Array.isArray(routes))
    routes = [ routes ];

  for (var i = 0, len = routes.length, fullPath, regex, keys; i < len; ++i) {
    keys = [];
    if (isRegExp(routes[i])) {
      fullPath = this.path;
      if (this.config['concat regex'] !== false)
        routes[i] = concatRegex(this.path, routes[i]);
      regex = pathRegex(routes[i], keys);
    } else if (routes === ROUTES_MATCH_ALL) {
      fullPath = this.path;
      regex = pathRegex(fullPath, keys, { end: false });
    } else {
      fullPath = urljoin(this.path, routes[i]);
      regex = pathRegex(fullPath, keys);
    }

    for (var h = handlersStart; h < handlersEnd; ++h) {
      var handlerfn = arguments[h],
          handlerRegex = regex;
      if (typeof handlerfn !== 'function' && !(handlerfn instanceof Router))
        continue;
      else if (handlerfn instanceof Router) {
        if (handlerfn._mounted)
          throw new Error('Cannot mount a Router in multiple places');
        if (routes !== ROUTES_MATCH_ALL && !isRegExp(routes[i]))
          handlerRegex = pathRegex(fullPath, keys, { end: false });
        handlerfn.path = fullPath;
        handlerfn.config = this.config;
        handlerfn._mounted = true;
        handlerfn = handlerfn.handler;
      }

      if (!keys.length)
        keys = NO_KEYS;

      this._stack.push({
        id: this._rc++,
        re: handlerRegex,
        keys: keys,
        handler: (method ? { method: method, handler: handlerfn } : handlerfn)
      });
    }
  }

  this.optimize();

  return this;
};

Router.prototype.all = Router.prototype.use;

METHODS.forEach(function(method) {
  Router.prototype[method] = function methodRoute() {
    var args = new Array(arguments.length + 1);
    for (var i = 0, len = args.length; i < len; ++i)
      args[i] = arguments[i];
    args[i] = method;
    return this.use.apply(this, args);
  };
});

Router.prototype.param = function param(name, cb) {
  if (typeof name !== 'string')
    throw new Error('Param name is required');
  if (typeof cb === 'function')
    this._paramFuncs[name] = cb;
};

Router.prototype.route = function route(path, opts) {
  var router = new Router(opts);
  this.use(path, router);
  return router;
};

// optimize() finds route handlers with the same path and converts all but the
// first to "pointers" to the first. This allows us to skip repeated regexp
// execution, allowing for a significant speedup and more consistent performance
// when many route handlers for the same path are added
Router.prototype.optimize = function optimize() {
  var stack = this._stack,
      len = stack.length,
      layer,
      i;

  for (i = 0; i < len; ++i) {
    layer = stack[i];
    if (!layer.re) {
      // skip optimized handlers
      continue;
    }
    // check for a previous route handler that has the same path
    for (var p = 0; p < i; ++p) {
      if (!stack[p].re) {
        // again, skip optimized handlers
        continue;
      }
      if (regexpsEqual(layer.re, stack[p].re)) {
        // we found a previous route handler we can point to
        stack[i] = {
          parent: stack[p],
          handler: stack[i].handler
        };
      }
    }
  }
};

module.exports = Router;

function decodeParam(val) {
  if (typeof val !== 'string')
    return val;

  try {
    return decodeURIComponent(val);
  } catch (e) {
    return false;
  }
}

function makeParams(m, keys) {
  if (!keys)
    return (m && m.length > 1 && m.slice(1, m.length));
  else if (!m || (m && m.length <= 1))
    return;

  var params = {};

  for (var i = 1, len = m.length, key, val; i < len; ++i) {
    key = keys[i - 1];
    val = decodeParam(m[i]);
    if (val === false)
      params[key.name] = val;
    else
      params[key.name] = m[i];
  }

  return params;
}

function regexpsEqual(re1, re2) {
  return (re1.source === re2.source
          && re1.global === re2.global
          && re1.ignoreCase === re2.ignoreCase
          && re1.multiline === re2.multiline);
}

function concatRegex(path, re) {
  var base = path,
      resrc = re.source.replace(/\\/g, ''),
      flags = '';

  // strip off ending '/' because most people will most likely start regexps
  // with a '/'
  if (base.substr(-1) === '/')
    base = base.slice(0, -1);

  if (resrc[0] === '^')
    resrc = resrc.slice(1);

  if (re.global)
    flags += 'g';
  if (re.ignoreCase)
    flags += 'i';
  if (re.multiline)
    flags += 'm';

  return new RegExp('^' + base + resrc, flags);
}<commit_msg>Router: revert immediate fail behavior<commit_after>var http = require('http'),
    isRegExp = require('util').isRegExp,
    isError = require('util').isError,
    inspect = require('util').inspect,
    resolve = require('path').resolve;

var qsparse = require('./querystring').parse,
    urljoin = require('./path-join'),
    Request = require('./Request'),
    Response = require('./Response'),
    View = require('./View');

var pathRegex = require('path-to-regexp');

var EMPTY_PARAMS = {},
    STATUS_CODES = http.STATUS_CODES,
    ROUTES_MATCH_ALL = [ '' ],
    NO_KEYS = [],
    METHODS;

if (http.METHODS)
  METHODS = http.METHODS;
else {
  METHODS = [
    'DELETE',
    'GET',
    'HEAD',
    'POST',
    'PUT',
    /* PATHOLOGICAL */
    'CONNECT',
    'OPTIONS',
    'TRACE',
    /* WEBDAV */
    'COPY',
    'LOCK',
    'MKCOL',
    'MOVE',
    'PROPFIND',
    'PROPPATCH',
    'SEARCH',
    'UNLOCK',
    /* SUBVERSION */
    'REPORT',
    'MKACTIVITY',
    'CHECKOUT',
    'MERGE',
    /* UPNP */
    'MSEARCH',
    'NOTIFY',
    'SUBSCRIBE',
    'UNSUBSCRIBE',
    /* RFC-5789 */
    'PATCH',
    'PURGE'
  ].sort();
}
METHODS = METHODS.map(function(method) { return method.toLowerCase(); });

function Router(opts) {
  var self = this,
      env = process.env.NODE_ENV || 'development';

  // the route handler stack contains two possible kinds of objects:
  //   1. A normal route handler containing:
  //
  //          id - This is an integer that uniquely identifies this handler in
  //               this router instance. This value allows us to efficiently
  //               check if a particular route handler's path was previously
  //               matched.
  //          re - This is a regular expression to use in matching the request's
  //               url.
  //        keys - This is an array containing the names of placeholders in
  //               the handler's regular expression. This is used to help
  //               populate req.$.params.
  //     handler - This is a function in the case of a non-method-specific
  //               middleware function, or an object containing the method
  //               and the handler function.
  //
  //   2. A "pointer" route handler. This is a special route handler that shares
  //      a path regexp and param keys with another route handler. It contains:
  //
  //      parent - This is a reference to the previous route handler with whom
  //               the regexp and param keys are shared.
  //     handler - This is the same format as normal route handlers.
  this._stack = [];

  this._paramFuncs = {};
  this._mounted = false;
  this._rc = 0;

  this.path = '/';
  this.locals = Object.create(null);
  this.config = {
    'etag': 'weak',
    'env': env,
    'query parser': 'extended',
    'subdomain offset': 2,
    'trust proxy': false,
    'view': View,
    'views': resolve('views'),
    'jsonp callback name': 'callback',
    'view cache': (env === 'production')
  };
  this.handler = function routerHandlerWrap(req, res, cb) {
    self.handle(req, res, cb);
  };
}

Router.prototype.handle = function handle(req, res, cb) {
  var stack = this._stack,
      stacklen = stack.length,
      paramFuncs = this._paramFuncs,
      paramsCache = {},
      paramPtr = 0,
      matched = {},
      ptr = 0,
      req$,
      res$;

  req$ = req.$;
  res$ = res.$;

  // req/res decorations
  if (!req$) {
    var urlpath = req.url,
        qm = urlpath.indexOf('?'),
        path_,
        query_;
    if (qm > -1) {
      path_ = urlpath.substring(0, qm);
      if (qm === urlpath.length - 1)
        query_ = {};
      else
        query_ = qsparse(urlpath.substring(qm + 1));
    } else {
      path_ = urlpath;
      query_ = {};
    }

    req$ = req.$ = new Request(this, req, res, next, path_, query_);
    res$ = res.$ = new Response(this, req, res);
  }
  // end req/res decorations

  var pathname = req$.path;

  function next(err) {
    var method = req.method,
        handler,
        parent,
        curmw,
        error,
        keys,
        ret,
        m;

    if (err !== undefined && err !== null) {
      if (isError(err))
        error = err;
      else if (typeof err === 'string') {
        error = new Error(err);
        error.code = err.code || this.DEFAULT_ERROR_STATUS;
      } else if (typeof err === 'number' && err >= 400 && err < 600) {
        error = new Error(method
                          + ' '
                          + pathname
                          + ' '
                          + (STATUS_CODES[err] || 'Unknown Error'));
        error.code = err;
      } else {
        error = new Error(method + ' ' + pathname + ' Unknown Error');
        error.code = this.DEFAULT_ERROR_STATUS;
      }
    }

    for (; ptr < stacklen; ++ptr, paramPtr = 0) {
      curmw = stack[ptr];
      handler = curmw.handler;
      if (curmw.parent
          && (typeof handler === 'function' || handler.method === method)) {
        // optimized route handler
        parent = curmw.parent;
        if ((m = matched[parent.id]) === undefined)
          continue;
        curmw = parent;
      } else if (m = curmw.re.exec(pathname)) {
        // normal route handler
        matched[curmw.id] = m;
      } else
        continue;

      if (method === handler.method)
        handler = handler.handler;

      var handlerLen = handler.length;
      if (!((error === undefined && handlerLen < 4)
            || (error !== undefined && handlerLen === 4)))
        continue;

      // check for `router.param('foo', cb);` calls before calling route handler
      if ((keys = curmw.keys).length) {
        var resrc = curmw.re.toString(),
            hadError = false,
            paramKeys,
            params,
            lenk,
            pk,
            fn;

        req$.params = params = (paramsCache[resrc]
                                || (paramsCache[resrc] = makeParams(m, keys)));
        paramKeys = Object.keys(params);
        for (lenk = paramKeys.length, fn; paramPtr < lenk; ++paramPtr) {
          pk = paramKeys[paramPtr];
          if (fn = paramFuncs[pk]) {
            ++paramPtr;
            ret = execParam(fn, req, res, next, params[pk]);
            if (ret && isError(ret)) {
              if (!error)
                hadError = true;
              error = ret;
              break;
            }
            return;
          }
        }
        if (hadError)
          continue;
      } else
        req$.params = EMPTY_PARAMS;

      // increment route handler index in case current handler immediately
      // calls next()
      ++ptr;

      ret = execHandler(handler, req, res, next, error);
      if (ret && isError(ret)) {
        error = ret;
        // reset route handler index otherwise the next handler will be skipped
        // because of our previous, early increment
        --ptr;
        continue;
      }
      return;
    }

    if (ptr === stacklen) {
      if (cb)
        cb(error);
      else if (error === undefined)
        res.end(method + ' ' + pathname + ' ' + STATUS_CODES[res.statusCode = 404]);
      else {
        res.statusCode = error.code;
        res.end(error.message);
      }
    }
  }

  next();
};

Router.prototype.DEFAULT_ERROR_STATUS = 500;

function execHandler(handler, req, res, next, error) {
  try {
    if (error === undefined)
      handler(req, res, next);
    else
      handler(error, req, res, next);
  } catch (ex) {
    console.error('!!! route handler exception:');
    console.error(ex.stack);
    ex.code = 500;
    return ex;
  }
}

function execParam(fn, req, res, next, val) {
  try {
    fn(req, res, next, val);
  } catch (ex) {
    console.error('!!! route param exception:');
    console.error(ex.stack);
    ex.code = 500;
    return ex;
  }
}

Router.prototype.use = function use(route) {
  var handlersEnd = arguments.length,
      handlersStart = 1,
      routes = route,
      method;

  // check for method-specific route, this is only the case when use() is called
  // internally from router.VERB()
  if (arguments.length > 2
      && typeof arguments[arguments.length - 1] === 'string') {
    method = arguments[arguments.length - 1].toUpperCase();
    --handlersEnd;
  }

  // match all routes by default when no path is given as the first argument
  if (typeof route === 'function' || route instanceof Router) {
    handlersStart = 0;
    routes = ROUTES_MATCH_ALL;
  }

  if (!Array.isArray(routes))
    routes = [ routes ];

  for (var i = 0, len = routes.length, fullPath, regex, keys; i < len; ++i) {
    keys = [];
    if (isRegExp(routes[i])) {
      fullPath = this.path;
      if (this.config['concat regex'] !== false)
        routes[i] = concatRegex(this.path, routes[i]);
      regex = pathRegex(routes[i], keys);
    } else if (routes === ROUTES_MATCH_ALL) {
      fullPath = this.path;
      regex = pathRegex(fullPath, keys, { end: false });
    } else {
      fullPath = urljoin(this.path, routes[i]);
      regex = pathRegex(fullPath, keys);
    }

    for (var h = handlersStart; h < handlersEnd; ++h) {
      var handlerfn = arguments[h],
          handlerRegex = regex;
      if (typeof handlerfn !== 'function' && !(handlerfn instanceof Router))
        continue;
      else if (handlerfn instanceof Router) {
        if (handlerfn._mounted)
          throw new Error('Cannot mount a Router in multiple places');
        if (routes !== ROUTES_MATCH_ALL && !isRegExp(routes[i]))
          handlerRegex = pathRegex(fullPath, keys, { end: false });
        handlerfn.path = fullPath;
        handlerfn.config = this.config;
        handlerfn._mounted = true;
        handlerfn = handlerfn.handler;
      }

      if (!keys.length)
        keys = NO_KEYS;

      this._stack.push({
        id: this._rc++,
        re: handlerRegex,
        keys: keys,
        handler: (method ? { method: method, handler: handlerfn } : handlerfn)
      });
    }
  }

  this.optimize();

  return this;
};

Router.prototype.all = Router.prototype.use;

METHODS.forEach(function(method) {
  Router.prototype[method] = function methodRoute() {
    var args = new Array(arguments.length + 1);
    for (var i = 0, len = args.length; i < len; ++i)
      args[i] = arguments[i];
    args[i] = method;
    return this.use.apply(this, args);
  };
});

Router.prototype.param = function param(name, cb) {
  if (typeof name !== 'string')
    throw new Error('Param name is required');
  if (typeof cb === 'function')
    this._paramFuncs[name] = cb;
};

Router.prototype.route = function route(path, opts) {
  var router = new Router(opts);
  this.use(path, router);
  return router;
};

// optimize() finds route handlers with the same path and converts all but the
// first to "pointers" to the first. This allows us to skip repeated regexp
// execution, allowing for a significant speedup and more consistent performance
// when many route handlers for the same path are added
Router.prototype.optimize = function optimize() {
  var stack = this._stack,
      len = stack.length,
      layer,
      i;

  for (i = 0; i < len; ++i) {
    layer = stack[i];
    if (!layer.re) {
      // skip optimized handlers
      continue;
    }
    // check for a previous route handler that has the same path
    for (var p = 0; p < i; ++p) {
      if (!stack[p].re) {
        // again, skip optimized handlers
        continue;
      }
      if (regexpsEqual(layer.re, stack[p].re)) {
        // we found a previous route handler we can point to
        stack[i] = {
          parent: stack[p],
          handler: stack[i].handler
        };
      }
    }
  }
};

module.exports = Router;

function decodeParam(val) {
  if (typeof val !== 'string')
    return val;

  try {
    return decodeURIComponent(val);
  } catch (e) {
    return false;
  }
}

function makeParams(m, keys) {
  if (!keys)
    return (m && m.length > 1 && m.slice(1, m.length));
  else if (!m || (m && m.length <= 1))
    return;

  var params = {};

  for (var i = 1, len = m.length, key, val; i < len; ++i) {
    key = keys[i - 1];
    val = decodeParam(m[i]);
    if (val === false)
      params[key.name] = val;
    else
      params[key.name] = m[i];
  }

  return params;
}

function regexpsEqual(re1, re2) {
  return (re1.source === re2.source
          && re1.global === re2.global
          && re1.ignoreCase === re2.ignoreCase
          && re1.multiline === re2.multiline);
}

function concatRegex(path, re) {
  var base = path,
      resrc = re.source.replace(/\\/g, ''),
      flags = '';

  // strip off ending '/' because most people will most likely start regexps
  // with a '/'
  if (base.substr(-1) === '/')
    base = base.slice(0, -1);

  if (resrc[0] === '^')
    resrc = resrc.slice(1);

  if (re.global)
    flags += 'g';
  if (re.ignoreCase)
    flags += 'i';
  if (re.multiline)
    flags += 'm';

  return new RegExp('^' + base + resrc, flags);
}<|endoftext|>