<commit_before>/*global d3 */

// construct query string
var params = d3.select("#search-form");

if (!params.empty()) {
  var site_url = params.attr('data-site-url');
  var search_url = params.attr('data-search-url');
  var page = getParameterByName('page');
  if (page === null) { page = 1; }
  var query = getParameterByName('query');
  var tag = getParameterByName('tag');

  var query_url = encodeURI(search_url + "/pages?page=" + page);
  if (query !== null) { query_url += "&query=" + query; }
  if (tag !== null) { query_url += "&tag=" + tag; }
}

// load the data from the DataCite Labs API API
if (query_url) {
  d3.json(query_url)
    .get(function(error, json) {
      if (error) { return console.warn(error); }
      searchResult(json);

      json.meta["page"] = page;
      json.meta["total_pages"] = Math.ceil(json.meta.total/25);
      paginate(json, "#content");
  });
}

// add search results to page
function searchResult(json) {
  data = json.data;
  meta = json.meta;

  d3.select("#q").property("value", q);

  json.href = "?page={{number}}";
  if (q !== null) { json.href += "&q=" + q; }
  if (tag !== null) { json.href += "&tag=" + tag; }

  if (typeof data === "undefined" || data.length === 0) {
    d3.select("#content").text("")
      .insert("div")
      .attr("class", "alert alert-info")
      .text("No blog posts found.");
    return;
  }

  if (json.meta.total > 1) {
    d3.select("#content").insert("h4")
      .attr("class", "results")
      .text(numberWithDelimiter(json.meta.total) + " Posts");
  }

  for (var i=0; i<data.length; i++) {
    var post = data[i];
    post["url"] = post.id.replace(/https:\/\/blog.datacite.org/, site_url);

    d3.select("#content").insert("div")
      .attr("class", "panel panel-default post-list")
      .attr("id", "panel-" + i).insert("div")
      .attr("class", "panel-body post-content")
      .attr("id", "panel-body-" + i);

    d3.select("#panel-body-" + i).append("header")
      .attr("class", "post-header")
      .append("h2")
      .attr("class", "post-title")
      .append("a")
      .attr("href", function() { return post.url; })
      .text(post.attributes.title);
    d3.select("#panel-body-" + i).append("section")
      .attr("class", "post-excerpt")
      .attr("itemprop", "description").insert("p")
      .html(post.attributes.description);

    d3.select("#panel-" + i).insert("div")
      .attr("class", "panel-footer")
      .attr("id", "panel-footer-" + i).append("span")
      .attr("class", "meta").append("time")
      .attr("datetime", post.attributes.issued)
      .text(formattedDate(post.attributes.issued.substring(0, 10)));

    d3.select("#panel-footer-" + i).insert("span")
      .attr("class", "meta")
      .text(formattedAuthorList(post.attributes.author));

    d3.select("#panel-footer-" + i).insert("a")
      .attr("href", function() { return post.url + "#disqus_thread"; })
      .text("0 comments");

    var t = post.attributes.tags;
    if (typeof t !== "undefined" && t.length > 0)  {
      d3.select("#panel-footer-" + i).insert("span")
        .attr("class", "tags pull-right").append("i")
        .attr("class", "fa fa-tags");
      for (var j=0; j<t.length; j++) {
        d3.select("#panel-footer-" + i + " .tags").append("a")
          .attr("href", function() { return "/index.html?tag=" + t[j]; })
          .text((j + 1 < t.length) ? t[j] + "," : t[j]);
      }
    }
  }

  // convert tags object to array for sorting
  tags = Object.keys(meta.tags);

  if (typeof tags !== "undefined" && tags.length > 0) {
    tags.sort(function (a, b) {
      if (meta.tags[a] > meta.tags[b]) {
        return -1;
      }
      if (meta.tags[a] < meta.tags[b]) {
        return 1;
      }
      // a must be equal to b
      return 0;
    });

    d3.select("#tags")
      .classed("panel facets", true).insert("div")
      .attr("class", "panel-body").insert("h4")
      .text("Tags");

    d3.select("#tags .panel-body").insert("ul");

    for (k = 0; k<tags.length; k++) {
      var key = tags[k];
      if (tag === key) {
        d3.select("#tags .panel-body ul").insert("li")
          .append("a")
          .attr("href", function() { return "/index.html"; }).insert("i")
          .attr("class", "fa fa-check-square-o");
      } else {
        d3.select("#tags .panel-body ul").insert("li")
          .append("a")
          .attr("href", function() { return "/index.html?tag=" + key; }).insert("i")
          .attr("class", "fa fa-square-o");
      }
      d3.select("#tags .panel-body ul li:last-child").insert("span")
        .text(key);
      d3.select("#tags .panel-body ul li:last-child").insert("span")
        .attr("class", "number pull-right")
        .text(meta.tags[key]);

    }
  }
}
<commit_msg>use correct name for query parameter<commit_after>/*global d3 */

// construct query string
var params = d3.select("#search-form");

if (!params.empty()) {
  var site_url = params.attr('data-site-url');
  var search_url = params.attr('data-search-url');
  var page = getParameterByName('page');
  if (page === null) { page = 1; }
  var query = getParameterByName('query');
  var tag = getParameterByName('tag');

  var query_url = encodeURI(search_url + "/pages?page=" + page);
  if (query !== null) { query_url += "&query=" + query; }
  if (tag !== null) { query_url += "&tag=" + tag; }
}

// load the data from the DataCite Labs API API
if (query_url) {
  d3.json(query_url)
    .get(function(error, json) {
      if (error) { return console.warn(error); }
      searchResult(json);

      json.meta["page"] = page;
      json.meta["total_pages"] = Math.ceil(json.meta.total/25);
      paginate(json, "#content");
  });
}

// add search results to page
function searchResult(json) {
  data = json.data;
  meta = json.meta;

  d3.select("#q").property("value", q);

  json.href = "?page={{number}}";
  if (query !== null) { json.href += "&query=" + query; }
  if (tag !== null) { json.href += "&tag=" + tag; }

  if (typeof data === "undefined" || data.length === 0) {
    d3.select("#content").text("")
      .insert("div")
      .attr("class", "alert alert-info")
      .text("No blog posts found.");
    return;
  }

  if (json.meta.total > 1) {
    d3.select("#content").insert("h4")
      .attr("class", "results")
      .text(numberWithDelimiter(json.meta.total) + " Posts");
  }

  for (var i=0; i<data.length; i++) {
    var post = data[i];
    post["url"] = post.id.replace(/https:\/\/blog.datacite.org/, site_url);

    d3.select("#content").insert("div")
      .attr("class", "panel panel-default post-list")
      .attr("id", "panel-" + i).insert("div")
      .attr("class", "panel-body post-content")
      .attr("id", "panel-body-" + i);

    d3.select("#panel-body-" + i).append("header")
      .attr("class", "post-header")
      .append("h2")
      .attr("class", "post-title")
      .append("a")
      .attr("href", function() { return post.url; })
      .text(post.attributes.title);
    d3.select("#panel-body-" + i).append("section")
      .attr("class", "post-excerpt")
      .attr("itemprop", "description").insert("p")
      .html(post.attributes.description);

    d3.select("#panel-" + i).insert("div")
      .attr("class", "panel-footer")
      .attr("id", "panel-footer-" + i).append("span")
      .attr("class", "meta").append("time")
      .attr("datetime", post.attributes.issued)
      .text(formattedDate(post.attributes.issued.substring(0, 10)));

    d3.select("#panel-footer-" + i).insert("span")
      .attr("class", "meta")
      .text(formattedAuthorList(post.attributes.author));

    d3.select("#panel-footer-" + i).insert("a")
      .attr("href", function() { return post.url + "#disqus_thread"; })
      .text("0 comments");

    var t = post.attributes.tags;
    if (typeof t !== "undefined" && t.length > 0)  {
      d3.select("#panel-footer-" + i).insert("span")
        .attr("class", "tags pull-right").append("i")
        .attr("class", "fa fa-tags");
      for (var j=0; j<t.length; j++) {
        d3.select("#panel-footer-" + i + " .tags").append("a")
          .attr("href", function() { return "/index.html?tag=" + t[j]; })
          .text((j + 1 < t.length) ? t[j] + "," : t[j]);
      }
    }
  }

  // convert tags object to array for sorting
  tags = Object.keys(meta.tags);

  if (typeof tags !== "undefined" && tags.length > 0) {
    tags.sort(function (a, b) {
      if (meta.tags[a] > meta.tags[b]) {
        return -1;
      }
      if (meta.tags[a] < meta.tags[b]) {
        return 1;
      }
      // a must be equal to b
      return 0;
    });

    d3.select("#tags")
      .classed("panel facets", true).insert("div")
      .attr("class", "panel-body").insert("h4")
      .text("Tags");

    d3.select("#tags .panel-body").insert("ul");

    for (k = 0; k<tags.length; k++) {
      var key = tags[k];
      if (tag === key) {
        d3.select("#tags .panel-body ul").insert("li")
          .append("a")
          .attr("href", function() { return "/index.html"; }).insert("i")
          .attr("class", "fa fa-check-square-o");
      } else {
        d3.select("#tags .panel-body ul").insert("li")
          .append("a")
          .attr("href", function() { return "/index.html?tag=" + key; }).insert("i")
          .attr("class", "fa fa-square-o");
      }
      d3.select("#tags .panel-body ul li:last-child").insert("span")
        .text(key);
      d3.select("#tags .panel-body ul li:last-child").insert("span")
        .attr("class", "number pull-right")
        .text(meta.tags[key]);

    }
  }
}
<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var
	mongoose = require('mongoose'),
	extend = require('mongoose-schema-extend'),
	Schema = mongoose.Schema;

/**
 * Layer schema
 */

var LayerSchema = new Schema({
	title: { type: String, required: true },
	description: String,
	creator: {type: Schema.ObjectId, ref: 'User'},
	features: [{type: Schema.ObjectId, ref: 'Feature'}],
	contents: [{type: Schema.ObjectId, ref: 'Content'}],
	createdAt: {type: Date, default: Date.now},
	updateAt: {type: Date, default: Date.now},
	tags: [String],
	visibility: { type: String, enum: ['Public', 'Visible', 'Private'], default: 'Private'},
	isDraft: {type: Boolean, default: true},
	type: { type: String, enum: ['FeatureLayer', 'TileLayer'], default: 'FeatureLayer'},
	url: String
}, { collection : 'Layer', discriminatorKey : '_type' })

/**
 * Statics
 */

LayerSchema.statics = {

	/**
	 * Find layer by id
	 *
	 * @param {ObjectId} id
	 * @param {Function} cb
	 * @api private
	 */

	load: function (id, cb) {
		this.findOne({ _id : id })
			.populate('creator')
			.populate('features')
			.populate('contents')
			.exec(cb)
	},

	/**
	 * List layers
	 *
	 * @param {Object} options
	 * @param {Function} cb
	 * @api private
	 */

	list: function (options, cb) {
		var criteria = options.criteria || {}

		this.find(criteria)
			.sort({'createdAt': -1}) // sort by date
			.limit(options.perPage)
			.skip(options.perPage * options.page)
		.exec(cb)
	}

}

mongoose.model('Layer', LayerSchema)

/**
 * TileLayer schema
 */

var TileLayerSchema = LayerSchema.extend({
	url: String
})

mongoose.model('TileLayer', LayerSchema)<commit_msg>fix layer model<commit_after>
/**
 * Module dependencies
 */

var
	mongoose = require('mongoose'),
	extend = require('mongoose-schema-extend'),
	Schema = mongoose.Schema;

/**
 * Layer schema
 */

var LayerSchema = new Schema({
	title: { type: String, required: true },
	description: String,
	creator: {type: Schema.ObjectId, ref: 'User'},
	features: [{type: Schema.ObjectId, ref: 'Feature'}],
	contents: [{type: Schema.ObjectId, ref: 'Content'}],
	createdAt: {type: Date, default: Date.now},
	updateAt: {type: Date, default: Date.now},
	tags: [String],
	visibility: { type: String, enum: ['Public', 'Visible', 'Private'], default: 'Private'},
	isDraft: {type: Boolean, default: true},
	type: { type: String, enum: ['FeatureLayer', 'TileLayer'], default: 'FeatureLayer'},
	url: String
});

/**
 * Statics
 */

LayerSchema.statics = {

	/**
	 * Find layer by id
	 *
	 * @param {ObjectId} id
	 * @param {Function} cb
	 * @api private
	 */

	load: function (id, cb) {
		this.findOne({ _id : id })
			.populate('creator')
			.populate('features')
			.populate('contents')
			.exec(cb)
	},

	/**
	 * List layers
	 *
	 * @param {Object} options
	 * @param {Function} cb
	 * @api private
	 */

	list: function (options, cb) {
		var criteria = options.criteria || {}

		this.find(criteria)
			.sort({'createdAt': -1}) // sort by date
			.limit(options.perPage)
			.skip(options.perPage * options.page)
		.exec(cb)
	}

}

mongoose.model('Layer', LayerSchema)<|endoftext|>


<commit_before>var app = angular.module('hpsa-client');

app.constant('AppSettings', {
   apiUrl: 'http://expense-api.52.38.36.91.xip.io',
})
<commit_msg>fixing url<commit_after>var app = angular.module('hpsa-client');

app.constant('AppSettings', {
   apiUrl: 'http://expense-api.52.38.36.91.xip.io/',
})
<|endoftext|>


<commit_before>Pillar = {} || Pillar;

Pillar.superOf = function(clazz)
{
    return clazz.constructor.__super__;
}

Pillar.extendEvents = function(view) {
    view.events = _.extend({}, Pillar.superOf(view).events, view.events);
}

Pillar.Templates = {
    templates: {},

    register: function(name, html)
    {
        this.templates[name] = html;
    },

    get: function(name)
    {
        return this.templates[name];
    },

    smartRegister: function(name)
    {
        this.register(name, Pillar.Templates.template( $("#_" + name + "_template").html() ));
    },

    evalIfFunction: function(obj, attr)
    {
        if (typeof obj[attr] == "function")
        {
            return obj[attr]();
        } else if (typeof obj.get(attr) == "string")
        {
            return obj.get(attr);
        } else {
            return "";
        }
    },

    getPlaceholder: function(string)
    {
        return "{{"+string+"}}";
    },

    // TODO: optimisation where we return a function from there, that concats string fragments and an attrs object using .join
    // Need to split final HTML string at {{tokens}};
    compileTemplate: function($template)
    {
        var $html = $template.clone();
        var $elems = $html.find("[data-link]").toArray();

        if (typeof $html.attr("data-link") != "undefined")
        {
            $elems.unshift($html);
        }

        _.each($elems, function(elem)
        {
            var $el = $(elem);
            var links = $el.attr("data-link");
            var toLink = links.split(",");

            $.each(toLink, function(index, property)
            {
                var trimmed = property.trim();
                var splitTerm = trimmed.split("<-");
                var attr = splitTerm[0];
                var valueName = splitTerm[1];

                var specialAttrs = {
                    "extraClass": function($el) {
                        $el.addClass(Pillar.Templates.getPlaceholder(valueName));
                    },
                    "visible": function($el) {
                        $el.attr("data-visible", Pillar.Templates.getPlaceholder(valueName));
                    },
                    "hidden": function($el) {
                        $el.attr("data-hidden", Pillar.Templates.getPlaceholder(valueName));
                    },
                    "text": function($el) {
                        $el.html(Pillar.Templates.getPlaceholder(valueName));
                    }
                };

                if (attr in specialAttrs)
                {
                    specialAttrs[attr]($el);
                } else {
                    $el.attr(attr, Pillar.Templates.getPlaceholder(valueName));
                }
            });

            $el.removeAttr("data-link");
            $el.attr("data-linked", "true");
        });

        var finalString = $html.clone().wrap("<div/>").parent().html();

        var reNoGroup = /\{\{[^}}]+\}\}/g;
        var re = /\{\{([^}}]+)\}\}/g;
        var parts = finalString.split(reNoGroup);
        var matches = finalString.match(re);

        var f = function (attrs)
        {
            var sBuild = [];
            // console.log(parts);
            for (var i = 0; i < parts.length; i++)
            {
                sBuild.push(parts[i]);
                if (i < matches.length)
                {
                    var cleanMatchName = matches[i].replace("{{", "").replace("}}", "");
                    // console.log(matches[i], Pillar.Templates.evalIfFunction(attrs, cleanMatchName));
                    sBuild.push(Pillar.Templates.evalIfFunction(attrs, cleanMatchName));
                }
            }

            return sBuild.join("")
        }

        return f;
    },

    template: function(html)
    {
        return this.compileTemplate($(html));
    },

    renderTemplate: function(template, attrs)
    {
        return template(attrs);
    }
}

Pillar.View = Backbone.View.extend({
    initialize: function(opts)
    {
        Pillar.superOf(this).initialize(opts);
        Pillar.extendEvents(this);

        if (opts)
        {
            if ("template" in opts)
            {
                this.template = opts.template;
            }
        }

        this.init(opts);
    },

    renderTemplate: Pillar.Templates.renderTemplate,

    _super: function()
    {
        return Pillar.superOf(this).initialize(opts);
    },

    // init is called up the extension stack the way one might expect
    init: function(opts)
    {

    },

    // Render is wrapped to always return this, and provide easy hookins for before and
    // after drawing
    render: function()
    {
        this.beforeDraw();
        this.draw();
        this.afterDraw();
        return this;
    },

    draw: function() {},
    beforeDraw: function() {},
    afterDraw: function() {}
});

Pillar.CollectionView = Pillar.View.extend({
    init: function(opts)
    {
        this.views = [];
    },

    // Remove all existing views from the collection view,
    // since we are about to redraw all of the child views.
    beforeDraw: function()
    {
        _.each(this.views, function(view)
        {
            view.remove();
        });

        this.views = [];
    },

    // Iterate over all models in the collection, drawing each individual model.
    // Shortcut for manually looping over the models.
    // If more control is needed, override draw and this will not be called.
    drawCollection: function(model)
    {

    },

    draw: function()
    {
        this.collection.each(this.drawCollection, this);
    }
});

Pillar.BaseTestView = Pillar.View.extend({

    init: function(opts)
    {
        console.log("Base INIT");
    },

    events: {
        "click": "helloWorld"
    },

    helloWorld: function(e)
    {
        console.log("Hello World");
    }
});

Pillar.TestView = Pillar.BaseTestView.extend({

    init: function(opts)
    {
        console.log("INIT");
    },

    events: {
        "click .all": "whatUp"
    },

    whatUp: function(e)
    {
        console.log("What Up!");
    }
});

Pillar.ExtendedTextView = Pillar.TestView.extend({
    init: function(opts)
    {
        console.log("Child INIT");
    }
});

// jQuery helper
$("script.js-template").each( function() {
    var niceName = $(this).attr("id").replace("_template", "").replace("_", "");
    Pillar.Templates.smartRegister(niceName);
});

<commit_msg>Helper method for replacing DOM elements for views<commit_after>Pillar = {} || Pillar;

Pillar.superOf = function(clazz)
{
    return clazz.constructor.__super__;
}

Pillar.extendEvents = function(view) {
    view.events = _.extend({}, Pillar.superOf(view).events, view.events);
}

Pillar.Templates = {
    templates: {},

    register: function(name, html)
    {
        this.templates[name] = html;
    },

    get: function(name)
    {
        return this.templates[name];
    },

    smartRegister: function(name)
    {
        this.register(name, Pillar.Templates.template( $("#_" + name + "_template").html() ));
    },

    evalIfFunction: function(obj, attr)
    {
        if (typeof obj[attr] == "function")
        {
            return obj[attr]();
        } else if (typeof obj.get(attr) == "string")
        {
            return obj.get(attr);
        } else {
            return "";
        }
    },

    getPlaceholder: function(string)
    {
        return "{{"+string+"}}";
    },

    // TODO: optimisation where we return a function from there, that concats string fragments and an attrs object using .join
    // Need to split final HTML string at {{tokens}};
    compileTemplate: function($template)
    {
        var $html = $template.clone();
        var $elems = $html.find("[data-link]").toArray();

        if (typeof $html.attr("data-link") != "undefined")
        {
            $elems.unshift($html);
        }

        _.each($elems, function(elem)
        {
            var $el = $(elem);
            var links = $el.attr("data-link");
            var toLink = links.split(",");

            $.each(toLink, function(index, property)
            {
                var trimmed = property.trim();
                var splitTerm = trimmed.split("<-");
                var attr = splitTerm[0];
                var valueName = splitTerm[1];

                var specialAttrs = {
                    "extraClass": function($el) {
                        $el.addClass(Pillar.Templates.getPlaceholder(valueName));
                    },
                    "visible": function($el) {
                        $el.attr("data-visible", Pillar.Templates.getPlaceholder(valueName));
                    },
                    "hidden": function($el) {
                        $el.attr("data-hidden", Pillar.Templates.getPlaceholder(valueName));
                    },
                    "text": function($el) {
                        $el.html(Pillar.Templates.getPlaceholder(valueName));
                    }
                };

                if (attr in specialAttrs)
                {
                    specialAttrs[attr]($el);
                } else {
                    $el.attr(attr, Pillar.Templates.getPlaceholder(valueName));
                }
            });

            $el.removeAttr("data-link");
            $el.attr("data-linked", "true");
        });

        var finalString = $html.clone().wrap("<div/>").parent().html();

        var reNoGroup = /\{\{[^}}]+\}\}/g;
        var re = /\{\{([^}}]+)\}\}/g;
        var parts = finalString.split(reNoGroup);
        var matches = finalString.match(re);

        var f = function (attrs)
        {
            var sBuild = [];
            // console.log(parts);
            for (var i = 0; i < parts.length; i++)
            {
                sBuild.push(parts[i]);
                if (i < matches.length)
                {
                    var cleanMatchName = matches[i].replace("{{", "").replace("}}", "");
                    // console.log(matches[i], Pillar.Templates.evalIfFunction(attrs, cleanMatchName));
                    sBuild.push(Pillar.Templates.evalIfFunction(attrs, cleanMatchName));
                }
            }

            return sBuild.join("")
        }

        return f;
    },

    template: function(html)
    {
        return this.compileTemplate($(html));
    },

    renderTemplate: function(template, attrs)
    {
        return template(attrs);
    }
}

Pillar.View = Backbone.View.extend({
    initialize: function(opts)
    {
        Pillar.superOf(this).initialize(opts);
        Pillar.extendEvents(this);

        if (opts)
        {
            if ("template" in opts)
            {
                this.template = opts.template;
            }
        }

        this.init(opts);
    },

    replaceElement: function(html)
    {
        var $oldEl = this.$el;
        this.setElement(html);
        $oldEl.replaceWith(this.$el);
    },

    renderTemplate: Pillar.Templates.renderTemplate,

    _super: function()
    {
        return Pillar.superOf(this).initialize(opts);
    },

    // init is called up the extension stack the way one might expect
    init: function(opts)
    {

    },

    // Render is wrapped to always return this, and provide easy hookins for before and
    // after drawing
    render: function()
    {
        this.beforeDraw();
        this.draw();
        this.afterDraw();
        return this;
    },

    draw: function() {},
    beforeDraw: function() {},
    afterDraw: function() {}
});

Pillar.CollectionView = Pillar.View.extend({
    init: function(opts)
    {
        this.views = [];
    },

    // Remove all existing views from the collection view,
    // since we are about to redraw all of the child views.
    beforeDraw: function()
    {
        _.each(this.views, function(view)
        {
            view.remove();
        });

        this.views = [];
    },

    // Iterate over all models in the collection, drawing each individual model.
    // Shortcut for manually looping over the models.
    // If more control is needed, override draw and this will not be called.
    drawCollection: function(model)
    {

    },

    draw: function()
    {
        this.collection.each(this.drawCollection, this);
    }
});

Pillar.BaseTestView = Pillar.View.extend({

    init: function(opts)
    {
        console.log("Base INIT");
    },

    events: {
        "click": "helloWorld"
    },

    helloWorld: function(e)
    {
        console.log("Hello World");
    }
});

Pillar.TestView = Pillar.BaseTestView.extend({

    init: function(opts)
    {
        console.log("INIT");
    },

    events: {
        "click .all": "whatUp"
    },

    whatUp: function(e)
    {
        console.log("What Up!");
    }
});

Pillar.ExtendedTextView = Pillar.TestView.extend({
    init: function(opts)
    {
        console.log("Child INIT");
    }
});

// jQuery helper
$("script.js-template").each( function() {
    var niceName = $(this).attr("id").replace("_template", "").replace("_", "");
    Pillar.Templates.smartRegister(niceName);
});

<|endoftext|>


<commit_before>jQuery(function() {
  // Initialize lunr with the fields to be searched, plus the boost.
  window.idx = lunr(function () {
    this.field('id');
    this.field('title');
    this.field('content', { boost: 10 });
    this.field('author');
    this.field('tags');
  });

 var base_url =  document.getElementById('base_url').getAttribute("value")
  // Get the generated search_data.json file so lunr.js can search it locally.
  window.data = $.getJSON(base_url + '/search_data/index.html');

  // Wait for the data to load and add it to lunr
  window.data.then(function(loaded_data){
    $.each(loaded_data, function(index, value){
      window.idx.add(
        $.extend({ "id": index }, value)
      );
    });
  });

  // Event when the form is submitted
  $("#site_search").submit(function(event){
      event.preventDefault();
      var query = $("#search_box").val(); // Get the value for the text field
      var results = window.idx.search(query); // Get lunr to perform a search
      display_search_results(results); // Hand the results off to be displayed
  });

  function display_search_results(results) {
    var $search_results = $("#search_results");

    // Wait for data to load
    window.data.then(function(loaded_data) {

      // Are there any results?
      if (results.length) {
        $search_results.empty(); // Clear any old results

        // Iterate over the results
        results.forEach(function(result) {
          var item = loaded_data[result.ref];

          // Build a snippet of HTML for this result
          var appendString = '
' + item.title + '
';

          // Add the snippet to the collection of results.
          $search_results.append(appendString);
        });
      } else {
        // If there are no results, let the user know.
        $search_results.html('
No results found.
Please check spelling, spacing, etc....
');
      }
    });
  }
});
<commit_msg>Fixed search redirects<commit_after>jQuery(function() {
  // Initialize lunr with the fields to be searched, plus the boost.
  window.idx = lunr(function () {
    this.field('id');
    this.field('title');
    this.field('content', { boost: 10 });
    this.field('author');
    this.field('tags');
  });

 var base_url =  document.getElementById('base_url').getAttribute("value")
  // Get the generated search_data.json file so lunr.js can search it locally.
  window.data = $.getJSON(base_url + '/search_data/index.html');

  // Wait for the data to load and add it to lunr
  window.data.then(function(loaded_data){
    $.each(loaded_data, function(index, value){
      window.idx.add(
        $.extend({ "id": index }, value)
      );
    });
  });

  // Event when the form is submitted
  $("#site_search").submit(function(event){
      event.preventDefault();
      var query = $("#search_box").val(); // Get the value for the text field
      var results = window.idx.search(query); // Get lunr to perform a search
      display_search_results(results); // Hand the results off to be displayed
  });

  function display_search_results(results) {
    var $search_results = $("#search_results");

    // Wait for data to load
    window.data.then(function(loaded_data) {

      // Are there any results?
      if (results.length) {
        $search_results.empty(); // Clear any old results

        // Iterate over the results
        results.forEach(function(result) {
          var item = loaded_data[result.ref];

          // Build a snippet of HTML for this result
          var appendString = '
' + item.title + '
';

          // Add the snippet to the collection of results.
          $search_results.append(appendString);
        });
      } else {
        // If there are no results, let the user know.
        $search_results.html('
No results found.
Please check spelling, spacing, etc....
');
      }
    });
  }
});
<|endoftext|>


<commit_before>/**
 * Movie Model
 * Interacts with the database to retrieve movie data
 * @copyright 2013 Jeremy Worboys
 */

module.exports.define = function(db) {

    return db.define('movie', {
        title:          String,
        classification: 'enum',
        coverurl:       String,
        synopsis:       String,
        runtime:        Number,
        director:       String
    }, {
        table: 'movies'
    });

};

module.exports.associations = function(db) {

    db.models.movie.hasMany('cinemas', db.models.cinema, {}, { mergeTable: 'cinema_movies' });

};
<commit_msg>Define enum values in model<commit_after>/**
 * Movie Model
 * Interacts with the database to retrieve movie data
 * @copyright 2013 Jeremy Worboys
 */

module.exports.define = function(db) {

    return db.define('movie', {
        title:          String,
        classification: { type: 'enum', values: ['CTC', 'G', 'PG', 'M', 'MA15+', 'R18+'] },
        coverurl:       String,
        synopsis:       String,
        runtime:        Number,
        director:       String
    }, {
        table: 'movies'
    });

};

module.exports.associations = function(db) {

    db.models.movie.hasMany('cinemas', db.models.cinema, {}, { mergeTable: 'cinema_movies' });

};
<|endoftext|>


<commit_before>const appActions = require('../../js/actions/appActions')
const debounce = require('../../js/lib/debounce')
const messages = require('../../js/constants/messages')
const Immutable = require('immutable')
const tabState = require('../common/state/tabState')
const {app, extensions} = require('electron')
const { makeImmutable } = require('../common/state/immutableUtil')

let currentWebContents = {}

const cleanupWebContents = (tabId) => {
  if (currentWebContents[tabId]) {
    delete currentWebContents[tabId]
    setImmediate(() => {
      appActions.tabClosed({ tabId })
    })
  }
}

const getTabValue = function (tabId) {
  let tab = api.getWebContents(tabId)
  if (tab) {
    let tabValue = makeImmutable(tab.tabValue())
    tabValue = tabValue.set('canGoBack', tab.canGoBack())
    tabValue = tabValue.set('canGoForward', tab.canGoForward())
    return tabValue.set('tabId', tabId)
  }
}

const tabUpdated = debounce(appActions.tabUpdated.bind(appActions), 5)

const updateTab = (tabId) => {
  let tabValue = getTabValue(tabId)
  if (tabValue) {
    setImmediate(() => {
      tabUpdated(tabValue)
    })
  }
}

const api = {
  init: (state, action) => {
    process.on('open-url-from-tab', (e, source, targetUrl, disposition) => {
      api.create({
        url: targetUrl,
        openerTabId: source.getId(),
        active: disposition !== 'background-tab'
      })
    })

    process.on('add-new-contents', (e, source, newTab, disposition, size, userGesture) => {
      if (userGesture === false) {
        e.preventDefault()
        return
      }

      let location = newTab.getURL()
      if (!location || location === '') {
        location = 'about:blank'
      }

      const openerTabId = !source.isDestroyed() ? source.getId() : -1
      let newTabValue = getTabValue(newTab.getId())
      let index
      if (newTabValue && newTabValue.get('index') !== -1) {
        index = newTabValue.get('index')
      }

      // TODO(bridiver) - handle pinned property?? - probably through tabValue
      const frameOpts = {
        location,
        partition: newTab.session.partition,
        guestInstanceId: newTab.guestInstanceId,
        openerTabId,
        disposition,
        index
      }

      if (disposition === 'new-window' || disposition === 'new-popup') {
        const windowOpts = makeImmutable(size)
        appActions.newWindow(makeImmutable(frameOpts), windowOpts)
      } else {
        let hostWebContents = source.hostWebContents || source
        hostWebContents.send(messages.SHORTCUT_NEW_FRAME, location, { frameOpts })
      }
    })

    process.on('chrome-tabs-updated', (e, tabId) => {
      updateTab(tabId)
    })

    process.on('chrome-tabs-removed', (e, tabId) => {
      cleanupWebContents(tabId)
    })

    app.on('web-contents-created', function (event, tab) {
      if (tab.isBackgroundPage() || !tab.isGuest()) {
        return
      }
      let tabId = tab.getId()
      tab.once('destroyed', cleanupWebContents.bind(null, tabId))
      tab.once('crashed', cleanupWebContents.bind(null, tabId))
      tab.once('close', cleanupWebContents.bind(null, tabId))
      tab.on('set-active', function (evt, active) {
        updateTab(tabId)
      })
      tab.on('set-tab-index', function (evt, index) {
        updateTab(tabId)
      })
      tab.on('page-favicon-updated', function (e, favicons) {
        if (favicons && favicons.length > 0) {
          // tab.setTabValues({
          //   faviconUrl: favicons[0]
          // })
          // updateTab(tabId)
        }
      })
      tab.on('unresponsive', () => {
        console.log('unresponsive')
      })
      tab.on('responsive', () => {
        console.log('responsive')
      })
      tab.on('did-attach', () => {
        updateTab(tabId)
      })
      tab.on('did-detach', () => {
        updateTab(tabId)
      })
      tab.on('page-title-updated', function () {
        updateTab(tabId)
      })
      tab.on('did-fail-load', function () {
        updateTab(tabId)
      })
      tab.on('did-fail-provisional-load', function () {
        updateTab(tabId)
      })
      tab.on('did-stop-loading', function () {
        updateTab(tabId)
      })
      tab.on('navigation-entry-commited', function (evt, url) {
        updateTab(tabId)
      })
      tab.on('did-navigate', function (evt, url) {
        updateTab(tabId)
      })
      tab.on('did-navigate-in-page', function (evt, url, isMainFrame) {
        updateTab(tabId)
      })
      tab.on('load-start', function (evt, url, isMainFrame, isErrorPage) {
        if (isMainFrame) {
          updateTab(tabId)
        }
      })
      tab.on('did-finish-load', function () {
        updateTab(tabId)
      })

      currentWebContents[tabId] = tab
      let tabValue = getTabValue(tabId)
      if (tabValue) {
        setImmediate(() => {
          appActions.tabCreated(tabValue)
        })
      }
    })

    process.on('on-tab-created', (tab, options) => {
      if (tab.isDestroyed()) {
        return
      }

      if (options.index !== undefined) {
        tab.setTabIndex(options.index)
      }

      tab.once('did-attach', () => {
        if (options.back) {
          tab.goBack()
        } else if (options.forward) {
          tab.goForward()
        }
      })
    })

    return state
  },

  sendToAll: (...args) => {
    for (let tabId in currentWebContents) {
      const tab = currentWebContents[tabId]
      try {
        if (tab && !tab.isDestroyed()) {
          tab.send(...args)
        }
      } catch (e) {
        // ignore exceptions
      }
    }
  },

  getWebContents: (tabId) => {
    return currentWebContents[tabId]
  },

  toggleDevTools: (state, action) => {
    action = makeImmutable(action)
    const tabId = action.get('tabId')
    const tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      if (tab.isDevToolsOpened()) {
        tab.closeDevTools()
      } else {
        tab.openDevTools()
      }
    }
    return state
  },

  setAudioMuted: (state, action) => {
    action = makeImmutable(action)
    let frameProps = action.get('frameProps')
    let muted = action.get('muted')
    let tabId = frameProps.get('tabId')
    let tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      tab.setAudioMuted(muted)
      let tabValue = getTabValue(tabId)
      return tabState.updateTab(state, { tabValue })
    }
    return state
  },

  clone: (state, action) => {
    action = makeImmutable(action)
    const tabId = action.get('tabId')
    let options = action.get('options') || Immutable.Map()
    let tabValue = getTabValue(tabId)
    if (tabValue && tabValue.get('index') !== undefined) {
      options = options.set('index', tabValue.get('index') + 1)
    }
    const tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      tab.clone(options.toJS(), (newTab) => {
      })
    }
    return state
  },

  closeTab: (state, action) => {
    action = makeImmutable(action)
    let tabId = action.get('tabId')
    let tab = api.getWebContents(tabId)
    try {
      if (!tab.isDestroyed()) {
        tab.close()
      }
    } catch (e) {
      // ignore
    }
    return tabState.removeTabByTabId(state, tabId)
  },

  create: (createProperties, cb = null) => {
    createProperties = makeImmutable(createProperties).toJS()
    extensions.createTab(createProperties, (tab) => {
      cb && cb(tab)
    })
  }
}

module.exports = api
<commit_msg>add api for running scripts in a background process auditors @mrose17 @bbondy<commit_after>const appActions = require('../../js/actions/appActions')
const config = require('../../js/constants/config')
const debounce = require('../../js/lib/debounce')
const messages = require('../../js/constants/messages')
const Immutable = require('immutable')
const tabState = require('../common/state/tabState')
const {app, BrowserWindow, extensions} = require('electron')
const { makeImmutable } = require('../common/state/immutableUtil')

let currentWebContents = {}

const cleanupWebContents = (tabId) => {
  if (currentWebContents[tabId]) {
    delete currentWebContents[tabId]
    setImmediate(() => {
      appActions.tabClosed({ tabId })
    })
  }
}

const getTabValue = function (tabId) {
  let tab = api.getWebContents(tabId)
  if (tab) {
    let tabValue = makeImmutable(tab.tabValue())
    tabValue = tabValue.set('canGoBack', tab.canGoBack())
    tabValue = tabValue.set('canGoForward', tab.canGoForward())
    return tabValue.set('tabId', tabId)
  }
}

const tabUpdated = debounce(appActions.tabUpdated.bind(appActions), 5)

const updateTab = (tabId) => {
  let tabValue = getTabValue(tabId)
  if (tabValue) {
    setImmediate(() => {
      tabUpdated(tabValue)
    })
  }
}

const api = {
  init: (state, action) => {
    process.on('open-url-from-tab', (e, source, targetUrl, disposition) => {
      api.create({
        url: targetUrl,
        openerTabId: source.getId(),
        active: disposition !== 'background-tab'
      })
    })

    process.on('add-new-contents', (e, source, newTab, disposition, size, userGesture) => {
      if (userGesture === false) {
        e.preventDefault()
        return
      }

      let location = newTab.getURL()
      if (!location || location === '') {
        location = 'about:blank'
      }

      const openerTabId = !source.isDestroyed() ? source.getId() : -1
      let newTabValue = getTabValue(newTab.getId())
      let index
      if (newTabValue && newTabValue.get('index') !== -1) {
        index = newTabValue.get('index')
      }

      // TODO(bridiver) - handle pinned property?? - probably through tabValue
      const frameOpts = {
        location,
        partition: newTab.session.partition,
        guestInstanceId: newTab.guestInstanceId,
        openerTabId,
        disposition,
        index
      }

      if (disposition === 'new-window' || disposition === 'new-popup') {
        const windowOpts = makeImmutable(size)
        appActions.newWindow(makeImmutable(frameOpts), windowOpts)
      } else {
        let hostWebContents = source.hostWebContents || source
        hostWebContents.send(messages.SHORTCUT_NEW_FRAME, location, { frameOpts })
      }
    })

    process.on('chrome-tabs-updated', (e, tabId) => {
      updateTab(tabId)
    })

    process.on('chrome-tabs-removed', (e, tabId) => {
      cleanupWebContents(tabId)
    })

    app.on('web-contents-created', function (event, tab) {
      if (tab.isBackgroundPage() || !tab.isGuest()) {
        return
      }
      let tabId = tab.getId()
      tab.once('destroyed', cleanupWebContents.bind(null, tabId))
      tab.once('crashed', cleanupWebContents.bind(null, tabId))
      tab.once('close', cleanupWebContents.bind(null, tabId))
      tab.on('set-active', function (evt, active) {
        updateTab(tabId)
      })
      tab.on('set-tab-index', function (evt, index) {
        updateTab(tabId)
      })
      tab.on('page-favicon-updated', function (e, favicons) {
        if (favicons && favicons.length > 0) {
          // tab.setTabValues({
          //   faviconUrl: favicons[0]
          // })
          // updateTab(tabId)
        }
      })
      tab.on('unresponsive', () => {
        console.log('unresponsive')
      })
      tab.on('responsive', () => {
        console.log('responsive')
      })
      tab.on('did-attach', () => {
        updateTab(tabId)
      })
      tab.on('did-detach', () => {
        updateTab(tabId)
      })
      tab.on('page-title-updated', function () {
        updateTab(tabId)
      })
      tab.on('did-fail-load', function () {
        updateTab(tabId)
      })
      tab.on('did-fail-provisional-load', function () {
        updateTab(tabId)
      })
      tab.on('did-stop-loading', function () {
        updateTab(tabId)
      })
      tab.on('navigation-entry-commited', function (evt, url) {
        updateTab(tabId)
      })
      tab.on('did-navigate', function (evt, url) {
        updateTab(tabId)
      })
      tab.on('did-navigate-in-page', function (evt, url, isMainFrame) {
        updateTab(tabId)
      })
      tab.on('load-start', function (evt, url, isMainFrame, isErrorPage) {
        if (isMainFrame) {
          updateTab(tabId)
        }
      })
      tab.on('did-finish-load', function () {
        updateTab(tabId)
      })

      currentWebContents[tabId] = tab
      let tabValue = getTabValue(tabId)
      if (tabValue) {
        setImmediate(() => {
          appActions.tabCreated(tabValue)
        })
      }
    })

    process.on('on-tab-created', (tab, options) => {
      if (tab.isDestroyed()) {
        return
      }

      if (options.index !== undefined) {
        tab.setTabIndex(options.index)
      }

      tab.once('did-attach', () => {
        if (options.back) {
          tab.goBack()
        } else if (options.forward) {
          tab.goForward()
        }
      })
    })

    return state
  },

  sendToAll: (...args) => {
    for (let tabId in currentWebContents) {
      const tab = currentWebContents[tabId]
      try {
        if (tab && !tab.isDestroyed()) {
          tab.send(...args)
        }
      } catch (e) {
        // ignore exceptions
      }
    }
  },

  getWebContents: (tabId) => {
    return currentWebContents[tabId]
  },

  toggleDevTools: (state, action) => {
    action = makeImmutable(action)
    const tabId = action.get('tabId')
    const tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      if (tab.isDevToolsOpened()) {
        tab.closeDevTools()
      } else {
        tab.openDevTools()
      }
    }
    return state
  },

  setAudioMuted: (state, action) => {
    action = makeImmutable(action)
    let frameProps = action.get('frameProps')
    let muted = action.get('muted')
    let tabId = frameProps.get('tabId')
    let tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      tab.setAudioMuted(muted)
      let tabValue = getTabValue(tabId)
      return tabState.updateTab(state, { tabValue })
    }
    return state
  },

  clone: (state, action) => {
    action = makeImmutable(action)
    const tabId = action.get('tabId')
    let options = action.get('options') || Immutable.Map()
    let tabValue = getTabValue(tabId)
    if (tabValue && tabValue.get('index') !== undefined) {
      options = options.set('index', tabValue.get('index') + 1)
    }
    const tab = api.getWebContents(tabId)
    if (tab && !tab.isDestroyed()) {
      tab.clone(options.toJS(), (newTab) => {
      })
    }
    return state
  },

  closeTab: (state, action) => {
    action = makeImmutable(action)
    let tabId = action.get('tabId')
    let tab = api.getWebContents(tabId)
    try {
      if (!tab.isDestroyed()) {
        tab.close()
      }
    } catch (e) {
      // ignore
    }
    return tabState.removeTabByTabId(state, tabId)
  },

  create: (createProperties, cb = null) => {
    createProperties = makeImmutable(createProperties).toJS()
    extensions.createTab(createProperties, (tab) => {
      cb && cb(tab)
    })
  },

  executeScriptInBackground: (script, cb) => {
    const win = new BrowserWindow({
      show: false
    })
    win.webContents.on('did-finish-load', (e) => {
      win.webContents.executeScriptInTab(config.braveExtensionId, script, {}, (err, url, result) => {
        win.close()
        cb(err, url, result)
      })
    })
    win.loadURL('about:blank')
  }
}

module.exports = api
<|endoftext|>


<commit_before>const fs = require('fs-extra');
const pkgUp = require('pkg-up');
const getDistTags = require('./fetch-dist-tags');
const calcDistTag = require('./calc-dist-tag');

module.exports = function autoDistTag(cwd) {
  let pkgPath, pkg;

  return pkgUp(cwd)
    .then(_pkgPath => (pkgPath = _pkgPath))
    .then(() => fs.readJson(pkgPath))
    .then(_pkg => (pkg = _pkg))
    .then(() => getDistTags(pkg.name))
    .then(distTags => calcDistTag(pkg.version, distTags));
};
<commit_msg>auto-dist-tag: Add "write" option<commit_after>const fs = require('fs-extra');
const pkgUp = require('pkg-up');
const getDistTags = require('./fetch-dist-tags');
const calcDistTag = require('./calc-dist-tag');

module.exports = function autoDistTag(cwd, options) {
  let pkgPath, pkg, tags, tag;

  return pkgUp(cwd)
    .then(_pkgPath => (pkgPath = _pkgPath))
    .then(() => fs.readJson(pkgPath))
    .then(_pkg => (pkg = _pkg))
    .then(() => getDistTags(pkg.name))
    .then(_tags => (tags = _tags))
    .then(() => calcDistTag(pkg.version, tags))
    .then(_tag => (tag = _tag))
    .then(() => {
      if (options && options.write) {
        pkg.publishConfig = pkg.publishConfig || {};
        pkg.publishConfig.tag = pkg.publishConfig.tag || tag;
      }
      return fs.writeJson(pkgPath, pkg, { spaces: 2 });
    })
    .then(() => tag);
};
<|endoftext|>


<commit_before>const ExifImage = require('exif').ExifImageg

try {
    new ExifImage({ image : 'test_images/test3.jpg' }, function (error, exifData) {
        if (error)
            console.log('Error: '+error.message)
        else
            console.log(exifData)
    });
} catch (error) {
    console.log('Error: ' + error.message)
}<commit_msg>extract image created date for all image files<commit_after>const fs = require('fs')
const ExifImage = require('exif').ExifImage
const testFolder = './test_images/'

const file_names = []

fs.readdirSync(testFolder).forEach(file => {
	new ExifImage({ image : testFolder + file }, function (error, exifData) {
        if (error) console.log(error)
        else {
        	const exif = exifData.exif
        	const image_data = exifData.image

        	const image_timespan = exif.DateTimeOriginal || exif.CreateDate || image_data.ModifyDate

            console.log(image_timespan)
        }
    })
})
<|endoftext|>


<commit_before>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/*.js',
			'app/Controller/*.js',
			'app/Controller/GraphsControllers/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/Controllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<commit_msg>fix karma.conf.js<commit_after>'use strict';

// Karma
module.exports = function(config) {
	config.set({

		// base path that will be used to resolve all patterns (eg. files, exclude)
		basePath: '',

		// frameworks to use
		// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
		frameworks: ['jasmine'],

		// list of files / patterns to load in the browser
		files: [
			//'app/bower_components/jquery/jquery.js',
			'app/bower_components/angular/angular.js',
			'app/bower_components/angular-mocks/angular-*.js',
			'app/bower_components/angular-route/angular-route.js',
			'app/bower_components/angular-resource/angular-resource.js',
			'bower_components/angular-socket-io/socket.js',
			'bower_components/socket.io-client/socket.io.js',
			'bower_components/angular-socket-io/mock/socket-io.js',
			'app/bower_components/angular-socket.io-mock.js',
			/*'app/bower_components/angular-sanitize/angular-sanitize.js',
			'app/bower_components/angular-cookies/angular-cookies.js',*/
			//'app/bower_components/underscore/underscore.js',
			'app/norris-nrti.js',
			'app/Model/*.js',
			'app/Model/GraphsModel/*.js',
			'app/Model/FlowsModel/*.js',
			'app/Model/PagesModel/*.js',
			'app/View/*.js',
			'app/Controller/*.js',
			'app/Controller/GraphsControllers/*.js',
			'test/unit/Model/*.js',
			'test/unit/Controller/*.js',
			'test/unit/View/*.js'
		],

		// list of files to exclude
		exclude: [

		],

		// preprocess matching files before serving them to the browser
		// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
		preprocessors: {
			// source files, that you wanna generate coverage for
			// do not include tests or libraries
			// (these files will be instrumented by Istanbul)
			'app/app.js': ['coverage'],
			'app/Model/*.js': ['coverage'],
			'app/Model/GraphsModel/*.js': ['coverage'],
			'app/Model/FlowsModel/*.js': ['coverage'],
			'app/Model/PagesModel/*.js': ['coverage'],
			'app/View/*.js': ['coverage'],
			'app/Controllers/*.js': ['coverage']
		},

		// test results reporter to use
		// possible values: 'dots', 'progress'
		// available reporters: https://npmjs.org/browse/keyword/karma-reporter
		reporters: ['progress', 'junit', 'coverage'],

		junitReporter: {
			outputFile: '../gh-pages/reports-frontend/test-result.xml'
		},

		// configure the reporter
		coverageReporter: {
			dir: '../gh-pages/reports-frontend/coverage/',
			reporters:[
		        {
		        	type: 'text',
		        	subdir: '.',
		        	file: 'text.txt'
		        },
		        {
		        	type: 'text-summary',
		        	subdir: '.',
		        	file: 'text-summary.txt'
		        },
				{
					type: 'lcov',
					subdir: '.',
					suite: 'frontend'
				}
			],
		},

		// web server port
		port: 15077,

		// enable / disable colors in the output (reporters and logs)
		colors: true,

		// level of logging
		// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
		logLevel: config.LOG_DEBUG,

		// enable / disable watching file and executing tests whenever any file changes
		autoWatch: false,

		// start these browsers
		// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
		browsers: ['PhantomJS'],

		// Continuous Integration mode
		// if true, Karma captures browsers, runs the tests and exits
		singleRun: true
	});
};<|endoftext|>


<commit_before>//设置页面固定显示宽度320;
setDeviceW();
function setDeviceW(){
	var w = $(window);
	var h = w.height();
	var g = w.width();
	var e , d;
	if(g / h >= 320 / 486 ){
		f = h / 486;
		d = (g / f - 320) / 2;
	}else{
		f = g / 320;
		e = (h / f - 486) / 2;
	}
	$("#eqMobileViewport").attr("content", "width=320, initial-scale=" + f + ", maximum-scale=" + f + ", user-scalable=no");
	e && $(".edit_area").css({marginTop: e});
	d && $(".edit_area").css({marginLeft: d});
};		<commit_msg>for pc and mobile<commit_after>function to_pc(){
	var system ={};
	    var p = navigator.platform;
	    system.win = p.indexOf("Win") == 0;
	    system.mac = p.indexOf("Mac") == 0;
	    system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
	    if(system.win||system.mac||system.xll){//电脑
	    	window.location.href = 'www.boluomi1314.com/demo/pc/web_share.html?pid='+ getQueryString(pid);
	    }else{
	    	//设置页面固定显示宽度320;
	    	setDeviceW();
	    }
}

//见设备是PC的  跳转到 www.boluomi1314.com/demo/pc/web_share.html?pid=18
to_pc();

function setDeviceW(){
	var w = $(window);
	var h = w.height();
	var g = w.width();
	var e , d;
	if(g / h >= 320 / 486 ){
		f = h / 486;
		d = (g / f - 320) / 2;
	}else{
		f = g / 320;
		e = (h / f - 486) / 2;
	}
	$("#eqMobileViewport").attr("content", "width=320, initial-scale=" + f + ", maximum-scale=" + f + ", user-scalable=no");
	e && $(".edit_area").css({marginTop: e});
	d && $(".edit_area").css({marginLeft: d});
};

function getQueryString(key){
   var search=window.location.search.substring(1).split('&');
   for (var i=0; i<search.length; i++){
       if (search[i].split('=')[0]==key){
           return decodeURI(search[i].split('=')[1]);
       }
   }
   return undefined;
}

<|endoftext|>


<commit_before>// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};
<commit_msg>use chrome headless for unit tests<commit_after>// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client:{
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    customLaunchers: {
      ChromeHeadless: {
        base: 'Chrome',
        flags: [
          '--headless',
          '--disable-gpu',
          // Without a remote debugging port, Google Chrome exits immediately.
          '--remote-debugging-port=9222',
        ],
      }
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, 'coverage'), reports: [ 'html', 'lcovonly' ],
      fixWebpackSourcePaths: true
    },
    angularCli: {
      environment: 'dev'
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['ChromeHeadless'],
    singleRun: false
  });
};
<|endoftext|>


<commit_before>import mongoose from 'mongoose'
import { capitalize } from '../lib/utils'

const Schema = mongoose.Schema

function reduce (data) {
  return data['BUTIKEROMBUD']['BUTIKOMBUD']
}

function transformOpeningHours (str) {
  let list = str ? str.split(/;;;0?\-?;/g) : []

  list = list.filter(str => {
    return str.length > 0
  })

  const formattedStr = list.map(str => {
    let opening = str.replace('_*', '').split(';')
    const [day, openingHours, closingHours] = opening

    return `${day} ${openingHours}-${closingHours}`
  })

  return formattedStr
}

function transformLabels (str) {
  const labels = str.split(';')

  return labels.map(capitalize)
}

function transformPhone (str) {
  return str.replace(/[\/\-]|\s/g, '')
}

const mapping = {
  NR: 'nr',
  NAMN: 'name',
  TYP: 'type',
  ADDRESS1: 'address',
  ADDRESS2: 'additional_address',
  ADDRESS3: 'zip_code',
  ADDRESS4: 'city',
  ADDRESS5: 'county',
  TELEFON: 'phone',
  BUTIKSTYP: 'shop_type',
  TJANSTER: 'services',
  SOKORD: 'labels',
  OPPETTIDER: 'opening_hours',
  RT90X: 'RT90x',
  RT90Y: 'RT90y'
}

const Store = new Schema({
  nr: { type: String, index: true, required: true },
  name: { type: String, index: true, default: '' },
  type: { type: String, index: true, default: '' },
  address: { type: String, index: true, default: '' },
  additional_address: { type: String, index: true, default: '' },
  zip_code: { type: String, index: true, default: '' },
  city: { type: String, index: true, default: '', set: capitalize },
  county: { type: String, index: true, default: '' },
  phone: { type: String, index: true, default: '', set: transformPhone },
  shop_type: { type: String, index: true, default: '' },
  services: { type: String, index: true, default: '' },
  labels: { type: Array, index: true, default: [], set: transformLabels },
  opening_hours: { type: Array, index: true, default: [], set: transformOpeningHours },
  RT90x: { type: Number, index: true, default: null },
  RT90y: { type: Number, index: true, default: null }
})

mongoose.model('Store', Store)

export { mapping, reduce }
<commit_msg>Adding empty check for label array<commit_after>import mongoose from 'mongoose'
import { capitalize } from '../lib/utils'

const Schema = mongoose.Schema

function reduce (data) {
  return data['BUTIKEROMBUD']['BUTIKOMBUD']
}

function transformOpeningHours (str) {
  let list = str ? str.split(/;;;0?\-?;/g) : []

  list = list.filter(str => {
    return str.length > 0
  })

  const formattedStr = list.map(str => {
    let opening = str.replace('_*', '').split(';')
    const [day, openingHours, closingHours] = opening

    return `${day} ${openingHours}-${closingHours}`
  })

  return formattedStr
}

function transformLabels (str) {
  const labels = str.split(';')

  return labels
    .filter(Boolean)
    .map(capitalize)
}

function transformPhone (str) {
  return str.replace(/[\/\-]|\s/g, '')
}

const mapping = {
  NR: 'nr',
  NAMN: 'name',
  TYP: 'type',
  ADDRESS1: 'address',
  ADDRESS2: 'additional_address',
  ADDRESS3: 'zip_code',
  ADDRESS4: 'city',
  ADDRESS5: 'county',
  TELEFON: 'phone',
  BUTIKSTYP: 'shop_type',
  TJANSTER: 'services',
  SOKORD: 'labels',
  OPPETTIDER: 'opening_hours',
  RT90X: 'RT90x',
  RT90Y: 'RT90y'
}

const Store = new Schema({
  nr: { type: String, index: true, required: true },
  name: { type: String, index: true, default: '' },
  type: { type: String, index: true, default: '' },
  address: { type: String, index: true, default: '' },
  additional_address: { type: String, index: true, default: '' },
  zip_code: { type: String, index: true, default: '' },
  city: { type: String, index: true, default: '', set: capitalize },
  county: { type: String, index: true, default: '' },
  phone: { type: String, index: true, default: '', set: transformPhone },
  shop_type: { type: String, index: true, default: '' },
  services: { type: String, index: true, default: '' },
  labels: { type: Array, index: true, default: [], set: transformLabels },
  opening_hours: { type: Array, index: true, default: [], set: transformOpeningHours },
  RT90x: { type: Number, index: true, default: null },
  RT90y: { type: Number, index: true, default: null }
})

mongoose.model('Store', Store)

export { mapping, reduce }
<|endoftext|>


<commit_before>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, question, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = question;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + Tools.escapeHTML(this.question) + '
';
		this.options.forEach((option, number) => {
			output += '
<button class="button" style="padding:2px 6px;font-size:11px;text-align:left" value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + Tools.escapeHTML(option.name) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + Tools.escapeHTML(this.question) + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + Tools.escapeHTML(i.value[1].name) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.commands = {
	poll: {
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");
			let params = target.split(target.includes('|') ? '|' : ',').map(param => param.trim());

			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");

			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");
			let options = [];

			for (let i = 1; i < params.length; i++) {
				options.push(params[i]);
			}

			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, params[0], options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};
<commit_msg>Poll: Add support for HTML in questions<commit_after>/*
* Poll chat plugin
* By bumbadadabum and Zarel.
*/

'use strict';

class Poll {
	constructor(room, questionData, options) {
		if (room.pollNumber) {
			room.pollNumber++;
		} else {
			room.pollNumber = 1;
		}
		this.room = room;
		this.question = questionData.source;
		this.supportHTML = questionData.supportHTML;
		this.voters = {};
		this.voterIps = {};
		this.totalVotes = 0;
		this.timeout = null;
		this.timeoutMins = 0;

		this.options = new Map();
		for (let i = 0; i < options.length; i++) {
			this.options.set(i + 1, {name: options[i], votes: 0});
		}
	}

	vote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You have already voted for this poll.");
		}

		this.voters[userid] = option;
		this.voterIps[ip] = option;
		this.options.get(option).votes++;
		this.totalVotes++;

		this.update();
	}

	blankvote(user, option) {
		let ip = user.latestIp;
		let userid = user.userid;

		if (userid in this.voters || ip in this.voterIps) {
			return user.sendTo(this.room, "You're already looking at the results.");
		} else {
			this.voters[userid] = 0;
			this.voterIps[ip] = 0;
		}

		this.updateTo(user);
	}

	generateVotes() {
		let output = '

 Poll ' + this.getQuestionMarkup() + '
';
		this.options.forEach((option, number) => {
			output += '
<button value="/poll vote ' + number + '" name="send" title="Vote for ' + number + '. ' + Tools.escapeHTML(option.name) + '">' + number + '. ' + Tools.escapeHTML(option.name) + '</button>
';
		});
		output += '
<button value="/poll results" name="send" title="View results - you will not be able to vote after viewing results">(View results)</button>
';
		output += '
';

		return output;
	}

	generateResults(ended, option) {
		let icon = ' ' + (ended ? "Poll ended" : "Poll") + '';
		let output = '

' + icon + ' ' + this.getQuestionMarkup() + '
';
		let iter = this.options.entries();

		let i = iter.next();
		let c = 0;
		let colors = ['#79A', '#8A8', '#88B'];
		while (!i.done) {
			let percentage = Math.round((i.value[1].votes * 100) / (this.totalVotes || 1));
			output += '
' + i.value[0] + '. ' + (i.value[0] === option ? '' : '') + Tools.escapeHTML(i.value[1].name) + (i.value[0] === option ? '' : '') + ' (' + i.value[1].votes + ' vote' + (i.value[1].votes === 1 ? '' : 's') + ')
 ' + percentage + '%
';
			i = iter.next();
			c++;
		}
		if (option === 0 && !ended) output += '
(You can\'t vote after viewing results)
';
		output += '
';

		return output;
	}

	getQuestionMarkup() {
		if (this.supportHTML) return this.question;
		return Tools.escapeHTML(this.question);
	}

	update() {
		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		// Update the poll results for everyone that has voted
		for (let i in this.room.users) {
			let user = this.room.users[i];
			if (user.userid in this.voters) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voters[user.userid]]);
			} else if (user.latestIp in this.voterIps) {
				user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + results[this.voterIps[user.latestIp]]);
			}
		}
	}

	updateTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	updateFor(user) {
		if (user.userid in this.voters) {
			user.sendTo(this.room, '|uhtmlchange|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		}
	}

	display() {
		let votes = this.generateVotes();

		let results = [];

		for (let i = 0; i <= this.options.size; i++) {
			results.push(this.generateResults(false, i));
		}

		for (let i in this.room.users) {
			let thisUser = this.room.users[i];
			if (thisUser.userid in this.voters) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voters[thisUser.userid]]);
			} else if (thisUser.latestIp in this.voterIps) {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + results[this.voterIps[thisUser.latestIp]]);
			} else {
				thisUser.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + votes);
			}
		}
	}

	displayTo(user, connection) {
		if (!connection) connection = user;
		if (user.userid in this.voters) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voters[user.userid]));
		} else if (user.latestIp in this.voterIps) {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateResults(false, this.voterIps[user.latestIp]));
		} else {
			connection.sendTo(this.room, '|uhtml|poll' + this.room.pollNumber + '|' + this.generateVotes());
		}
	}

	onConnect(user, connection) {
		this.displayTo(user, connection);
	}

	end() {
		let results = this.generateResults(true);

		this.room.send('|uhtmlchange|poll' + this.room.pollNumber + '|
(The poll has ended – scroll down to see the results)
');
		this.room.add('|html|' + results);
	}
}

exports.commands = {
	poll: {
		htmlcreate: 'new',
		create: 'new',
		new: function (target, room, user, connection, cmd, message) {
			if (!target) return this.parse('/help poll new');
			if (target.length > 1024) return this.errorReply("Poll too long.");
			let params = target.split(target.includes('|') ? '|' : ',').map(param => param.trim());
			const supportHTML = cmd === 'htmlcreate';

			if (!this.can('minigame', null, room)) return false;
			if (supportHTML && !this.can('declare', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (room.poll) return this.errorReply("There is already a poll in progress in this room.");
			if (params.length < 3) return this.errorReply("Not enough arguments for /poll new.");

			const questionSource = supportHTML ? this.canHTML(params[0]) : params[0];
			if (!questionSource) return;

			let options = [];

			for (let i = 1; i < params.length; i++) {
				options.push(params[i]);
			}

			if (options.length > 8) {
				return this.errorReply("Too many options for poll (maximum is 8).");
			}

			room.poll = new Poll(room, {source: params[0], supportHTML: supportHTML}, options);
			room.poll.display();

			this.logEntry("" + user.name + " used " + message);
			return this.privateModCommand("(A poll was started by " + user.name + ".)");
		},
		newhelp: ["/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~"],

		vote: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!target) return this.parse('/help poll vote');

			if (target === 'blank') {
				room.poll.blankvote(user);
				return;
			}

			let parsed = parseInt(target);
			if (isNaN(parsed)) return this.errorReply("To vote, specify the number of the option.");

			if (!room.poll.options.has(parsed)) return this.sendReply("Option not in poll.");

			room.poll.vote(user, parsed);
		},
		votehelp: ["/poll vote [number] - Votes for option [number]."],

		timer: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			if (target) {
				if (!this.can('minigame', null, room)) return false;
				if (target === 'clear') {
					if (!room.poll.timeout) return this.errorReply("There is no timer to clear.");
					clearTimeout(room.poll.timeout);
					room.poll.timeout = null;
					room.poll.timeoutMins = 0;
					return this.add("The poll timer was turned off.");
				}
				let timeout = parseFloat(target);
				if (isNaN(timeout) || timeout <= 0 || timeout > 0x7FFFFFFF) return this.errorReply("Invalid time given.");
				if (room.poll.timeout) clearTimeout(room.poll.timeout);
				room.poll.timeoutMins = timeout;
				room.poll.timeout = setTimeout(() => {
					room.poll.end();
					delete room.poll;
				}, (timeout * 60000));
				room.add("The poll timer was turned on: the poll will end in " + timeout + " minute(s).");
				return this.privateModCommand("(The poll timer was set to " + timeout + " minute(s) by " + user.name + ".)");
			} else {
				if (!this.runBroadcast()) return;
				if (room.poll.timeout) {
					return this.sendReply("The poll timer is on and will end in " + room.poll.timeoutMins + " minute(s).");
				} else {
					return this.sendReply("The poll timer is off.");
				}
			}
		},
		timerhelp: ["/poll timer [minutes] - Sets the poll to automatically end after [minutes] minutes. Requires: % @ # & ~", "/poll timer clear - Clears the poll's timer. Requires: % @ # & ~"],

		results: function (target, room, user) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");

			return room.poll.blankvote(user);
		},
		resultshelp: ["/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this."],

		close: 'end',
		stop: 'end',
		end: function (target, room, user) {
			if (!this.can('minigame', null, room)) return false;
			if (!this.canTalk()) return this.errorReply("You cannot do this while unable to talk.");
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (room.poll.timeout) clearTimeout(room.poll.timeout);

			room.poll.end();
			delete room.poll;
			return this.privateModCommand("(The poll was ended by " + user.name + ".)");
		},
		endhelp: ["/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],

		show: 'display',
		display: function (target, room, user, connection) {
			if (!room.poll) return this.errorReply("There is no poll running in this room.");
			if (!this.runBroadcast()) return;
			room.update();

			if (this.broadcasting) {
				room.poll.display();
			} else {
				room.poll.displayTo(user, connection);
			}
		},
		displayhelp: ["/poll display - Displays the poll"],

		'': function (target, room, user) {
			this.parse('/help poll');
		},
	},
	pollhelp: ["/poll allows rooms to run their own polls. These polls are limited to one poll at a time per room.",
				"Accepts the following commands:",
				"/poll create [question], [option1], [option2], [...] - Creates a poll. Requires: % @ # & ~",
				"/poll vote [number] - Votes for option [number].",
				"/poll timer [minutes] - Sets the poll to automatically end after [minutes]. Requires: % @ # & ~",
				"/poll results - Shows the results of the poll without voting. NOTE: you can't go back and vote after using this.",
				"/poll display - Displays the poll",
				"/poll end - Ends a poll and displays the results. Requires: % @ # & ~"],
};
<|endoftext|>