<commit_before>;
/*!
 * Tarsier JavaScript Library v1.0.1
 * http://github.com/moky/Tarsier/
 *
 * Includes jquery.js
 * http://jquery.com/
 *
 * Copyright 2013 moKy at slanissue.com
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2013-12-11 T10:43Z
 */

/**
 *
 *  main
 *
 *  Author: moKy @ Dec. 11, 2013
 *
 */

if (typeof(window.tarsier) != "object") {
	window.tarsier = {
		version: "1.0.1"
	};
}

// base functions
(function(tarsier) {

	////////////////////////////////////////////////////////////////////////////
	//
	//  namespace: base
	//
	tarsier.base = {
		// importing tasks
		importings: []
	};

	/**
	 *  class: Task
	 */
	tarsier.base.Task = function(args) {
		this.url = args.url;
		this.type = args.type;
		this.callback = args.callback;
		return this;
	};

	// finished current task
	tarsier.base.Task.prototype.finished = function() {
		tarsier.base.importings.shift(); // remove current task
		if (tarsier.base.importings.length > 0) {
			tarsier.base.importings[0].run(); // run next task
		} else {
			tarsier.events.onload(); // all tasks finished
		}
		return this;
	};

	// import js
	tarsier.base.Task.prototype.js = function() {
		var task = this;
		var doc = task.document || window.document;

		var script = doc.createElement("script");
		if (script) {
			script.type = "text/javascript";
			script.src = task.url;

			// callback
			script.onload = function() {
				try {
					if (task.callback) task.callback();
				} catch(e) {
					alert("[Tarsier] callback error: " + e);
				}
				task.finished();
			}
			script.onreadystatechange = function() { // IE
				if (this.readyState == "complete") {
					script.onload();
				}
			}

			// load
			var head = doc.getElementsByTagName("head");
			if (head) {
				head.item(0).appendChild(script);
			}
		}
		return this;
	};

	// import css
	tarsier.base.Task.prototype.css = function() {
		var task = this;
		var doc = task.document || window.document;

		var link = doc.createElement("link");
		if (link) {
			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = task.url;

			// load
			var head = doc.getElementsByTagName("head");
			if (head) {
				head.item(0).appendChild(link);
			}
		}

		task.finished();
		return this;
	};

	// run a task
	tarsier.base.Task.prototype.run = function() {
		if (this.type == "text/javascript") {
			this.js();
		} else if (this.type == "text/css") {
			this.css();
		} else {
			alert("[Tarsier] task run: could not happen");
		}
		return this;
	};

	// check duplicated
	tarsier.base.Task.prototype.isDuplicated = function() {
		for (var i = 0; i < tarsier.base.importings.length; ++i) {
			if (tarsier.base.importings[i].url == this.url) return true;
		}
		return false;
	}

	//
	// import something
	//
	tarsier.base.import = function(args) {
		var task;
		var type = args.type;
		if (type == "text/javascript") {
			task = new this.Task({url: args.src, type: args.type, callback: args.callback});
		} else if (type == "text/css") {
			task = new this.Task({url: args.href, type: args.type, callback: args.callback});
		} else {
			alert("[Tarsier] unknown import type: " + type);
			return this;
		}

		if (task.isDuplicated()) {
			//alert("[Tarsier] duplicated url: " + task.url);
			return this;
		}

		this.importings[this.importings.length] = task;
		if (this.importings.length == 1) {
			task.run();
		}
		return this;
	};

	////////////////////////////////////////////////////////////////////////////
	//
	//  namespace: events
	//

	tarsier.events = {

		isWindowLoaded: false,

		handlers: {
			onload: []
		}
	};

	// window loaded & importing finished?
	tarsier.events.isReady = function() {
		return this.isWindowLoaded && tarsier.base.importings.length == 0;
	};

	// add handler for event name
	tarsier.events.add = function(name, handler) {
		if (!name || !handler) return;
		if (name == "load" || name == "onload" || name == "ready") {
			this.handlers.onload[this.handlers.onload.length] = handler;
		} else {
			alert("[Tarsier] unknown event: " + name + " handler: " + handler);
		}
		return this;
	};

	tarsier.events.onload = function() {
		if (this.isReady()) {
			for (var i = 0; i < this.handlers.onload.length; ++i) {
				this.handlers.onload[i]();
			}
			this.handlers.onload = [];
		}
		return this;
	};

	//--------------------------------------------------------------------------

	/**
	 *  import javascript file
	 */
	tarsier.importJS = function(url, callback) {
		this.base.import({
						 src: url,
						 type: "text/javascript",
						 callback: callback
		});
		return this;
	};
	/**
	 *  import style sheet
	 */
	tarsier.importCSS = function(url) {
		this.base.import({
						 href: url,
						 type: "text/css"
		});
		return this;
	};

	/**
	 *  adding handler for 'onload' event
	 */
	tarsier.ready = function(func) {
		this.events.add("onload", func);
		return this;
	};

	var window_onload = window.onload; // save old handler
	window.onload = function() {
		tarsier.events.isWindowLoaded = true;
		tarsier.events.onload();
		if (typeof(window_onload) == "function") {
			window_onload(); // call old handler
		}
	};

	//--------------------------------------------------------------------------

	/**
	 *  Environment variables
	 */
	var __FILE__ = "http://moky.github.io/Tarsier/tarsier.js"; // current filename
	var __PATH__ = "http://moky.github.io/Tarsier/"; // current filepath

	var scripts = document.getElementsByTagName("script");
	if (scripts && scripts.length > 0) {
		for (var i = scripts.length - 1; i >= 0; --i) {
			if (scripts[i].src.lastIndexOf("tarsier.js") > 0) {
				__FILE__ = scripts[i].src;
				var pos = __FILE__.lastIndexOf("/");
				if (pos >= 0) {
					__PATH__ = __FILE__.substring(0, pos + 1);
					__FILE__ = __FILE__.substring(pos + 1);
				}
			}
		}
	}

	// include all dependences
//	tarsier.importJS("http://code.jquery.com/jquery.min.js");
//	tarsier.importJS("http://borismoore.github.io/jquery-tmpl/jquery.tmpl.min.js");
//	tarsier.importJS("http://jquery-xml2json-plugin.googlecode.com/svn/trunk/jquery.xml2json.js");
//	tarsier.importJS("http://steamdev.com/snippet/js/jquery.snippet.min.js");
//	tarsier.importCSS("http://steamdev.com/snippet/css/jquery.snippet.min.css");

	tarsier.importJS(__PATH__ + "3rd/jquery.min.js");
	tarsier.importJS(__PATH__ + "3rd/jquery.tmpl.min.js");
	tarsier.importJS(__PATH__ + "3rd/jquery.xml2json.js");
//	tarsier.importJS(__PATH__ + "3rd/jquery.snippet.min.js");
//	tarsier.importCSS(__PATH__ + "3rd/jquery.snippet.min.css");

	tarsier.importJS(__PATH__ + "http.js");
	tarsier.importJS(__PATH__ + "xml.js");
	tarsier.importJS(__PATH__ + "template.js");
	tarsier.importJS(__PATH__ + "widget.js");

})(tarsier);
<commit_msg>v1.0.1<commit_after>;
/*!
 * Tarsier JavaScript Library v1.0.1
 * http://github.com/moky/Tarsier/
 *
 * Includes jquery.js
 * http://jquery.com/
 *
 * Copyright 2013 moKy at slanissue.com
 * Released under the MIT license
 * http://jquery.org/license
 *
 * Date: 2013-12-11 T10:43Z
 */

/**
 *
 *  main
 *
 *  Author: moKy @ Dec. 11, 2013
 *
 */

if (typeof(window.tarsier) != "object") {
	window.tarsier = {
		version: "1.0.1"
	};
}

// base functions
(function(tarsier) {

	////////////////////////////////////////////////////////////////////////////
	//
	//  namespace: base
	//
	tarsier.base = {
		// importing tasks
		importings: []
	};

	/**
	 *  class: Task
	 */
	tarsier.base.Task = function(args) {
		this.url = args.url;
		this.type = args.type;
		this.callback = args.callback;
		return this;
	};

	// finished current task
	tarsier.base.Task.prototype.finished = function() {
		tarsier.base.importings.shift(); // remove current task
		if (tarsier.base.importings.length > 0) {
			tarsier.base.importings[0].run(); // run next task
		} else {
			tarsier.events.onload(); // all tasks finished
		}
		return this;
	};

	// import js
	tarsier.base.Task.prototype.js = function() {
		var task = this;
		var doc = task.document || window.document;

		var script = doc.createElement("script");
		if (script) {
			script.type = "text/javascript";
			script.src = task.url;
			script.async = task.async;
			script.charset = task.charset || "UTF-8";

			// callback
			script.onload = function() {
				try {
					if (task.callback) task.callback();
				} catch(e) {
					alert("[Tarsier] callback error: " + e);
				}
				task.finished();
			}
			script.onreadystatechange = function() { // IE
				if (this.readyState == "complete") {
					script.onload();
				}
			}

			// load
			var head = doc.getElementsByTagName("head");
			if (head) {
				head.item(0).appendChild(script);
			}
		}
		return this;
	};

	// import css
	tarsier.base.Task.prototype.css = function() {
		var task = this;
		var doc = task.document || window.document;

		var link = doc.createElement("link");
		if (link) {
			link.rel = "stylesheet";
			link.type = "text/css";
			link.href = task.url;

			// load
			var head = doc.getElementsByTagName("head");
			if (head) {
				head.item(0).appendChild(link);
			}
		}

		task.finished();
		return this;
	};

	// run a task
	tarsier.base.Task.prototype.run = function() {
		if (this.type == "text/javascript") {
			this.js();
		} else if (this.type == "text/css") {
			this.css();
		} else {
			alert("[Tarsier] task run: could not happen");
		}
		return this;
	};

	// check duplicated
	tarsier.base.Task.prototype.isDuplicated = function() {
		for (var i = 0; i < tarsier.base.importings.length; ++i) {
			if (tarsier.base.importings[i].url == this.url) return true;
		}
		return false;
	}

	//
	// import something
	//
	tarsier.base.import = function(args) {
		var task;
		var type = args.type;
		if (type == "text/javascript") {
			task = new this.Task({url: args.src,
								 type: "text/javascript",
								 charset: args.charset,
								 async: args.async,
								 callback: args.callback});
		} else if (type == "text/css") {
			task = new this.Task({url: args.href,
								 type: "text/css"});
		} else {
			alert("[Tarsier] unknown import type: " + type);
			return this;
		}

		if (task.isDuplicated()) {
			//alert("[Tarsier] duplicated url: " + task.url);
			return this;
		}

		this.importings[this.importings.length] = task;
		if (this.importings.length == 1) {
			task.run();
		}
		return this;
	};

	////////////////////////////////////////////////////////////////////////////
	//
	//  namespace: events
	//

	tarsier.events = {

		isWindowLoaded: false,

		handlers: {
			onload: []
		}
	};

	// window loaded & importing finished?
	tarsier.events.isReady = function() {
		return this.isWindowLoaded && tarsier.base.importings.length == 0;
	};

	// add handler for event name
	tarsier.events.add = function(name, handler) {
		if (!name || !handler) return;
		if (name == "load" || name == "onload" || name == "ready") {
			this.handlers.onload[this.handlers.onload.length] = handler;
		} else {
			alert("[Tarsier] unknown event: " + name + " handler: " + handler);
		}
		return this;
	};

	tarsier.events.onload = function() {
		if (this.isReady()) {
			for (var i = 0; i < this.handlers.onload.length; ++i) {
				this.handlers.onload[i]();
			}
			this.handlers.onload = [];
		}
		return this;
	};

	//--------------------------------------------------------------------------

	/**
	 *  import javascript file
	 */
	tarsier.importJS = function(url, callback) {
		this.base.import({
						 src: url,
						 type: "text/javascript",
						 callback: callback
		});
		return this;
	};
	/**
	 *  import style sheet
	 */
	tarsier.importCSS = function(url) {
		this.base.import({
						 href: url,
						 type: "text/css"
		});
		return this;
	};

	/**
	 *  adding handler for 'onload' event
	 */
	tarsier.ready = function(func) {
		this.events.add("onload", func);
		return this;
	};

	var window_onload = window.onload; // save old handler
	window.onload = function() {
		tarsier.events.isWindowLoaded = true;
		tarsier.events.onload();
		if (typeof(window_onload) == "function") {
			window_onload(); // call old handler
		}
	};

	//--------------------------------------------------------------------------

	/**
	 *  Environment variables
	 */
	var __FILE__ = "http://moky.github.io/Tarsier/tarsier.js"; // current filename
	var __PATH__ = "http://moky.github.io/Tarsier/"; // current filepath

	var scripts = document.getElementsByTagName("script");
	if (scripts && scripts.length > 0) {
		for (var i = scripts.length - 1; i >= 0; --i) {
			if (scripts[i].src.lastIndexOf("tarsier.js") > 0) {
				__FILE__ = scripts[i].src;
				var pos = __FILE__.lastIndexOf("/");
				if (pos >= 0) {
					__PATH__ = __FILE__.substring(0, pos + 1);
					__FILE__ = __FILE__.substring(pos + 1);
				}
			}
		}
	}

	// include all dependences
//	tarsier.importJS("http://code.jquery.com/jquery.min.js");
//	tarsier.importJS("http://borismoore.github.io/jquery-tmpl/jquery.tmpl.min.js");
//	tarsier.importJS("http://jquery-xml2json-plugin.googlecode.com/svn/trunk/jquery.xml2json.js");
//	tarsier.importJS("http://steamdev.com/snippet/js/jquery.snippet.min.js");
//	tarsier.importCSS("http://steamdev.com/snippet/css/jquery.snippet.min.css");

	tarsier.importJS(__PATH__ + "3rd/jquery.min.js");
	tarsier.importJS(__PATH__ + "3rd/jquery.tmpl.min.js");
	tarsier.importJS(__PATH__ + "3rd/jquery.xml2json.js");
//	tarsier.importJS(__PATH__ + "3rd/jquery.snippet.min.js");
//	tarsier.importCSS(__PATH__ + "3rd/jquery.snippet.min.css");

	tarsier.importJS(__PATH__ + "http.js");
	tarsier.importJS(__PATH__ + "xml.js");
	tarsier.importJS(__PATH__ + "template.js");
	tarsier.importJS(__PATH__ + "widget.js");

})(tarsier);
<|endoftext|>


<commit_before>const { Client } = require('@elastic/elasticsearch')
const client = new Client(config.elasticSearch)

let bulkUpdates = {}

module.exports = {
  ensureIndexes: async function (updateSettings) {
    // await client.indices.delete({index: 'imports'})
    // imports index
    let exists = await client.indices.exists({index: 'import'})
    if (!exists || !exists.body) {
      await client.indices.create({index: 'import'})
    }

    if (updateSettings) {
      await client.indices.close({index: 'import'})
      await client.indices.putSettings({index: 'import', body: {
        analysis: {
          analyzer: {
            default_search: {
              tokenizer: 'standard',
              filter: [
                'lowercase',
                'word_delimiter'
              ]
            }
          }
      }}})
      await client.indices.open({index: 'import'})
    }

    await client.indices.putMapping({index: 'import', body: {
      properties: {
        id: {type: 'text', index: false},
        name: {type: 'text'},
        slug: {type: 'text'},
        description: {type: 'text'},
        categories: {type: 'keyword'},
        categoriesRoot: {type: 'short'},
        categoriesTotal: {type: 'short'},
        expansion: {type: 'byte'},
        installs: {type: 'integer'},
        stars: {type: 'integer'},
        views: {type: 'integer'},
        viewsThisWeek: {type: 'integer'},
        comments: {type: 'integer'},
        installScore: {type: 'half_float'},
        starScore: {type: 'half_float'},
        viewsScore: {type: 'half_float'},
        thumbnail: {type: 'text', index: false},
        timestamp: {type: 'integer'},
        hidden: {type: 'boolean'},
        type: {type: 'keyword'},
        restrictions: {type: 'keyword'},
        userId: {type: 'keyword'},
        userName: {type: 'text', index: false},
        userAvatar: {type: 'text', index: false},
        userClass: {type: 'text', index: false},
        userLinked: {type: 'boolean', index: false}
      }
    }})

    // code index
    exists = await client.indices.exists({index: 'code'})
    if (!exists || !exists.body) {
      await client.indices.create({index: 'code'})
    }

    await client.indices.putMapping({index: 'code', body: {
      properties: {
        id: {type: 'text', index: false},
        name: {type: 'text', index: false},
        versionString: {type: 'text', index: false},
        code: {type: 'text'},
        expansion: {type: 'byte'},
        timestamp: {type: 'integer'},
        hidden: {type: 'boolean'},
        type: {type: 'keyword'},
        restrictions: {type: 'keyword'},
        userId: {type: 'keyword'},
        userName: {type: 'text', index: false},
        userAvatar: {type: 'text', index: false},
        userClass: {type: 'text', index: false},
        userLinked: {type: 'boolean', index: false}
      }
    }})

    // console.log(JSON.stringify((await client.indices.getMapping({index: 'import'})).body, null, 2))
    // console.log(JSON.stringify((await client.indices.getSettings({index: 'import'})).body, null, 2))
  },

  addDoc: async function (index, doc, syncing) {
    if (!bulkUpdates[index]) {
      bulkUpdates[index] = {docs: [], timeout: null}
    }
    if (!syncing) {
      for (let i = 0; i < bulkUpdates[index].docs.length; i++) {
        if (bulkUpdates[index].docs[i].id === doc.id) {
          bulkUpdates[index].docs[i] = doc
          return this.checkBulk(index)
        }
      }
    }
    console.log('about to add..', bulkUpdates[index].docs)
    bulkUpdates[index].docs.push({index: { _index: index }})
    console.log('added index', bulkUpdates[index].docs)
    bulkUpdates[index].docs.push(doc)
    console.log('added doc', bulkUpdates[index].docs)
    this.checkBulk(index)
  },

  removeDoc: async function (index, id) {
    if (!bulkUpdates[index]) {
      bulkUpdates[index] = {docs: [], timeout: null}
    }
    bulkUpdates[index].docs.push({delete: {_index: index, _id: id}})
    this.checkBulk(index)
  },

  checkBulk: async function (index) {
    if (bulkUpdates[index].docs.length >= 1000) {
      clearTimeout(bulkUpdates[index].timeout)
      bulkUpdates[index].timeout = null
      this.bulkProcessing(index)
    }
    else if (!bulkUpdates[index].timeout && bulkUpdates[index].docs.length) {
      bulkUpdates[index].timeout = setTimeout(() => {
        this.bulkProcessing(index)
        bulkUpdates[index].timeout = null
      }, 30000)
    }
  },

  bulkProcessing: async function (index) {
    try {
      console.log(bulkUpdates[index].docs)
      let p = client.bulk({refresh: true, body: bulkUpdates[index].docs})
      bulkUpdates[index].docs = []
      await p
    }
    catch (e) {
      console.log(e)
    }
    this.checkBulk(index)
  },

  search: async function (o) {
    const resultsPerPage = 25
    let results = []
    if (o.algorithm === 'popular') {
      results = await client.search({
        index: o.index,
        body: {
          query: {
            function_score: {
              query: {
                bool: o.query,
              },
              functions: [
                {gauss: {
                  timestamp: {
                    origin: Date.now() / 1000,
                    scale: 86400 * 120,
                    offset: 86400 * 75,
                    decay : 0.25
                  }
                }},
                // {field_value_factor: {field: "installScore", missing: 0, factor: 8}},
                {field_value_factor: {field: "starScore", missing: 0, factor: 6}},
                {field_value_factor: {field: "viewsScore", missing: 0, modifier: "ln1p", factor: 1}},
                {field_value_factor: {field: "ageScore", missing: 0, modifier: "ln1p", factor: .01}},
                {field_value_factor: {field: "installs", missing: 0, modifier: "ln1p", factor: .05}},
                {field_value_factor: {field: "stars", missing: 0, modifier: "ln1p", factor: .05}},
                {field_value_factor: {field: "viewsThisWeek", missing: 0, modifier: "ln1p", factor: .01}},
              ],
              score_mode: "sum"
            }
          },
          sort: o.sort || ['_score'],
          size: resultsPerPage,
          from: resultsPerPage * (o.page || 0)
        }
      })
    }
    else {
      results = await client.search({
        index: o.index,
        body: {
          query: {
            bool: o.query,
          },
          sort: o.sort || ['_score'],
          size: resultsPerPage,
          from: resultsPerPage * (o.page || 0)
        }
      })
    }
    try {
      return {hits: results.body.hits.hits.map(d => Object.assign(d._source, {_score: d._score})), total: results.body.hits.total.value, query: o.textQuery, index: o.index}
    }
    catch (e) {
      console.log(e)
      return {hits: [], total: 0, query: o.query}
    }
  }
}<commit_msg>remove debugging<commit_after>const { Client } = require('@elastic/elasticsearch')
const client = new Client(config.elasticSearch)

let bulkUpdates = {}

module.exports = {
  ensureIndexes: async function (updateSettings) {
    // await client.indices.delete({index: 'imports'})
    // imports index
    let exists = await client.indices.exists({index: 'import'})
    if (!exists || !exists.body) {
      await client.indices.create({index: 'import'})
    }

    if (updateSettings) {
      await client.indices.close({index: 'import'})
      await client.indices.putSettings({index: 'import', body: {
        analysis: {
          analyzer: {
            default_search: {
              tokenizer: 'standard',
              filter: [
                'lowercase',
                'word_delimiter'
              ]
            }
          }
      }}})
      await client.indices.open({index: 'import'})
    }

    await client.indices.putMapping({index: 'import', body: {
      properties: {
        id: {type: 'text', index: false},
        name: {type: 'text'},
        slug: {type: 'text'},
        description: {type: 'text'},
        categories: {type: 'keyword'},
        categoriesRoot: {type: 'short'},
        categoriesTotal: {type: 'short'},
        expansion: {type: 'byte'},
        installs: {type: 'integer'},
        stars: {type: 'integer'},
        views: {type: 'integer'},
        viewsThisWeek: {type: 'integer'},
        comments: {type: 'integer'},
        installScore: {type: 'half_float'},
        starScore: {type: 'half_float'},
        viewsScore: {type: 'half_float'},
        thumbnail: {type: 'text', index: false},
        timestamp: {type: 'integer'},
        hidden: {type: 'boolean'},
        type: {type: 'keyword'},
        restrictions: {type: 'keyword'},
        userId: {type: 'keyword'},
        userName: {type: 'text', index: false},
        userAvatar: {type: 'text', index: false},
        userClass: {type: 'text', index: false},
        userLinked: {type: 'boolean', index: false}
      }
    }})

    // code index
    exists = await client.indices.exists({index: 'code'})
    if (!exists || !exists.body) {
      await client.indices.create({index: 'code'})
    }

    await client.indices.putMapping({index: 'code', body: {
      properties: {
        id: {type: 'text', index: false},
        name: {type: 'text', index: false},
        versionString: {type: 'text', index: false},
        code: {type: 'text'},
        expansion: {type: 'byte'},
        timestamp: {type: 'integer'},
        hidden: {type: 'boolean'},
        type: {type: 'keyword'},
        restrictions: {type: 'keyword'},
        userId: {type: 'keyword'},
        userName: {type: 'text', index: false},
        userAvatar: {type: 'text', index: false},
        userClass: {type: 'text', index: false},
        userLinked: {type: 'boolean', index: false}
      }
    }})

    // console.log(JSON.stringify((await client.indices.getMapping({index: 'import'})).body, null, 2))
    // console.log(JSON.stringify((await client.indices.getSettings({index: 'import'})).body, null, 2))
  },

  addDoc: async function (index, doc, syncing) {
    if (!bulkUpdates[index]) {
      bulkUpdates[index] = {docs: [], timeout: null}
    }
    if (!syncing) {
      for (let i = 0; i < bulkUpdates[index].docs.length; i++) {
        if (bulkUpdates[index].docs[i].id === doc.id) {
          bulkUpdates[index].docs[i] = doc
          return this.checkBulk(index)
        }
      }
    }
    bulkUpdates[index].docs.push({index: { _index: index }})
    bulkUpdates[index].docs.push(doc)
    this.checkBulk(index)
  },

  removeDoc: async function (index, id) {
    if (!bulkUpdates[index]) {
      bulkUpdates[index] = {docs: [], timeout: null}
    }
    bulkUpdates[index].docs.push({delete: {_index: index, _id: id}})
    this.checkBulk(index)
  },

  checkBulk: async function (index) {
    if (bulkUpdates[index].docs.length >= 1000) {
      clearTimeout(bulkUpdates[index].timeout)
      bulkUpdates[index].timeout = null
      this.bulkProcessing(index)
    }
    else if (!bulkUpdates[index].timeout && bulkUpdates[index].docs.length) {
      bulkUpdates[index].timeout = setTimeout(() => {
        this.bulkProcessing(index)
        bulkUpdates[index].timeout = null
      }, 30000)
    }
  },

  bulkProcessing: async function (index) {
    try {
      let p = client.bulk({refresh: true, body: bulkUpdates[index].docs})
      bulkUpdates[index].docs = []
      await p
    }
    catch (e) {
      console.log(e)
    }
    this.checkBulk(index)
  },

  search: async function (o) {
    const resultsPerPage = 25
    let results = []
    if (o.algorithm === 'popular') {
      results = await client.search({
        index: o.index,
        body: {
          query: {
            function_score: {
              query: {
                bool: o.query,
              },
              functions: [
                {gauss: {
                  timestamp: {
                    origin: Date.now() / 1000,
                    scale: 86400 * 120,
                    offset: 86400 * 75,
                    decay : 0.25
                  }
                }},
                // {field_value_factor: {field: "installScore", missing: 0, factor: 8}},
                {field_value_factor: {field: "starScore", missing: 0, factor: 6}},
                {field_value_factor: {field: "viewsScore", missing: 0, modifier: "ln1p", factor: 1}},
                {field_value_factor: {field: "ageScore", missing: 0, modifier: "ln1p", factor: .01}},
                {field_value_factor: {field: "installs", missing: 0, modifier: "ln1p", factor: .05}},
                {field_value_factor: {field: "stars", missing: 0, modifier: "ln1p", factor: .05}},
                {field_value_factor: {field: "viewsThisWeek", missing: 0, modifier: "ln1p", factor: .01}},
              ],
              score_mode: "sum"
            }
          },
          sort: o.sort || ['_score'],
          size: resultsPerPage,
          from: resultsPerPage * (o.page || 0)
        }
      })
    }
    else {
      results = await client.search({
        index: o.index,
        body: {
          query: {
            bool: o.query,
          },
          sort: o.sort || ['_score'],
          size: resultsPerPage,
          from: resultsPerPage * (o.page || 0)
        }
      })
    }
    try {
      return {hits: results.body.hits.hits.map(d => Object.assign(d._source, {_score: d._score})), total: results.body.hits.total.value, query: o.textQuery, index: o.index}
    }
    catch (e) {
      console.log(e)
      return {hits: [], total: 0, query: o.query}
    }
  }
}<|endoftext|>


<commit_before>// @flow
import GhostContentAPI from "@tryghost/content-api";

const ghostContentAPI =
  window.GHOST_URL &&
  window.GHOST_CONTENT_API_KEY &&
  new GhostContentAPI({
    url: window.GHOST_URL,
    key: window.GHOST_CONTENT_API_KEY,
    version: "v3",
  });

export type GhostPost = {|
  title: string,
  url: string,
  slug: string,
  excerpt: ?string,
  custom_excerpt: ?string,
  feature_image: string,
|};

class ghostApiHelper {
  static browse(
    tag: string,
    successCallback: ($ReadOnlyArray<GhostPost>) => void,
    errCallback: string => void
  ) {
    ghostContentAPI.posts
      .browse({
        filter: "tag:" + tag,
        fields: "title, url, slug, excerpt, custom_excerpt, feature_image",
      })
      .then(postsResponse => {
        successCallback
          ? successCallback(postsResponse)
          : console.log(JSON.stringify(postsResponse));
      })
      .catch(err => {
        errCallback ? errCallback(err) : console.error(err);
      });
  }
  static isConfigured(): boolean {
    return !!ghostContentAPI;
  }
}
// get the two most recent posts, return more fields than the carousel uses
// this is for the homepage's blog section, which is meant to be a static display
export class ghostApiRecent {
  static browse(
    successCallback: ($ReadOnlyArray<GhostPost>) => void,
    errCallback: string => void
  ) {
    ghostContentAPI.posts
      .browse({
        limit: 2,
        include: "tags,authors",
      })
      .then(postsResponse => {
        successCallback
          ? successCallback(postsResponse)
          : console.log(JSON.stringify(postsResponse));
      })
      .catch(err => {
        errCallback ? errCallback(err) : console.error(err);
      });
  }
  static isConfigured(): boolean {
    return !!ghostContentAPI;
  }
}

export default ghostApiHelper;
<commit_msg>limit fields in api return<commit_after>// @flow
import GhostContentAPI from "@tryghost/content-api";

const ghostContentAPI =
  window.GHOST_URL &&
  window.GHOST_CONTENT_API_KEY &&
  new GhostContentAPI({
    url: window.GHOST_URL,
    key: window.GHOST_CONTENT_API_KEY,
    version: "v3",
  });

export type GhostPost = {|
  title: string,
  url: string,
  slug: string,
  excerpt: ?string,
  custom_excerpt: ?string,
  feature_image: string,
|};

class ghostApiHelper {
  static browse(
    tag: string,
    successCallback: ($ReadOnlyArray<GhostPost>) => void,
    errCallback: string => void
  ) {
    ghostContentAPI.posts
      .browse({
        filter: "tag:" + tag,
        fields: "title, url, slug, excerpt, custom_excerpt, feature_image",
      })
      .then(postsResponse => {
        successCallback
          ? successCallback(postsResponse)
          : console.log(JSON.stringify(postsResponse));
      })
      .catch(err => {
        errCallback ? errCallback(err) : console.error(err);
      });
  }
  static isConfigured(): boolean {
    return !!ghostContentAPI;
  }
}
// get the two most recent posts, return more fields than the carousel uses
// this is for the homepage's blog section, which is meant to be a static display
export class ghostApiRecent {
  static browse(
    successCallback: ($ReadOnlyArray<GhostPost>) => void,
    errCallback: string => void
  ) {
    ghostContentAPI.posts
      .browse({
        limit: 2,
        include: "tags,authors",
        fields: "title, url, slug, excerpt, custom_excerpt, feature_image, reading_time, primary_author, primary_tag, published_at, updated_at"
      })
      .then(postsResponse => {
        successCallback
          ? successCallback(postsResponse)
          : console.log(JSON.stringify(postsResponse));
      })
      .catch(err => {
        errCallback ? errCallback(err) : console.error(err);
      });
  }
  static isConfigured(): boolean {
    return !!ghostContentAPI;
  }
}

export default ghostApiHelper;
<|endoftext|>


<commit_before>const browser = require('webextension-polyfill');

browser.contextMenus.create({
  title: 'Add to Are.na',
  contexts: ['page', 'selection', 'link', 'editable', 'image', 'video', 'audio'],
  onclick(options) {
    browser.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      browser.tabs.sendMessage(tabs[0].id, {
        text: 'open:dialog',
        options,
        title: tabs[0].title,
        url: tabs[0].url,
        tab_id: tabs[0].id,
      });
    });
  },
});

browser.browserAction.onClicked.addListener((tab) => {
  browser.tabs.sendMessage(tab.id, {
    text: 'open:dialog',
    options: { srcUrl: tab.url },
    title: tab.title,
    url: tab.url,
    tab_id: tab.id,
  });
});
<commit_msg>Fix context menu adding<commit_after>const browser = require('webextension-polyfill');

browser.contextMenus.create({
  title: 'Add to Are.na',
  contexts: ['page', 'selection', 'link', 'editable', 'image', 'video', 'audio'],
  onclick(options, tab) {
    browser.tabs.sendMessage(tab.id, {
      text: 'open:dialog',
      options,
      title: tab.title,
      url: tab.url,
      tab_id: tab.id,
    });
  },
});

browser.browserAction.onClicked.addListener((tab) => {
  browser.tabs.sendMessage(tab.id, {
    text: 'open:dialog',
    options: { srcUrl: tab.url },
    title: tab.title,
    url: tab.url,
    tab_id: tab.id,
  });
});
<|endoftext|>


<commit_before>var mbaasApi = require('fh-mbaas-api');
var express = require('express');
var mbaasExpress = mbaasApi.mbaasExpress();
var cors = require('cors');
var _ = require('underscore');
var async = require('async');

// list the endpoints which you want to make securable here
var securableEndpoints;
// fhlint-begin: securable-endpoints
securableEndpoints = ['/hello'];
// fhlint-end

var app = express();

// Enable CORS for all requests
app.use(cors());

// Note: add this first to override mbaas api
app.get('/mbaas/forms/:appId/:formId', function(req, res, next) {
  console.log("Lookup form: " + req.params.formId);
  if (req.params.formId == '560c2d68d1596bad10c427d3') {
    mbaasApi.forms.getForm({
     "_id": req.params.formId
    }, function (err, form) {
      if (err) return res.status(500).json(err);

      console.log("Retrieved form: " + form.name + " = " + form.description);

      var asyncTasks = [];

      _.each(form.pages, function(page, index, list){
        _.each(page.fields, function(field, index, list){
          if (field.type != 'dropdown') return;
          asyncTasks.push(function(callback){

            var fieldCode = field.fieldCode.toLowerCase().slice(1);

            console.log("Searching data for " + fieldCode);

            var options = [];

            var params = {
              "act": "list",
              "type": fieldCode, // Entity/Collection name
            };

            mbaasApi.db(params, function (err, data) {
              if (err) return res.status(500).json(err);
              //console.log("Results " + JSON.stringify(data));
              options = _.map(data.list, function(row, index, list){
                //console.log(JSON.stringify(row));
                return { "checked" : false, "label" : row.fields.value };
              });
              //console.log(JSON.stringify(options));
              field.fieldOptions.definition.options = options;

              callback();
            });
          });
        });
      });

      async.parallel(asyncTask, function(){
        console.log(JSON.stringify(form));
        return res.json(form);
      });
    });
  } else {
    next();
  }
});

// Note: the order which we add middleware to Express here is important!
app.use('/sys', mbaasExpress.sys(securableEndpoints));
app.use('/mbaas', mbaasExpress.mbaas);

// allow serving of static files from the public directory
app.use(express.static(__dirname + '/public'));

// Note: important that this is added just before your own Routes
app.use(mbaasExpress.fhmiddleware());

// fhlint-begin: custom-routes
//app.use('/hello', require('./lib/overrider.js')());
// fhlint-end

// Important that this is last!
app.use(mbaasExpress.errorHandler());

var port = process.env.FH_PORT || process.env.OPENSHIFT_NODEJS_PORT || 8001;
var host = process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';
var server = app.listen(port, host, function() {
  console.log("App started at: " + new Date() + " on port: " + port);
});
<commit_msg>update file /application.js<commit_after>var mbaasApi = require('fh-mbaas-api');
var express = require('express');
var mbaasExpress = mbaasApi.mbaasExpress();
var cors = require('cors');
var _ = require('underscore');
var async = require('async');

// list the endpoints which you want to make securable here
var securableEndpoints;
// fhlint-begin: securable-endpoints
securableEndpoints = ['/hello'];
// fhlint-end

var app = express();

// Enable CORS for all requests
app.use(cors());

// Note: add this first to override mbaas api
app.get('/mbaas/forms/:appId/:formId', function(req, res, next) {
  console.log("Lookup form: " + req.params.formId);
  if (req.params.formId == '560c2d68d1596bad10c427d3') {
    mbaasApi.forms.getForm({
     "_id": req.params.formId
    }, function (err, form) {
      if (err) return res.status(500).json(err);

      console.log("Retrieved form: " + form.name + " = " + form.description);

      var asyncTasks = [];

      _.each(form.pages, function(page, index, list){
        _.each(page.fields, function(field, index, list){
          if (field.type != 'dropdown') return;
          asyncTasks.push(function(callback){

            var fieldCode = field.fieldCode.toLowerCase().slice(1);

            console.log("Searching data for " + fieldCode);

            var options = [];

            var params = {
              "act": "list",
              "type": fieldCode, // Entity/Collection name
            };

            mbaasApi.db(params, function (err, data) {
              if (err) return res.status(500).json(err);

              options = _.map(data.list, function(row, index, list){
                return { "checked" : false, "label" : row.fields.value };
              });
              field.fieldOptions.definition.options = options;

              // Async call is done, alert via callback
              callback();
            });
          });
        });
      });

      // run async tasks and return
      async.parallel(asyncTask, function(){
        console.log(JSON.stringify(form));
        return res.json(form);
      });
    });
  } else {
    next();
  }
});

// Note: the order which we add middleware to Express here is important!
app.use('/sys', mbaasExpress.sys(securableEndpoints));
app.use('/mbaas', mbaasExpress.mbaas);

// allow serving of static files from the public directory
app.use(express.static(__dirname + '/public'));

// Note: important that this is added just before your own Routes
app.use(mbaasExpress.fhmiddleware());

// fhlint-begin: custom-routes
//app.use('/hello', require('./lib/overrider.js')());
// fhlint-end

// Important that this is last!
app.use(mbaasExpress.errorHandler());

var port = process.env.FH_PORT || process.env.OPENSHIFT_NODEJS_PORT || 8001;
var host = process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0';
var server = app.listen(port, host, function() {
  console.log("App started at: " + new Date() + " on port: " + port);
});
<|endoftext|>


<commit_before>
LIST_VIEW = '' +
            '
' +
            '{{#details}}' +
              '
' +
                '' +
                '
' +
                  '
{{datetime-figure}}	{{name}}
' +
                  '
{{datetime-unit}}	{{dist}} m
' +
                '
' +
                '' +
              '
' +
            '{{/details}}' +
            '{{^details}}' +
              '

No events. :(

' +
            '{{/details}}' +
            '
' +
            '';

LIST_VIEW_DIST = '' +
            '
' +
            '{{#dists}}' +
              '
{{sep}}
' +
              '{{#details}}' +
                '
' +
                  '' +
                  '{{name}}
' +
                  '{{date}}' +
                  '' +
                '
' +
              '{{/details}}' +
            '{{/dists}}' +
            '{{^dists}}' +
              '

No events. :(

' +
            '{{/dists}}' +
            '
' +
            '';

EVENT = '' +
        '{{#event}}' +
        '
' +
          'Back' +
          '
{{name}}
' +
        '
' +
        '
' +
          '
' +
            '
Description
' +
            '
 {{description}}
' +
            '
Where
' +
            '{{#where}}' +
            '
 {{address}}
' +
            '{{/where}}' +
            '
When
' +
            '
{{when}}
' +
            /*'
Tags
' +
            '{{#tags}}' +
            '
{{tag}}
' +
            '{{/tags}}' +*/
          '
' +
        '
' +
        '{{/event}}' +
          '';

function validateDateTime(){

	var date = $('#add_event #add_date').val().split('-');

	var time = $('#add_event #add_time').val().split(':');
	var year = parseInt(date[0],10);
	var month = parseInt(date[1],10);
	var day = parseInt(date[2],10);
	var hour = parseInt(time[0],10);
	var minute = parseInt(time[1],10);

	var today = new Date();
	console.log("today year: " + today.getFullYear() + " entered year: " + year);
	if (year > today.getFullYear()){
		return true;
	} else if (year < today.getFullYear()){
		return false;
	}
	console.log("today month: " + (today.getMonth + 1) + " entered month: " + month);
	if (month > (today.getMonth() + 1)){
		return true;
	} else if (month < (today.getMonth() + 1)){
		return false;
	}
	console.log("today day: " + today.getDate() + " entered day: " + day);
	if (day > today.getDate()){
		return true;
	} else if (day < today.getDate()){
		return false;
	}
	console.log("today hour: " + today.getHours() + " entered hour: " + hour);
	if (hour != 23){
		hour++;
	}
	if (hour > today.getHours()){
		return true;
	} else if (hour < today.getHours()){
		return false;
	}
	console.log("today minute: " + today.getMinutes() + " entered minute: " + minute);
	/*
	if (minute > today.getMinutes()){
		return true;
	} else if (minute < today.getMinutes()){
		return false;
	}
	*/
	return true;
}

// -- needed for mobilesafari bug

function loadFoodEvent(el) {
  $('#event_title').html('Loading...');
  $('#event_body').html('');
  var pk = $(el).data('pk');
  $.getJSON('/api/event/' + pk, function(evt) {
      $('#event').html($.mustache(EVENT, evt));
  });
}

function formatDT() {
  var datetime = {
    hour: this.getHours(),
    minute: this.getMinutes(),
    day: this.getDate(),
    month: this.getMonth() + 1,
    year: this.getFullYear()
  }
  return $.mustache(
    "{{hour}}:{{minute}} {{year}}-{{month}}-{{day}}",
    datetime
  );
}

function formatD() {
  var datetime = {
    day: this.getDate(),
    month: this.getMonth() + 1,
    year: this.getFullYear()
  }
  return $.mustache(
    "{{year}}-{{month}}-{{day}}",
    datetime
  );
}

function padN (number, padding) {
  var s = number.toString();
  while (s.length < padding) { s = '0' + s; }
  return s;
}

function formatT(){
		var h = padN(this.getHours(), 2);
		var m = this.getMinutes()
		m = padN(15 * Math.floor(m/15), 2);
		var datetime = {
    hour: h,
    minute: m
  }
  return $.mustache(
    "{{hour}}:{{minute}}",
    datetime
  );
}

function until_time () {
  var until = new Date();
  var time_selector = $('#listview').data('timewindow');
  switch(time_selector) {
    case 'hour':
      until = new Date(until.getTime() + 60 * 60 * 1000);
      break;
    case 'day':
      until = new Date(until.getTime() + 24 * 60 * 60 * 1000);
      break;
    case 'week':
      until = new Date(until.getTime() + 7 * 24 * 60 * 60 * 1000);
      break;
    default:
      until = '';
  }
  return until.toString();
}

function refresh_list (el) {
  $.location('update', function(me) {
    var options = {};
    var time_window = until_time();
    if (time_window) {
      options['until'] = time_window;
    }
    $.getJSON('/api/event/list', options, function(event_list) {
      event_list.details.forEach(function(ev) {
        // calculate location
        var loc = {
          latitude: ev.lat,
          longitude: ev.lng
        };
        ev.dist = $.location('distance', me, loc);
        ev.dist = ev.dist.toFixed(2);

        //relatize datetime
        var rel_datetime = $.relatizeDate( ev.datetime );
        ev['datetime-figure'] = rel_datetime.figure;
        ev['datetime-unit'] = rel_datetime.unit;
      });
      var storage = window.sessionStorage;
      storage.setItem('event_list', JSON.stringify(event_list));
      resort_list();
    });
  });
}

// -- sortlist helpers

function resort_list () {
  var storage = window.sessionStorage;
  try {
    var event_list = JSON.parse(storage.getItem('event_list'));
  } catch (err) {
    console.log('event_list not loaded before sort attempted');
    return;
  }
  var sort_type = $('#listview').data('sorttype')
    , sort_button = $('#sortList')[0];
  switch(sort_type) {
    case 'time':
      // now must sort by time
      $('#listview').html($.mustache(LIST_VIEW, event_list));
      // set the button
      sort_button.innerText = 'Sort by distance';
      break;
    case 'distance':
    default:
      dist_bins = {}
      // now must sort by distance
      event_list.details.forEach(function(ev) {
        bin_dist(dist_bins, ev);
      });
      dist_list = [];
      $.each(dist_bins, function(bin, v) {
        dist_bin = parseInt(bin, 10);
        details = $.extend(v, {
          sep: ((dist_bin % 10 == 1) ? '> ' : '< ') + bin + 'm',
          dist_bin: dist_bin
        })
        insert_sorted(dist_list, function(a) { return a['dist_bin']; }, details);
      });
      $('#listview').html($.mustache(LIST_VIEW_DIST, { dists: dist_list }));
      // set the button
      sort_button.innerText = 'Sort by time';
  }
}

function bin_dist (dist_bins, ev) {
  var key = '0';
  if (ev.dist < 100) {
    key = '100';
  } else if (ev.dist < 500) {
    key = '500';
  } else {
    key = '501';
  }
  dist_bins[key] = dist_bins[key] || {};
  dist_bins[key].details = dist_bins[key].details || [];
  insert_sorted(dist_bins[key].details, function(a) { return a['dist']; }, ev);
}

function insert_sorted (arr, key, elem) {
  var ii = 0;
  while (arr[ii] && key(elem) > key(arr[ii])) {
    ii++;
  }
  arr.splice(ii, 0, elem);
}

/**
  --- Setup button handlers
*/

function click_sortChange (el) {
  var cur_sort_type = $('#listview').data('sorttype');
  switch(cur_sort_type) {
    case 'distance':
      $('#listview').data('sorttype', 'time');
      $('#listview').data('timewindow', 'all');
      $('#sortChange').html('Events Now');
      break;
    case 'time':
    default:
      $('#listview').data('sorttype', 'distance');
      $('#listview').data('timewindow', 'hour');
      $('#sortChange').html('All Events');
  }
  refresh_list();
}

function click_timeWindow (el) {
  var time_window = $(el).data('timewindow');
  $('#listview').data('timewindow', time_window);
  $(el).parent().children('.selected').removeClass('selected');
  $(el).addClass('selected');
}

function click_addEvent (el) {
  $('#add_location').val('Loading current location');
  // location
  $.location('update', function(loc) {
    $('#add_location_lat').val(loc.latitude);
    $('#add_location_lng').val(loc.longitude);
    $.location('revgeocode', loc, function(address) {
      $('#add_location').val(address);
    });
  });
  // time
  var now = new Date();
  $('#add_time').val(formatT.call(now));
  $('#add_date').val(formatD.call(now));
}

function click_submit (el) {
  // Disable the submit button
  $('#add_event #add_submit').attr("disabled", "disabled");
  // Clear and hide any error messages
  $('#add_event .formError').html('');
  // Set temaprary variables for the script
  var isFocus=0;
  var isError=0;
  // Get the data from the form

  var name= $('#add_event #add_name').val();
  var location= $('#add_event #add_location').val();
  var lat = $('#add_event #add_location_lat').val();
  var longitude = $('#add_event #add_location_lng').val();
  var date= $('#add_event #add_date').val();
  var time= $('#add_event #add_time').val();
  var description = $('#add_event #add_description').val();

  if (name==''){
    $('#add_event #errorName').html('This is a required field.');
    $('#add_event #add_name').focus()
    isFocus = 1;
    isError = 1;
  }

  if (!validateDateTime()){
    $('#add_event #errorDate').html('Error, this time has already passed');
    if (isFocus == 0){
      $('#add_event #add_date').focus();
      isFocus = 1;
    }
    isError = 1;
  }

  // Terminate the script if an error is found
  if(isError==1) {
    // Activate the submit button
    $('#add_event #add_submit').attr("disabled", "");
    return false;
  }

  var dataString = 'name='+ name + '&time=' + time + ' ' + date + '&lat=' + lat + '&long=' + longitude + '&address=' + location + '&description=' + description;

  $.ajax({
    type: "POST",
    url: "/api/event/add_event",
    data: dataString,
    success: function(msg) {
      $('#add_event #add_description').val('');
      $('#add_event #add_name').val('');
      $('#add_event #add_submit').attr("disabled", "");
      window.jQT.goTo("#list", "dissolve");
    },
    error: function(ob,errStr) {
      console.log("nooooo! failure");
      console.log(errStr);
    }

  });

  return false;
}

function shuffle_event() {

}

function set_homepage(ev) {
  $('#ev_name').text(ev.name);
  $('#ev_when').data('when', ev.when);
  relatize_homepage_date();
  $('#ev_image').attr('src', ev.image);
  $('#ev_where').attr('href', "http://maps.google.com/maps?q=" + ev.address);
  $('#ev_where').text(ev.where);
  $('#ev_description').text(ev.description);
}

function relatize_homepage_date() {
  var offset = $.relatizeDate($('#ev_when').data('when'))
  $('#ev_when_figure').text(offset.figure);
  $('#ev_when_unit').text(offset.unit);
}

function calc_homapage_dist() {
  $.location('update', function(myloc) {
    var ev_location = {
      latitude: $('#ev_dist').data('lat'),
      longitude: $('#ev_dist').data('lon')
    };
    var dist = $.location('distance', myloc, ev_location);
    $('#ev_dist_figure').text(dist.figure.toFixed(2));
    $('#ev_dist_unit').text(dist.unit);
  });
}

$(document).ready(function() {

  window.jQT = new $.jQTouch({
    icon: '/static/imgs/icon.png',
    addGlossToIcon: false,
    startupScreen: 'jqt_startup.png',
    statusBar: 'black',
    preloadImages: [
      '/static/themes/jqt/img/activeButton.png',
      '/static/themes/jqt/img/back_button.png',
      '/static/themes/jqt/img/back_button_clicked.png',
      '/static/themes/jqt/img/blueButton.png',
      '/static/themes/jqt/img/button.png',
      '/static/themes/jqt/img/button_clicked.png',
      '/static/themes/jqt/img/grayButton.png',
      '/static/themes/jqt/img/greenButton.png',
      '/static/themes/jqt/img/redButton.png',
      '/static/themes/jqt/img/whiteButton.png',
      '/static/themes/jqt/img/loading.gif'
      ]
  });

  // -- listview

  $('#list').bind('pageAnimationEnd', function(e, info) {
    if (info.direction == 'in') {
      refresh_list();
    }
  });

  // -- location_choice

  $('#add_location').blur(function(e) {
    var address = $('#add_location').val();
    if (address) {
      $.location('geocode', address, function(loc) {
        $('#add_location_lat').val(loc.latitude);
        $('#add_location_lng').val(loc.longitude);
      });
    }
  });

  // -- initialize event details
  // relatize date in #ev_when
  relatize_homepage_date()
  calc_homapage_dist()
  // get all the other events and store them

  // -- init events

});
<commit_msg>set_homepage up to par with django template<commit_after>
LIST_VIEW = '' +
            '
' +
            '{{#details}}' +
              '
' +
                '' +
                '
' +
                  '
{{datetime-figure}}	{{name}}
' +
                  '
{{datetime-unit}}	{{dist}} m
' +
                '
' +
                '' +
              '
' +
            '{{/details}}' +
            '{{^details}}' +
              '

No events. :(

' +
            '{{/details}}' +
            '
' +
            '';

LIST_VIEW_DIST = '' +
            '
' +
            '{{#dists}}' +
              '
{{sep}}
' +
              '{{#details}}' +
                '
' +
                  '' +
                  '{{name}}
' +
                  '{{date}}' +
                  '' +
                '
' +
              '{{/details}}' +
            '{{/dists}}' +
            '{{^dists}}' +
              '

No events. :(

' +
            '{{/dists}}' +
            '
' +
            '';

EVENT = '' +
        '{{#event}}' +
        '
' +
          'Back' +
          '
{{name}}
' +
        '
' +
        '
' +
          '
' +
            '
Description
' +
            '
 {{description}}
' +
            '
Where
' +
            '{{#where}}' +
            '
 {{address}}
' +
            '{{/where}}' +
            '
When
' +
            '
{{when}}
' +
            /*'
Tags
' +
            '{{#tags}}' +
            '
{{tag}}
' +
            '{{/tags}}' +*/
          '
' +
        '
' +
        '{{/event}}' +
          '';

function validateDateTime(){

	var date = $('#add_event #add_date').val().split('-');

	var time = $('#add_event #add_time').val().split(':');
	var year = parseInt(date[0],10);
	var month = parseInt(date[1],10);
	var day = parseInt(date[2],10);
	var hour = parseInt(time[0],10);
	var minute = parseInt(time[1],10);

	var today = new Date();
	console.log("today year: " + today.getFullYear() + " entered year: " + year);
	if (year > today.getFullYear()){
		return true;
	} else if (year < today.getFullYear()){
		return false;
	}
	console.log("today month: " + (today.getMonth + 1) + " entered month: " + month);
	if (month > (today.getMonth() + 1)){
		return true;
	} else if (month < (today.getMonth() + 1)){
		return false;
	}
	console.log("today day: " + today.getDate() + " entered day: " + day);
	if (day > today.getDate()){
		return true;
	} else if (day < today.getDate()){
		return false;
	}
	console.log("today hour: " + today.getHours() + " entered hour: " + hour);
	if (hour != 23){
		hour++;
	}
	if (hour > today.getHours()){
		return true;
	} else if (hour < today.getHours()){
		return false;
	}
	console.log("today minute: " + today.getMinutes() + " entered minute: " + minute);
	/*
	if (minute > today.getMinutes()){
		return true;
	} else if (minute < today.getMinutes()){
		return false;
	}
	*/
	return true;
}

// -- needed for mobilesafari bug

function loadFoodEvent(el) {
  $('#event_title').html('Loading...');
  $('#event_body').html('');
  var pk = $(el).data('pk');
  $.getJSON('/api/event/' + pk, function(evt) {
      $('#event').html($.mustache(EVENT, evt));
  });
}

function formatDT() {
  var datetime = {
    hour: this.getHours(),
    minute: this.getMinutes(),
    day: this.getDate(),
    month: this.getMonth() + 1,
    year: this.getFullYear()
  }
  return $.mustache(
    "{{hour}}:{{minute}} {{year}}-{{month}}-{{day}}",
    datetime
  );
}

function formatD() {
  var datetime = {
    day: this.getDate(),
    month: this.getMonth() + 1,
    year: this.getFullYear()
  }
  return $.mustache(
    "{{year}}-{{month}}-{{day}}",
    datetime
  );
}

function padN (number, padding) {
  var s = number.toString();
  while (s.length < padding) { s = '0' + s; }
  return s;
}

function formatT(){
		var h = padN(this.getHours(), 2);
		var m = this.getMinutes()
		m = padN(15 * Math.floor(m/15), 2);
		var datetime = {
    hour: h,
    minute: m
  }
  return $.mustache(
    "{{hour}}:{{minute}}",
    datetime
  );
}

function until_time () {
  var until = new Date();
  var time_selector = $('#listview').data('timewindow');
  switch(time_selector) {
    case 'hour':
      until = new Date(until.getTime() + 60 * 60 * 1000);
      break;
    case 'day':
      until = new Date(until.getTime() + 24 * 60 * 60 * 1000);
      break;
    case 'week':
      until = new Date(until.getTime() + 7 * 24 * 60 * 60 * 1000);
      break;
    default:
      until = '';
  }
  return until.toString();
}

function refresh_list (el) {
  $.location('update', function(me) {
    var options = {};
    var time_window = until_time();
    if (time_window) {
      options['until'] = time_window;
    }
    $.getJSON('/api/event/list', options, function(event_list) {
      event_list.details.forEach(function(ev) {
        // calculate location
        var loc = {
          latitude: ev.lat,
          longitude: ev.lng
        };
        ev.dist = $.location('distance', me, loc);
        ev.dist = ev.dist.toFixed(2);

        //relatize datetime
        var rel_datetime = $.relatizeDate( ev.datetime );
        ev['datetime-figure'] = rel_datetime.figure;
        ev['datetime-unit'] = rel_datetime.unit;
      });
      var storage = window.sessionStorage;
      storage.setItem('event_list', JSON.stringify(event_list));
      resort_list();
    });
  });
}

// -- sortlist helpers

function resort_list () {
  var storage = window.sessionStorage;
  try {
    var event_list = JSON.parse(storage.getItem('event_list'));
  } catch (err) {
    console.log('event_list not loaded before sort attempted');
    return;
  }
  var sort_type = $('#listview').data('sorttype')
    , sort_button = $('#sortList')[0];
  switch(sort_type) {
    case 'time':
      // now must sort by time
      $('#listview').html($.mustache(LIST_VIEW, event_list));
      // set the button
      sort_button.innerText = 'Sort by distance';
      break;
    case 'distance':
    default:
      dist_bins = {}
      // now must sort by distance
      event_list.details.forEach(function(ev) {
        bin_dist(dist_bins, ev);
      });
      dist_list = [];
      $.each(dist_bins, function(bin, v) {
        dist_bin = parseInt(bin, 10);
        details = $.extend(v, {
          sep: ((dist_bin % 10 == 1) ? '> ' : '< ') + bin + 'm',
          dist_bin: dist_bin
        })
        insert_sorted(dist_list, function(a) { return a['dist_bin']; }, details);
      });
      $('#listview').html($.mustache(LIST_VIEW_DIST, { dists: dist_list }));
      // set the button
      sort_button.innerText = 'Sort by time';
  }
}

function bin_dist (dist_bins, ev) {
  var key = '0';
  if (ev.dist < 100) {
    key = '100';
  } else if (ev.dist < 500) {
    key = '500';
  } else {
    key = '501';
  }
  dist_bins[key] = dist_bins[key] || {};
  dist_bins[key].details = dist_bins[key].details || [];
  insert_sorted(dist_bins[key].details, function(a) { return a['dist']; }, ev);
}

function insert_sorted (arr, key, elem) {
  var ii = 0;
  while (arr[ii] && key(elem) > key(arr[ii])) {
    ii++;
  }
  arr.splice(ii, 0, elem);
}

/**
  --- Setup button handlers
*/

function click_sortChange (el) {
  var cur_sort_type = $('#listview').data('sorttype');
  switch(cur_sort_type) {
    case 'distance':
      $('#listview').data('sorttype', 'time');
      $('#listview').data('timewindow', 'all');
      $('#sortChange').html('Events Now');
      break;
    case 'time':
    default:
      $('#listview').data('sorttype', 'distance');
      $('#listview').data('timewindow', 'hour');
      $('#sortChange').html('All Events');
  }
  refresh_list();
}

function click_timeWindow (el) {
  var time_window = $(el).data('timewindow');
  $('#listview').data('timewindow', time_window);
  $(el).parent().children('.selected').removeClass('selected');
  $(el).addClass('selected');
}

function click_addEvent (el) {
  $('#add_location').val('Loading current location');
  // location
  $.location('update', function(loc) {
    $('#add_location_lat').val(loc.latitude);
    $('#add_location_lng').val(loc.longitude);
    $.location('revgeocode', loc, function(address) {
      $('#add_location').val(address);
    });
  });
  // time
  var now = new Date();
  $('#add_time').val(formatT.call(now));
  $('#add_date').val(formatD.call(now));
}

function click_submit (el) {
  // Disable the submit button
  $('#add_event #add_submit').attr("disabled", "disabled");
  // Clear and hide any error messages
  $('#add_event .formError').html('');
  // Set temaprary variables for the script
  var isFocus=0;
  var isError=0;
  // Get the data from the form

  var name= $('#add_event #add_name').val();
  var location= $('#add_event #add_location').val();
  var lat = $('#add_event #add_location_lat').val();
  var longitude = $('#add_event #add_location_lng').val();
  var date= $('#add_event #add_date').val();
  var time= $('#add_event #add_time').val();
  var description = $('#add_event #add_description').val();

  if (name==''){
    $('#add_event #errorName').html('This is a required field.');
    $('#add_event #add_name').focus()
    isFocus = 1;
    isError = 1;
  }

  if (!validateDateTime()){
    $('#add_event #errorDate').html('Error, this time has already passed');
    if (isFocus == 0){
      $('#add_event #add_date').focus();
      isFocus = 1;
    }
    isError = 1;
  }

  // Terminate the script if an error is found
  if(isError==1) {
    // Activate the submit button
    $('#add_event #add_submit').attr("disabled", "");
    return false;
  }

  var dataString = 'name='+ name + '&time=' + time + ' ' + date + '&lat=' + lat + '&long=' + longitude + '&address=' + location + '&description=' + description;

  $.ajax({
    type: "POST",
    url: "/api/event/add_event",
    data: dataString,
    success: function(msg) {
      $('#add_event #add_description').val('');
      $('#add_event #add_name').val('');
      $('#add_event #add_submit').attr("disabled", "");
      window.jQT.goTo("#list", "dissolve");
    },
    error: function(ob,errStr) {
      console.log("nooooo! failure");
      console.log(errStr);
    }

  });

  return false;
}

function shuffle_event() {

}

function set_homepage(ev) {
  $('#ev_name').text(ev.name);
  $('#ev_when').data('when', ev.when);
  $('#ev_image').attr('src', ev.image);
  $('#ev_where').attr('href', "http://maps.google.com/maps?q=" + ev.address);
  $('#ev_where').text(ev.where);
  $('#ev_dist').data('lat', ev.lat);
  $('#ev_dist').data('lon', ev.lon);
  $('#ev_description').text(ev.description);
  relatize_homepage_date();
  calc_homapage_dist();
}

function relatize_homepage_date() {
  var offset = $.relatizeDate($('#ev_when').data('when'))
  $('#ev_when_figure').text(offset.figure);
  $('#ev_when_unit').text(offset.unit);
}

function calc_homapage_dist() {
  $.location('update', function(myloc) {
    var ev_location = {
      latitude: $('#ev_dist').data('lat'),
      longitude: $('#ev_dist').data('lon')
    };
    var dist = $.location('distance', myloc, ev_location);
    $('#ev_dist_figure').text(dist.figure.toFixed(2));
    $('#ev_dist_unit').text(dist.unit);
  });
}

$(document).ready(function() {

  window.jQT = new $.jQTouch({
    icon: '/static/imgs/icon.png',
    addGlossToIcon: false,
    startupScreen: 'jqt_startup.png',
    statusBar: 'black',
    preloadImages: [
      '/static/themes/jqt/img/activeButton.png',
      '/static/themes/jqt/img/back_button.png',
      '/static/themes/jqt/img/back_button_clicked.png',
      '/static/themes/jqt/img/blueButton.png',
      '/static/themes/jqt/img/button.png',
      '/static/themes/jqt/img/button_clicked.png',
      '/static/themes/jqt/img/grayButton.png',
      '/static/themes/jqt/img/greenButton.png',
      '/static/themes/jqt/img/redButton.png',
      '/static/themes/jqt/img/whiteButton.png',
      '/static/themes/jqt/img/loading.gif'
      ]
  });

  // -- listview

  $('#list').bind('pageAnimationEnd', function(e, info) {
    if (info.direction == 'in') {
      refresh_list();
    }
  });

  // -- location_choice

  $('#add_location').blur(function(e) {
    var address = $('#add_location').val();
    if (address) {
      $.location('geocode', address, function(loc) {
        $('#add_location_lat').val(loc.latitude);
        $('#add_location_lng').val(loc.longitude);
      });
    }
  });

  // -- initialize event details
  // relatize date in #ev_when
  relatize_homepage_date()
  calc_homapage_dist()
  // get all the other events and store them

  // -- init events

});
<|endoftext|>


<commit_before>/**
 * Created by chunkyvr on 5/4/15.
 */
/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2015 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

describe("In Equipment Inventory Controller,", function () {
  var scope, $httpBackend, ctrl, routeParams, messageService;
  var geographicZone = {"name": "District 1", "parent": {"name": "Region 1"}};
  var facility = {"id": 1, "name": "Test Facility", "code": "F1", "description": "Test Facility Description",
    "geographicZone": geographicZone};
  var program = {"id": 2, "name": "Program 2", "code": "P2", "description": "Program 2 Description"};
  var program2 = {"id": 8, "name": "Program 8", "code": "P8", "description": "Program 8 Description"};
  var equipmentType = {"id": 3, "name": "Equipment Type 3", "code": "ET2",
    "description": "Equipment Type 3 Description"};
  var equipmentType2 = {"id": 9, "name": "Equipment Type 9", "code": "ET9",
    "description": "Equipment Type 9 Description"};
  var status = {"id": 4, "name": "Functional", "isBad": false};
  var status2 = {"id": 5, "name": "Not Functional", "isBad": true};
  var equipment = {"id": 6, name: "Dometic 400", code: "Dometic 400", "equipmentType": equipmentType,
    "equipmentTypeId": equipmentType.id, "manufacturer": "Dometic", "model": "400", "energyType": "gas"};
  var inventory = {"id": 7, "programId": program.id, "equipment": equipment, "facility": facility,
    "operationalStatusId": status.id};
  var pagination = {"totalRecords": 1, "page": 1};
  var groups = {};
  groups[geographicZone.parent.name] = {};
  groups[geographicZone.parent.name][geographicZone.name] = [inventory];

  beforeEach(module('openlmis'));

  beforeEach(inject(function ($rootScope, _$httpBackend_, $controller, $routeParams, _messageService_) {
    scope = $rootScope.$new();
    routeParams = $routeParams;
    $httpBackend = _$httpBackend_;
    messageService = _messageService_;

    ctrl = $controller(EquipmentInventoryController, {$scope: scope, $routeParams: routeParams,
      messageService: messageService});
    $httpBackend.whenGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
    $httpBackend.whenGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program, program2]});
    $httpBackend.whenGET('/equipment/type/operational-status.json').respond(200, {"status": [status]});
    $httpBackend.flush();
  }));

  describe("Programs", function () {
    it("should populate my facility and its programs if 'my facility' selected and home facility assigned", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program, program2]});
      $httpBackend.flush();
      expect(scope.myFacility).toEqual(facility);
      expect(scope.facilityDisplayName).toEqual(facility.code + " - " + facility.name);
      expect(scope.programs).toEqual([program, program2]);
    });

    it("should populate none assigned facility and no programs if 'my facility' selected and home facility not assigned", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": []});
      $httpBackend.flush();
      expect(scope.myFacility).toEqual(undefined);
      expect(scope.facilityDisplayName).toEqual(messageService.get("label.none.assigned"));
    });

    it("should populate programs if 'supervised facilities' selected", function () {
      scope.selectedType = "1";
      scope.loadPrograms();
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program, program2]});
      $httpBackend.flush();
      expect(scope.programs).toEqual([program, program2]);
    });

    it("should select program and load equipment types when only one program for my facility", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
    });

    it("should select program and load equipment types when only one program for supervised facilities", function () {
      scope.selectedType = "1";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
    });
  });

  describe("Equipment types", function () {

    it("should load equipment types for a program", function () {
      scope.selectedProgram = program;
      scope.loadEquipmentTypes();
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+scope.selectedProgram.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.equipmentTypes).toEqual([equipmentType, equipmentType2]);
    });

    it("should select program and equipment type (and load inventory) when only one option for each", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.loadEquipmentTypes();
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });
  });

  describe("Inventory", function () {

    it("should load inventory if program selected and equipment type selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = equipmentType;
      scope.loadInventory();
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.inventory).toEqual([inventory]);
    });

    it("should load EMPTY inventory if program selected and equipment type NOT selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = undefined;
      scope.loadInventory();
      expect(scope.inventory).toBeUndefined();
    });

    it("should load EMPTY inventory if program NOT selected and equipment type NOT selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = undefined;
      scope.selectedEquipmentType = undefined;
      scope.loadInventory();
      expect(scope.inventory).toBeUndefined();
    });

    it("should load districts and regions for facility inventory", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = equipmentType;
      scope.loadInventory();
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.groups).toEqual(groups);
    });
  });

  describe("Functional Status", function () {
    it("should NOT update status when inventory initially loaded", function () {
      var item = inventory;
      scope.updateStatus(item);
      expect(item.showSuccess).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should NOT update status when status is the same as previous", function () {
      var item = inventory;
      item.prevStatusId = status.id;
      scope.updateStatus(item);
      expect(item.showSuccess).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should update status when status is different from previous, and show success when successful", function () {
      var item = inventory;
      item.prevStatusId = status2.id;
      item.operationalStatusId = status.id;
      scope.operationalStatusList = [status, status2];
      scope.updateStatus(item);
      $httpBackend.expectPOST('/equipment/inventory/status/update.json').respond(200, {"inventory": [inventory]});
      $httpBackend.flush();
      expect(item.showSuccess).toBeDefined();
      expect(item.showError).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should open modal when status is different from previous and it is 'bad'", function () {
      var item = inventory;
      item.prevStatusId = status.id.toString();
      item.operationalStatusId = status2.id.toString();
      scope.operationalStatusList = [status, status2];
      scope.updateStatus(item);
      expect(scope.notFunctionalModal).toBeTruthy();
      expect(scope.modalItem).toBeDefined();
    });
  });

  describe("Initial load", function () {
    it("should select program and load equipment types when program is in route for my facility", function () {
      scope.selectedType = "0";
      routeParams.program = program.id.toString();
      routeParams.equipmentType = equipmentType.id.toString();
      scope.loadPrograms(true);
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });

    it("should select program and load equipment types when program is in route for supervised facilities", function () {
      scope.selectedType = "1";
      routeParams.program = program.id.toString();
      routeParams.equipmentType = equipmentType.id.toString();
      scope.loadPrograms(true);
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });
  });

  describe("Not Functional Modal", function () {
    it("should close modal on cancel, and reset values", function () {
      scope.origModalItem = angular.copy(inventory);
      scope.inventory = [inventory];
      scope.modalItem = inventory;
      scope.modalItem.operationalStatusId = status2.id.toString();
      scope.closeModal();
      expect(scope.notFunctionalModal).toBeFalsy();
//      expect(scope.inventory[0].operationalStatusId).toEqual(status.id.toString()); // TODO: need to uncomment and fix this
    });

    it("should save modal on successful save, and reset any values", function () {
      scope.modalItem = inventory;
      scope.notFunctionalForm = {};
      scope.notFunctionalForm.$invalid = false;
      scope.saveModal();
      $httpBackend.expectPOST('/equipment/inventory/save.json').respond(200, {"success": "Saved successfully"});
      $httpBackend.flush();
      expect(scope.notFunctionalModal).toBeFalsy();
      expect(scope.modalItem.showSuccess).toBeTruthy();
    });

    it("should give an error on invalid save", function () {
      scope.notFunctionalForm = {};
      scope.notFunctionalForm.$invalid = true;
      scope.saveModal();
      expect(scope.modalError).toEqual(messageService.get("message.equipment.inventory.data.invalid"));
    });

    it("should check for 'bad' functional status", function () {
      scope.notFunctionalStatusList = [status, status2];
      scope.modalItem = {};
      scope.checkForBadFunctionalStatus(status.id.toString());
      expect(scope.modalItem.badFunctionalStatusSelected).toEqual(status.isBad);
      scope.checkForBadFunctionalStatus(status2.id.toString());
      expect(scope.modalItem.badFunctionalStatusSelected).toEqual(status2.isBad);
    });
  });
});<commit_msg>Add test cases for new installation year code in inventory list screen<commit_after>/**
 * Created by chunkyvr on 5/4/15.
 */
/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2015 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License along with this program.  If not, see http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

describe("In Equipment Inventory Controller,", function () {
  var scope, $httpBackend, ctrl, routeParams, messageService;
  var geographicZone = {"name": "District 1", "parent": {"name": "Region 1"}};
  var facility = {"id": 1, "name": "Test Facility", "code": "F1", "description": "Test Facility Description",
    "geographicZone": geographicZone};
  var program = {"id": 2, "name": "Program 2", "code": "P2", "description": "Program 2 Description"};
  var program2 = {"id": 8, "name": "Program 8", "code": "P8", "description": "Program 8 Description"};
  var equipmentType = {"id": 3, "name": "Equipment Type 3", "code": "ET2",
    "description": "Equipment Type 3 Description"};
  var equipmentType2 = {"id": 9, "name": "Equipment Type 9", "code": "ET9",
    "description": "Equipment Type 9 Description"};
  var status = {"id": 4, "name": "Functional", "isBad": false};
  var status2 = {"id": 5, "name": "Not Functional", "isBad": true};
  var equipment = {"id": 6, name: "Dometic 400", code: "Dometic 400", "equipmentType": equipmentType,
    "equipmentTypeId": equipmentType.id, "manufacturer": "Dometic", "model": "400", "energyType": "gas"};
  var inventory = {"id": 7, "programId": program.id, "equipment": equipment, "facility": facility,
    "operationalStatusId": status.id};
  var pagination = {"totalRecords": 1, "page": 1};
  var groups = {};
  groups[geographicZone.parent.name] = {};
  groups[geographicZone.parent.name][geographicZone.name] = [inventory];

  beforeEach(module('openlmis'));

  beforeEach(inject(function ($rootScope, _$httpBackend_, $controller, $routeParams, _messageService_) {
    scope = $rootScope.$new();
    routeParams = $routeParams;
    $httpBackend = _$httpBackend_;
    messageService = _messageService_;

    ctrl = $controller(EquipmentInventoryController, {$scope: scope, $routeParams: routeParams,
      messageService: messageService});
    $httpBackend.whenGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
    $httpBackend.whenGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program, program2]});
    $httpBackend.whenGET('/equipment/type/operational-status.json').respond(200, {"status": [status]});
    $httpBackend.flush();
  }));

  describe("Programs", function () {
    it("should populate my facility and its programs if 'my facility' selected and home facility assigned", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program, program2]});
      $httpBackend.flush();
      expect(scope.myFacility).toEqual(facility);
      expect(scope.facilityDisplayName).toEqual(facility.code + " - " + facility.name);
      expect(scope.programs).toEqual([program, program2]);
    });

    it("should populate none assigned facility and no programs if 'my facility' selected and home facility not assigned", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": []});
      $httpBackend.flush();
      expect(scope.myFacility).toEqual(undefined);
      expect(scope.facilityDisplayName).toEqual(messageService.get("label.none.assigned"));
    });

    it("should populate programs if 'supervised facilities' selected", function () {
      scope.selectedType = "1";
      scope.loadPrograms();
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program, program2]});
      $httpBackend.flush();
      expect(scope.programs).toEqual([program, program2]);
    });

    it("should select program and load equipment types when only one program for my facility", function () {
      scope.selectedType = "0";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/facility/programs.json?facilityId='+facility.id).respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
    });

    it("should select program and load equipment types when only one program for supervised facilities", function () {
      scope.selectedType = "1";
      scope.loadPrograms();
      $httpBackend.expectGET('/user/facilities.json').respond(200, {"facilityList": [facility]});
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
    });
  });

  describe("Equipment types", function () {

    it("should load equipment types for a program", function () {
      scope.selectedProgram = program;
      scope.loadEquipmentTypes();
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+scope.selectedProgram.id+'.json').respond(200, {"equipment_types": [equipmentType, equipmentType2]});
      $httpBackend.flush();
      expect(scope.equipmentTypes).toEqual([equipmentType, equipmentType2]);
    });

    it("should select program and equipment type (and load inventory) when only one option for each", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.loadEquipmentTypes();
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });
  });

  describe("Inventory", function () {

    it("should load inventory if program selected and equipment type selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = equipmentType;
      scope.loadInventory();
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.inventory).toEqual([inventory]);
    });

    it("should load EMPTY inventory if program selected and equipment type NOT selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = undefined;
      scope.loadInventory();
      expect(scope.inventory).toBeUndefined();
    });

    it("should load EMPTY inventory if program NOT selected and equipment type NOT selected", function () {
      scope.selectedType = "0";
      scope.selectedProgram = undefined;
      scope.selectedEquipmentType = undefined;
      scope.loadInventory();
      expect(scope.inventory).toBeUndefined();
    });

    it("should load districts and regions for facility inventory", function () {
      scope.selectedType = "0";
      scope.selectedProgram = program;
      scope.selectedEquipmentType = equipmentType;
      scope.loadInventory();
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.groups).toEqual(groups);
    });
  });

  describe("Functional Status", function () {
    it("should NOT update status when inventory initially loaded", function () {
      var item = inventory;
      scope.updateStatus(item);
      expect(item.showSuccess).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should NOT update status when status is the same as previous", function () {
      var item = inventory;
      item.prevStatusId = status.id;
      scope.updateStatus(item);
      expect(item.showSuccess).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should update status when status is different from previous, and show success when successful", function () {
      var item = inventory;
      item.prevStatusId = status2.id;
      item.operationalStatusId = status.id;
      scope.operationalStatusList = [status, status2];
      scope.updateStatus(item);
      $httpBackend.expectPOST('/equipment/inventory/status/update.json').respond(200, {"inventory": [inventory]});
      $httpBackend.flush();
      expect(item.showSuccess).toBeDefined();
      expect(item.showError).toBeUndefined();
      expect(item.prevStatusId).toEqual(item.operationalStatusId);
    });

    it("should open modal when status is different from previous and it is 'bad'", function () {
      var item = inventory;
      item.prevStatusId = status.id.toString();
      item.operationalStatusId = status2.id.toString();
      scope.operationalStatusList = [status, status2];
      scope.updateStatus(item);
      expect(scope.notFunctionalModal).toBeTruthy();
      expect(scope.modalItem).toBeDefined();
    });
  });

  describe("Initial load", function () {
    it("should select program and load equipment types when program is in route for my facility", function () {
      scope.selectedType = "0";
      routeParams.program = program.id.toString();
      routeParams.equipmentType = equipmentType.id.toString();
      scope.loadPrograms(true);
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });

    it("should select program and load equipment types when program is in route for supervised facilities", function () {
      scope.selectedType = "1";
      routeParams.program = program.id.toString();
      routeParams.equipmentType = equipmentType.id.toString();
      scope.loadPrograms(true);
      $httpBackend.expectGET('/equipment/inventory/programs.json').respond(200, {"programs": [program]});
      $httpBackend.expectGET('/equipment/manage/typesByProgram/'+program.id+'.json').respond(200, {"equipment_types": [equipmentType]});
      $httpBackend.whenGET('/equipment/inventory/list.json?equipmentTypeId='+equipmentType.id+'&page='+pagination.page+'&programId='+program.id+'&typeId='+scope.selectedType).respond(200, {"inventory": [inventory], "pagination": pagination});
      $httpBackend.flush();
      expect(scope.selectedProgram).toEqual(program);
      expect(scope.selectedEquipmentType).toEqual(equipmentType);
      expect(scope.inventory).toEqual([inventory]);
    });
  });

  describe("Not Functional Modal", function () {
    it("should close modal on cancel, and reset values", function () {
      scope.origModalItem = angular.copy(inventory);
      scope.inventory = [inventory];
      scope.modalItem = inventory;
      scope.modalItem.operationalStatusId = status2.id.toString();
      scope.closeModal();
      expect(scope.notFunctionalModal).toBeFalsy();
//      expect(scope.inventory[0].operationalStatusId).toEqual(status.id.toString()); // TODO: need to uncomment and fix this
    });

    it("should save modal on successful save, and reset any values", function () {
      scope.modalItem = inventory;
      scope.notFunctionalForm = {};
      scope.notFunctionalForm.$invalid = false;
      scope.saveModal();
      $httpBackend.expectPOST('/equipment/inventory/save.json').respond(200, {"success": "Saved successfully"});
      $httpBackend.flush();
      expect(scope.notFunctionalModal).toBeFalsy();
      expect(scope.modalItem.showSuccess).toBeTruthy();
    });

    it("should give an error on invalid save", function () {
      scope.notFunctionalForm = {};
      scope.notFunctionalForm.$invalid = true;
      scope.saveModal();
      expect(scope.modalError).toEqual(messageService.get("message.equipment.inventory.data.invalid"));
    });

    it("should check for 'bad' functional status", function () {
      scope.notFunctionalStatusList = [status, status2];
      scope.modalItem = {};
      scope.checkForBadFunctionalStatus(status.id.toString());
      expect(scope.modalItem.badFunctionalStatusSelected).toEqual(status.isBad);
      scope.checkForBadFunctionalStatus(status2.id.toString());
      expect(scope.modalItem.badFunctionalStatusSelected).toEqual(status2.isBad);
    });
  });

  describe("Helper functions", function () {
    it("getAge should return age if installation year exists", function () {
      var testYear = 2015;
      var result = scope.getAge(testYear);
      expect(result).toBe(new Date().getFullYear() - testYear);
    });

    it("getAge should return null if installation year does NOT exist", function () {
      var testYear = undefined;
      var result = scope.getAge(testYear);
      expect(result).toBeNull();
    });

    it("getReplacementYear should return replacement year if installation year exists", function () {
      var testYear = 2015;
      var result = scope.getReplacementYear(testYear);
      expect(result).toBe(testYear + 10);
    });

    it("getReplacementYear should return null if installation year does NOT exist", function () {
      var testYear = undefined;
      var result = scope.getReplacementYear(testYear);
      expect(result).toBeNull();
    });
  });
});<|endoftext|>