<commit_before>module.exports = {

  status: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Retrieving fan status...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.getStatus().then(result => {
        return res.json({ device: device, result: result })
      }, reason => {
        return res.serverError(reason)
      })
    })
  },

  refresh: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Retrieving fan status...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.refresh()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  off: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan off...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.off()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  low: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to low...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.low()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  medium: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to medium...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.medium()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  high: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to high...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.high()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  }
}
<commit_msg>Method name fix<commit_after>module.exports = {

  status: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Retrieving fan status...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.getStatus().then(result => {
        return res.json({ device: device, result: result })
      }, reason => {
        return res.serverError(reason)
      })
    })
  },

  refresh: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Retrieving fan status...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.refresh()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  off: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan off...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.fanOff()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  low: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to low...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.fanLow()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  medium: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to medium...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.fanMedium()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  },

  high: (req, res) => {
    var insteonId = req.params.insteonId
    console.log(`[${insteonId}] Turning fan to high...`)
    Fan.findOne({ insteonId: insteonId }).exec((err, device) => {
      if (err) {
        return res.serverError(err)
      }
      if (!device) {
        return res.notFound({ error: `Fan with Insteon ID ${insteonId} not found` })
      }
      device.fanHigh()
      return res.json({ insteon_id: insteonId, command: req.options.action })
    })
  }
}
<|endoftext|>


<commit_before>/*
JsUnit - a JUnit port for JavaScript
Copyright (C) 1999,2000,2001,2002,2003,2006 Joerg Schaible

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation in version 2 of the License.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

Test suites built with JsUnit are derivative works derived from tested
classes and functions in their production; they are not affected by this
license.
*/

var hasExceptions = "false";
var exceptionsWorking = "false";
var i;

function throwEx()
{
    throw new Object();
}
function testEx()
{
    var me = this;
    try { hasExceptions = "true"; new throwEx(); }
    catch( ex ) { exceptionsWorking = this == me; }
}
new testEx();

if( !this.JsUtil )
{
    if( this.WScript )
    {
        var fso = new ActiveXObject( "Scripting.FileSystemObject" );
        var file = fso.OpenTextFile( "../lib/jsunit/lib/JsUtil.js", 1 );
        var all = file.ReadAll();
        file.Close();
        eval( all );
    }
    else
        load( "../lib/jsunit/lib/JsUtil.js" );
}
if( !JsUtil.prototype.isBrowser )
{
    var writer = JsUtil.prototype.getSystemWriter();
    /*
    // o = this.Environment;
    o = this;
    writer.println( "Object: " + o );
    for( i in o )
        writer.println( " i is " + i );
    JsUtil.ptototype.quit(0);
    */
    writer.println( "\nJavaScript compatibility:" );
    writer.println( "\thas exceptions: " + hasExceptions );
    writer.println( "\texceptions working: " + exceptionsWorking );

    writer.println( "\nJavaScript engine detection:" );
    for( i in JsUtil.prototype )
        if( typeof JsUtil.prototype[i] != "function" && i.match( /^(is|has)/ ))
            writer.println( "\t" + i + ": " + JsUtil.prototype[i] );

    writer.println( "\nJsUnit Test Suite:\n" );
}
if( exceptionsWorking )
{
    eval( JsUtil.prototype.include( "../lib/jsunit/lib/JsUnit.js" ));
    eval( JsUtil.prototype.include( "../plugins/hash/hash.js" ));
    eval( JsUtil.prototype.include( "HashTest.js" ));

    function AllTests()
    {
        TestSuite.call( this, "AllTests" );
    }
    function AllTests_suite()
    {
        var suite = new AllTests();
        suite.addTest( JsUtilTestSuite.prototype.suite());
        return suite;
    }
    AllTests.prototype = new TestSuite();
    AllTests.prototype.suite = AllTests_suite;
}
if( JsUtil.prototype.isShell )
{
    if( !exceptionsWorking )
    {
        writer.println( "\tSorry, exceptions not working!\n" );
        JsUtil.prototype.quit( -1 );
    }
    var args;
    if( this.WScript )
    {
        args = new Array();
        for( i = 0; i < WScript.Arguments.Count(); ++i )
            args[i] = WScript.Arguments( i );
    }
    else if( this.arguments )
        args = arguments;
    else
        args = new Array();

    var result = TextTestRunner.prototype.main( args );
    JsUtil.prototype.quit( result );
}

<commit_msg>Removing JSUnit test runner file in preparation for moving to Windmill for unit-testing.<commit_after><|endoftext|>


<commit_before>$.Model.extend('ApiType',
/* @Static */
{

	init: function() {
		this.types = [];
		this.url_param_reg = /\{.*?\}/g;

		this.interpolations = {lists: {}};
		//console.log("initting apitype");
	},

	find_type: function(uri) {
	//		console.log("finding type " + uri)
		var r = $.grep(this.types, function(t){return (t.type == uri);});
		return r[0];
	},

	find_all_types_and_calls: function() {
		SMART.get_ontology().then(function(r) {
            var ont = r.graph;
			this.ontology = ont;

			// Get all types
			var types = ont.where("?cls rdf:type owl:Class")
			               .where("?cls rdfs:label ?class_name")
		   		       .optional("?cls api:example ?class_example");

			for (var i = 0; i < types.length; i++) {
				ApiType.create(types[i]);
			}

			// Get all calls
			var calls = ont.where("?call rdf:type api:Call")
			   .where("?call api:path ?call_path")
			   .where("?call api:target ?call_target")
			   .where("?call api:cardinality ?call_cardinality")
			   .where("?call api:category ?call_category")
			   .where("?call api:httpMethod ?call_method")
			   .optional("?call api:example ?call_example");

			for (var i = 0; i < calls.length; i++) {
			    if (calls[i].call_category.value.match(/^record/)) {
                    var params = [];
                    var filters = ont.where(calls[i].call.toString() + " rdf:type api:Call")
                                     .where(calls[i].call.toString() + " api:hasFilter ?f")
                                     .where("?f api:clientParameterName ?call_parameter");
                    for (var j = 0; j < filters.length; j++) {
                        params.push (filters[j].call_parameter.value.toString());
                    }
                    ApiCall.create(calls[i], params);
                }
			}

			ApiCallGroup.make_groups();
		    OpenAjax.hub.publish("ontology_parsed");
		});
	},

	addInterpolationValue: function(field, value) {
        //console.log ("adding interpolation " + field + " " + value);
		if (this.interpolations.lists[field] === undefined)
			this.interpolations.lists[field] = [];

		if ($.inArray(value, this.interpolations.lists[field]) === -1)
			this.interpolations.lists[field].push(value);

	},
	interpolationVars: function(url) {
		var fields = url.match(ApiType.url_param_reg);
		if (fields === null)
		    return [];

		return fields;
	},

	interpolatedPath: function(url) {
		var url = url.replace("http://smartplatforms.org", "");
		var fields = this.interpolationVars(url);

		for (var i = 0; i < fields.length; i++) {
			var f = fields[i];
			var fsans = f.substring(1, f.length-1);

			url = url.replace(f, this.interpolations[fsans]);
		}
		return url;
	},

    paramsArray: function(params) {
		var res = {},
            val;

		for (var i = 0; i < params.length; i++) {
			val = this.interpolations[params[i]];
            if (val && val.length > 0) {
                res[params[i]] = val;
            }
		}
		return res;
	},

	create: function(t) {
		var ret = new ApiType({
			type: t.cls.value._string,
			name: t.class_name.value,
			example: t.class_example && t.class_example.value  || ""
		});
		this.types.push(ret);
		return ret;
	},

	pathRegex: function(path) {
		var b = path;
		var fields = b.match(ApiType.url_param_reg);
  	        if (fields == null) fields = [];

		var b = b.replace("http://smartplatforms.org", "");
		for (var i = 0; i < fields.length; i++) {
			var f = fields[i];
			var fsans = f.substring(1, f.length-1);

			b = b.replace(fields[i], "([^/]*)");
		}

		var re = RegExp(b);
		var ret = function(s) {
			var matches = {};
			var m = s.match(re);
			if (m === null) return matches;
			if (m.length != fields.length+1){
				throw "Expected match size = fields size+1"  +
							 m.join(", ")+ " vs. " + fields.join(", ");
			}
			for (var i = 0; i < fields.length; i++) {
				var f = fields[i];
				var fsans = f.substring(1, f.length-1);
				matches[fsans] = m[i+1];
			}

			return matches;
		};

		//console.log("regex " + b);
		return ret;
	}

},
/* @Prototype */
{

  init: function()
  {
	this.calls = [];
  },

  fetchParameters: function() {
   		var c = this.fetchParametersCall();
   		var base_regexes = [];

		$.each(ApiCall.calls, function(path, call) {
			if (call.method == "GET")
				base_regexes.push(ApiType.pathRegex(call.path));
		});

		var args = c.buildCallArgs("", function(r) {

            if (r.contentType === "application/rdf+xml") {
                var rdf = SMART.process_rdf(r.contentType, r.body);
                var typed_entities = rdf.where("?s rdf:type ?p");
                //console.log("Typed entities available: " + typed_entities.length);

                $.each(typed_entities, function() {
                    var entity_url = ""+this.s.value;
                    $.each(base_regexes, function() {
                        var matched = this(entity_url);
                        $.each(matched, function(fieldname, value) {
                            ApiType.addInterpolationValue(fieldname, value);
                        });

                    });

                });
            }
            ApiType.addInterpolationValue("user_id",SMART.user.id);
            ApiType.addInterpolationValue("smart_app_id",SMART.manifest.id);

   		});

   		SMART.api_call(args[0], args[1]);
   },

   fetchParametersCall: function() {
	   var uri = this.type;
  	   var c = $.grep(ApiCall.calls, function(c) {return c.method=="GET" && c.target === uri && c.category==="record" &&
                      c.cardinality=="multiple";})[0];
       return c;
   }
});

$.Model.extend('ApiCall',
/* @Static */
{

	init: function() {
		this.calls = [];
		this.payload_methods = ['PUT', 'POST'];
		},

	create: function(t, params) {

        if (t.call_cardinality.value === "multiple") {
            params.push ("limit");
            //params.push ("offset");
        }

		ret = new ApiCall({path: t.call_path.value,
						   target: t.call_target.value._string,
                  				   example: t.call_example?t.call_example.value : undefined,
						   category: t.call_category.value,
						   cardinality: t.call_cardinality.value,
						   method: t.call_method.value,
                           parameters: params,
                          });

		this.calls.push(ret);
		return ret;
	}

},
/* @Prototype */
{
	init: function()
	{

	},

	contentType: function() {
		if ($.inArray(this.method, this.Class.payload_methods) !== -1) {
            var path = this.path
            var suffix = "/preferences";
            if (path.indexOf(suffix, path.length - suffix.length) !== -1) {
                // Preferences API call
                return "text/plain";
            } else {
                // All other calls
                return "application/rdf+xml";
            }
		}
		return "application/x-www-form-urlencoded";
	},

	callAPI: function(data, callback) {
		var args = this.buildCallArgs(data, callback);
		SMART.api_call(args[0], args[1]);
	},

	buildCallArgs: function(data, callback) {

        var params = ApiType.paramsArray(this.parameters);

		var call_args = [{
			method: this.method,
			url: ApiType.interpolatedPath(this.path),
			contentType: this.contentType(),
			data: data || params
		}, callback];

		return call_args;
	}
});

$.Model.extend('ApiCallGroup',
/* @Static */
{

	init: function() {
		this.groups= [];
	},

	get_top_groups: function() {
		var g = $.grep(this.groups, function(g) {return (g.group_parent === null);});
		g.sort(function(a,b) {if (a.group_name === b.group_name) return 0; if (a.group_name < b.group_name) return -1; return 1;});
		return g;
	},

	make_groups: function() {
		this.make_record_item_groups();
	},

	make_record_item_groups: function() {
		this.record_item_groups_by_path();
		this.record_item_groups_by_target();

	},

	record_item_groups_by_path: function() {
		var by_path = {};

		$.each(ApiCall.calls, function(i, call) {
			if (by_path[call.path] === undefined )
				by_path[call.path] = [];
			by_path[call.path].push(call);
		});

		$.each(by_path, function(path, calls) {
			var gn = calls[0].cardinality;

			var p = {
					   group_name: gn,
					   group_members: calls,
					   group_parent: null,
					   group_indentation_hint: null,
					   group_type: ApiType.find_type(calls[0].target)
					 };

			var g = new ApiCallGroup({p: p})
			ApiCallGroup.groups.push(g);
		});
	},

	record_item_groups_by_target: function() {
		var by_target = {};
		$.each(ApiCallGroup.groups, function(i, group) {
			var t = group.group_members[0].target;
			if (by_target[t] === undefined )
				by_target[t] = [];
			by_target[t].push(group);
		});

		$.each(by_target, function(target, groups) {
			groups.sort(function(a,b) {if (a.group_name === b.group_name) return 0; if (a.group_name > b.group_name) return -1; return 1;});

			var t= ApiType.find_type(target);

			var p = {
					   group_name: t.name,
					   group_members: groups,
					   group_parent: null,
					   group_indentation_hint: null,
					   group_type: groups[0].group_type
					 };
			var parent = new ApiCallGroup({p: p});
			ApiCallGroup.groups.push(parent);
			$.each(groups, function(i,g) {g.group_parent = parent});
		});
	}

},
/* @Prototype */
{
	init: function()
	{
		this.group_name = this.p.group_name;
		this.group_members = this.p.group_members;

		// the group to which this belongs, if any
		this.group_parent = this.p.group_parent;

		// e.g. fills should be indented below meds
		this.group_indentation_hint = this.p.group_indentation_hint;
		this.group_type = this.p.group_type;
    }
});
<commit_msg>Enables parameter listing in API Playground<commit_after>$.Model.extend('ApiType',
/* @Static */
{

	init: function() {
		this.types = [];
		this.url_param_reg = /\{.*?\}/g;

		this.interpolations = {lists: {}};
		//console.log("initting apitype");
	},

	find_type: function(uri) {
	//		console.log("finding type " + uri)
		var r = $.grep(this.types, function(t){return (t.type == uri);});
		return r[0];
	},

	find_all_types_and_calls: function() {
		SMART.get_ontology().then(function(r) {
            var ont = r.graph;
			this.ontology = ont;

			// Get all types
			var types = ont.where("?cls rdf:type owl:Class")
			               .where("?cls rdfs:label ?class_name")
		   		       .optional("?cls api:example ?class_example");

			for (var i = 0; i < types.length; i++) {
				ApiType.create(types[i]);
			}

			// Get all calls
			var calls = ont.where("?call rdf:type api:Call")
			   .where("?call api:path ?call_path")
			   .where("?call api:target ?call_target")
			   .where("?call api:cardinality ?call_cardinality")
			   .where("?call api:category ?call_category")
			   .where("?call api:httpMethod ?call_method")
			   .optional("?call api:example ?call_example");

			for (var i = 0; i < calls.length; i++) {
			    if (calls[i].call_category.value.match(/^record/)) {
                    var params = [];
                    var parameters = ont.where(calls[i].call.toString() + " rdf:type api:Call")
                                     .where(calls[i].call.toString() + " api:hasParameter ?p")
                                     .where("?p api:clientParameterName ?call_parameter");
                    for (var j = 0; j < parameters.length; j++) {
                        params.push (parameters[j].call_parameter.value.toString());
                    }
                    var filters = ont.where(calls[i].call.toString() + " rdf:type api:Call")
                                     .where(calls[i].call.toString() + " api:hasFilter ?f")
                                     .where("?f api:clientParameterName ?call_parameter");
                    for (var j = 0; j < filters.length; j++) {
                        params.push (filters[j].call_parameter.value.toString());
                    }
                    ApiCall.create(calls[i], params);
                }
			}

			ApiCallGroup.make_groups();
		    OpenAjax.hub.publish("ontology_parsed");
		});
	},

	addInterpolationValue: function(field, value) {
        //console.log ("adding interpolation " + field + " " + value);
		if (this.interpolations.lists[field] === undefined)
			this.interpolations.lists[field] = [];

		if ($.inArray(value, this.interpolations.lists[field]) === -1)
			this.interpolations.lists[field].push(value);

	},
	interpolationVars: function(url) {
		var fields = url.match(ApiType.url_param_reg);
		if (fields === null)
		    return [];

		return fields;
	},

	interpolatedPath: function(url) {
		var url = url.replace("http://smartplatforms.org", "");
		var fields = this.interpolationVars(url);

		for (var i = 0; i < fields.length; i++) {
			var f = fields[i];
			var fsans = f.substring(1, f.length-1);

			url = url.replace(f, this.interpolations[fsans]);
		}
		return url;
	},

    paramsArray: function(params) {
		var res = {},
            val;

		for (var i = 0; i < params.length; i++) {
			val = this.interpolations[params[i]];
            if (val && val.length > 0) {
                res[params[i]] = val;
            }
		}
		return res;
	},

	create: function(t) {
		var ret = new ApiType({
			type: t.cls.value._string,
			name: t.class_name.value,
			example: t.class_example && t.class_example.value  || ""
		});
		this.types.push(ret);
		return ret;
	},

	pathRegex: function(path) {
		var b = path;
		var fields = b.match(ApiType.url_param_reg);
  	        if (fields == null) fields = [];

		var b = b.replace("http://smartplatforms.org", "");
		for (var i = 0; i < fields.length; i++) {
			var f = fields[i];
			var fsans = f.substring(1, f.length-1);

			b = b.replace(fields[i], "([^/]*)");
		}

		var re = RegExp(b);
		var ret = function(s) {
			var matches = {};
			var m = s.match(re);
			if (m === null) return matches;
			if (m.length != fields.length+1){
				throw "Expected match size = fields size+1"  +
							 m.join(", ")+ " vs. " + fields.join(", ");
			}
			for (var i = 0; i < fields.length; i++) {
				var f = fields[i];
				var fsans = f.substring(1, f.length-1);
				matches[fsans] = m[i+1];
			}

			return matches;
		};

		//console.log("regex " + b);
		return ret;
	}

},
/* @Prototype */
{

  init: function()
  {
	this.calls = [];
  },

  fetchParameters: function() {
   		var c = this.fetchParametersCall();
   		var base_regexes = [];

		$.each(ApiCall.calls, function(path, call) {
			if (call.method == "GET")
				base_regexes.push(ApiType.pathRegex(call.path));
		});

		var args = c.buildCallArgs("", function(r) {

            if (r.contentType === "application/rdf+xml") {
                var rdf = SMART.process_rdf(r.contentType, r.body);
                var typed_entities = rdf.where("?s rdf:type ?p");
                //console.log("Typed entities available: " + typed_entities.length);

                $.each(typed_entities, function() {
                    var entity_url = ""+this.s.value;
                    $.each(base_regexes, function() {
                        var matched = this(entity_url);
                        $.each(matched, function(fieldname, value) {
                            ApiType.addInterpolationValue(fieldname, value);
                        });

                    });

                });
            }
            ApiType.addInterpolationValue("user_id",SMART.user.id);
            ApiType.addInterpolationValue("smart_app_id",SMART.manifest.id);

   		});

   		SMART.api_call(args[0], args[1]);
   },

   fetchParametersCall: function() {
	   var uri = this.type;
  	   var c = $.grep(ApiCall.calls, function(c) {return c.method=="GET" && c.target === uri && c.category==="record" &&
                      c.cardinality=="multiple";})[0];
       return c;
   }
});

$.Model.extend('ApiCall',
/* @Static */
{

	init: function() {
		this.calls = [];
		this.payload_methods = ['PUT', 'POST'];
		},

	create: function(t, params) {

        if (t.call_cardinality.value === "multiple") {
            params.push ("limit");
            //params.push ("offset");
        }

		ret = new ApiCall({path: t.call_path.value,
						   target: t.call_target.value._string,
                  				   example: t.call_example?t.call_example.value : undefined,
						   category: t.call_category.value,
						   cardinality: t.call_cardinality.value,
						   method: t.call_method.value,
                           parameters: params,
                          });

		this.calls.push(ret);
		return ret;
	}

},
/* @Prototype */
{
	init: function()
	{

	},

	contentType: function() {
		if ($.inArray(this.method, this.Class.payload_methods) !== -1) {
            var path = this.path
            var suffix = "/preferences";
            if (path.indexOf(suffix, path.length - suffix.length) !== -1) {
                // Preferences API call
                return "text/plain";
            } else {
                // All other calls
                return "application/rdf+xml";
            }
		}
		return "application/x-www-form-urlencoded";
	},

	callAPI: function(data, callback) {
		var args = this.buildCallArgs(data, callback);
		SMART.api_call(args[0], args[1]);
	},

	buildCallArgs: function(data, callback) {

        var params = ApiType.paramsArray(this.parameters);

		var call_args = [{
			method: this.method,
			url: ApiType.interpolatedPath(this.path),
			contentType: this.contentType(),
			data: data || params
		}, callback];

		return call_args;
	}
});

$.Model.extend('ApiCallGroup',
/* @Static */
{

	init: function() {
		this.groups= [];
	},

	get_top_groups: function() {
		var g = $.grep(this.groups, function(g) {return (g.group_parent === null);});
		g.sort(function(a,b) {if (a.group_name === b.group_name) return 0; if (a.group_name < b.group_name) return -1; return 1;});
		return g;
	},

	make_groups: function() {
		this.make_record_item_groups();
	},

	make_record_item_groups: function() {
		this.record_item_groups_by_path();
		this.record_item_groups_by_target();

	},

	record_item_groups_by_path: function() {
		var by_path = {};

		$.each(ApiCall.calls, function(i, call) {
			if (by_path[call.path] === undefined )
				by_path[call.path] = [];
			by_path[call.path].push(call);
		});

		$.each(by_path, function(path, calls) {
			var gn = calls[0].cardinality;

			var p = {
					   group_name: gn,
					   group_members: calls,
					   group_parent: null,
					   group_indentation_hint: null,
					   group_type: ApiType.find_type(calls[0].target)
					 };

			var g = new ApiCallGroup({p: p})
			ApiCallGroup.groups.push(g);
		});
	},

	record_item_groups_by_target: function() {
		var by_target = {};
		$.each(ApiCallGroup.groups, function(i, group) {
			var t = group.group_members[0].target;
			if (by_target[t] === undefined )
				by_target[t] = [];
			by_target[t].push(group);
		});

		$.each(by_target, function(target, groups) {
			groups.sort(function(a,b) {if (a.group_name === b.group_name) return 0; if (a.group_name > b.group_name) return -1; return 1;});

			var t= ApiType.find_type(target);

			var p = {
					   group_name: t.name,
					   group_members: groups,
					   group_parent: null,
					   group_indentation_hint: null,
					   group_type: groups[0].group_type
					 };
			var parent = new ApiCallGroup({p: p});
			ApiCallGroup.groups.push(parent);
			$.each(groups, function(i,g) {g.group_parent = parent});
		});
	}

},
/* @Prototype */
{
	init: function()
	{
		this.group_name = this.p.group_name;
		this.group_members = this.p.group_members;

		// the group to which this belongs, if any
		this.group_parent = this.p.group_parent;

		// e.g. fills should be indented below meds
		this.group_indentation_hint = this.p.group_indentation_hint;
		this.group_type = this.p.group_type;
    }
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import {
  ActivityIndicator,
  AppRegistry,
  StyleSheet,
  Text,
  View,
} from 'react-native';

import { Button } from 'react-native-elements';

import { API_URL } from '../Config';

export default class SetupNuimo extends Component {
  static navigationOptions = {
    title: 'Nuimo',
  };

  constructor() {
    super()

    this.state = {
      nuimos: [],
    }
  }

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View style={styles.container}>
        <View>
          <Text style={styles.title}>
            We're now looking for your Nuimo
          </Text>
        </View>

        <View style={this.state.nuimos.length > 0 ? '' : styles.hidden}>
          <Text style={styles.title}>
            Found your Nuimo {this.state.nuimos[0]}
          </Text>
        </View>

        <ActivityIndicator animating={this.state.nuimos.length === 0} />

        <View>
          <Button buttonStyle={styles.button} disabled={this.state.nuimos.length === 0} onPress={() => navigate('Devices')} title="Continue" />
        </View>
      </View>
    );
  }

  componentDidMount() {
    this.bootstrapNuimos()
  }

  bootstrapNuimos() {
    //TODO: Promise chain doesn't get cancelled when component unmounts
    //TODO: Write tests for all possible API call responses, server not available, etc.
    fetch(API_URL + '/-/setup/nuimo/bootstrap', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({})
    })
      .then((response) => response.json())
      .then((response) => {
        let controllers = response.connectedControllers
        if (controllers.length > 0) {
          this.setState({ nuimos: controllers })
        }
        else {
          // Try again to bootstrap
          this.bootstrapNuimos()
        }
      })
      .catch((error) => {
        console.error(error)
        // Try again to bootstrap
        //TODO: Only retry after a few seconds after an error occurred. Cancel timer if component unmounted.
        this.bootstrapNuimos()
      })
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    padding: 10,
  },
  title: {
    fontSize: 18,
    textAlign: 'center',
    margin: 10,
  },
  hidden: {
    display: 'none',
  },
  button: {
    backgroundColor: '#397af8',
  }
});

AppRegistry.registerComponent('SetupNuimo', () => SetupNuimo);
<commit_msg>Show error and stop calling failing endpoint<commit_after>import React, { Component } from 'react';
import {
  ActivityIndicator,
  AppRegistry,
  StyleSheet,
  Text,
  View,
} from 'react-native';

import { Button } from 'react-native-elements';

import { API_URL } from '../Config';

export default class SetupNuimo extends Component {
  static navigationOptions = {
    title: 'Nuimo',
  };

  constructor() {
    super()

    this.state = {
      nuimos: [],
    }
  }

  render() {
    const { navigate } = this.props.navigation;
    return (
      <View style={styles.container}>
        <View>
          <Text style={styles.title}>
            We're now looking for your Nuimo
          </Text>
        </View>

        <View style={this.state.nuimos.length > 0 ? '' : styles.hidden}>
          <Text style={styles.title}>
            Found your Nuimo {this.state.nuimos[0]}
          </Text>
        </View>

        <ActivityIndicator animating={this.state.nuimos.length === 0} />

        <View>
          <Button buttonStyle={styles.button} disabled={this.state.nuimos.length === 0} onPress={() => navigate('Devices')} title="Continue" />
        </View>
      </View>
    );
  }

  componentDidMount() {
    this.bootstrapNuimos()
  }

  bootstrapNuimos() {
    //TODO: Promise chain doesn't get cancelled when component unmounts
    //TODO: Write tests for all possible API call responses, server not available, etc.
    fetch(API_URL + '/-/setup/nuimo/bootstrap', {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({})
    })
      .then((response) => {
        if (response.ok) {
          return response.json()
        }
        throw new Error('Request failed: ' + JSON.stringify(response))
      })
      .then((response) => {
        let controllers = response.connectedControllers
        if (controllers.length > 0) {
          this.setState({ nuimos: controllers })
        }
        else {
          // Try again to bootstrap
          this.bootstrapNuimos()
        }
      })
      .catch((error) => {
        alert(error)
      })
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'column',
    justifyContent: 'space-between',
    padding: 10,
  },
  title: {
    fontSize: 18,
    textAlign: 'center',
    margin: 10,
  },
  hidden: {
    display: 'none',
  },
  button: {
    backgroundColor: '#397af8',
  }
});

AppRegistry.registerComponent('SetupNuimo', () => SetupNuimo);
<|endoftext|>


<commit_before>/*! This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

"use strict";

var {setTimeout} = require("sdk/timers");
var {getBrowserForTab, getTabForId} = require("sdk/tabs/utils");
var tabs = require("sdk/tabs");
var {data} = require("sdk/self");
var logger = require("./logger");
var {URL} = require("sdk/url");
var utils = require("./utils");
var notifications = require("sdk/notifications");
var featuredata = require("./featuredata");
var config = require("./config");
var windows = require("sdk/windows");
var {sendEvent, sendToGA, override}  = require("./utils");
var ui = require("./ui");
var info = require("./generalInfo");

//stores what action each webpage should map to
var URLToActionMapper = {
	"www.youtube.com": ytDetected,
	"www.gmail.com": gmailDetected, "mail.google.com": gmailDetected,
	"www.fifa.com": soccerDetected,
	"www.goal.com": soccerDetected,
	"www.reddit.com": redditDetected,
	"www.amazon.com": amazonDetected, "www.amazon.ca": amazonDetected,
	"stoppornculture.org": pornDetected,
	"www.facebook.com": facebookDetected,
	"www.google.com": googleDetected, "www.google.ca": googleDetected,  "www.bing.com": bingDetected, "www.bing.ca": bingDetected,
	"en.wikipedia.org": wikipediaDetected, "search.yahoo.com": yahooDetected, "search.yahoo.ca": yahooDetected
};

//stores basic information needed when recommending addons
var addonData = {
	"1click-yt-download": {name: "1-Click YouTube Video Download", link: "https://addons.mozilla.org/firefox/downloads/latest/13990/addon-13990-latest.xpi"},
	"gmail-notifier": {name: "Gmail Notifier", link: "https://addons.mozilla.org/firefox/downloads/latest/406178/addon-406178-latest.xpi"},
	"flashgot": {name: "FlashGot Mass Downloader", link: "https://addons.mozilla.org/firefox/downloads/latest/220/addon-220-latest.xpi"},
	"googletranslator": {name: "Google™ Translator", link: "https://addons.mozilla.org/firefox/downloads/latest/493406/addon-493406-latest.xpi"},
	"redditenhancement": {name: "Reddit Enhancement Suite", link: "https://addons.mozilla.org/firefox/downloads/latest/387429/addon-387429-latest.xpi"},
	"amazonwishlistbutton": {name: "Amazon \"Add to Wish List\" Button", link: "https://addons.mozilla.org/firefox/downloads/latest/257015/addon-257015-latest.xpi"},
	"quickmark": {name: "Quick Mark", link: "https://addons.mozilla.org/firefox/downloads/latest/462572/addon-462572-latest.xpi"}
}

//carries out right action based on (hostname of) current website
function mapActiveURLToAction(aBrowser, aWebProgress, aRequest, aLocation){

	var tab = tabs.activeTab;
	logger.log("mapActiveURLToAction");

	// if (tab.id == activeTab.id)
	if (getBrowserForTab(getTabForId(tab.id)) == aBrowser) {

		var hostname = URL(tab.url).hostname;
		logger.log(hostname);

		if (hostname in URLToActionMapper) URLToActionMapper[hostname]();
	}

}

function minorTriggerCount(featurename){
	var count = featuredata.get(featurename, "count");
	count ++;
	featuredata.set(featurename, "count", count);

	utils.sendMinorTriggerEvent({count: count}, featurename);

	return count;
}

// recommends installing a DL manager to user
function recommendDLManager(download){

	var count = minorTriggerCount("download");

	var triggerId = "newdownload";
	var name = "newdownload";

	if (count == config.DOWNLOAD_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "flashgot", triggerId: triggerId});
	}
}

// recommends a specific addon to user
function recommendAddon(options){

	logger.log("recommending addon");

	ui.showNotification({
		message: "Wanna try downloading " + addonData[options.addonID].name + " ?",
		header: "A Cool Addon",
		reactionType: "openlinkinactivetab",
		reactionOptions: {url: addonData[options.addonID].link},
		buttonLabel: "Install Addon",
		id: options.triggerId
		});

	utils.sendOfferingEvent(config.TYPE_OFFERING_ADDON, options, options.triggerId);

}

function ytDetected(){

	logger.log("ytDetect");

	var count = minorTriggerCount("youtube");

	var triggerId = "youtube";
	var name = "youtube";

	if (count == config.YOUTUBE_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "1click-yt-download", triggerId: triggerId});
	}
}

function gmailDetected(){
	var count = minorTriggerCount("gmail");

	var triggerId = "gmail";
	var name = "gmail";

	if (count == config.GMAIL_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "gmail-notifier", triggerId: triggerId});
	}
}

function soccerDetected(){

}

function recommendTranslator(){
	var count = minorTriggerCount("translator");

	var triggerId = "foreignpage";
	var name = "foreignpage";

	if (count == config.TRANSLATOR_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "googletranslator", triggerId: triggerId});
	}
}

function redditDetected(){
	var count = minorTriggerCount("reddit");

	var triggerId = "reddit";
	var name = "reddit";

	if (count == config.REDDIT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "redditenhancement", triggerId: triggerId});
	}
}

function amazonDetected(){
	var count = minorTriggerCount("amazon");

	var triggerId = "amazon";
	var name = "amazon";

	if (count == config.AMAZON_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "amazonwishlistbutton", triggerId: triggerId});
	}

	extractSearchQuery("amazon");
}

function googleDetected(){

	extractSearchQuery("google");
}

function bingDetected(){
	extractSearchQuery("bing");
}

function yahooDetected(){
	extractSearchQuery("yahoo");
}

function wikipediaDetected(){
	extractSearchQuery("wikipedia");
}

function extractSearchQuery(engine){

	logger.log(URL(tabs.activeTab.url).search);

	var qSeparator;
	var qKeyword;

	var autoSeparator = true; //if set, checks both + and space (ignores qSeparator)

	switch (engine){
		case "google":
			qSeparator = "+";
			qKeyword = "q";
		break;
		case "bing":
			qSeparator = "+";
			qKeyword = "q";
		break;
		case "wikipedia":
			qSeparator = "+";
			qKeyword = "search";

		break;
		case "yahoo":
			qSeparator = "+";
			qKeyword = "p";

		break;
		case "amazon":
			qSeparator = "+";
			qKeyword = "field-keywords";

		break;

	}

	var queryRegExp = new RegExp(".*(?:\\?|\\#|&)" + qKeyword + "=(.*?)(?:\\&|$)", "i");

	var allQs = queryRegExp.exec(URL(tabs.activeTab.url).search + URL(tabs.activeTab.url).hash);

	if (allQs){
		logger.log("matched a " + engine + " search query: " + allQs[1]);

		if (autoSeparator){
			var queries = allQs[1].split(/\W(?:\d|\W)*/i);
			// if (allQs[1].search(/ /i) != -1)
			// 	qSeparator = " ";
			// else
			// 	qSeparator = "+";
		}
		else
			var queries = allQs[1].split(qSeparator);

		queries.map(function (elm){
			logger.log(elm);
		});
	}
}

function recommendPinTab(){

	ui.showNotification({
		message: "It seems that you frequently visit this page. You might want to pin its tab!",
		header: "App Page",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/pinned-tabs-keep-favorite-websites-open"},
		buttonLabel: "Show Me How",
		id: options.triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_PINTAB, options, options.triggerId);

}

function facebookDetected(){

	var count = minorTriggerCount("facebook");

	var triggerId = "facebook";
	var name = "facebook";

	if (count == config.FACEBOOK_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendPinTab({triggerId: triggerId});
	}
}

function recommendPrivateWindow(options){

	ui.showNotification({
	message: "You might want to view this page in a private window.",
	header: "Private Page",
	reactionType: "openlinkinnewtab",
	reactionOptions: {url: "https://support.mozilla.org/en-US/kb/private-browsing-browse-web-without-saving-info#w_how-do-i-open-a-new-private-window"},
	buttonLabel: "Show Me How",
	id: options.triggerId
	});

	utils.sendOfferingEvent(config.TYPE_OFFERING_PRIVATEWINDOW, options, triggerId);
}

function blushyPageDetected(){

	if (!require("sdk/private-browsing").isPrivate(tabs.activeTab)){

		var count = minorTriggerCount("blushypage");

		var triggerId = "blushypage";
		var name = "blushypage";

		if (count == config.BLUSHYPAGE_COUNT_THRESHOLD){

			utils.sendTriggerEvent({name: name, count: count}, triggerId);

			recommendPrivateWindow({triggerId: triggerId});

		}

	}

}

// TODO: create a recommendPrivateWindow function
function pornDetected(){

	logger.log("pornDetected");

	blushyPageDetected();

}

function recommendKeyboardShortcut(){
	//TODO
}

//recommends using a keyboard shortcut to open a  new tab
function recommendNewTabShortcut(){
	logger.log("recommendNewTabShortcut");

	var count = minorTriggerCount("newtabshortcut");

	var triggerId = "newtab";
	var name = "newtab";

	if (count == config.NEW_TAB_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+T to open a new tab! Why don't you give it a try?",
		header: "New Tab",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_windows-tabs"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);

	}

}

function recommendCloseTabShortcut(){
	logger.log("recommendCloseTabShortcut");

	var count = minorTriggerCount("closetabshortcut");

	var triggerId = "closetab";
	var name = "closetab";

	if (count == config.CLOSE_TAB_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+W to close a tab!",
		header: "Close Tab",
		reactionType: "noreaction",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_windows-tabs"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);

	}
}

function recommendBookmarkManager(){
	var count = minorTriggerCount("newbookmark");

	var triggerId = "newbookmark";
	var name = "newbookmark";

	if (count == config.BOOKMARK_MANAGER_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "quickmark", triggerId: triggerId});
	}
}

function recommendNewBookmarkShortcut(){
	var count = minorTriggerCount("newbookmark");

	var triggerId = "newbookmarknoshortcut";
	var name = "newbookmarknoshortcut";

	if (count == config.BOOKMARK_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+D to to bookmark a page! Why don't you give it a try?",
		header: "New Bookmark",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_bookmarks"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);
	}

}

exports.mapActiveURLToAction = mapActiveURLToAction;
exports.recommendDLManager = recommendDLManager;
exports.recommendNewTabShortcut = recommendNewTabShortcut;
exports.recommendTranslator = recommendTranslator;
exports.recommendNewBookmarkShortcut = recommendNewBookmarkShortcut;
exports.recommendBookmarkManager = recommendBookmarkManager;
exports.recommendCloseTabShortcut = recommendCloseTabShortcut;<commit_msg>extraOptions added to recommend addon (re #64, re #88)<commit_after>/*! This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

"use strict";

var {setTimeout} = require("sdk/timers");
var {getBrowserForTab, getTabForId} = require("sdk/tabs/utils");
var tabs = require("sdk/tabs");
var {data} = require("sdk/self");
var logger = require("./logger");
var {URL} = require("sdk/url");
var utils = require("./utils");
var notifications = require("sdk/notifications");
var featuredata = require("./featuredata");
var config = require("./config");
var windows = require("sdk/windows");
var {sendEvent, sendToGA, override}  = require("./utils");
var ui = require("./ui");
var info = require("./generalInfo");

//stores what action each webpage should map to
var URLToActionMapper = {
	"www.youtube.com": ytDetected,
	"www.gmail.com": gmailDetected, "mail.google.com": gmailDetected,
	"www.fifa.com": soccerDetected,
	"www.goal.com": soccerDetected,
	"www.reddit.com": redditDetected,
	"www.amazon.com": amazonDetected, "www.amazon.ca": amazonDetected,
	"stoppornculture.org": pornDetected,
	"www.facebook.com": facebookDetected,
	"www.google.com": googleDetected, "www.google.ca": googleDetected,  "www.bing.com": bingDetected, "www.bing.ca": bingDetected,
	"en.wikipedia.org": wikipediaDetected, "search.yahoo.com": yahooDetected, "search.yahoo.ca": yahooDetected
};

//stores basic information needed when recommending addons
var addonData = {
	"1click-yt-download": {name: "1-Click YouTube Video Download", link: "https://addons.mozilla.org/firefox/downloads/latest/13990/addon-13990-latest.xpi"},
	"gmail-notifier": {name: "Gmail Notifier", link: "https://addons.mozilla.org/firefox/downloads/latest/406178/addon-406178-latest.xpi"},
	"flashgot": {name: "FlashGot Mass Downloader", link: "https://addons.mozilla.org/firefox/downloads/latest/220/addon-220-latest.xpi"},
	"googletranslator": {name: "Google™ Translator", link: "https://addons.mozilla.org/firefox/downloads/latest/493406/addon-493406-latest.xpi"},
	"redditenhancement": {name: "Reddit Enhancement Suite", link: "https://addons.mozilla.org/firefox/downloads/latest/387429/addon-387429-latest.xpi"},
	"amazonwishlistbutton": {name: "Amazon \"Add to Wish List\" Button", link: "https://addons.mozilla.org/firefox/downloads/latest/257015/addon-257015-latest.xpi"},
	"quickmark": {name: "Quick Mark", link: "https://addons.mozilla.org/firefox/downloads/latest/462572/addon-462572-latest.xpi"}
}

//carries out right action based on (hostname of) current website
function mapActiveURLToAction(aBrowser, aWebProgress, aRequest, aLocation){

	var tab = tabs.activeTab;
	logger.log("mapActiveURLToAction");

	// if (tab.id == activeTab.id)
	if (getBrowserForTab(getTabForId(tab.id)) == aBrowser) {

		var hostname = URL(tab.url).hostname;
		logger.log(hostname);

		if (hostname in URLToActionMapper) URLToActionMapper[hostname]({hostname: hostname});
	}

}

function minorTriggerCount(featurename){
	var count = featuredata.get(featurename, "count");
	count ++;
	featuredata.set(featurename, "count", count);

	utils.sendMinorTriggerEvent({count: count}, featurename);

	return count;
}

// recommends installing a DL manager to user
function recommendDLManager(download){

	var count = minorTriggerCount("download");

	var triggerId = "newdownload";
	var name = "newdownload";

	if (count == config.DOWNLOAD_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "flashgot", triggerId: triggerId, extraOptions: options}});
	}
}

// recommends a specific addon to user
function recommendAddon(options){

	logger.log("recommending addon");

	var explanationMessage = options.extraOptions.explanationMessage || ("you visited " + options.hostname);

	ui.showNotification({
		message: "Wanna try downloading " + addonData[options.addonID].name + " ?",
		header: "A Cool Addon",
		reactionType: "openlinkinactivetab",
		reactionOptions: {url: addonData[options.addonID].link},
		buttonLabel: "Install Addon",
		id: options.triggerId,
		explanationMessage: explanationMessage
		});

	utils.sendOfferingEvent(config.TYPE_OFFERING_ADDON, options, options.triggerId);

}

function ytDetected(options){

	logger.log("ytDetect");

	var count = minorTriggerCount("youtube");

	var triggerId = "youtube";
	var name = "youtube";

	if (count == config.YOUTUBE_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "1click-yt-download", triggerId: triggerId, extraOptions: options}});
	}
}

function gmailDetected(options){
	var count = minorTriggerCount("gmail");

	var triggerId = "gmail";
	var name = "gmail";

	if (count == config.GMAIL_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "gmail-notifier", triggerId: triggerId, extraOptions: options}});
	}
}

function soccerDetected(options){

}

function recommendTranslator(options){
	var count = minorTriggerCount("translator");

	var triggerId = "foreignpage";
	var name = "foreignpage";

	if (count == config.TRANSLATOR_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "googletranslator", triggerId: triggerId, extraOptions: options}});
	}
}

function redditDetected(options){
	var count = minorTriggerCount("reddit");

	var triggerId = "reddit";
	var name = "reddit";

	if (count == config.REDDIT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "redditenhancement", triggerId: triggerId, extraOptions: options}});
	}
}

function amazonDetected(options){
	var count = minorTriggerCount("amazon");

	var triggerId = "amazon";
	var name = "amazon";

	if (count == config.AMAZON_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "amazonwishlistbutton", triggerId: triggerId, extraOptions: options}});
	}

	extractSearchQuery("amazon");
}

function googleDetected(options){

	extractSearchQuery("google");
}

function bingDetected(options){
	extractSearchQuery("bing");
}

function yahooDetected(options){
	extractSearchQuery("yahoo");
}

function wikipediaDetected(options){
	extractSearchQuery("wikipedia");
}

function extractSearchQuery(engine){

	logger.log(URL(tabs.activeTab.url).search);

	var qSeparator;
	var qKeyword;

	var autoSeparator = true; //if set, checks both + and space (ignores qSeparator)

	switch (engine){
		case "google":
			qSeparator = "+";
			qKeyword = "q";
		break;
		case "bing":
			qSeparator = "+";
			qKeyword = "q";
		break;
		case "wikipedia":
			qSeparator = "+";
			qKeyword = "search";

		break;
		case "yahoo":
			qSeparator = "+";
			qKeyword = "p";

		break;
		case "amazon":
			qSeparator = "+";
			qKeyword = "field-keywords";

		break;

	}

	var queryRegExp = new RegExp(".*(?:\\?|\\#|&)" + qKeyword + "=(.*?)(?:\\&|$)", "i");

	var allQs = queryRegExp.exec(URL(tabs.activeTab.url).search + URL(tabs.activeTab.url).hash);

	if (allQs){
		logger.log("matched a " + engine + " search query: " + allQs[1]);

		if (autoSeparator){
			var queries = allQs[1].split(/\W(?:\d|\W)*/i);
			// if (allQs[1].search(/ /i) != -1)
			// 	qSeparator = " ";
			// else
			// 	qSeparator = "+";
		}
		else
			var queries = allQs[1].split(qSeparator);

		queries.map(function (elm){
			logger.log(elm);
		});
	}
}

function recommendPinTab(){

	ui.showNotification({
		message: "It seems that you frequently visit this page. You might want to pin its tab!",
		header: "App Page",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/pinned-tabs-keep-favorite-websites-open"},
		buttonLabel: "Show Me How",
		id: options.triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_PINTAB, options, options.triggerId);

}

function facebookDetected(options){

	var count = minorTriggerCount("facebook");

	var triggerId = "facebook";
	var name = "facebook";

	if (count == config.FACEBOOK_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendPinTab({triggerId: triggerId});
	}
}

function recommendPrivateWindow(options){

	ui.showNotification({
	message: "You might want to view this page in a private window.",
	header: "Private Page",
	reactionType: "openlinkinnewtab",
	reactionOptions: {url: "https://support.mozilla.org/en-US/kb/private-browsing-browse-web-without-saving-info#w_how-do-i-open-a-new-private-window"},
	buttonLabel: "Show Me How",
	id: options.triggerId
	});

	utils.sendOfferingEvent(config.TYPE_OFFERING_PRIVATEWINDOW, options, triggerId);
}

function blushyPageDetected(options){

	if (!require("sdk/private-browsing").isPrivate(tabs.activeTab)){

		var count = minorTriggerCount("blushypage");

		var triggerId = "blushypage";
		var name = "blushypage";

		if (count == config.BLUSHYPAGE_COUNT_THRESHOLD){

			utils.sendTriggerEvent({name: name, count: count}, triggerId);

			recommendPrivateWindow({triggerId: triggerId});

		}

	}

}

// TODO: create a recommendPrivateWindow function
function pornDetected(options){

	logger.log("pornDetected");

	blushyPageDetected(options);

}

function recommendKeyboardShortcut(){
	//TODO
}

//recommends using a keyboard shortcut to open a  new tab
function recommendNewTabShortcut(){
	logger.log("recommendNewTabShortcut");

	var count = minorTriggerCount("newtabshortcut");

	var triggerId = "newtab";
	var name = "newtab";

	if (count == config.NEW_TAB_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+T to open a new tab! Why don't you give it a try?",
		header: "New Tab",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_windows-tabs"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);

	}

}

function recommendCloseTabShortcut(){
	logger.log("recommendCloseTabShortcut");

	var count = minorTriggerCount("closetabshortcut");

	var triggerId = "closetab";
	var name = "closetab";

	if (count == config.CLOSE_TAB_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+W to close a tab!",
		header: "Close Tab",
		reactionType: "noreaction",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_windows-tabs"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);

	}
}

function recommendBookmarkManager(){
	var count = minorTriggerCount("newbookmark");

	var triggerId = "newbookmark";
	var name = "newbookmark";

	if (count == config.BOOKMARK_MANAGER_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		recommendAddon({addonID: "quickmark", triggerId: triggerId, extraOptions: options}});
	}
}

function recommendNewBookmarkShortcut(){
	var count = minorTriggerCount("newbookmark");

	var triggerId = "newbookmarknoshortcut";
	var name = "newbookmarknoshortcut";

	if (count == config.BOOKMARK_SHORTCUT_COUNT_THRESHOLD){

		utils.sendTriggerEvent({name: name, count: count}, triggerId);

		ui.showNotification({
		message: "You can also use " + info.getMetakeyStr() + "+D to to bookmark a page! Why don't you give it a try?",
		header: "New Bookmark",
		reactionType: "openlinkinnewtab",
		reactionOptions: {url: "https://support.mozilla.org/en-US/kb/keyboard-shortcuts-perform-firefox-tasks-quickly#w_bookmarks"},
		buttonLabel: "Show More",
		id: triggerId
		});

		var options = {};

		utils.sendOfferingEvent(config.TYPE_OFFERING_KEYBOARDSHORTCUT, options, triggerId);
	}

}

exports.mapActiveURLToAction = mapActiveURLToAction;
exports.recommendDLManager = recommendDLManager;
exports.recommendNewTabShortcut = recommendNewTabShortcut;
exports.recommendTranslator = recommendTranslator;
exports.recommendNewBookmarkShortcut = recommendNewBookmarkShortcut;
exports.recommendBookmarkManager = recommendBookmarkManager;
exports.recommendCloseTabShortcut = recommendCloseTabShortcut;<|endoftext|>


<commit_before>import {Button} from 'widget/button'
import {Combo} from 'widget/combo'
import {activator} from 'widget/activation/activator'
import {compose} from 'compose'
import {msg} from 'translate'
import {selectFrom} from 'stateUtils'
import {withMapAreaContext} from '../mapAreaContext'
import {withRecipe} from 'app/home/body/process/recipeContext'
import PropTypes from 'prop-types'
import React from 'react'
import RemoveButton from 'widget/removeButton'
import guid from 'guid'

const mapRecipeToProps = (recipe, {source}) => ({
    userDefinedVisualizations: selectFrom(recipe, ['layers.userDefinedVisualizations', source.id]) || []
})

class _VisualizationSelector extends React.Component {
    state = {}

    render() {
        const {selectedVisParams, presetOptions} = this.props
        const options = this.getOptions()
        const idMatch = selectedVisParams && selectedVisParams.id &&
            this.flattenOptions(options).find(option => option.value === selectedVisParams.id)
        const selectedOption = idMatch || (selectedVisParams &&
            this.flattenOptions(presetOptions).find(({visParams: {bands}}) =>
                bands.join(',') === selectedVisParams.bands.join(',')
            )
        )
        const editMode = selectedOption && selectedOption.visParams.userDefined ? 'edit' : 'clone'
        return (
            <Combo
                label={msg('map.visualizationSelector.label')}
                labelButtons={[
                    <Button
                        key='add'
                        tooltip={msg('map.visualizationSelector.add.tooltip')}
                        icon='plus'
                        chromeless
                        shape='circle'
                        size='small'
                        onClick={() => this.addVisParams()}
                    />,
                    <Button
                        key='edit'
                        tooltip={msg(`map.visualizationSelector.${editMode}.tooltip`)}
                        icon={editMode}
                        chromeless
                        shape='circle'
                        size='small'
                        disabled={!selectedOption}
                        onClick={() => this.editVisParams(selectedOption.visParams, editMode)}
                    />,
                    <RemoveButton
                        key='remove'
                        chromeless
                        shape='circle'
                        size='small'
                        tooltip={msg('map.visualizationSelector.remove.tooltip')}
                        disabled={!selectedOption || editMode === 'clone'}
                        onRemove={() => this.removeVisParams(selectedOption.visParams)}
                    />
                ]}
                placeholder={'Select bands to visualize...'}
                options={options}
                value={selectedOption && selectedOption.value}
                onChange={({visParams}) => this.selectVisParams(visParams)}
            />
        )
    }

    getOptions() {
        const {userDefinedVisualizations, presetOptions} = this.props
        const userDefinedOptions = userDefinedVisualizations.map(visParams => ({
            value: visParams.id,
            label: visParams.bands.join(', '),
            visParams
        }))
        return [
            {label: msg('map.visualizationSelector.userDefined.label'), options: userDefinedOptions},
            ...presetOptions
        ]
    }

    flattenOptions(options) {
        return options
            .map(option => option.options || [option])
            .flat()
    }

    selectVisParams(visParams) {
        const {mapAreaContext: {updateLayerConfig}} = this.props
        updateLayerConfig({visParams})
    }

    addVisParams() {
        const {recipe, source, activator: {activatables}, mapAreaContext: {area}} = this.props
        const visParamsPanel = activatables[`visParams-${area}`]
        visParamsPanel.activate({recipe, imageLayerSourceId: source.id})
    }

    editVisParams(visParamsToEdit, editMode) {
        const {recipe, source, activator: {activatables}, mapAreaContext: {area}} = this.props
        const visParamsPanel = activatables[`visParams-${area}`]
        const visParams = editMode === 'clone'
            ? {...visParamsToEdit, id: guid()}
            : visParamsToEdit
        visParamsPanel.activate({recipe, imageLayerSourceId: source.id, visParams})
    }

    removeVisParams(visParams) {
        const {source, recipeActionBuilder} = this.props
        recipeActionBuilder('REMOVE_VIS_PARAMS', {visParams})
            .del(['layers.userDefinedVisualizations', source.id, {id: visParams.id}])
            .dispatch()
        const options = this.flattenOptions(this.getOptions())
        this.selectVisParams(options.length
            ? options[0].visParams
            : null
        )
    }
}

export const VisualizationSelector = compose(
    _VisualizationSelector,
    withRecipe(mapRecipeToProps),
    activator(),
    withMapAreaContext()
)

VisualizationSelector.defaultProps = {
    presetOptions: []
}

VisualizationSelector.propTypes = {
    source: PropTypes.any.isRequired,
    presetOptions: PropTypes.array,
    recipe: PropTypes.object
}
<commit_msg>Cleanup.<commit_after>import {Button} from 'widget/button'
import {Combo} from 'widget/combo'
import {activator} from 'widget/activation/activator'
import {compose} from 'compose'
import {msg} from 'translate'
import {selectFrom} from 'stateUtils'
import {withMapAreaContext} from '../mapAreaContext'
import {withRecipe} from 'app/home/body/process/recipeContext'
import PropTypes from 'prop-types'
import React from 'react'
import RemoveButton from 'widget/removeButton'
import guid from 'guid'

const mapRecipeToProps = (recipe, {source}) => ({
    userDefinedVisualizations: selectFrom(recipe, ['layers.userDefinedVisualizations', source.id]) || []
})

class _VisualizationSelector extends React.Component {
    state = {}

    render() {
        const {selectedVisParams, presetOptions} = this.props
        const options = this.getOptions()
        const idMatch = selectedVisParams && selectedVisParams.id &&
            this.flattenOptions(options).find(option => option.value === selectedVisParams.id)
        const selectedOption = idMatch || (selectedVisParams &&
            this.flattenOptions(presetOptions).find(({visParams: {bands}}) =>
                bands.join(',') === selectedVisParams.bands.join(',')
            )
        )
        const editMode = selectedOption && selectedOption.visParams.userDefined ? 'edit' : 'clone'
        return (
            <Combo
                label={msg('map.visualizationSelector.label')}
                labelButtons={[
                    <Button
                        key='add'
                        chromeless
                        shape='circle'
                        size='small'
                        icon='plus'
                        tooltip={msg('map.visualizationSelector.add.tooltip')}
                        onClick={() => this.addVisParams()}
                    />,
                    <Button
                        key='edit'
                        chromeless
                        shape='circle'
                        size='small'
                        icon={editMode}
                        tooltip={msg(`map.visualizationSelector.${editMode}.tooltip`)}
                        disabled={!selectedOption}
                        onClick={() => this.editVisParams(selectedOption.visParams, editMode)}
                    />,
                    <RemoveButton
                        key='remove'
                        chromeless
                        shape='circle'
                        size='small'
                        tooltip={msg('map.visualizationSelector.remove.tooltip')}
                        disabled={!selectedOption || editMode === 'clone'}
                        onRemove={() => this.removeVisParams(selectedOption.visParams)}
                    />
                ]}
                placeholder={'Select bands to visualize...'}
                options={options}
                value={selectedOption && selectedOption.value}
                onChange={({visParams}) => this.selectVisParams(visParams)}
            />
        )
    }

    getOptions() {
        const {userDefinedVisualizations, presetOptions} = this.props
        const userDefinedOptions = userDefinedVisualizations.map(visParams => ({
            value: visParams.id,
            label: visParams.bands.join(', '),
            visParams
        }))
        return [
            {label: msg('map.visualizationSelector.userDefined.label'), options: userDefinedOptions},
            ...presetOptions
        ]
    }

    flattenOptions(options) {
        return options
            .map(option => option.options || [option])
            .flat()
    }

    selectVisParams(visParams) {
        const {mapAreaContext: {updateLayerConfig}} = this.props
        updateLayerConfig({visParams})
    }

    addVisParams() {
        const {recipe, source, activator: {activatables}, mapAreaContext: {area}} = this.props
        const visParamsPanel = activatables[`visParams-${area}`]
        visParamsPanel.activate({recipe, imageLayerSourceId: source.id})
    }

    editVisParams(visParamsToEdit, editMode) {
        const {recipe, source, activator: {activatables}, mapAreaContext: {area}} = this.props
        const visParamsPanel = activatables[`visParams-${area}`]
        const visParams = editMode === 'clone'
            ? {...visParamsToEdit, id: guid()}
            : visParamsToEdit
        visParamsPanel.activate({recipe, imageLayerSourceId: source.id, visParams})
    }

    removeVisParams(visParams) {
        const {source, recipeActionBuilder} = this.props
        recipeActionBuilder('REMOVE_VIS_PARAMS', {visParams})
            .del(['layers.userDefinedVisualizations', source.id, {id: visParams.id}])
            .dispatch()
        const options = this.flattenOptions(this.getOptions())
        this.selectVisParams(options.length
            ? options[0].visParams
            : null
        )
    }
}

export const VisualizationSelector = compose(
    _VisualizationSelector,
    withRecipe(mapRecipeToProps),
    activator(),
    withMapAreaContext()
)

VisualizationSelector.defaultProps = {
    presetOptions: []
}

VisualizationSelector.propTypes = {
    source: PropTypes.any.isRequired,
    presetOptions: PropTypes.array,
    recipe: PropTypes.object
}
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular
    .module('meetings')
    .controller('MeetingsListController', MeetingsListController);

  MeetingsListController.$inject = ['$scope', 'MeetingsService', 'meetingResolve', 'listFacilitiesResolve', 'listApplicationsResolve', 'listCompaniesResolve', 'Authentication'];

  function MeetingsListController($scope, MeetingsService, meeting, facilities, applications, companies, Authentication) {
    var vm = this;

    vm.meetings = MeetingsService.query();
    vm.facilities = facilities;
    vm.applications = applications;
    vm.companies = companies;

    // Examples of variables that are useful;
    vm.startDate = new Date('----', '--', '--', '--', '--', '--');
    vm.endDate = new Date('----', '--', '--', '--', '--', '--');
    vm.startHours = new Date('----', '--', '--', '--', '--', '--');
    vm.endHours = new Date('----', '--', '--', '--', '--', '--');
    vm.meetingTimeLength = '';
    vm.lunchStart = new Date('----', '--', '--', '--', '--', '--');
    vm.lunchEnd = new Date('----', '--', '--', '--', '--', '--');
    vm.chosenFacilities = {};
    //TODO: Implement method to add many meetings with some coinstraints.

    // puts the chosen facilities into an array
    var chosenFacilitiesArray = function(){
      var array=[];

      angular.forEach(vm.chosenFacilities, function(key,value){
        if(key)
          array.push(value);
      });
      return array;
    };

  /*  var startBeforeOrEndDate = function(start, end) {
      return start < end; //kolla timmar minuter osv.
    };*/

    var startNotAfterEndTime = function(start, end) {
      if(start.getHours() < end.getHours()){
        return true;
      } else if (start.getMinutes() <= end.getMinutes() && start.getHours() === end.getHours()) {
        return true;
      } else {
        return false;
      }
    };

    var createMeeting = function(facility, date) {
      var meeting = {};
      meeting.facility = facility;
      meeting.date = date;

      MeetingsService.post(meeting);
      console.log('Möte: ' + facility + ' ' + date);
    };

    var startNotAfterEndDate = function(start, end) {
        if(start.getFullYear() < end.getFullYear()){
          return true;
        } else if (start.getMonth() < end.getMonth() && start.getFullYear() === end.getFullYear()){
          return true;
        } else if (start.getDate() < end.getDate() && start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()) {
          return true;
        } else if (start.getDate() === end.getDate() && start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()) {
          return true;
        } else {
          return false;
        }
      };

    $scope.createMeetings = function() {
        var facilitiesArray = chosenFacilitiesArray();
       if(!!vm.startDate && !!vm.endDate && !!vm.startHours && !!vm.endHours && !!vm.lunchStart && !!vm.lunchEnd && facilitiesArray !== 'undefined' && vm.meetingTimeLength !== '' && facilitiesArray.length !== 0) {
          console.log('Alla fält är ifyllda');

          var date = vm.startDate;
          var time = vm.startHours; //ändras aldrig, kan lika gärna använda vm.startDate och vm.startHours
          var meetingDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());

          while(startNotAfterEndDate(meetingDate, vm.endDate)){
           while(startNotAfterEndTime(new Date(meetingDate.getFullYear(), meetingDate.getMonth(), meetingDate.getDate(), meetingDate.getHours(), meetingDate.getMinutes() + vm.meetingTimeLength), vm.endHours)) {
              /*if( true){ //om det är lunch
                time = vm.lunchEnd;
              } else {*/
                //creating the current meeting
                /*facilitiesArray.forEach(function(facility) {  //"Don't make functions within a loop." Hur lösa detta annorlunda, kolla upp.
                createMeeting(facility, meetingDate);
               });

                //updating the time to the next meeting time
                time = time.setMinutes(time.getMinutes() + vm.meetingTimeLength);
             /* } */

             createMeeting(facilitiesArray[0], meetingDate);
             meetingDate.setMinutes(meetingDate.getMinutes() + vm.meetingTimeLength);
            }
            //updating the date to the next day
            meetingDate.setDate(meetingDate.getDate() + 1);
            //reseting the time of the next day
            meetingDate.setHours(time.getHours());
            meetingDate.setMinutes(time.getMinutes());
            console.log('meetingDate: ', meetingDate);
          }
        } else {
          console.log('Alla fält är inte ifyllda!');
        }
    };
  }
})();
<commit_msg>Kan generera möten fÃ. Ännu ej kontroll för lunch.<commit_after>(function () {
  'use strict';

  angular
    .module('meetings')
    .controller('MeetingsListController', MeetingsListController);

  MeetingsListController.$inject = ['$scope', 'MeetingsService', 'meetingResolve', 'listFacilitiesResolve', 'listApplicationsResolve', 'listCompaniesResolve', 'Authentication'];

  function MeetingsListController($scope, MeetingsService, meeting, facilities, applications, companies, Authentication) {
    var vm = this;

    vm.meetings = MeetingsService.query();
    vm.facilities = facilities;
    vm.applications = applications;
    vm.companies = companies;

    // Examples of variables that are useful;
    vm.startDate = new Date('----', '--', '--', '--', '--', '--');
    vm.endDate = new Date('----', '--', '--', '--', '--', '--');
    vm.startHours = new Date('----', '--', '--', '--', '--', '--');
    vm.endHours = new Date('----', '--', '--', '--', '--', '--');
    vm.meetingTimeLength = '';
    vm.lunchStart = new Date('----', '--', '--', '--', '--', '--');
    vm.lunchEnd = new Date('----', '--', '--', '--', '--', '--');
    vm.chosenFacilities = {};
    //TODO: Implement method to add many meetings with some coinstraints.

    // puts the chosen facilities into an array
    var chosenFacilitiesArray = function(){
      var array=[];

      angular.forEach(vm.chosenFacilities, function(key,value){
        if(key)
          array.push(value);
      });
      return array;
    };

  /*  var startBeforeOrEndDate = function(start, end) {
      return start < end; //kolla timmar minuter osv.
    };*/

    var startNotAfterEndTime = function(start, end) {
      if(start.getHours() < end.getHours()){
        return true;
      } else if (start.getMinutes() <= end.getMinutes() && start.getHours() === end.getHours()) {
        return true;
      } else {
        return false;
      }
    };

    var createMeeting = function(facility, date) {
      var meeting = {};
      meeting.facility = facility;
      meeting.date = date;

      MeetingsService.post(meeting);
      console.log('Möte: ' + facility + ' ' + date);
    };

    var startNotAfterEndDate = function(start, end) {
        if(start.getFullYear() < end.getFullYear()){
          return true;
        } else if (start.getMonth() < end.getMonth() && start.getFullYear() === end.getFullYear()){
          return true;
        } else if (start.getDate() < end.getDate() && start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()) {
          return true;
        } else if (start.getDate() === end.getDate() && start.getMonth() === end.getMonth() && start.getFullYear() === end.getFullYear()) {
          return true;
        } else {
          return false;
        }
      };

    $scope.createMeetings = function() {
        var facilitiesArray = chosenFacilitiesArray();
       if(!!vm.startDate && !!vm.endDate && !!vm.startHours && !!vm.endHours && !!vm.lunchStart && !!vm.lunchEnd && facilitiesArray !== 'undefined' && vm.meetingTimeLength !== '' && facilitiesArray.length !== 0) {
          console.log('Alla fält är ifyllda');

          var date = vm.startDate;
          var time = vm.startHours; //ändras aldrig, kan lika gärna använda vm.startDate och vm.startHours
          var meetingDate = new Date(date.getFullYear(), date.getMonth(), date.getDate(), time.getHours(), time.getMinutes());

          while(startNotAfterEndDate(meetingDate, vm.endDate)){
           while(startNotAfterEndTime(new Date(meetingDate.getFullYear(), meetingDate.getMonth(), meetingDate.getDate(), meetingDate.getHours(), meetingDate.getMinutes() + vm.meetingTimeLength), vm.endHours)) {
              /*if( true){ //om det är lunch
                time = vm.lunchEnd;
              } else {*/
                //creating the current meeting
                /*facilitiesArray.forEach(function(facility) {  //"Don't make functions within a loop." Hur lösa detta annorlunda, kolla upp.
                createMeeting(facility, meetingDate);
               });

                //updating the time to the next meeting time
                time = time.setMinutes(time.getMinutes() + vm.meetingTimeLength);
             /* } */

             for (var i=0; i < facilitiesArray.length; i++){
              createMeeting(facilitiesArray[i], meetingDate);
             }

             meetingDate.setMinutes(meetingDate.getMinutes() + vm.meetingTimeLength);
            }
            //updating the date to the next day
            meetingDate.setDate(meetingDate.getDate() + 1);
            //reseting the time of the next day
            meetingDate.setHours(time.getHours());
            meetingDate.setMinutes(time.getMinutes());
            console.log('meetingDate: ', meetingDate);
          }
        } else {
          console.log('Alla fält är inte ifyllda!');
        }
    };
  }
})();
<|endoftext|>


<commit_before>var PokeGoLure = PokeGoLure || {}
PokeGoLure.Manage = (function ($) {
    'use strict';

    var map;

    function _initMap() {
        map = new google.maps.Map(document.getElementById('gmap'), {
            center: {lat: -34.397, lng: 150.644},
            scrollwheel: false,
            zoom: 8,
            clickableIcons: false,
            disableDefaultUI: true
        });

    }

    return {
        initMap: _initMap
    }
})(jQuery);<commit_msg>defaulting map to Melbourne CBD<commit_after>var PokeGoLure = PokeGoLure || {}
PokeGoLure.Manage = (function ($) {
    'use strict';

    var map,
        DEFAULT_LAT = -37.8150085,
        DEFAULT_LONG = 144.9658801;

    function _initMap() {
        map = new google.maps.Map(document.getElementById('gmap'), {
            center: {lat: DEFAULT_LAT, lng: DEFAULT_LONG},
            scrollwheel: false,
            zoom: 8,
            clickableIcons: false,
            disableDefaultUI: true
        });

    }

    return {
        initMap: _initMap
    }
})(jQuery);<|endoftext|>


<commit_before>/**
 * controlPanel.js
 */
(() => {
  "use strict";
  const html = document.querySelector("html");
  const controlPanelForm = document.getElementById("controlPanelForm");
  const buttonIcon = document.getElementById("buttonIcon");
  const buttonIconGray = document.getElementById("buttonIconGray");
  const buttonIconWhite = document.getElementById("buttonIconWhite");
  const selectIcon = document.getElementById("selectIcon");
  const currentName = document.getElementById("currentEditorName");
  const editorName = document.getElementById("editorName");
  const editorLabel = document.getElementById("editorLabel");
  const storeLabel = document.getElementById("storeLabel");
  const openAddonManager = document.getElementById("openAddonManager");
  const inputRadios = document.querySelectorAll("input[type=radio]");

  /**
   * remove child nodes
   * @param {Object} node
   */
  const removeChildNodes = node => {
    if(node && node.hasChildNodes()) {
      while(node.firstChild) {
        node.removeChild(node.firstChild);
      }
    }
  };

  /**
   * toggle form inputs
   */
  const toggleInputs = () => {
    editorLabel && storeLabel && (
      editorName && editorName.value && currentName &&
      currentName.hasChildNodes() ? (
        removeChildNodes(currentName),
        currentName.appendChild(document.createTextNode(editorName.value)),
        editorLabel.removeAttributeNS("", "disabled"),
        storeLabel.removeAttributeNS("", "disabled")
      ) : (
        editorLabel.setAttributeNS("", "disabled", "disabled"),
        storeLabel.setAttributeNS("", "disabled", "disabled")
      )
    );
  };

  /**
   * get radio button value
   * @param {string} name - radio button name
   * @return {?string} - checked radio button value
   */
  const getRadioValue = name => {
    let value = null;
    for(let node of inputRadios) {
      if(node.name && node.name === name && node.checked && node.value) {
        value = node.value;
        break;
      }
    }
    return value;
  };

  /**
   * self port emit
   * @param {Object} evt - event
   */
  const selfPortEmit = evt => {
    const type = evt && evt.type;
    const target = evt && evt.target;
    switch(type) {
      case "load":
        window.self.port.emit(type);
        break;
      case "change":
      case "submit":
        window.self.port.emit(type, {
          editorName: editorLabel && editorLabel.value ||
                      editorName && editorName.value || "",
          buttonIcon: buttonIcon && getRadioValue(buttonIcon.name) || null
        });
        evt.preventDefault();
        break;
      case "click":
        target && target.hasAttributeNS("", "data-href") &&
          window.self.port.emit(type, target.getAttributeNS("", "data-href"));
        evt.preventDefault();
    }
  };

  /**
   * check if radio button is checked
   * @param {Object} evt - event
   */
  const isRadioChecked = evt => {
    evt && evt.target && evt.target.checked && selfPortEmit(evt);
  };

  /* update control panel */
  window.self.port.on("editorValue", res => {
    res && (
      editorName && (
        editorName.value = res.editorName,
        currentName && (
          currentName.hasChildNodes() && removeChildNodes(currentName),
          currentName.appendChild(
            document.createTextNode(editorName.value || res.currentEditorName)
          )
        ),
        editorLabel.value = editorName.value
      ),
      toggleInputs()
    );
  });

  /* localize control panel */
  window.self.port.on("htmlValue", res => {
    res && (
      html && (html.lang = res.lang),
      selectIcon && (selectIcon.value = res.submit),
      currentName && (
        currentName.hasChildNodes() && removeChildNodes(currentName),
        currentName.appendChild(document.createTextNode(res.currentEditorName))
      ),
      editorLabel && (editorLabel.placeholder = res.editorLabel),
      storeLabel && (storeLabel.value = res.submit)
    );
  });

  /* on initial run */
  (() => {
    window.addEventListener("load", selfPortEmit, false);
    controlPanelForm &&
      controlPanelForm.addEventListener("submit", selfPortEmit, false);
    buttonIcon &&
      buttonIcon.addEventListener("change", isRadioChecked, false);
    buttonIconGray &&
      buttonIconGray.addEventListener("change", isRadioChecked, false);
    buttonIconWhite &&
      buttonIconWhite.addEventListener("change", isRadioChecked, false);
    openAddonManager &&
      openAddonManager.addEventListener("click", selfPortEmit, false);
    toggleInputs();
  })();
})();
<commit_msg>Fix buttonIcon value expression<commit_after>/**
 * controlPanel.js
 */
(() => {
  "use strict";
  const html = document.querySelector("html");
  const controlPanelForm = document.getElementById("controlPanelForm");
  const buttonIcon = document.getElementById("buttonIcon");
  const buttonIconGray = document.getElementById("buttonIconGray");
  const buttonIconWhite = document.getElementById("buttonIconWhite");
  const selectIcon = document.getElementById("selectIcon");
  const currentName = document.getElementById("currentEditorName");
  const editorName = document.getElementById("editorName");
  const editorLabel = document.getElementById("editorLabel");
  const storeLabel = document.getElementById("storeLabel");
  const openAddonManager = document.getElementById("openAddonManager");
  const inputRadios = document.querySelectorAll("input[type=radio]");

  /**
   * remove child nodes
   * @param {Object} node
   */
  const removeChildNodes = node => {
    if(node && node.hasChildNodes()) {
      while(node.firstChild) {
        node.removeChild(node.firstChild);
      }
    }
  };

  /**
   * toggle form inputs
   */
  const toggleInputs = () => {
    editorLabel && storeLabel && (
      editorName && editorName.value && currentName &&
      currentName.hasChildNodes() ? (
        removeChildNodes(currentName),
        currentName.appendChild(document.createTextNode(editorName.value)),
        editorLabel.removeAttributeNS("", "disabled"),
        storeLabel.removeAttributeNS("", "disabled")
      ) : (
        editorLabel.setAttributeNS("", "disabled", "disabled"),
        storeLabel.setAttributeNS("", "disabled", "disabled")
      )
    );
  };

  /**
   * get radio button value
   * @param {string} name - radio button name
   * @return {?string} - checked radio button value
   */
  const getRadioValue = name => {
    let value = null;
    for(let node of inputRadios) {
      if(node.name && node.name === name && node.checked && node.value) {
        value = node.value;
        break;
      }
    }
    return value;
  };

  /**
   * self port emit
   * @param {Object} evt - event
   */
  const selfPortEmit = evt => {
    const type = evt && evt.type;
    const target = evt && evt.target;
    switch(type) {
      case "load":
        window.self.port.emit(type);
        break;
      case "change":
      case "submit":
        window.self.port.emit(type, {
          editorName: editorLabel && editorLabel.value ||
                      editorName && editorName.value || "",
          buttonIcon: target && target.checked && target.name ||
                      buttonIcon && getRadioValue(buttonIcon.name) || null
        });
        evt.preventDefault();
        break;
      case "click":
        target && target.hasAttributeNS("", "data-href") &&
          window.self.port.emit(type, target.getAttributeNS("", "data-href"));
        evt.preventDefault();
    }
  };

  /**
   * check if radio button is checked
   * @param {Object} evt - event
   */
  const isRadioChecked = evt => {
    evt && evt.target && evt.target.checked && selfPortEmit(evt);
  };

  /* update control panel */
  window.self.port.on("editorValue", res => {
    res && (
      editorName && (
        editorName.value = res.editorName,
        currentName && (
          currentName.hasChildNodes() && removeChildNodes(currentName),
          currentName.appendChild(
            document.createTextNode(editorName.value || res.currentEditorName)
          )
        ),
        editorLabel.value = editorName.value
      ),
      toggleInputs()
    );
  });

  /* localize control panel */
  window.self.port.on("htmlValue", res => {
    res && (
      html && (html.lang = res.lang),
      selectIcon && (selectIcon.value = res.submit),
      currentName && (
        currentName.hasChildNodes() && removeChildNodes(currentName),
        currentName.appendChild(document.createTextNode(res.currentEditorName))
      ),
      editorLabel && (editorLabel.placeholder = res.editorLabel),
      storeLabel && (storeLabel.value = res.submit)
    );
  });

  /* on initial run */
  (() => {
    window.addEventListener("load", selfPortEmit, false);
    controlPanelForm &&
      controlPanelForm.addEventListener("submit", selfPortEmit, false);
    buttonIcon &&
      buttonIcon.addEventListener("change", isRadioChecked, false);
    buttonIconGray &&
      buttonIconGray.addEventListener("change", isRadioChecked, false);
    buttonIconWhite &&
      buttonIconWhite.addEventListener("change", isRadioChecked, false);
    openAddonManager &&
      openAddonManager.addEventListener("click", selfPortEmit, false);
    toggleInputs();
  })();
})();
<|endoftext|>


<commit_before>const sConnections = Symbol("connections");

class Connection {
  constructor(...args) {
    [this.sender, this.eventName, this.receiver, this.action] = args;

    this.sender.on(this.eventName, this.action);

    this.sender[sConnections].add(this);
    this.receiver[sConnections].add(this);
  }

  dispose() {
    this.sender[sConnections].delete(this);
    this.receiver[sConnections].delete(this);

    this.sender.removeListener(this.eventName, this.action);
  }
}

export default
class Component extends EventEmitter {

  constructor() {
    this[sConnections] = new Set();
    this.emit("init");
  }

  dispose() {
    this.emit("deinit");
    for (const c of Array.from(this[sConnections])) {
      c.dispose();
    }
  }

  on(...args) {
    if (args.length === 2) {
      return super.on(...args);
    }
    new Connection(this, ...args);
  }

  static property(name) {
    const sName = Symbol(name);
    const eventName = `change:${name}`;

    this.prototype.defineProperty(name, {
      enumerable: true,
      get() {
        return this[sName];
      },
      set(value) {
        if (this[sName] !== value) {
          this[privateName] = value;
          this.emit(eventName, value);
        }
      }
    });
  }
}
<commit_msg>Improve component<commit_after>import {EventEmitter} from "events";

const sConnections = Symbol("connections");

class Connection {
  constructor(...args) {
    [this.sender, this.eventName, this.receiver, this.action] = args;

    this.sender.on(this.eventName, this.action);

    this.sender[sConnections].add(this);
    this.receiver[sConnections].add(this);
  }

  dispose() {
    this.sender[sConnections].delete(this);
    this.receiver[sConnections].delete(this);

    this.sender.removeListener(this.eventName, this.action);
  }
}

export default
class Component extends EventEmitter {

  constructor() {
    super();
    this[sConnections] = new Set();
    this.link();
    this.load();
    this.init();
  }

  // connect events and properties
  link() {

  }

  // load actual UI component (e.g. DOM)
  load() {

  }

  // general init
  init() {

  }

  // general deinit
  deinit() {

  }

  // unload actual UI component
  unload() {

  }

  // disconnect events and properties
  unlink() {
    for (const c of Array.from(this[sConnections])) {
      c.dispose();
    }
  }

  dispose() {
    this.deinit();
    this.unload();
    this.unlink();
  }

  connect(eventName, receiver, action) {
    new Connection(this, eventName, receiver, action);
  }

  static property(name) {
    const sName = Symbol(name);
    const eventName = `change:${name}`;

    this.prototype.defineProperty(name, {
      enumerable: true,
      get() {
        return this[sName];
      },
      set(value) {
        if (this[sName] !== value) {
          this[privateName] = value;
          this.emit(eventName, value);
        }
      }
    });
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

describe('App', () => {
  test('renders without crashing', () => {
    const div = document.createElement('div');
    ReactDOM.render(<App />, div);
  });
});
<commit_msg>Make the redux store available while testing the App component<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import App from './App';
import reducer from '../reducers';

describe('App', () => {
  test('renders without crashing', () => {
    const store = createStore(reducer);
    const div = document.createElement('div');
    ReactDOM.render(
      <Provider store={store}>
        <App />
      </Provider>,
      div
    );
  });
});
<|endoftext|>


<commit_before>import {override} from 'core-decorators';
import {uniq} from 'lodash/fp';
import ConfigFile from '../lib/config-file';
import log from '../lib/decorators/log';
import {sort} from '../lib/format-json';
import {save} from '../lib/fs';

export default class Circle extends ConfigFile {
  static FILENAME = `circle.yml`;

  @log()
  async addDeployment({name, branch, script}) {
    const deployment = this.data.deployment || {};
    deployment[name] = deployment[name] || {
      branch,
      commands: []
    };

    const commands = deployment[name].commands;
    commands.push(script);
    deployment[name].commands = uniq(commands);

    this.data.deployment = deployment;
  }

  @override
  @log()
  async save() {
    const d = this.toJSON();
    if (d && Object.keys(d).length) {
      await save(this.constructor.FILENAME, d);
    }
  }

  @log()
  setNodeVersion(version) {
    version = version
      .replace(/>/g, ``)
      .replace(/\=/g, ``)
      .replace(/\=/g, ``);
    this.g.config.circle.set(`machine.node.version`, version);
  }

  @override
  @log()
  toJSON() {
    const cOrder = this.config.get(`circle.order`);
    const unsorted = this.data;
    return sort(cOrder, unsorted);
  }
}
<commit_msg>fix(circle): add additional filtering of node version number<commit_after>import {override} from 'core-decorators';
import {uniq} from 'lodash/fp';
import ConfigFile from '../lib/config-file';
import log from '../lib/decorators/log';
import {sort} from '../lib/format-json';
import {save} from '../lib/fs';

export default class Circle extends ConfigFile {
  static FILENAME = `circle.yml`;

  @log()
  async addDeployment({name, branch, script}) {
    const deployment = this.data.deployment || {};
    deployment[name] = deployment[name] || {
      branch,
      commands: []
    };

    const commands = deployment[name].commands;
    commands.push(script);
    deployment[name].commands = uniq(commands);

    this.data.deployment = deployment;
  }

  @override
  @log()
  async save() {
    const d = this.toJSON();
    if (d && Object.keys(d).length) {
      await save(this.constructor.FILENAME, d);
    }
  }

  @log()
  setNodeVersion(version) {
    version = version
      .replace(/>/g, ``)
      .replace(/\=/g, ``)
      .replace(/\=/g, ``)
      .replace(/\.x/g, ``);
    this.g.config.circle.set(`machine.node.version`, version);
  }

  @override
  @log()
  toJSON() {
    const cOrder = this.config.get(`circle.order`);
    const unsorted = this.data;
    return sort(cOrder, unsorted);
  }
}
<|endoftext|>