<commit_before>/*
 * grunt-commonjs-tamer
 * https://github.com/freezedev/grunt-commonjs-tamer
 *
 */

'use strict';

var path = require('path');
var beautify = require('js-beautify').js_beautify;
var coffeeFormatter = require('coffee-formatter');
var SourceMapConcat = require('sourcemap-concat').SourceMapConcatenator;

module.exports = function(grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('commonjs_tamer', 'Tame your CommonJS modules', function() {
    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      register: 'require.register',
      separator: grunt.util.linefeed,
      beautify: true,
      normalizeIndexFile: true,
      base: null,
      namespace: null,
      sourcemap: true,
      processName: function(name /*, basename */) {
        return name;
      },
      process: function(content /*, moduleName */) {
        return content;
      },
      blacklist: [],
      banner: '',
      footer: ''
    });

    var quotes = (options.doubleQuotes) ? '"' : '\'';

    // Iterate over all specified file groups.
    this.files.forEach(function(f) {

      var sourceMapConcat = new SourceMapConcat({file: f.dest});

      var src = '';

      if (options.banner) {
        if (typeof options.banner === 'function') {
          src += options.banner(f.dest);
        } else {
          src += options.banner;
        }
      }

      // Concat specified files.
      src = f.src.filter(function(filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      }).map(function(filepath) {
        // Read file source.
        var source = grunt.file.read(filepath);
        var extension = path.extname(filepath);

        var isCoffeeScript = extension === '.coffee';

        var moduleName = filepath.split(extension)[0];
        if (options.base && moduleName.indexOf(options.base) === 0) {
          moduleName = moduleName.split(options.base)[1];
        }

        if (options.normalizeIndexFile && moduleName.indexOf('/index') > 0) {
          moduleName = moduleName.replace('/index', '');
        }

        if (options.namespace) {
          // If index.js is in the root folder and a namespace has been defined
          // use the namespace as the module name
          if (options.normalizeIndexFile && moduleName === 'index') {
            moduleName = options.namespace;
          } else {
            moduleName = options.namespace + '/' + moduleName;
          }
        }

        moduleName = options.processName(moduleName, path.basename(moduleName));

        if (options.blacklist.indexOf(moduleName) >= 0) {
          return;
        }

        if (isCoffeeScript) {
          source = options.register + ' ' + quotes + moduleName + quotes + ', (require, module, exports) ->' +
          options.separator + (function(s) {
            return s.split(options.separator).map(function(l) {
              return '\t' + l;
            }).join(options.separator);
          })(source) + options.separator + options.separator;
        } else {
          source = options.register + '(' + quotes + moduleName + quotes + ', function(require, module, exports) {' +
          options.separator + source + options.separator + '});';
        }

        if (options.beautify) {
          if (!isCoffeeScript) {
            var beautifyOptions = {};

            if (typeof options.beautify === 'object') {
              beautifyOptions = options.beautify;
            } else {
              beautifyOptions = {
                indent_size: 2
              };
            }

            source = beautify(source, beautifyOptions);
          } else {
            source = coffeeFormatter.formatTwoSpaceOperator(source);
          }
        }

        source = options.process(source, moduleName);

        if (options.sourceMap) {
          var prevSourceMap;
          var sourceMapFile = filepath + '.map';
          var file;

          var sourceMapComment = '//# sourceMappingURL=';
          var hasSourceMap = source.indexOf(sourceMapComment) >= 0;

          if (hasSourceMap) {
            var smMatch = source.match(/\/\# sourceMappingURL=(.*)/);
            file = smMatch[1];

            var basename = path.basename(filepath);
            var sourceMapPath = filepath.split(basename)[0];

            sourceMapFile = [sourceMapPath, file].join(path.sep);
          }

          if (grunt.file.exists(sourceMapFile)) {
            prevSourceMap = grunt.file.readJSON(sourceMapFile);
          }

          if (hasSourceMap) {
            source = source.split(sourceMapComment + file)[0];
          }

          var fileSource = source;

          if (index !== filtered.length - 1 && !prevSourceMap) {
            fileSource += grunt.util.normalizelf(options.separator);
          }

          sourceMapConcat.add(filepath, source, prevSourceMap);
        }

        return source;
      }).join(grunt.util.normalizelf(options.separator));

      if (options.footer) {
        if (typeof options.footer === 'function') {
          src += options.footer(f.dest);
        } else {
          src += options.footer;
        }
      }

      // Write the destination file.
      grunt.file.write(f.dest, src);

      // Print a success message.
      grunt.log.writeln('File "' + f.dest + '" created.');
    });
  });

};
<commit_msg>Yeah, everything in order<commit_after>/*
 * grunt-commonjs-tamer
 * https://github.com/freezedev/grunt-commonjs-tamer
 *
 */

'use strict';

var path = require('path');
var beautify = require('js-beautify').js_beautify;
var coffeeFormatter = require('coffee-formatter');
var SourceMapConcat = require('sourcemap-concat').SourceMapConcatenator;

module.exports = function (grunt) {

  // Please see the Grunt documentation for more information regarding task
  // creation: http://gruntjs.com/creating-tasks

  grunt.registerMultiTask('commonjs_tamer', 'Tame your CommonJS modules', function () {
    // Merge task-specific and/or target-specific options with these defaults.
    var options = this.options({
      register: 'require.register',
      separator: grunt.util.linefeed,
      beautify: true,
      normalizeIndexFile: true,
      base: null,
      namespace: null,
      sourcemap: true,
      processName: function (name /*, basename */) {
        return name;
      },
      process: function (content /*, moduleName */) {
        return content;
      },
      blacklist: [],
      banner: '',
      footer: ''
    });

    var quotes = (options.doubleQuotes) ? '"' : '\'';

    // Iterate over all specified file groups.
    this.files.forEach(function (f) {

      var sourceMapConcat = new SourceMapConcat({file: f.dest});

      var src = '';

      if (options.banner) {
        if (typeof options.banner === 'function') {
          src += options.banner(f.dest);
        } else {
          src += options.banner;
        }
      }

      // Concat specified files.
      var filtered = f.src.filter(function (filepath) {
        // Warn on and remove invalid source files (if nonull was set).
        if (!grunt.file.exists(filepath)) {
          grunt.log.warn('Source file "' + filepath + '" not found.');
          return false;
        } else {
          return true;
        }
      });

      src += filtered.map(function (filepath, index) {
        // Read file source.
        var source = grunt.file.read(filepath);
        var extension = path.extname(filepath);

        var isCoffeeScript = extension === '.coffee';

        var moduleName = filepath.split(extension)[0];
        if (options.base && moduleName.indexOf(options.base) === 0) {
          moduleName = moduleName.split(options.base)[1];
        }

        if (options.normalizeIndexFile && moduleName.indexOf('/index') > 0) {
          moduleName = moduleName.replace('/index', '');
        }

        if (options.namespace) {
          // If index.js is in the root folder and a namespace has been defined
          // use the namespace as the module name
          if (options.normalizeIndexFile && moduleName === 'index') {
            moduleName = options.namespace;
          } else {
            moduleName = options.namespace + '/' + moduleName;
          }
        }

        moduleName = options.processName(moduleName, path.basename(moduleName));

        if (options.blacklist.indexOf(moduleName) >= 0) {
          return;
        }

        if (isCoffeeScript) {
          source = options.register + ' ' + quotes + moduleName + quotes + ', (require, module, exports) ->' +
          options.separator + (function (s) {
            return s.split(options.separator).map(function (l) {
              return '\t' + l;
            }).join(options.separator);
          })(source) + options.separator + options.separator;
        } else {
          source = options.register + '(' + quotes + moduleName + quotes + ', function(require, module, exports) {' +
          options.separator + source + options.separator + '});';
        }

        if (options.beautify) {
          if (!isCoffeeScript) {
            var beautifyOptions = {};

            if (typeof options.beautify === 'object') {
              beautifyOptions = options.beautify;
            } else {
              beautifyOptions = {
                indent_size: 2
              };
            }

            source = beautify(source, beautifyOptions);
          } else {
            source = coffeeFormatter.formatTwoSpaceOperator(source);
          }
        }

        source = options.process(source, moduleName);

        if (options.sourceMap) {
          var prevSourceMap;
          var sourceMapFile = filepath + '.map';
          var file;

          var sourceMapComment = '//# sourceMappingURL=';
          var hasSourceMap = source.indexOf(sourceMapComment) >= 0;

          if (hasSourceMap) {
            var smMatch = source.match(/\/\# sourceMappingURL=(.*)/);
            file = smMatch[1];

            var basename = path.basename(filepath);
            var sourceMapPath = filepath.split(basename)[0];

            sourceMapFile = [sourceMapPath, file].join(path.sep);
          }

          if (grunt.file.exists(sourceMapFile)) {
            prevSourceMap = grunt.file.readJSON(sourceMapFile);
          }

          if (hasSourceMap) {
            source = source.split(sourceMapComment + file)[0];
          }

          var fileSource = source;

          if (index !== filtered.length - 1 && !prevSourceMap) {
            fileSource += grunt.util.normalizelf(options.separator);
          }

          sourceMapConcat.add(filepath, source, prevSourceMap);
        }

        return source;
      }).join(grunt.util.normalizelf(options.separator));

      if (options.footer) {
        if (typeof options.footer === 'function') {
          src += options.footer(f.dest);
        } else {
          src += options.footer;
        }
      }

      // Write the destination file.
      grunt.file.write(f.dest, src);

      // Print a success message.
      grunt.log.writeln('File "' + f.dest + '" created.');
    });
  });

};
<|endoftext|>


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

$(document).ready(function () {

	var queryListObject = $("#query-list");
	queryListObject.numberObject = $("#queryNumber");
	queryListObject.template = $("#query-template").find("li").eq(0);
	queryListObject.type = "query";

	var variableListObject = $("#variable-list");
	variableListObject.numberObject = $("#variableNumber");
	variableListObject.template = $("#variable-template").find("li").eq(0);
	variableListObject.type = "variable";

	var scheduleFields = $("#schedule-fields");
	var scheduleInterval = $("#schedule-interval");
	var hourTimeField = $("#time-field");

	function updateListOrder(listObject){
		var elements = listObject.find("li");
		var first = 0;
		var last = elements.length - 1;

		$.each(elements, function(index){
			var element = $(this);
			element.attr("id", listObject.type + index);
			$.each(element.find("input").add(element.find("select")).add(element.find("textarea")), function(i){setNameFromTemplate($(this), index);} );
			var btnBumpUp = element.find('.bump-up');
			var btnBumpDown = element.find('.bump-down');
			var btnDelete = element.find('.delete');

			$(btnBumpUp).removeClass('first last disabled');
			$(btnBumpDown).removeClass('first last disabled');
			element.removeClass('first last')

			if (index == first) {
				element.addClass('first');
				$(btnBumpUp).addClass('disabled');
			}
			if (index == last) {
				element.addClass('last');
				$(btnBumpDown).addClass('disabled');
			}
			if(last == first) {
				$(btnDelete).hide();
			}
			else {
				$(btnDelete).show();
			}

			var textArea = element.find("textarea")[0];
			if(textArea && !textArea.codeMirror) {
				var selectType = element.find("select").val();
				var mime = "text/x-sql";
				if(selectType.indexOf("Pig") != -1) {
					mime = "text/x-pig";
				}
				textArea.codeMirror = CodeMirror.fromTextArea(textArea, {
					lineNumbers: true,
					lineWrapping: true,
					theme: "solarized dark",
					mode: mime
				});

				// Enable resizing of the CodeMirror code editor.
				$('.CodeMirror').resizable({
			        resize: function() {
			            textArea.codeMirror.setSize($(this).width(), $(this).height());
			        }
			    });
			}
		});

		listObject.numberObject.attr("value", elements.length);
	}

	function setNameFromTemplate(element, index){
		if(typeof element.attr("nametemplate") != 'undefined') {
			element.attr("name", element.attr("nametemplate").replace(/\#/g, index));
		}
	}

	function scheduleOptionChangeHandler(item) {
		if(item.checked) {
			scheduleFields.show();
		} else {
			scheduleFields.hide();
		}
	};

	function handleDelete(event, listObject) {
		event.preventDefault();
		if (confirm("Are you sure you want to delete this?")) {
			$(this).closest('li').remove();
			updateListOrder(listObject);
		}
	}

	function handleBumpUp(event, listObject) {
		event.preventDefault();
		var element = $(this).closest('li');
		var siblings = element.siblings();
		var index = element.index();
		var newIndex = index - 1;

		if (newIndex < 0) {
			return;
		}

		$(element).insertBefore(siblings[newIndex]).hide().fadeIn();
		updateListOrder(listObject);
	}

	function handleBumpDown(event, listObject) {
		event.preventDefault();
		var element = $(this).closest('li');
		var siblings = element.siblings();
		var index = element.index();
		var newIndex = index - 1;

		if (index >= siblings.length) {
			return;
		}

		$(element).insertAfter(siblings[index]).hide().fadeIn();
		updateListOrder(listObject);
	}

	queryListObject.delegate(".delete", "click", function (event) {
		handleDelete.call(this, event, queryListObject);
	})
	.delegate('.bump-up', 'click', function (event) {
		handleBumpUp.call(this, event, queryListObject);
	})
	.delegate('.bump-down', 'click', function (event) {
		handleBumpDown.call(this, event, queryListObject);
	})
	.delegate("select", "change", function (event) {
		var element = $(this).closest('li');
		var textArea = element.find("textarea")[0];
		if(textArea.codeMirror) {
			var selectType = element.find("select").val();
			var mime = "text/x-sql";
			if(selectType.indexOf("Pig") != -1) {
				mime = "text/x-pig";
			}

			textArea.codeMirror.setOption("mode", mime);
		}
	});

	variableListObject.delegate(".delete", "click", function (event) {
		handleDelete.call(this, event, variableListObject);
	})
	.delegate('.bump-up', 'click', function (event) {
		handleBumpUp.call(this, event, variableListObject);
	})
	.delegate('.bump-down', 'click', function (event) {
		handleBumpDown.call(this, event, variableListObject);
	});

	function handleAdd(listObject) {
		listObject.template.clone().appendTo(listObject);
		updateListOrder(listObject);
	}

	$("#buttonAddQuery").click(function(){
		handleAdd(queryListObject);
	});

	$("#buttonAddVariable").click(function(){
		handleAdd(variableListObject);
	});

	$("#schedule-options").change(function (event) {
		scheduleOptionChangeHandler(this);
	});

	//Load schedule options
	scheduleOptionChangeHandler($("#schedule-options")[0]);

	//Load starting queries
	for (var i = 0; i < startQueries.length; i++) {
		startQueries[i].script = $("#script" + startQueries[i].num).text();
	};

	function addInitialQueries() {
		function addQuery(item) {
			var element = queryListObject.template.clone();
			element.find(".querytitle").eq(0).val(item.title);
			var select = element.find(".querytype").eq(0).val(item.type);
			element.find(".queryscript").eq(0).val(item.script);
			element.appendTo(queryListObject);
		}
		for (var i = 0; i < startQueries.length; i++) {
			addQuery(startQueries[i]);
		};
		updateListOrder(queryListObject);
	}
	addInitialQueries();

	function addInitialVariables() {
		function addVariable(item) {
			var element = variableListObject.template.clone();
			element.find(".variabletitle").eq(0).val(item.title);
			element.find(".variablename").eq(0).val(item.name);
			element.appendTo(variableListObject);
		}
		for (var i = 0; i < startVariables.length; i++) {
			addVariable(startVariables[i]);
		};
		updateListOrder(variableListObject);
	}
	addInitialVariables();

	scheduleInterval.on('change',function(event){
		var interval = $(this).val();

		if (interval === 'h') { // hourly
			hourTimeField.hide();
		} else {
			hourTimeField.show();
		}
	});
});<commit_msg>Allow deleting all variables<commit_after>/*
 * Copyright 2012 LinkedIn Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

$(document).ready(function () {

	var queryListObject = $("#query-list");
	queryListObject.numberObject = $("#queryNumber");
	queryListObject.template = $("#query-template").find("li").eq(0);
	queryListObject.type = "query";

	var variableListObject = $("#variable-list");
	variableListObject.numberObject = $("#variableNumber");
	variableListObject.template = $("#variable-template").find("li").eq(0);
	variableListObject.type = "variable";

	var scheduleFields = $("#schedule-fields");
	var scheduleInterval = $("#schedule-interval");
	var hourTimeField = $("#time-field");

	function updateListOrder(listObject, requireAtLeastOneElement){
		var elements = listObject.find("li");
		var first = 0;
		var last = elements.length - 1;

		$.each(elements, function(index){
			var element = $(this);
			element.attr("id", listObject.type + index);
			$.each(element.find("input").add(element.find("select")).add(element.find("textarea")), function(i){setNameFromTemplate($(this), index);} );
			var btnBumpUp = element.find('.bump-up');
			var btnBumpDown = element.find('.bump-down');
			var btnDelete = element.find('.delete');

			$(btnBumpUp).removeClass('first last disabled');
			$(btnBumpDown).removeClass('first last disabled');
			element.removeClass('first last')

			if (index == first) {
				element.addClass('first');
				$(btnBumpUp).addClass('disabled');
			}
			if (index == last) {
				element.addClass('last');
				$(btnBumpDown).addClass('disabled');
			}
			if(last == first && requireAtLeastOneElement) {
				$(btnDelete).hide();
			}
			else {
				$(btnDelete).show();
			}

			var textArea = element.find("textarea")[0];
			if(textArea && !textArea.codeMirror) {
				var selectType = element.find("select").val();
				var mime = "text/x-sql";
				if(selectType.indexOf("Pig") != -1) {
					mime = "text/x-pig";
				}
				textArea.codeMirror = CodeMirror.fromTextArea(textArea, {
					lineNumbers: true,
					lineWrapping: true,
					theme: "solarized dark",
					mode: mime
				});

				// Enable resizing of the CodeMirror code editor.
				$('.CodeMirror').resizable({
			        resize: function() {
			            textArea.codeMirror.setSize($(this).width(), $(this).height());
			        }
			    });
			}
		});

		listObject.numberObject.attr("value", elements.length);
	}

	function setNameFromTemplate(element, index){
		if(typeof element.attr("nametemplate") != 'undefined') {
			element.attr("name", element.attr("nametemplate").replace(/\#/g, index));
		}
	}

	function scheduleOptionChangeHandler(item) {
		if(item.checked) {
			scheduleFields.show();
		} else {
			scheduleFields.hide();
		}
	};

	function handleDelete(event, listObject, requireAtLeastOneElement) {
		event.preventDefault();
		if (confirm("Are you sure you want to delete this?")) {
			$(this).closest('li').remove();
			updateListOrder(listObject, requireAtLeastOneElement);
		}
	}

	function handleBumpUp(event, listObject) {
		event.preventDefault();
		var element = $(this).closest('li');
		var siblings = element.siblings();
		var index = element.index();
		var newIndex = index - 1;

		if (newIndex < 0) {
			return;
		}

		$(element).insertBefore(siblings[newIndex]).hide().fadeIn();
		updateListOrder(listObject);
	}

	function handleBumpDown(event, listObject) {
		event.preventDefault();
		var element = $(this).closest('li');
		var siblings = element.siblings();
		var index = element.index();
		var newIndex = index - 1;

		if (index >= siblings.length) {
			return;
		}

		$(element).insertAfter(siblings[index]).hide().fadeIn();
		updateListOrder(listObject);
	}

	queryListObject.delegate(".delete", "click", function (event) {
		handleDelete.call(this, event, queryListObject, true);
	})
	.delegate('.bump-up', 'click', function (event) {
		handleBumpUp.call(this, event, queryListObject);
	})
	.delegate('.bump-down', 'click', function (event) {
		handleBumpDown.call(this, event, queryListObject);
	})
	.delegate("select", "change", function (event) {
		var element = $(this).closest('li');
		var textArea = element.find("textarea")[0];
		if(textArea.codeMirror) {
			var selectType = element.find("select").val();
			var mime = "text/x-sql";
			if(selectType.indexOf("Pig") != -1) {
				mime = "text/x-pig";
			}

			textArea.codeMirror.setOption("mode", mime);
		}
	});

	variableListObject.delegate(".delete", "click", function (event) {
		handleDelete.call(this, event, variableListObject);
	})
	.delegate('.bump-up', 'click', function (event) {
		handleBumpUp.call(this, event, variableListObject);
	})
	.delegate('.bump-down', 'click', function (event) {
		handleBumpDown.call(this, event, variableListObject);
	});

	function handleAdd(listObject) {
		listObject.template.clone().appendTo(listObject);
		updateListOrder(listObject);
	}

	$("#buttonAddQuery").click(function(){
		handleAdd(queryListObject);
	});

	$("#buttonAddVariable").click(function(){
		handleAdd(variableListObject);
	});

	$("#schedule-options").change(function (event) {
		scheduleOptionChangeHandler(this);
	});

	//Load schedule options
	scheduleOptionChangeHandler($("#schedule-options")[0]);

	//Load starting queries
	for (var i = 0; i < startQueries.length; i++) {
		startQueries[i].script = $("#script" + startQueries[i].num).text();
	};

	function addInitialQueries() {
		function addQuery(item) {
			var element = queryListObject.template.clone();
			element.find(".querytitle").eq(0).val(item.title);
			var select = element.find(".querytype").eq(0).val(item.type);
			element.find(".queryscript").eq(0).val(item.script);
			element.appendTo(queryListObject);
		}
		for (var i = 0; i < startQueries.length; i++) {
			addQuery(startQueries[i]);
		};
		updateListOrder(queryListObject, true);
	}
	addInitialQueries();

	function addInitialVariables() {
		function addVariable(item) {
			var element = variableListObject.template.clone();
			element.find(".variabletitle").eq(0).val(item.title);
			element.find(".variablename").eq(0).val(item.name);
			element.appendTo(variableListObject);
		}
		for (var i = 0; i < startVariables.length; i++) {
			addVariable(startVariables[i]);
		};
		updateListOrder(variableListObject);
	}
	addInitialVariables();

	scheduleInterval.on('change',function(event){
		var interval = $(this).val();

		if (interval === 'h') { // hourly
			hourTimeField.hide();
		} else {
			hourTimeField.show();
		}
	});
});<|endoftext|>


<commit_before>mainApp.controller('WordLookupQuizOnlineCtrl', function ($scope, $route, $rootScope, $http, $modal, $route, $location, lookupService, imageSearchService, $timeout, user) {

});<commit_msg>creating synoynma controller<commit_after>mainApp.controller('SynonymaCtrl', function ($scope, $route, $rootScope, $http, $modal, $route, $location, lookupService, imageSearchService, $timeout, user) {

});<|endoftext|>


<commit_before>'use strict';

const EventEmitter = require('events');
const dgram = require('dgram');

const Device = require('../device');

const CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';

const SubDevice = require('./gateway/subdevice');
const devices = {
	// Temperature and Humidity sensor
	'sensor_ht': require('./gateway/sensor_ht'),

	// Switch (button)
	'switch': require('./gateway/switch'),
	// Cube controller
	'cube': require('./gateway/cube'),
	// Light Switches (1 and 2 buttons)
	'86sw1': require('./gateway/86sw1'),
	'86sw2': require('./gateway/86sw2'),

	// Motion sensor
	'motion': require('./gateway/motion'),
	// Magnet sensor for windows and doors
	'magnet': require('./gateway/magnet'),

	// Simple plug for sockets
	'plug': require('./gateway/plug'),

	// Plugs for light switches
	'ctrl_neutral1': require('./gateway/ctrl_neutral1'),
	'ctrl_neutral2': require('./gateway/ctrl_neutral2')
};

/**
 * Generate a key for use with the local developer API. This does not generate
 * a secure key, but the Mi Home app does not seem to do that either.
 */
function generateKey() {
	let result = '';
	for(let i=0; i<16; i++) {
		let idx = Math.floor(Math.random() * CHARS.length)
		result += CHARS[idx];
	}
	return result;
}

/**
 * Abstraction over a Mi Smart Home Gateway.
 *
 * This device is very different from most other miIO-devices in that it has
 * devices on its own. There seems to be a way to talk to these devices by
 * setting `sid` in the requests, which means we can poll them. The problem
 * is that certain devices also send events, but these seem to only be
 * accessible over a local developer connection.
 *
 * So to work around this we activate the local developer connection and use
 * that together with the regular calls for changing things on the gateway.
 */
class Gateway extends Device {
	static get TYPE() { return 'gateway' }

	static get SubDevice() { return SubDevice }

	static registerSubDevice(id, deviceClass) {
		devices[id] = deviceClass;
	}

	constructor(options) {
		super(options);

		this.type = Gateway.TYPE;

		this._devices = {};
	}

	_report(properties) {
		Object.keys(properties).forEach(key => {
			switch(key) {
				case 'illumination':
					// TODO: What scale does this actually have? Lux?
					this.setProperty('illuminance', properties[key]);
					break;
			}
		});
	}

	set developerKey(key) {
		this._developerKey = key;

		// If we are already connected to the Developer API skip init
		if(this.devApi) return;

		this.devApi = new DeveloperApi(this, this.address);

		this.devApi.on('deviceAvailable', deviceInfo => {
			const currentDevice = this._devices[deviceInfo.id];
			if(currentDevice) {
				currentDevice.destroy();
				this.emit('deviceUnavailable', currentDevice);
			}

			const type = devices[deviceInfo.model] || SubDevice;
			const device = this._devices[deviceInfo.id] = new type(this, deviceInfo);
			device._report(deviceInfo);
			this.emit('deviceAvailable', device);
		});
		this.devApi.on('deviceUpdated', deviceInfo => {
			const currentDevice = this._devices[deviceInfo.id];
			if(! currentDevice) return;

			currentDevice._report(deviceInfo);
		});
		this.devApi.on('properties', this._report.bind(this));

		// TODO: Device removal
	}

	init() {
		return super.init()
			.then(() => this.findDeveloperKey())
			.then(() => this);
	}

	get devices() {
		const result = [];
		Object.keys(this._devices).forEach(key => result.push(this._devices[key]));
		return result;
	}

	/**
	 * Either resolve the developer key or setup a new one.
	 */
	findDeveloperKey() {
		// We already have a developer key
		if(this._developerKey) return Promise.resolve(this._developerKey);

		// Call and fetch the key or set a new key
		return this.call('get_lumi_dpf_aes_key')
			.then(result => {
				let key = result[0];
				if(key && key.length === 16) {
					// This is a valid key, store it
					this.developerKey = key;
					return key;
				}

				key = generateKey();
				return this.call('set_lumi_dpf_aes_key', [ key ])
					.then(() => {
						this.developerKey = key;
						return key;
					});
			})
	}

	/**
	 * Start the process of adding a device. Allows Zigbee devices to join for
	 * 30 seconds.
	 */
	addDevice() {
		return this.call('start_zigbee_join', [ 30 ])
			.then(result => result[0] == 'ok');
	}

	/**
	 * Stop allowing devices to join.
	 */
	stopAddDevice() {
		return this.call('start_zigbee_join', [ 0 ])
			.then(result => result[0] == 'ok');
	}

	/**
	 * Remove a device from the gateway using the identifier of the device.
	 */
	removeDevice(id) {
		return this.call('remove_device', [ id ]);
	}

	destroy() {
		super.destroy();

		if(this.net) {
			this.net.destroy();
		}
	}
}

const MULTICAST_ADDRESS = '224.0.0.50';
const MULTICAST_PORT = 4321;

class DeveloperApi extends EventEmitter {
	constructor(parent, address) {
		super();

		this.address = address;
		this.debug = parent.debug;

		this.socket = dgram.createSocket({
			type: 'udp4',
			reuseAddr: true
		});

		this.socket.on('message', this._onMessage.bind(this));
		this.socket.on('listening', () => {
			this.socket.addMembership(MULTICAST_ADDRESS);

			this.send({
				cmd: 'whois'
			});
		});
		this.socket.bind(9898);

		this.devices = [];
	}

	destroy() {
		this.socket.destroy();
	}

	send(data) {
		const json = JSON.stringify(data);
		this.debug('DEV ->', json);
		this.socket.send(json, 0, json.length, this.port || MULTICAST_PORT, this.address);
	}

	_onMessage(msg) {
		let data;
		try {
			this.debug('DEV <-', msg.toString());
			data = JSON.parse(msg.toString());
		} catch(ex) {
			this.emit('error', ex);
			return;
		}

		switch(data.cmd) {
			case 'iam':
			// TODO: Do we need to protect against multiple gateways?
				console.log('whomi' +  JSON.stringify(data));
				// This is a response to the initial whois
				this.sid = data.sid;
				this.port = data.port;

				// Discover devices
				this.send({
					cmd: 'get_id_list'
				});

				break;
			case 'heartbeat':
				console.log('heartbeat' +  JSON.stringify(data));
				if(data.sid === this.sid) {
					this.token = data.token;
				}
				break;
			case 'get_id_list_ack':
				console.log('list ack' +  JSON.stringify(data));
				if(data.sid === this.sid) {
					// Response with all identifiers of the devices
					this.token = data.token;
					const devices = JSON.parse(data.data);

					// Read data for ourself
					this.send({
						cmd: 'read',
						sid: this.sid
					});

					const now = Date.now();
					devices.forEach(id => {
						this.devices[id] = {
							id: id,
							lastSeen: now
						};

						this.send({
							cmd: 'read',
							sid: id
						});
					});
				}
				break;
			case 'read_ack': {
				console.log('Read ack' +  JSON.stringify(data));
				const device = this.devices[data.sid];
				if(device) {
					device.lastSeen = Date.now();
					device.data = JSON.parse(data.data);

					if(! device.model) {
						// This is the first read
						device.model = data.model;
						this.emit('deviceAvailable', device);
					}
				} else if(data.sid === this.sid) {
					this.emit('properties', JSON.parse(data.data));
				}
				break;
			}
			case 'report': {
					console.log('report ack' +  JSON.stringify(data));
				const device = this.devices[data.sid];
				if(device) {
					device.lastSeen = Date.now();

					const parsed = JSON.parse(data.data);
					Object.keys(parsed).forEach(key => {
						device.data[key] = parsed[key];
					});

					this.emit('deviceUpdated', device);
				} else if(data.sid === this.sid) {
					this.emit('properties', JSON.parse(data.data));
				}
			}
			case 'write_ack': {
				console.log('Write ack' +  JSON.stringify(data));
			}
		}
	}
}

module.exports = Gateway;
<commit_msg>update test<commit_after>'use strict';

const EventEmitter = require('events');
const dgram = require('dgram');

const Device = require('../device');

const CHARS = '0123456789abcdefghijklmnopqrstuvwxyz';

const SubDevice = require('./gateway/subdevice');
const devices = {
	// Temperature and Humidity sensor
	'sensor_ht': require('./gateway/sensor_ht'),

	// Switch (button)
	'switch': require('./gateway/switch'),
	// Cube controller
	'cube': require('./gateway/cube'),
	// Light Switches (1 and 2 buttons)
	'86sw1': require('./gateway/86sw1'),
	'86sw2': require('./gateway/86sw2'),

	// Motion sensor
	'motion': require('./gateway/motion'),
	// Magnet sensor for windows and doors
	'magnet': require('./gateway/magnet'),

	// Simple plug for sockets
	'plug': require('./gateway/plug'),

	// Plugs for light switches
	'ctrl_neutral1': require('./gateway/ctrl_neutral1'),
	'ctrl_neutral2': require('./gateway/ctrl_neutral2')
};

/**
 * Generate a key for use with the local developer API. This does not generate
 * a secure key, but the Mi Home app does not seem to do that either.
 */
function generateKey() {
	let result = '';
	for(let i=0; i<16; i++) {
		let idx = Math.floor(Math.random() * CHARS.length)
		result += CHARS[idx];
	}
	return result;
}

/**
 * Abstraction over a Mi Smart Home Gateway.
 *
 * This device is very different from most other miIO-devices in that it has
 * devices on its own. There seems to be a way to talk to these devices by
 * setting `sid` in the requests, which means we can poll them. The problem
 * is that certain devices also send events, but these seem to only be
 * accessible over a local developer connection.
 *
 * So to work around this we activate the local developer connection and use
 * that together with the regular calls for changing things on the gateway.
 */
class Gateway extends Device {
	static get TYPE() { return 'gateway' }

	static get SubDevice() { return SubDevice }

	static registerSubDevice(id, deviceClass) {
		devices[id] = deviceClass;
	}

	constructor(options) {
		super(options);

		this.type = Gateway.TYPE;

		this._devices = {};
	}

	_report(properties) {
		Object.keys(properties).forEach(key => {
			switch(key) {
				case 'illumination':
					// TODO: What scale does this actually have? Lux?
					this.setProperty('illuminance', properties[key]);
					break;
			}
		});
	}

	set developerKey(key) {
		this._developerKey = key;

		// If we are already connected to the Developer API skip init
		if(this.devApi) return;

		this.devApi = new DeveloperApi(this, this.address);

		this.devApi.on('deviceAvailable', deviceInfo => {
			const currentDevice = this._devices[deviceInfo.id];
			if(currentDevice) {
				currentDevice.destroy();
				this.emit('deviceUnavailable', currentDevice);
			}

			const type = devices[deviceInfo.model] || SubDevice;
			const device = this._devices[deviceInfo.id] = new type(this, deviceInfo);
			device._report(deviceInfo);
			this.emit('deviceAvailable', device);
		});
		this.devApi.on('deviceUpdated', deviceInfo => {
			const currentDevice = this._devices[deviceInfo.id];
			if(! currentDevice) return;

			currentDevice._report(deviceInfo);
		});
		this.devApi.on('properties', this._report.bind(this));

		// TODO: Device removal
	}

	init() {
		return super.init()
			.then(() => this.findDeveloperKey())
			.then(() => this);
	}

	get devices() {
		const result = [];
		Object.keys(this._devices).forEach(key => result.push(this._devices[key]));
		return result;
	}

	/**
	 * Either resolve the developer key or setup a new one.
	 */
	findDeveloperKey() {
		// We already have a developer key
		if(this._developerKey) return Promise.resolve(this._developerKey);

		// Call and fetch the key or set a new key
		return this.call('get_lumi_dpf_aes_key')
			.then(result => {
				let key = result[0];
				if(key && key.length === 16) {
					// This is a valid key, store it
					this.developerKey = key;
					return key;
				}

				key = generateKey();
				return this.call('set_lumi_dpf_aes_key', [ key ])
					.then(() => {
						this.developerKey = key;
						return key;
					});
			})
	}

	/**
	 * Start the process of adding a device. Allows Zigbee devices to join for
	 * 30 seconds.
	 */
	addDevice() {
		return this.call('start_zigbee_join', [ 30 ])
			.then(result => result[0] == 'ok');
	}

	/**
	 * Stop allowing devices to join.
	 */
	stopAddDevice() {
		return this.call('start_zigbee_join', [ 0 ])
			.then(result => result[0] == 'ok');
	}

	/**
	 * Remove a device from the gateway using the identifier of the device.
	 */
	removeDevice(id) {
		return this.call('remove_device', [ id ]);
	}

	destroy() {
		super.destroy();

		if(this.net) {
			this.net.destroy();
		}
	}
}

const MULTICAST_ADDRESS = '224.0.0.50';
const MULTICAST_PORT = 4321;

class DeveloperApi extends EventEmitter {
	constructor(parent, address) {
		super();

		this.address = address;
		this.debug = parent.debug;

		this.socket = dgram.createSocket({
			type: 'udp4',
			reuseAddr: true
		});

		this.socket.on('message', this._onMessage.bind(this));
		this.socket.on('listening', () => {
			this.socket.addMembership(MULTICAST_ADDRESS);

			this.send({
				cmd: 'whois'
			});
		});
		this.socket.bind(9898);

		this.devices = [];
	}

	destroy() {
		this.socket.destroy();
	}

	send(data) {
		const json = JSON.stringify(data);
		this.debug('DEV ->', json);
		this.socket.send(json, 0, json.length, this.port || MULTICAST_PORT, this.address);
	}

	_onMessage(msg) {
		let data;
		try {
			this.debug('DEV <-', msg.toString());
			data = JSON.parse(msg.toString());
		} catch(ex) {
			this.emit('error', ex);
			return;
		}
		console.log(data);

		switch(data.cmd) {
			case 'iam':
			// TODO: Do we need to protect against multiple gateways?
				// This is a response to the initial whois
				this.sid = data.sid;
				this.port = data.port;

				// Discover devices
				this.send({
					cmd: 'get_id_list'
				});

				break;
			case 'heartbeat':
				if(data.sid === this.sid) {
					this.token = data.token;
				}
				break;
			case 'get_id_list_ack':
				if(data.sid === this.sid) {
					// Response with all identifiers of the devices
					this.token = data.token;
					const devices = JSON.parse(data.data);

					// Read data for ourself
					this.send({
						cmd: 'read',
						sid: this.sid
					});

					const now = Date.now();
					devices.forEach(id => {
						this.devices[id] = {
							id: id,
							lastSeen: now
						};

						this.send({
							cmd: 'read',
							sid: id
						});
					});
				}
				break;
			case 'read_ack': {
				const device = this.devices[data.sid];
				if(device) {
					device.lastSeen = Date.now();
					device.data = JSON.parse(data.data);

					if(! device.model) {
						// This is the first read
						device.model = data.model;
						this.emit('deviceAvailable', device);
					}
				} else if(data.sid === this.sid) {
					this.emit('properties', JSON.parse(data.data));
				}
				break;
			}
			case 'report': {
				const device = this.devices[data.sid];
				if(device) {
					device.lastSeen = Date.now();

					const parsed = JSON.parse(data.data);
					Object.keys(parsed).forEach(key => {
						device.data[key] = parsed[key];
					});

					this.emit('deviceUpdated', device);
				} else if(data.sid === this.sid) {
					this.emit('properties', JSON.parse(data.data));
				}
			}
		}
	}
}

module.exports = Gateway;
<|endoftext|>


<commit_before>var gShiftKey = false;
var gEpsilon = "Ɛ";
var gGraph =
  {
  };

gGraph.init = function () {
  var svg = d3.select ("svg");
  gGraph.width = svg.node ().getBoundingClientRect ().width;
  gGraph.height = svg.node ().getBoundingClientRect ().height;

  gGraph.charSet = sortString (getURLParam ("charset"));
  gGraph.epsilonEnabled = true;

  gNodes.init ();
  gEdges.init ();
  gBrush.init ();
  gTape.init ();
  gTestMenu.init ();
  gGraph.initDelete ();

  gBehaviors.addBehavior ("page", "keydown", function () { return true; }, gGraph.keydown);
  gBehaviors.addBehavior ("page", "keyup", function () { return true; }, gGraph.keyup);
  gBehaviors.apply ("page", d3.select("body"));
  gBehaviors.apply ("background", d3.select("div.gui"));

  gNodes.addNode ();
  gNodes.addNode ();
  gGraph.draw ();
}

gGraph.keydown = function () {
  gShiftKey = d3.event.shiftKey || d3.event.metaKey;
};

gGraph.keyup = function () {
  gShiftKey = d3.event.shiftKey || d3.event.metaKey;
};

gGraph.save = function () {
  return {
      nodes: gNodes.save (),
      edges: gEdges.save ()
    };
};

gGraph.load = function (saveData) {
  gNodes.load (saveData.nodes);
  gEdges.load (saveData.edges);
};

gGraph.draw = function () {
  gNodes.draw ();
  gEdges.draw ();
  gTopMenu.draw ();
  gTableMenu.draw ();
  gTape.draw ();
}

// TODO: Make the resizing more useful
// TODO: Find a non-kludgey way for the svg to have 100% height
gGraph.resize = function () {
  var svg = d3.select ("svg");
  gGraph.width = svg.node ().getBoundingClientRect ().width;
  gGraph.height = svg.node ().getBoundingClientRect ().height;
  d3.select (".background").attr("width", gGraph.width);
  d3.select (".background").attr("height", gGraph.height);
};<commit_msg>Added redirect in case no charSet is defined<commit_after>var gShiftKey = false;
var gEpsilon = "Ɛ";
var gGraph =
  {
  };

gGraph.init = function () {
  var svg = d3.select ("svg");
  gGraph.width = svg.node ().getBoundingClientRect ().width;
  gGraph.height = svg.node ().getBoundingClientRect ().height;

  var charSetParam = getURLParam ("charset");
  if (charSetParam == null) {
    var url = window.location.href;
    var loc = url.lastIndexOf ("/") + 1;
    if (loc <= 8) {
      url += "/";
    } else {
      url = url.substring (0, loc);
    }
    window.location = url + "index.html";
  }
  gGraph.charSet = sortString (charSetParam);
  gGraph.epsilonEnabled = true;

  gNodes.init ();
  gEdges.init ();
  gBrush.init ();
  gTape.init ();
  gTestMenu.init ();
  gGraph.initDelete ();

  gBehaviors.addBehavior ("page", "keydown", function () { return true; }, gGraph.keydown);
  gBehaviors.addBehavior ("page", "keyup", function () { return true; }, gGraph.keyup);
  gBehaviors.apply ("page", d3.select("body"));
  gBehaviors.apply ("background", d3.select("div.gui"));

  gNodes.addNode ();
  gNodes.addNode ();
  gGraph.draw ();
}

gGraph.keydown = function () {
  gShiftKey = d3.event.shiftKey || d3.event.metaKey;
};

gGraph.keyup = function () {
  gShiftKey = d3.event.shiftKey || d3.event.metaKey;
};

gGraph.save = function () {
  return {
      nodes: gNodes.save (),
      edges: gEdges.save ()
    };
};

gGraph.load = function (saveData) {
  gNodes.load (saveData.nodes);
  gEdges.load (saveData.edges);
};

gGraph.draw = function () {
  gNodes.draw ();
  gEdges.draw ();
  gTopMenu.draw ();
  gTableMenu.draw ();
  gTape.draw ();
}

// TODO: Make the resizing more useful
// TODO: Find a non-kludgey way for the svg to have 100% height
gGraph.resize = function () {
  var svg = d3.select ("svg");
  gGraph.width = svg.node ().getBoundingClientRect ().width;
  gGraph.height = svg.node ().getBoundingClientRect ().height;
  d3.select (".background").attr("width", gGraph.width);
  d3.select (".background").attr("height", gGraph.height);
};<|endoftext|>


<commit_before>'use strict';

var separate          = require('es5-ext/array/#/separate')
  , assign            = require('es5-ext/object/assign')
  , forEach           = require('es5-ext/object/for-each')
  , isPlainObject     = require('es5-ext/object/is-plain-object')
  , callable          = require('es5-ext/object/valid-callable')
  , validObject       = require('es5-ext/object/valid-object')
  , validValue        = require('es5-ext/object/valid-value')
  , memoize           = require('memoizee/plain')
  , memoizeMethods    = require('memoizee/methods-plain')
  , getNormalizer     = require('memoizee/normalizers/get-1')
  , d                 = require('d')
  , autoBind          = require('d/auto-bind')
  , isObservable      = require('observable-value/is-observable')
  , isObservableArray = require('observable-array/is-observable-array')
  , isObservableSet   = require('observable-set/is-observable-set')
  , clear             = require('dom-ext/element/#/clear')
  , exclude           = require('dom-ext/element/#/exclude')
  , include           = require('dom-ext/element/#/include')
  , replace           = require('dom-ext/element/#/replace')
  , replaceContent    = require('dom-ext/element/#/replace-content')
  , isNested          = require('dbjs/is-dbjs-nested-object')
  , resolveOptions    = require('./utils/resolve-options')
  , DOMInput          = require('./_controls/input')
  , DOMRadio          = require('./_controls/radio')
  , DOMSelect         = require('./_controls/select')
  , DOMMultiple       = require('./_multiple/checkbox')
  , DOMComposite      = require('./_composite')

  , map = Array.prototype.map, create = Object.create, defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , createOption = DOMSelect.prototype.createOption
  , createRadio = DOMRadio.prototype.createOption
  , getId = function (args) { return args[0].__id__; }

  , Radio, Select, Edit, Multiple, resolveDbOptions, getResolver;

getResolver = memoize(function (type) {
	return function (obj) {
		if (!obj || ((obj.constructor !== type) && !type.isPrototypeOf(obj.constructor))) {
			throw new TypeError(obj + " is not a " + type.__id__);
		}
		return obj;
	};
}, { normalizer: getNormalizer() });

resolveDbOptions = function (type, options) {
	if (options.list != null) {
		if (isObservableSet(options.list)) {
			this.dbOptions = options.list.toArray().map(getResolver(type), this);
		} else if (isObservableArray(options.list)) {
			this.dbOptions = options.list.map(getResolver(type), this);
		} else {
			this.dbOptions = map.call(options.list, getResolver(type), this);
		}
	} else {
		this.dbOptions = type.instances.toArray(options.compare);
	}
	if (!this.group) this.dbOptions = this.dbOptions.map(this.createOption, this);
	if (typeof this.dbOptions.on !== 'function') return;
	this.dbOptions.on('change', this.reload);
};

Select = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	DOMSelect.call(this, document, type, options);
	if (options.getOptionLabel != null) this.getOptionLabel = callable(options.getOptionLabel);
	else this.property = options.property;
	if (options.group != null) {
		this.group = validObject(options.group);
		validValue(this.group.propertyName);
	}
	resolveDbOptions.call(this, type, options);
	this.reload();
};
Select.prototype = Object.create(DOMSelect.prototype, assign({
	constructor: d(Select)
}, memoizeMethods({
	createOption: d(function (obj) {
		var value;
		if (this.getOptionLabel) {
			value = this.getOptionLabel(obj);
			if (isObservable(value)) value = value.toDOM(this.document);
		} else if (this.property) {
			value = obj._get(this.property);
			if (isObservable(value)) value = value.toDOM(this.document);
		} else {
			value = this.document.createTextNode(obj);
		}
		return createOption.call(this, obj.__id__, value);
	}, { normalizer: getId }),
	createOptgroup: d(function (obj) {
		var el = this.document.createElement('optgroup'), value;
		if (this.group.labelPropertyName) {
			value = obj._get(this.group.labelPropertyName);
			if (isObservable(value)) value = value.toDOMAttr(el, 'label');
			else el.setAttribute('label', value);
		} else {
			el.setAttribute('label', value);
		}
		return el;
	}, { normalizer: getId })
}), autoBind({
	reload: d(function () {
		var els, done, options = this.dbOptions;
		if (this.group) {
			els = [];
			done = create(null);
			options.forEach(function (obj) {
				var group = obj[this.group.propertyName], optgroup, option;
				if (!group) {
					console.warn("No group found for", obj.__id__, "searched by", this.group.propertyName);
					return;
				}
				optgroup = this.createOptgroup(group);
				option = this.createOption(obj);
				if (!done[group.__id__]) {
					clear.call(optgroup);
					done[group.__id__] = true;
					els.push(optgroup);
				}
				optgroup.appendChild(option);
			}, this);
		} else {
			els = options;
		}
		replaceContent.call(this.control, this.chooseOption, els);
	})
})));

Radio = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	DOMRadio.call(this, document, type, options);
	this.dom.classList.add('object-list');
	this.property = options.property;
	resolveDbOptions.call(this, type, options);
	this.reload();
};
Radio.prototype = Object.create(DOMRadio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (obj) {
		return createRadio.call(this, obj.__id__,
			this.property ? obj._get(this.property).toDOM(this.document) :
					this.document.createTextNode(obj));
	})
}, autoBind({
	reload: d(function () { replaceContent.call(this.dom, this.dbOptions); })
})));

Edit = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	if (options.render) this._render = callable(options.render);
	DOMComposite.call(this, document, type, options);
	if (options.render) delete this._render;
	this.objInput = this.dom.appendChild(this.make('input', { type: 'hidden' }));
	exclude.call(this.objInput);
};
Edit.prototype = Object.create(DOMComposite.prototype, {
	constructor: d(Edit),
	_render: d(function (options) {
		var el = this.make, props, desc = options.dbOptions
		  , obj = this.type.prototype;
		props = options.inputProperties || desc.inputProperties ||
			this.type.inputProperties ||
			this.type.prototype.toSet('key').toArray();
		this.dom = el('div', { class: 'inputs' }, separate.call(props.map(function (name) {
			var observable = obj._get(name);
			return this.addItem(observable.toDOMInput(this.document,
				this.getOptions(observable.descriptor)), name);
		}, this), ' '));
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		if (name) {
			if (this.obj) {
				this.objInput.setAttribute('name', name);
			} else {
				forEach(this.items, function (item, propName) {
					item.name = name + '.' + propName;
				});
			}
		} else if (this.obj) {
			this.objInput.removeAttribute('name');
		} else {
			forEach(this.items, function (item) { item.name = null; });
		}
	}),
	value: d.gs(function () { return this.inputValue; }, function (value) {
		var obj;
		if (value === undefined) value = null;
		if (this.obj === value) return;
		obj = value || this.type.prototype;
		forEach(this.items, function (oldInput, name) {
			var nuInput, observable = obj._get(name);
			oldInput.destroy();
			nuInput = observable.toDOMInput(this.document,
				this.getOptions(observable.descriptor));
			replace.call(oldInput.dom, nuInput.dom);
			if (!value) nuInput.name = this.name + '.' + name;
			this.addItem(nuInput, name);
		}, this);
		if (value && !isNested(value)) {
			this.objInput.setAttribute('value', value.__id__);
			include.call(this.objInput);
		} else {
			this.objInput.removeAttribute('value');
			exclude.call(this.objInput);
		}
		this.obj = value;
		this.name = this._name;
	})
});

Multiple = function (document, type/*, options*/) {
	var options = arguments[2], getLabel, list, toData;

	this.type = type;
	options = resolveOptions(options, type);
	getLabel = function (obj) {
		var label = options.itemLabel;
		if (typeof label === 'function') return label(obj);
		if (typeof label === 'string') return obj._get(label);
		return obj;
	};
	if (options.list) {
		list = options.list;
	} else {
		list = type.instances.toArray(options.sort);
	}
	toData = function (obj) {
		return { label: getLabel(obj), value: obj.__id__ };
	};
	this.dbList = list.map(toData);
	if (isObservable(this.dbList)) this.dbList.on('change', this.reload);
	DOMMultiple.call(this, document, type, options);
};

Multiple.prototype = Object.create(DOMMultiple.prototype, {
	constructor: d(Multiple)
});

module.exports = exports = function (db) {
	defineProperties(db.Object, {
		choosLabel: d("Choose:"),
		fromInputValue: d(function (value) {
			var empty;
			if (value == null) return null;
			if (isPlainObject(value)) {
				empty = true;
				forEach(value, function (subValue, name) {
					var sKey = this._serialize_(name)
					  , desc = this._getDescriptor_(sKey);
					subValue = desc.type.fromInputValue(subValue);
					value[name] = subValue;
					if (subValue != null) empty = false;
				}, this.prototype);
				return empty ? null : value;
			}
			value = value.trim();
			if (!value) return null;
			return this.getById(value);
		}),
		toInputValue: d(function (value) {
			if (value == null) return null;
			if (!this.is(value)) return null;
			return value.__id__;
		}),
		DOMRadio: d(Radio),
		DOMSelect: d(Select),
		DOMEdit: d(Edit),
		DOMInput: d(Select),
		DOMMultipleCheckbox: d(Multiple),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple) {
				if (options.type === 'checkbox') {
					return new Multiple(document, this, options);
				}
				return new this.DOMMultipleInput(document, this, options);
			}
			if (options.type === 'edit') {
				return new this.DOMEdit(document, this, options);
			}
			if (options.type === 'radio') {
				return new this.DOMRadio(document, this, options);
			}
			return new this.DOMInput(document, this, options);
		})
	});

	defineProperties(db.Object.$getOwn('chooseLabel'), {
		type: d('', db.String),
		required: d(true)
	});
};

exports.Select = Select;
exports.Radio = Radio;
exports.Edit = Edit;
exports.Multiple = Multiple;
<commit_msg>Ensure proper resolution of nested objects<commit_after>'use strict';

var separate          = require('es5-ext/array/#/separate')
  , assign            = require('es5-ext/object/assign')
  , forEach           = require('es5-ext/object/for-each')
  , isPlainObject     = require('es5-ext/object/is-plain-object')
  , callable          = require('es5-ext/object/valid-callable')
  , validObject       = require('es5-ext/object/valid-object')
  , validValue        = require('es5-ext/object/valid-value')
  , includes          = require('es5-ext/string/#/contains')
  , memoize           = require('memoizee/plain')
  , memoizeMethods    = require('memoizee/methods-plain')
  , getNormalizer     = require('memoizee/normalizers/get-1')
  , d                 = require('d')
  , autoBind          = require('d/auto-bind')
  , isObservable      = require('observable-value/is-observable')
  , isObservableArray = require('observable-array/is-observable-array')
  , isObservableSet   = require('observable-set/is-observable-set')
  , clear             = require('dom-ext/element/#/clear')
  , exclude           = require('dom-ext/element/#/exclude')
  , include           = require('dom-ext/element/#/include')
  , replace           = require('dom-ext/element/#/replace')
  , replaceContent    = require('dom-ext/element/#/replace-content')
  , isNested          = require('dbjs/is-dbjs-nested-object')
  , resolveOptions    = require('./utils/resolve-options')
  , DOMInput          = require('./_controls/input')
  , DOMRadio          = require('./_controls/radio')
  , DOMSelect         = require('./_controls/select')
  , DOMMultiple       = require('./_multiple/checkbox')
  , DOMComposite      = require('./_composite')

  , map = Array.prototype.map, create = Object.create, defineProperties = Object.defineProperties
  , getName = Object.getOwnPropertyDescriptor(DOMInput.prototype, 'name').get
  , createOption = DOMSelect.prototype.createOption
  , createRadio = DOMRadio.prototype.createOption
  , getId = function (args) { return args[0].__id__; }

  , Radio, Select, Edit, Multiple, resolveDbOptions, getResolver;

getResolver = memoize(function (type) {
	return function (obj) {
		if (!obj || ((obj.constructor !== type) && !type.isPrototypeOf(obj.constructor))) {
			throw new TypeError(obj + " is not a " + type.__id__);
		}
		return obj;
	};
}, { normalizer: getNormalizer() });

resolveDbOptions = function (type, options) {
	if (options.list != null) {
		if (isObservableSet(options.list)) {
			this.dbOptions = options.list.toArray().map(getResolver(type), this);
		} else if (isObservableArray(options.list)) {
			this.dbOptions = options.list.map(getResolver(type), this);
		} else {
			this.dbOptions = map.call(options.list, getResolver(type), this);
		}
	} else {
		this.dbOptions = type.instances.toArray(options.compare);
	}
	if (!this.group) this.dbOptions = this.dbOptions.map(this.createOption, this);
	if (typeof this.dbOptions.on !== 'function') return;
	this.dbOptions.on('change', this.reload);
};

Select = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	DOMSelect.call(this, document, type, options);
	if (options.getOptionLabel != null) this.getOptionLabel = callable(options.getOptionLabel);
	else this.property = options.property;
	if (options.group != null) {
		this.group = validObject(options.group);
		validValue(this.group.propertyName);
	}
	resolveDbOptions.call(this, type, options);
	this.reload();
};
Select.prototype = Object.create(DOMSelect.prototype, assign({
	constructor: d(Select)
}, memoizeMethods({
	createOption: d(function (obj) {
		var value;
		if (this.getOptionLabel) {
			value = this.getOptionLabel(obj);
			if (isObservable(value)) value = value.toDOM(this.document);
		} else if (this.property) {
			value = obj._get(this.property);
			if (isObservable(value)) value = value.toDOM(this.document);
		} else {
			value = this.document.createTextNode(obj);
		}
		return createOption.call(this, obj.__id__, value);
	}, { normalizer: getId }),
	createOptgroup: d(function (obj) {
		var el = this.document.createElement('optgroup'), value;
		if (this.group.labelPropertyName) {
			value = obj._get(this.group.labelPropertyName);
			if (isObservable(value)) value = value.toDOMAttr(el, 'label');
			else el.setAttribute('label', value);
		} else {
			el.setAttribute('label', value);
		}
		return el;
	}, { normalizer: getId })
}), autoBind({
	reload: d(function () {
		var els, done, options = this.dbOptions;
		if (this.group) {
			els = [];
			done = create(null);
			options.forEach(function (obj) {
				var group = obj[this.group.propertyName], optgroup, option;
				if (!group) {
					console.warn("No group found for", obj.__id__, "searched by", this.group.propertyName);
					return;
				}
				optgroup = this.createOptgroup(group);
				option = this.createOption(obj);
				if (!done[group.__id__]) {
					clear.call(optgroup);
					done[group.__id__] = true;
					els.push(optgroup);
				}
				optgroup.appendChild(option);
			}, this);
		} else {
			els = options;
		}
		replaceContent.call(this.control, this.chooseOption, els);
	})
})));

Radio = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	DOMRadio.call(this, document, type, options);
	this.dom.classList.add('object-list');
	this.property = options.property;
	resolveDbOptions.call(this, type, options);
	this.reload();
};
Radio.prototype = Object.create(DOMRadio.prototype, assign({
	constructor: d(Radio),
	createOption: d(function (obj) {
		return createRadio.call(this, obj.__id__,
			this.property ? obj._get(this.property).toDOM(this.document) :
					this.document.createTextNode(obj));
	})
}, autoBind({
	reload: d(function () { replaceContent.call(this.dom, this.dbOptions); })
})));

Edit = function (document, type/*, options*/) {
	var options = arguments[2];
	this.type = type;
	options = resolveOptions(options, type);
	if (options.render) this._render = callable(options.render);
	DOMComposite.call(this, document, type, options);
	if (options.render) delete this._render;
	this.objInput = this.dom.appendChild(this.make('input', { type: 'hidden' }));
	exclude.call(this.objInput);
};
Edit.prototype = Object.create(DOMComposite.prototype, {
	constructor: d(Edit),
	_render: d(function (options) {
		var el = this.make, props, desc = options.dbOptions
		  , obj = this.type.prototype;
		props = options.inputProperties || desc.inputProperties ||
			this.type.inputProperties ||
			this.type.prototype.toSet('key').toArray();
		this.dom = el('div', { class: 'inputs' }, separate.call(props.map(function (name) {
			var observable = obj._get(name);
			return this.addItem(observable.toDOMInput(this.document,
				this.getOptions(observable.descriptor)), name);
		}, this), ' '));
	}),
	name: d.gs(getName, function (name) {
		this._name = name;
		name = this.name;
		if (name) {
			if (this.obj) {
				this.objInput.setAttribute('name', name);
			} else {
				forEach(this.items, function (item, propName) {
					item.name = name + '.' + propName;
				});
			}
		} else if (this.obj) {
			this.objInput.removeAttribute('name');
		} else {
			forEach(this.items, function (item) { item.name = null; });
		}
	}),
	value: d.gs(function () { return this.inputValue; }, function (value) {
		var obj;
		if (value === undefined) value = null;
		if (this.obj === value) return;
		obj = value || this.type.prototype;
		forEach(this.items, function (oldInput, name) {
			var nuInput, observable = obj._get(name);
			oldInput.destroy();
			nuInput = observable.toDOMInput(this.document,
				this.getOptions(observable.descriptor));
			replace.call(oldInput.dom, nuInput.dom);
			if (!value) nuInput.name = this.name + '.' + name;
			this.addItem(nuInput, name);
		}, this);
		if (value && !isNested(value)) {
			this.objInput.setAttribute('value', value.__id__);
			include.call(this.objInput);
		} else {
			this.objInput.removeAttribute('value');
			exclude.call(this.objInput);
		}
		this.obj = value;
		this.name = this._name;
	})
});

Multiple = function (document, type/*, options*/) {
	var options = arguments[2], getLabel, list, toData;

	this.type = type;
	options = resolveOptions(options, type);
	getLabel = function (obj) {
		var label = options.itemLabel;
		if (typeof label === 'function') return label(obj);
		if (typeof label === 'string') return obj._get(label);
		return obj;
	};
	if (options.list) {
		list = options.list;
	} else {
		list = type.instances.toArray(options.sort);
	}
	toData = function (obj) {
		return { label: getLabel(obj), value: obj.__id__ };
	};
	this.dbList = list.map(toData);
	if (isObservable(this.dbList)) this.dbList.on('change', this.reload);
	DOMMultiple.call(this, document, type, options);
};

Multiple.prototype = Object.create(DOMMultiple.prototype, {
	constructor: d(Multiple)
});

module.exports = exports = function (db) {
	defineProperties(db.Object, {
		choosLabel: d("Choose:"),
		fromInputValue: d(function (value) {
			var empty, result, owner, index;
			if (value == null) return null;
			if (isPlainObject(value)) {
				empty = true;
				forEach(value, function (subValue, name) {
					var sKey = this._serialize_(name)
					  , desc = this._getDescriptor_(sKey);
					subValue = desc.type.fromInputValue(subValue);
					value[name] = subValue;
					if (subValue != null) empty = false;
				}, this.prototype);
				return empty ? null : value;
			}
			value = value.trim();
			if (!value) return null;
			result = this.getById(value);
			if (result) return result;
			if (db.objects.getById(value)) return null;
			// If it's about nested, it might be that it's not loaded into memory
			// Below logic ensures it's retrieved properly
			index = value.indexOf('/');
			if (index === -1) return null;
			owner = db.objects.getById(value.slice(0, index));
			if (!owner) return null;
			value = owner.resolveSKeyPath(value.slice(index + 1)).value;
			if (value && (value instanceof this)) return value;
			return null;
		}),
		toInputValue: d(function (value) {
			if (value == null) return null;
			if (!this.is(value)) return null;
			return value.__id__;
		}),
		DOMRadio: d(Radio),
		DOMSelect: d(Select),
		DOMEdit: d(Edit),
		DOMInput: d(Select),
		DOMMultipleCheckbox: d(Multiple),
		toDOMInput: d(function (document/*, options*/) {
			var options = Object(arguments[1]);
			if (options.multiple) {
				if (options.type === 'checkbox') {
					return new Multiple(document, this, options);
				}
				return new this.DOMMultipleInput(document, this, options);
			}
			if (options.type === 'edit') {
				return new this.DOMEdit(document, this, options);
			}
			if (options.type === 'radio') {
				return new this.DOMRadio(document, this, options);
			}
			return new this.DOMInput(document, this, options);
		})
	});

	defineProperties(db.Object.$getOwn('chooseLabel'), {
		type: d('', db.String),
		required: d(true)
	});
};

exports.Select = Select;
exports.Radio = Radio;
exports.Edit = Edit;
exports.Multiple = Multiple;
<|endoftext|>


<commit_before>function prepareCrimes(rows, before = null, after = null) {
  if (rows.length === 0) return [];
  return rows.map((itm) => {
    return {
      incident_id: itm.incident_id,
      agency: itm.agency,
      date_occurred: itm.date_occurred,
      case_number: itm.case_number,
      address: itm.address,
      geo_beat: itm.geo_beat,
      geo_report_area: itm.geo_report_area,
      x: itm.x_wgs,
      y: itm.y_wgs,
      offense_short_description: itm.offense_short_description,
      offense_long_description: itm.offense_long_description,
      offense_code: itm.offense_code,
      offense_group_code: itm.offense_group_code,
      offense_group_level: itm.offense_group_level,
      offense_group_short_description: itm.offense_group_short_description,
    };
  })
  .filter((item) => {
    let keep = true;
    if (after || before) {
      const date1 = new Date(item.date_occurred);
      if (after && date1 < new Date(`${after} 00:00:00 GMT-0500`)) {
        keep = false;
      }
      if (keep && before && date1 > new Date(`${before} 00:00:00 GMT-0500`)) {
        keep = false;
      }
    }
    return keep;
  });
}

const resolvers = {
  Query: {
    crimes_by_address(obj, args, context) {
      const logger = context.logger;
      const civicaddressId = String(args.civicaddress_id);
      const radius = Number(args.radius); // State plane units are feet
      const query = 'SELECT A.incident_id, A.date_occurred, A.case_number, '
      + 'A.address, A.geo_beat, A.x, A.y, A.x_wgs, A.y_wgs, A.offense_short_description, '
      + 'A.offense_long_description, A.offense_code, A.offense_group_code, '
      + 'A.offense_group_level, A.offense_group_short_description '
      + 'from simplicity.v_simplicity_crimes as A '
      + 'left outer join internal.coa_bc_address_master as B '
      + 'on ST_Point_Inside_Circle(ST_Point(A.x, A.y), B.address_x, B.address_y, $2) '
      + 'where b.civicaddress_id = $1 '; // Future function name change - ST_PointInsideCircle

      return context.pool.query(query, [civicaddressId, radius])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        logger.error(`Got an error in crimes_by_address: ${err}`);
        throw new Error(`Got an error in crimes_by_address: ${err}`);
      });
    },
    crimes_by_street(obj, args, context) {
      const logger = context.logger;
      const radius = (args.radius) ? Number(args.radius) : 100; // State plane units are feet
      const ids = args.centerline_ids;
      if (ids.length <= 0) return [];
      const query = 'SELECT * FROM simplicity.get_crimes_along_streets($1, $2)';
      return context.pool.query(query, [ids, radius])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        if (err) {
          logger.error(`Got an error in crimes_by_street: ${err}`);
          throw new Error(err);
        }
      });
    },
    crimes_by_neighborhood(obj, args, context) {
      const logger = context.logger;
      if (args.nbrhd_ids.length <= 0) return [];
      const query = 'SELECT * FROM simplicity.get_crimes_by_neighborhood($1)';

      return context.pool.query(query, [args.nbrhd_ids])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        if (err) {
          logger.error(`Got an error in crimes_by_neighborhood: ${err}`);
          throw new Error(err);
        }
      });
    },
    crimes(obj, args, context) {
      const logger = context.logger;
      if (args.incident_ids.length <= 0) return [];
      const query = 'SELECT incident_id, agency, date_occurred, case_number, '
      + 'address, geo_beat, geo_report_area, x, y, x_wgs, y_wgs, offense_short_description, '
      + 'offense_long_description, offense_code, offense_group_code, '
      + 'offense_group_level, offense_group_short_description, '
      + 'offense_group_long_description '
      + 'FROM simplicity.v_simplicity_crimes WHERE incident_id = ANY ($1) ';
      return context.pool.query(query, [args.incident_ids])
      .then((result) => {
        return prepareCrimes(result.rows);
      })
      .catch((err) => {
        logger.error(`ERROR: ${err}`);
        throw new Error(`Got an error in crimes: ${err}`);
      });
    },
  },
};

module.exports = resolvers;
<commit_msg>3.10.1<commit_after>function prepareCrimes(rows, before = null, after = null) {
  if (rows.length === 0) return [];
  return rows.map((itm) => {
    return {
      incident_id: itm.incident_id,
      agency: itm.agency,
      date_occurred: itm.date_occurred,
      case_number: itm.case_number,
      address: itm.address,
      geo_beat: itm.geo_beat,
      geo_report_area: itm.geo_report_area,
      x: itm.x_wgs,
      y: itm.y_wgs,
      offense_short_description: itm.offense_short_description,
      offense_long_description: itm.offense_long_description,
      offense_code: itm.offense_code,
      offense_group_code: itm.offense_group_code,
      offense_group_level: itm.offense_group_level,
      offense_group_short_description: itm.offense_group_short_description,
    };
  })
  .filter((item) => {
    let keep = true;
    if (after || before) {
      const date1 = new Date(item.date_occurred);
      if (after && date1 < new Date(`${after} 00:00:00 GMT-0500`)) {
        keep = false;
      }
      if (keep && before && date1 > new Date(`${before} 00:00:00 GMT-0500`)) {
        keep = false;
      }
    }
    return keep;
  });
}

const resolvers = {
  Query: {
    crimes_by_address(obj, args, context) {
      const logger = context.logger;
      const civicaddressId = String(args.civicaddress_id);
      const radius = Number(args.radius); // State plane units are feet
      const query = 'SELECT A.incident_id, TO_CHAR(A.date_occurred, \'YYYY-MM-DD\') date_occurred, A.case_number, '
      + 'A.address, A.geo_beat, A.x, A.y, A.x_wgs, A.y_wgs, A.offense_short_description, '
      + 'A.offense_long_description, A.offense_code, A.offense_group_code, '
      + 'A.offense_group_level, A.offense_group_short_description '
      + 'from simplicity.v_simplicity_crimes as A '
      + 'left outer join internal.coa_bc_address_master as B '
      + 'on ST_Point_Inside_Circle(ST_Point(A.x, A.y), B.address_x, B.address_y, $2) '
      + 'where b.civicaddress_id = $1 '; // Future function name change - ST_PointInsideCircle

      return context.pool.query(query, [civicaddressId, radius])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        logger.error(`Got an error in crimes_by_address: ${err}`);
        throw new Error(`Got an error in crimes_by_address: ${err}`);
      });
    },
    crimes_by_street(obj, args, context) {
      const logger = context.logger;
      const radius = (args.radius) ? Number(args.radius) : 100; // State plane units are feet
      const ids = args.centerline_ids;
      if (ids.length <= 0) return [];
      const query = 'SELECT * FROM simplicity.get_crimes_along_streets($1, $2)';
      return context.pool.query(query, [ids, radius])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        if (err) {
          logger.error(`Got an error in crimes_by_street: ${err}`);
          throw new Error(err);
        }
      });
    },
    crimes_by_neighborhood(obj, args, context) {
      const logger = context.logger;
      if (args.nbrhd_ids.length <= 0) return [];
      const query = 'SELECT * FROM simplicity.get_crimes_by_neighborhood($1)';

      return context.pool.query(query, [args.nbrhd_ids])
      .then(result => {
        return prepareCrimes(result.rows, args.before, args.after);
      })
      .catch((err) => {
        if (err) {
          logger.error(`Got an error in crimes_by_neighborhood: ${err}`);
          throw new Error(err);
        }
      });
    },
    crimes(obj, args, context) {
      const logger = context.logger;
      if (args.incident_ids.length <= 0) return [];
      const query = 'SELECT incident_id, agency, TO_CHAR(A.date_occurred, \'YYYY-MM-DD\') date_occurred, case_number, '
      + 'address, geo_beat, geo_report_area, x, y, x_wgs, y_wgs, offense_short_description, '
      + 'offense_long_description, offense_code, offense_group_code, '
      + 'offense_group_level, offense_group_short_description, '
      + 'offense_group_long_description '
      + 'FROM simplicity.v_simplicity_crimes WHERE incident_id = ANY ($1) ';
      return context.pool.query(query, [args.incident_ids])
      .then((result) => {
        return prepareCrimes(result.rows);
      })
      .catch((err) => {
        logger.error(`ERROR: ${err}`);
        throw new Error(`Got an error in crimes: ${err}`);
      });
    },
  },
};

module.exports = resolvers;
<|endoftext|>


<commit_before>import Component from 'ember-component';
import layout from '../../templates/components/basic-dropdown/content';
import Ember from 'ember';
import computed from 'ember-computed';
import { join, scheduleOnce } from 'ember-runloop';
import { htmlSafe } from 'ember-string';
import fallbackIfUndefined from '../../utils/computed-fallback-if-undefined';
import { getScrollParent } from '../../utils/calculate-position';

function closestContent(el) {
  while (el && !el.classList.contains('ember-basic-dropdown-content')) {
    el = el.parentElement;
  }
  return el;
}
const { testing } = Ember;
const MutObserver = self.window.MutationObserver || self.window.WebKitMutationObserver;
const rAF = self.window.requestAnimationFrame || function(cb) {
  cb();
};

function waitForAnimations(element, callback) {
  rAF(function() {
    let computedStyle = self.window.getComputedStyle(element);
    if (computedStyle.animationName !== 'none' && computedStyle.animationPlayState === 'running') {
      let eventCallback = function() {
        element.removeEventListener('animationend', eventCallback);
        callback();
      };
      element.addEventListener('animationend', eventCallback);
    } else {
      callback();
    }
  });
}

export default Component.extend({
  layout,
  tagName: '',
  animationEnabled: !testing,
  isTouchDevice: (!!self.window && 'ontouchstart' in self.window),
  hasMoved: false,
  animationClass: '',
  transitioningInClass: 'ember-basic-dropdown--transitioning-in',
  transitionedInClass: 'ember-basic-dropdown--transitioned-in',
  transitioningOutClass: 'ember-basic-dropdown--transitioning-out',

  _contentTagName: fallbackIfUndefined('div'),

  // Lifecycle hooks
  init() {
    this._super(...arguments);
    this.handleRootMouseDown = this.handleRootMouseDown.bind(this);
    this.touchStartHandler = this.touchStartHandler.bind(this);
    this.touchMoveHandler = this.touchMoveHandler.bind(this);
    let dropdown = this.get('dropdown');
    this.scrollableAncestors = [];
    this.dropdownId = `ember-basic-dropdown-content-${dropdown.uniqueId}`;
    if (this.get('animationEnabled')) {
      this.set('animationClass', this.get('transitioningInClass'));
    }
    this.runloopAwareReposition = function() {
      join(dropdown.actions.reposition);
    };
  },

  willDestroyElement() {
    this._super(...arguments);
    this._teardown();
  },

  didReceiveAttrs() {
    this._super(...arguments);
    let oldDropdown = this.get('oldDropdown') || {};
    let dropdown = this.get('dropdown');
    if (!oldDropdown.isOpen && dropdown.isOpen) {
      scheduleOnce('afterRender', this, this.open);
    } else if (oldDropdown.isOpen && !dropdown.isOpen) {
      this.close();
    }
    this.set('oldDropdown', dropdown);
  },

  // CPs
  to: computed('destination', {
    get() {
      return this.get('destination');
    },
    set(_, v) {
      Ember.deprecate('Passing `to="id-of-elmnt"` to the {{#dropdown.content}} has been deprecated. Please pass `destination="id-of-elmnt"` to the {{#basic-dropdown}} component instead', false, { id: 'ember-basic-dropdown-to-in-content', until: '0.40' });
      return v === undefined ? this.get('destination') : v;
    }
  }),

  style: computed('top', 'left', 'right', 'width', 'height', function() {
    let style = '';
    let { top, left, right, width, height } = this.getProperties('top', 'left', 'right', 'width', 'height');
    if (top) {
      style += `top: ${top};`;
    }
    if (left) {
      style += `left: ${left};`;
    }
    if (right) {
      style += `right: ${right};`;
    }
    if (width) {
      style += `width: ${width};`;
    }
    if (height) {
      style += `height: ${height}`;
    }
    if (style.length > 0) {
      return htmlSafe(style);
    }
  }),

  // Methods
  open() {
    let dropdown = this.get('dropdown');
    this.triggerElement = this.triggerElement || document.querySelector(`[data-ebd-id=${dropdown.uniqueId}-trigger]`);
    this.dropdownElement = document.getElementById(this.dropdownId);
    self.document.body.addEventListener('mousedown', this.handleRootMouseDown, true);
    if (this.get('isTouchDevice')) {
      self.document.body.addEventListener('touchstart', this.touchStartHandler, true);
      self.document.body.addEventListener('touchend', this.handleRootMouseDown, true);
    }
    let onFocusIn = this.get('onFocusIn');
    if (onFocusIn) {
      this.dropdownElement.addEventListener('focusin', (e) => onFocusIn(dropdown, e));
    }
    let onFocusOut = this.get('onFocusOut');
    if (onFocusOut) {
      this.dropdownElement.addEventListener('focusout', (e) => onFocusOut(dropdown, e));
    }
    let onMouseEnter = this.get('onMouseEnter');
    if (onMouseEnter) {
      this.dropdownElement.addEventListener('mouseenter', (e) => onMouseEnter(dropdown, e));
    }
    let onMouseLeave = this.get('onMouseLeave');
    if (onMouseLeave) {
      this.dropdownElement.addEventListener('mouseleave', (e) => onMouseLeave(dropdown, e));
    }
    let changes = dropdown.actions.reposition();
    if (!this.get('renderInPlace')) {
      this.destinationElement = document.getElementById(this.get('destination'));
      this.scrollableAncestors = this.getScrollableAncestors();
      this.addGlobalEvents();
      this.startObservingDomMutations();
    } else if (changes.vPosition === 'above') {
      this.startObservingDomMutations();
    }

    if (this.get('animationEnabled')) {
      scheduleOnce('afterRender', this, this.animateIn);
    }
  },

  close() {
    this._teardown();
    if (this.get('animationEnabled')) {
      this.animateOut(this.dropdownElement);
    }
    this.dropdownElement = null;
  },

  // Methods
  handleRootMouseDown(e) {
    if (this.hasMoved || this.dropdownElement.contains(e.target) || this.triggerElement && this.triggerElement.contains(e.target)) {
      this.hasMoved = false;
      return;
    }

    let closestDropdown = closestContent(e.target);
    if (closestDropdown) {
      let trigger = document.querySelector(`[aria-owns=${closestDropdown.attributes.id.value}]`);
      let parentDropdown = closestContent(trigger);
      if (parentDropdown && parentDropdown.attributes.id.value === this.dropdownId) {
        this.hasMoved = false;
        return;
      }
    }

    this.get('dropdown').actions.close(e, true);
  },

  addGlobalEvents() {
    self.window.addEventListener('scroll', this.runloopAwareReposition);
    this.scrollableAncestors.forEach((el) => {
      el.addEventListener('scroll', this.runloopAwareReposition);
    });
    self.window.addEventListener('resize', this.runloopAwareReposition);
    self.window.addEventListener('orientationchange', this.runloopAwareReposition);
  },

  startObservingDomMutations() {
    if (MutObserver) {
      this.mutationObserver = new MutObserver((mutations) => {
        if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {
          this.runloopAwareReposition();
        }
      });
      this.mutationObserver.observe(this.dropdownElement, { childList: true, subtree: true });
    } else {
      this.dropdownElement.addEventListener('DOMNodeInserted', this.runloopAwareReposition, false);
      this.dropdownElement.addEventListener('DOMNodeRemoved', this.runloopAwareReposition, false);
    }
  },

  removeGlobalEvents() {
    self.window.removeEventListener('scroll', this.runloopAwareReposition);
    this.scrollableAncestors.forEach((el) => {
      el.removeEventListener('scroll', this.runloopAwareReposition);
    });
    self.window.removeEventListener('resize', this.runloopAwareReposition);
    self.window.removeEventListener('orientationchange', this.runloopAwareReposition);
  },

  stopObservingDomMutations() {
    if (MutObserver) {
      if (this.mutationObserver) {
        this.mutationObserver.disconnect();
        this.mutationObserver = null;
      }
    } else {
      if (this.dropdownElement) {
        this.dropdownElement.removeEventListener('DOMNodeInserted', this.runloopAwareReposition);
        this.dropdownElement.removeEventListener('DOMNodeRemoved', this.runloopAwareReposition);
      }
    }
  },

  animateIn() {
    waitForAnimations(this.dropdownElement, () => {
      this.set('animationClass', this.get('transitionedInClass'));
    });
  },

  animateOut(dropdownElement) {
    let parentElement = this.get('renderInPlace') ? dropdownElement.parentElement.parentElement : dropdownElement.parentElement;
    let clone = dropdownElement.cloneNode(true);
    clone.id = `${clone.id}--clone`;
    let transitioningInClass = this.get('transitioningInClass');
    clone.classList.remove(this.get('transitionedInClass'));
    clone.classList.remove(transitioningInClass);
    clone.classList.add(this.get('transitioningOutClass'));
    parentElement.appendChild(clone);
    this.set('animationClass', transitioningInClass);
    waitForAnimations(clone, function() {
      parentElement.removeChild(clone);
    });
  },

  touchStartHandler() {
    self.document.body.addEventListener('touchmove', this.touchMoveHandler, true);
  },

  touchMoveHandler() {
    this.hasMoved = true;
    self.document.body.removeEventListener('touchmove', this.touchMoveHandler, true);
  },

  // All ancestors with scroll (except the BODY, which is treated differently)
  getScrollableAncestors() {
    let scrollableAncestors = [];
    if (this.triggerElement) {
      let nextScrollable = getScrollParent(this.triggerElement.parentNode);
      while (nextScrollable && nextScrollable.tagName !== 'BODY') {
        scrollableAncestors.push(nextScrollable);
        nextScrollable = getScrollParent(nextScrollable.parentNode);
      }
    }
    return scrollableAncestors;
  },

  _teardown() {
    this.removeGlobalEvents();
    this.destinationElement = null;
    this.scrollableAncestors = [];
    this.stopObservingDomMutations();
    self.document.body.removeEventListener('mousedown', this.handleRootMouseDown, true);
    if (this.get('isTouchDevice')) {
      self.document.body.removeEventListener('touchstart', this.touchStartHandler, true);
      self.document.body.removeEventListener('touchend', this.handleRootMouseDown, true);
    }
  }
});

<commit_msg>Stop looking at ancestors on body or html<commit_after>import Component from 'ember-component';
import layout from '../../templates/components/basic-dropdown/content';
import Ember from 'ember';
import computed from 'ember-computed';
import { join, scheduleOnce } from 'ember-runloop';
import { htmlSafe } from 'ember-string';
import fallbackIfUndefined from '../../utils/computed-fallback-if-undefined';
import { getScrollParent } from '../../utils/calculate-position';

function closestContent(el) {
  while (el && !el.classList.contains('ember-basic-dropdown-content')) {
    el = el.parentElement;
  }
  return el;
}
const { testing } = Ember;
const MutObserver = self.window.MutationObserver || self.window.WebKitMutationObserver;
const rAF = self.window.requestAnimationFrame || function(cb) {
  cb();
};

function waitForAnimations(element, callback) {
  rAF(function() {
    let computedStyle = self.window.getComputedStyle(element);
    if (computedStyle.animationName !== 'none' && computedStyle.animationPlayState === 'running') {
      let eventCallback = function() {
        element.removeEventListener('animationend', eventCallback);
        callback();
      };
      element.addEventListener('animationend', eventCallback);
    } else {
      callback();
    }
  });
}

export default Component.extend({
  layout,
  tagName: '',
  animationEnabled: !testing,
  isTouchDevice: (!!self.window && 'ontouchstart' in self.window),
  hasMoved: false,
  animationClass: '',
  transitioningInClass: 'ember-basic-dropdown--transitioning-in',
  transitionedInClass: 'ember-basic-dropdown--transitioned-in',
  transitioningOutClass: 'ember-basic-dropdown--transitioning-out',

  _contentTagName: fallbackIfUndefined('div'),

  // Lifecycle hooks
  init() {
    this._super(...arguments);
    this.handleRootMouseDown = this.handleRootMouseDown.bind(this);
    this.touchStartHandler = this.touchStartHandler.bind(this);
    this.touchMoveHandler = this.touchMoveHandler.bind(this);
    let dropdown = this.get('dropdown');
    this.scrollableAncestors = [];
    this.dropdownId = `ember-basic-dropdown-content-${dropdown.uniqueId}`;
    if (this.get('animationEnabled')) {
      this.set('animationClass', this.get('transitioningInClass'));
    }
    this.runloopAwareReposition = function() {
      join(dropdown.actions.reposition);
    };
  },

  willDestroyElement() {
    this._super(...arguments);
    this._teardown();
  },

  didReceiveAttrs() {
    this._super(...arguments);
    let oldDropdown = this.get('oldDropdown') || {};
    let dropdown = this.get('dropdown');
    if (!oldDropdown.isOpen && dropdown.isOpen) {
      scheduleOnce('afterRender', this, this.open);
    } else if (oldDropdown.isOpen && !dropdown.isOpen) {
      this.close();
    }
    this.set('oldDropdown', dropdown);
  },

  // CPs
  to: computed('destination', {
    get() {
      return this.get('destination');
    },
    set(_, v) {
      Ember.deprecate('Passing `to="id-of-elmnt"` to the {{#dropdown.content}} has been deprecated. Please pass `destination="id-of-elmnt"` to the {{#basic-dropdown}} component instead', false, { id: 'ember-basic-dropdown-to-in-content', until: '0.40' });
      return v === undefined ? this.get('destination') : v;
    }
  }),

  style: computed('top', 'left', 'right', 'width', 'height', function() {
    let style = '';
    let { top, left, right, width, height } = this.getProperties('top', 'left', 'right', 'width', 'height');
    if (top) {
      style += `top: ${top};`;
    }
    if (left) {
      style += `left: ${left};`;
    }
    if (right) {
      style += `right: ${right};`;
    }
    if (width) {
      style += `width: ${width};`;
    }
    if (height) {
      style += `height: ${height}`;
    }
    if (style.length > 0) {
      return htmlSafe(style);
    }
  }),

  // Methods
  open() {
    let dropdown = this.get('dropdown');
    this.triggerElement = this.triggerElement || document.querySelector(`[data-ebd-id=${dropdown.uniqueId}-trigger]`);
    this.dropdownElement = document.getElementById(this.dropdownId);
    self.document.body.addEventListener('mousedown', this.handleRootMouseDown, true);
    if (this.get('isTouchDevice')) {
      self.document.body.addEventListener('touchstart', this.touchStartHandler, true);
      self.document.body.addEventListener('touchend', this.handleRootMouseDown, true);
    }
    let onFocusIn = this.get('onFocusIn');
    if (onFocusIn) {
      this.dropdownElement.addEventListener('focusin', (e) => onFocusIn(dropdown, e));
    }
    let onFocusOut = this.get('onFocusOut');
    if (onFocusOut) {
      this.dropdownElement.addEventListener('focusout', (e) => onFocusOut(dropdown, e));
    }
    let onMouseEnter = this.get('onMouseEnter');
    if (onMouseEnter) {
      this.dropdownElement.addEventListener('mouseenter', (e) => onMouseEnter(dropdown, e));
    }
    let onMouseLeave = this.get('onMouseLeave');
    if (onMouseLeave) {
      this.dropdownElement.addEventListener('mouseleave', (e) => onMouseLeave(dropdown, e));
    }
    let changes = dropdown.actions.reposition();
    if (!this.get('renderInPlace')) {
      this.destinationElement = document.getElementById(this.get('destination'));
      this.scrollableAncestors = this.getScrollableAncestors();
      this.addGlobalEvents();
      this.startObservingDomMutations();
    } else if (changes.vPosition === 'above') {
      this.startObservingDomMutations();
    }

    if (this.get('animationEnabled')) {
      scheduleOnce('afterRender', this, this.animateIn);
    }
  },

  close() {
    this._teardown();
    if (this.get('animationEnabled')) {
      this.animateOut(this.dropdownElement);
    }
    this.dropdownElement = null;
  },

  // Methods
  handleRootMouseDown(e) {
    if (this.hasMoved || this.dropdownElement.contains(e.target) || this.triggerElement && this.triggerElement.contains(e.target)) {
      this.hasMoved = false;
      return;
    }

    let closestDropdown = closestContent(e.target);
    if (closestDropdown) {
      let trigger = document.querySelector(`[aria-owns=${closestDropdown.attributes.id.value}]`);
      let parentDropdown = closestContent(trigger);
      if (parentDropdown && parentDropdown.attributes.id.value === this.dropdownId) {
        this.hasMoved = false;
        return;
      }
    }

    this.get('dropdown').actions.close(e, true);
  },

  addGlobalEvents() {
    self.window.addEventListener('scroll', this.runloopAwareReposition);
    this.scrollableAncestors.forEach((el) => {
      el.addEventListener('scroll', this.runloopAwareReposition);
    });
    self.window.addEventListener('resize', this.runloopAwareReposition);
    self.window.addEventListener('orientationchange', this.runloopAwareReposition);
  },

  startObservingDomMutations() {
    if (MutObserver) {
      this.mutationObserver = new MutObserver((mutations) => {
        if (mutations[0].addedNodes.length || mutations[0].removedNodes.length) {
          this.runloopAwareReposition();
        }
      });
      this.mutationObserver.observe(this.dropdownElement, { childList: true, subtree: true });
    } else {
      this.dropdownElement.addEventListener('DOMNodeInserted', this.runloopAwareReposition, false);
      this.dropdownElement.addEventListener('DOMNodeRemoved', this.runloopAwareReposition, false);
    }
  },

  removeGlobalEvents() {
    self.window.removeEventListener('scroll', this.runloopAwareReposition);
    this.scrollableAncestors.forEach((el) => {
      el.removeEventListener('scroll', this.runloopAwareReposition);
    });
    self.window.removeEventListener('resize', this.runloopAwareReposition);
    self.window.removeEventListener('orientationchange', this.runloopAwareReposition);
  },

  stopObservingDomMutations() {
    if (MutObserver) {
      if (this.mutationObserver) {
        this.mutationObserver.disconnect();
        this.mutationObserver = null;
      }
    } else {
      if (this.dropdownElement) {
        this.dropdownElement.removeEventListener('DOMNodeInserted', this.runloopAwareReposition);
        this.dropdownElement.removeEventListener('DOMNodeRemoved', this.runloopAwareReposition);
      }
    }
  },

  animateIn() {
    waitForAnimations(this.dropdownElement, () => {
      this.set('animationClass', this.get('transitionedInClass'));
    });
  },

  animateOut(dropdownElement) {
    let parentElement = this.get('renderInPlace') ? dropdownElement.parentElement.parentElement : dropdownElement.parentElement;
    let clone = dropdownElement.cloneNode(true);
    clone.id = `${clone.id}--clone`;
    let transitioningInClass = this.get('transitioningInClass');
    clone.classList.remove(this.get('transitionedInClass'));
    clone.classList.remove(transitioningInClass);
    clone.classList.add(this.get('transitioningOutClass'));
    parentElement.appendChild(clone);
    this.set('animationClass', transitioningInClass);
    waitForAnimations(clone, function() {
      parentElement.removeChild(clone);
    });
  },

  touchStartHandler() {
    self.document.body.addEventListener('touchmove', this.touchMoveHandler, true);
  },

  touchMoveHandler() {
    this.hasMoved = true;
    self.document.body.removeEventListener('touchmove', this.touchMoveHandler, true);
  },

  // All ancestors with scroll (except the BODY, which is treated differently)
  getScrollableAncestors() {
    let scrollableAncestors = [];
    if (this.triggerElement) {
      let nextScrollable = getScrollParent(this.triggerElement.parentNode);
      while (nextScrollable && nextScrollable.tagName !== 'BODY' && nextScrollable.tagName !== 'HTML') {
        scrollableAncestors.push(nextScrollable);
        nextScrollable = getScrollParent(nextScrollable.parentNode);
      }
    }
    return scrollableAncestors;
  },

  _teardown() {
    this.removeGlobalEvents();
    this.destinationElement = null;
    this.scrollableAncestors = [];
    this.stopObservingDomMutations();
    self.document.body.removeEventListener('mousedown', this.handleRootMouseDown, true);
    if (this.get('isTouchDevice')) {
      self.document.body.removeEventListener('touchstart', this.touchStartHandler, true);
      self.document.body.removeEventListener('touchend', this.handleRootMouseDown, true);
    }
  }
});

<|endoftext|>


<commit_before>import { Fetch, Node, Bind, Draw } from '../../xs';
import './style.scss';

const action = {
  download() { alert('YOYO'); }
};

export default () => {
  const $download = document.createElement('download-');

  Fetch([{}])
  .then(Node(() => `
    <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
        <path d="M0 0h24v24H0z" fill="none"/>
    </svg>
  `))
  .then(Bind('svg')('click')(action.download))
  .then(Draw($download));

  return $download;
};
<commit_msg>Implements download functionality<commit_after>import { Fetch, Node, Bind, Draw } from '../../xs';
import './style.scss';

const action = {
  download() {
    const $swatch = document.querySelector('swatch-');
    const guid = ('0000' + (Math.random()*Math.pow(36,4) << 0).toString(36)).slice(-4).toUpperCase();
    const page = `
      <!DOCTYPE html>
      <html>
      	<head>
      		<meta charset="utf-8">
      		<title>Malette | SWATCH-${guid}</title>
          <style>
            * {
              box-sizing: border-box;
            }
            body {
              width: 100%;
              height: 100vh;
              margin: 0;
              padding: 2rem;
              background: #fff;
              font-family: sans-serif;
            }
            swatch- {
              width: 100%;
              height: 100%;
              display: flex;
              flex-flow: row wrap;
            }
            color- {
              flex: 1 1 15rem;
              display: flex;
              align-items: center;
              justify-content: center;
              flex-direction: column;
            }
            name- {
              font-size: 1rem;
              font-weight: bold;
            }
            hex- {
              font-size: .618rem;
              margin-top: .32rem;
            }
            [normal-text] { color: rgba(255, 255, 255, 0.7); }
            [dark-text] { color: rgba(0, 0, 0, 0.87); }
            [dark-strong-text] { color: #000; }
            [light-text] { color: rgba(255, 255, 255, 0.87); }
            [light-strong-text] { color: #fff; }
          </style>
      	</head>
      	<body>${$swatch.outerHTML}</body>
      </html>
    `;

    // Create a blob from binary array
    const f = new Blob([page], {type:'application/octet-stream'});
    const a = document.createElement('a');
    window.URL = window.URL || window.webkitURL;
    a.href = window.URL.createObjectURL(f);
    a.download = `SWATCH-${guid}`;
    document.body.appendChild(a);
    a.click();

    // Remove the temporary link
    document.body.removeChild(document.body.lastElementChild);
  }
};

export default () => {
  const $download = document.createElement('download-');

  Fetch([{}])
  .then(Node(() => `
    <svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
        <path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
        <path d="M0 0h24v24H0z" fill="none"/>
    </svg>
  `))
  .then(Bind('svg')('click')(action.download))
  .then(Draw($download));

  return $download;
};
<|endoftext|>