<commit_before>define([
	"dojo/_base/kernel", // to test dojo.hash
	"dojo/_base/array",
	"dojo/_base/config",
	"dojo/_base/connect",
	"dojo/_base/lang",
	"dojo/_base/window",
	"dojo/dom-class",
	"dojo/dom-construct",
	"dojo/dom-style",
//	"dojo/hash", // optionally prereq'ed
	"dojo/ready",
	"dijit/registry",	// registry.toArray
	"./sniff",
	"./uacss"
], function(dojo, array, config, connect, lang, win, domClass, domConstruct, domStyle, ready, registry, has, uacss){

	var dm = lang.getObject("dojox.mobile", true);
/*=====
	var dm = dojox.mobile;
=====*/

	// module:
	//		dojox/mobile/common
	// summary:
	//		A common module for dojox.mobile.
	// description:
	//		This module includes common utility functions that are used by
	//		dojox.mobile widgets. Also, it provides functions that are commonly
	//		necessary for mobile web applications, such as the hide address bar
	//		function.

	dm.getScreenSize = function(){
		// summary:
		//		Returns the dimensions of the browser window.
		return {
			h: win.global.innerHeight || win.doc.documentElement.clientHeight,
			w: win.global.innerWidth || win.doc.documentElement.clientWidth
		};
	};

	dm.updateOrient = function(){
		// summary:
		//		Updates the orientation specific css classes, 'dj_portrait' and
		//		'dj_landscape'.
		var dim = dm.getScreenSize();
		domClass.replace(win.doc.documentElement,
				  dim.h > dim.w ? "dj_portrait" : "dj_landscape",
				  dim.h > dim.w ? "dj_landscape" : "dj_portrait");
	};
	dm.updateOrient();

	dm.tabletSize = 500;
	dm.detectScreenSize = function(/*Boolean?*/force){
		// summary:
		//		Detects the screen size and determines if the screen is like
		//		phone or like tablet. If the result is changed,
		//		it sets either of the following css class to <html>
		//			- 'dj_phone'
		//			- 'dj_tablet'
		//		and it publishes either of the following events.
		//			- '/dojox/mobile/screenSize/phone'
		//			- '/dojox/mobile/screenSize/tablet'
		var dim = dm.getScreenSize();
		var sz = Math.min(dim.w, dim.h);
		var from, to;
		if(sz >= dm.tabletSize && (force || (!this._sz || this._sz < dm.tabletSize))){
			from = "phone";
			to = "tablet";
		}else if(sz < dm.tabletSize && (force || (!this._sz || this._sz >= dm.tabletSize))){
			from = "tablet";
			to = "phone";
		}
		if(to){
			domClass.replace(win.doc.documentElement, "dj_"+to, "dj_"+from);
			connect.publish("/dojox/mobile/screenSize/"+to, [dim]);
		}
		this._sz = sz;
	};
	dm.detectScreenSize();

	// dojox.mobile.hideAddressBarWait: Number
	//		The time in milliseconds to wait before the fail-safe hiding address
	//		bar runs. The value must be larger than 800.
	dm.hideAddressBarWait = typeof(config["mblHideAddressBarWait"]) === "number" ?
		config["mblHideAddressBarWait"] : 1500;

	dm.hide_1 = function(){
		// summary:
		//		Internal function to hide the address bar.
		scrollTo(0, 1);
		dm._hidingTimer = (dm._hidingTimer == 0) ? 200 : dm._hidingTimer * 2;
		if(dm.isAddressBarHidden() || dm._hidingTimer > dm.hideAddressBarWait){
			// Succeeded to hide address bar, or failed but timed out
			win.body().style.minHeight = dm.getScreenSize().h + "px";
			dm.resizeAll();
			dm._hiding = false;
		}else{
			// Failed to hide address bar, so retry after a while
			setTimeout(dm.hide_1, dm._hidingTimer);
			if(has('android')){
				dm.resizeAll();
			}
		}
	};

	dm.hideAddressBar = function(/*Event?*/evt){
		// summary:
		//		Hides the address bar.
		// description:
		//		Tries hiding of the address bar a couple of times to do it as
		//		quick as possible while ensuring resize is done after the hiding
		//		finishes.
		if(dm.disableHideAddressBar || dm._hiding){ return; }
		dm._hiding = true;
		dm._hidingTimer = has('iphone') ? 200 : 0; // Need to wait longer in case of iPhone
		var minH = has('android') ? (outerHeight / devicePixelRatio) : screen.availHeight;
		win.body().style.minHeight = minH + "px"; // to ensure enough height for scrollTo to work
		setTimeout(dm.hide_1, dm._hidingTimer);
	};

	dm.isAddressBarHidden = function(){
		return pageYOffset === 1;
	};

	dm.resizeAll = function(/*Event?*/evt, /*Widget?*/root){
		// summary:
		//		Call the resize() method of all the top level resizable widgets.
		// description:
		//		Find all widgets that do not have a parent or the parent does not
		//		have the resize() method, and call resize() for them.
		//		If a widget has a parent that has resize(), call of the widget's
		//		resize() is its parent's responsibility.
		// evt:
		//		Native event object
		// root:
		//		If specified, search the specified widget recursively for top level
		//		resizable widgets.
		//		root.resize() is always called regardless of whether root is a
		//		top level widget or not.
		//		If omitted, search the entire page.
		if(dm.disableResizeAll){ return; }
		connect.publish("/dojox/mobile/resizeAll", [evt, root]);
		dm.updateOrient();
		dm.detectScreenSize();
		var isTopLevel = function(w){
			var parent = w.getParent && w.getParent();
			return !!((!parent || !parent.resize) && w.resize);
		};
		var resizeRecursively = function(w){
			array.forEach(w.getChildren(), function(child){
				if(isTopLevel(child)){ child.resize(); }
				resizeRecursively(child);
			});
		};
		if(root){
			if(root.resize){ root.resize(); }
			resizeRecursively(root);
		}else{
			array.forEach(array.filter(registry.toArray(), isTopLevel),
					function(w){ w.resize(); });
		}
	};

	dm.openWindow = function(url, target){
		// summary:
		//		Opens a new browser window with the given url.
		win.global.open(url, target || "_blank");
	};

	if(config.parseOnLoad){
		ready(90, function(){
			// avoid use of query
			/*
			var list = query('[lazy=true] [dojoType]', null);
			list.forEach(function(node, index, nodeList){
				node.setAttribute("__dojoType", node.getAttribute("dojoType"));
				node.removeAttribute("dojoType");
			});
			*/

			var nodes = win.body().getElementsByTagName("*");
			var i, len, s;
			len = nodes.length;
			for(i = 0; i < len; i++){
				s = nodes[i].getAttribute("dojoType");
				if(s){
					if(nodes[i].parentNode.getAttribute("lazy") == "true"){
						nodes[i].setAttribute("__dojoType", s);
						nodes[i].removeAttribute("dojoType");
					}
				}
			}
		});
	}

	if(win.global._no_dojo_dm){
		// deviceTheme seems to be loaded from a script tag (= non-dojo usage)
		var _dm = win.global._no_dojo_dm;
		for(var i in _dm){
			dm[i] = _dm[i];
		}
		dm.deviceTheme.setDm(dm);
	}

	ready(function(){
		dm.detectScreenSize(true);
		if(config["mblApplyPageStyles"] !== false){
			domClass.add(win.doc.documentElement, "mobile");
		}
		if(has('chrome')){
			// dojox.mobile does not load uacss (only _compat does), but we need dj_chrome.
			domClass.add(win.doc.documentElement, "dj_chrome");
		}

		//	You can disable hiding the address bar with the following djConfig.
		//	var djConfig = { mblHideAddressBar: false };
		var f = dm.resizeAll;
		if(config["mblHideAddressBar"] !== false &&
			navigator.appVersion.indexOf("Mobile") != -1 ||
			config["mblForceHideAddressBar"] === true){
			dm.hideAddressBar();
			if(config["mblAlwaysHideAddressBar"] === true){
				f = dm.hideAddressBar;
			}
		}
		connect.connect(null, (win.global.onorientationchange !== undefined && !has('android'))
			? "onorientationchange" : "onresize", null, f);

		// avoid use of query
		/*
		var list = query('[__dojoType]', null);
		list.forEach(function(node, index, nodeList){
			node.setAttribute("dojoType", node.getAttribute("__dojoType"));
			node.removeAttribute("__dojoType");
		});
		*/

		var nodes = win.body().getElementsByTagName("*");
		var i, len = nodes.length, s;
		for(i = 0; i < len; i++){
			s = nodes[i].getAttribute("__dojoType");
			if(s){
				nodes[i].setAttribute("dojoType", s);
				nodes[i].removeAttribute("__dojoType");
			}
		}

		if(dojo.hash){
			// find widgets under root recursively
			var findWidgets = function(root){
				if(!root){ return []; }
				var arr = registry.findWidgets(root);
				var widgets = arr;
				for(var i = 0; i < widgets.length; i++){
					arr = arr.concat(findWidgets(widgets[i].containerNode));
				}
				return arr;
			};
			connect.subscribe("/dojo/hashchange", null, function(value){
				var view = dm.currentView;
				if(!view){ return; }
				var params = dm._params;
				if(!params){ // browser back/forward button was pressed
					var moveTo = value ? value : dm._defaultView.id;
					var widgets = findWidgets(view.domNode);
					var dir = 1, transition = "slide";
					for(i = 0; i < widgets.length; i++){
						var w = widgets[i];
						if("#"+moveTo == w.moveTo){
							// found a widget that has the given moveTo
							transition = w.transition;
							dir = (w instanceof dm.Heading) ? -1 : 1;
							break;
						}
					}
					params = [ moveTo, dir, transition ];
				}
				view.performTransition.apply(view, params);
				dm._params = null;
			});
		}

		win.body().style.visibility = "visible";
	});

	// To search _parentNode first.  TODO:1.8 reconsider this redefinition.
	registry.getEnclosingWidget = function(node){
		while(node){
			var id = node.getAttribute && node.getAttribute("widgetId");
			if(id){
				return registry.byId(id);
			}
			node = node._parentNode || node.parentNode;
		}
		return null;
	};

	return dm;
});
<commit_msg>Refs #14602 !strict. Removed the old hash change subscriber.<commit_after>define([
	"dojo/_base/kernel", // to test dojo.hash
	"dojo/_base/array",
	"dojo/_base/config",
	"dojo/_base/connect",
	"dojo/_base/lang",
	"dojo/_base/window",
	"dojo/dom-class",
	"dojo/dom-construct",
	"dojo/dom-style",
//	"dojo/hash", // optionally prereq'ed
	"dojo/ready",
	"dijit/registry",	// registry.toArray
	"./sniff",
	"./uacss"
], function(dojo, array, config, connect, lang, win, domClass, domConstruct, domStyle, ready, registry, has, uacss){

	var dm = lang.getObject("dojox.mobile", true);
/*=====
	var dm = dojox.mobile;
=====*/

	// module:
	//		dojox/mobile/common
	// summary:
	//		A common module for dojox.mobile.
	// description:
	//		This module includes common utility functions that are used by
	//		dojox.mobile widgets. Also, it provides functions that are commonly
	//		necessary for mobile web applications, such as the hide address bar
	//		function.

	dm.getScreenSize = function(){
		// summary:
		//		Returns the dimensions of the browser window.
		return {
			h: win.global.innerHeight || win.doc.documentElement.clientHeight,
			w: win.global.innerWidth || win.doc.documentElement.clientWidth
		};
	};

	dm.updateOrient = function(){
		// summary:
		//		Updates the orientation specific css classes, 'dj_portrait' and
		//		'dj_landscape'.
		var dim = dm.getScreenSize();
		domClass.replace(win.doc.documentElement,
				  dim.h > dim.w ? "dj_portrait" : "dj_landscape",
				  dim.h > dim.w ? "dj_landscape" : "dj_portrait");
	};
	dm.updateOrient();

	dm.tabletSize = 500;
	dm.detectScreenSize = function(/*Boolean?*/force){
		// summary:
		//		Detects the screen size and determines if the screen is like
		//		phone or like tablet. If the result is changed,
		//		it sets either of the following css class to <html>
		//			- 'dj_phone'
		//			- 'dj_tablet'
		//		and it publishes either of the following events.
		//			- '/dojox/mobile/screenSize/phone'
		//			- '/dojox/mobile/screenSize/tablet'
		var dim = dm.getScreenSize();
		var sz = Math.min(dim.w, dim.h);
		var from, to;
		if(sz >= dm.tabletSize && (force || (!this._sz || this._sz < dm.tabletSize))){
			from = "phone";
			to = "tablet";
		}else if(sz < dm.tabletSize && (force || (!this._sz || this._sz >= dm.tabletSize))){
			from = "tablet";
			to = "phone";
		}
		if(to){
			domClass.replace(win.doc.documentElement, "dj_"+to, "dj_"+from);
			connect.publish("/dojox/mobile/screenSize/"+to, [dim]);
		}
		this._sz = sz;
	};
	dm.detectScreenSize();

	// dojox.mobile.hideAddressBarWait: Number
	//		The time in milliseconds to wait before the fail-safe hiding address
	//		bar runs. The value must be larger than 800.
	dm.hideAddressBarWait = typeof(config["mblHideAddressBarWait"]) === "number" ?
		config["mblHideAddressBarWait"] : 1500;

	dm.hide_1 = function(){
		// summary:
		//		Internal function to hide the address bar.
		scrollTo(0, 1);
		dm._hidingTimer = (dm._hidingTimer == 0) ? 200 : dm._hidingTimer * 2;
		if(dm.isAddressBarHidden() || dm._hidingTimer > dm.hideAddressBarWait){
			// Succeeded to hide address bar, or failed but timed out
			win.body().style.minHeight = dm.getScreenSize().h + "px";
			dm.resizeAll();
			dm._hiding = false;
		}else{
			// Failed to hide address bar, so retry after a while
			setTimeout(dm.hide_1, dm._hidingTimer);
			if(has('android')){
				dm.resizeAll();
			}
		}
	};

	dm.hideAddressBar = function(/*Event?*/evt){
		// summary:
		//		Hides the address bar.
		// description:
		//		Tries hiding of the address bar a couple of times to do it as
		//		quick as possible while ensuring resize is done after the hiding
		//		finishes.
		if(dm.disableHideAddressBar || dm._hiding){ return; }
		dm._hiding = true;
		dm._hidingTimer = has('iphone') ? 200 : 0; // Need to wait longer in case of iPhone
		var minH = has('android') ? (outerHeight / devicePixelRatio) : screen.availHeight;
		win.body().style.minHeight = minH + "px"; // to ensure enough height for scrollTo to work
		setTimeout(dm.hide_1, dm._hidingTimer);
	};

	dm.isAddressBarHidden = function(){
		return pageYOffset === 1;
	};

	dm.resizeAll = function(/*Event?*/evt, /*Widget?*/root){
		// summary:
		//		Call the resize() method of all the top level resizable widgets.
		// description:
		//		Find all widgets that do not have a parent or the parent does not
		//		have the resize() method, and call resize() for them.
		//		If a widget has a parent that has resize(), call of the widget's
		//		resize() is its parent's responsibility.
		// evt:
		//		Native event object
		// root:
		//		If specified, search the specified widget recursively for top level
		//		resizable widgets.
		//		root.resize() is always called regardless of whether root is a
		//		top level widget or not.
		//		If omitted, search the entire page.
		if(dm.disableResizeAll){ return; }
		connect.publish("/dojox/mobile/resizeAll", [evt, root]);
		dm.updateOrient();
		dm.detectScreenSize();
		var isTopLevel = function(w){
			var parent = w.getParent && w.getParent();
			return !!((!parent || !parent.resize) && w.resize);
		};
		var resizeRecursively = function(w){
			array.forEach(w.getChildren(), function(child){
				if(isTopLevel(child)){ child.resize(); }
				resizeRecursively(child);
			});
		};
		if(root){
			if(root.resize){ root.resize(); }
			resizeRecursively(root);
		}else{
			array.forEach(array.filter(registry.toArray(), isTopLevel),
					function(w){ w.resize(); });
		}
	};

	dm.openWindow = function(url, target){
		// summary:
		//		Opens a new browser window with the given url.
		win.global.open(url, target || "_blank");
	};

	if(config.parseOnLoad){
		ready(90, function(){
			// avoid use of query
			/*
			var list = query('[lazy=true] [dojoType]', null);
			list.forEach(function(node, index, nodeList){
				node.setAttribute("__dojoType", node.getAttribute("dojoType"));
				node.removeAttribute("dojoType");
			});
			*/

			var nodes = win.body().getElementsByTagName("*");
			var i, len, s;
			len = nodes.length;
			for(i = 0; i < len; i++){
				s = nodes[i].getAttribute("dojoType");
				if(s){
					if(nodes[i].parentNode.getAttribute("lazy") == "true"){
						nodes[i].setAttribute("__dojoType", s);
						nodes[i].removeAttribute("dojoType");
					}
				}
			}
		});
	}

	if(win.global._no_dojo_dm){
		// deviceTheme seems to be loaded from a script tag (= non-dojo usage)
		var _dm = win.global._no_dojo_dm;
		for(var i in _dm){
			dm[i] = _dm[i];
		}
		dm.deviceTheme.setDm(dm);
	}

	ready(function(){
		dm.detectScreenSize(true);
		if(config["mblApplyPageStyles"] !== false){
			domClass.add(win.doc.documentElement, "mobile");
		}
		if(has('chrome')){
			// dojox.mobile does not load uacss (only _compat does), but we need dj_chrome.
			domClass.add(win.doc.documentElement, "dj_chrome");
		}

		//	You can disable hiding the address bar with the following djConfig.
		//	var djConfig = { mblHideAddressBar: false };
		var f = dm.resizeAll;
		if(config["mblHideAddressBar"] !== false &&
			navigator.appVersion.indexOf("Mobile") != -1 ||
			config["mblForceHideAddressBar"] === true){
			dm.hideAddressBar();
			if(config["mblAlwaysHideAddressBar"] === true){
				f = dm.hideAddressBar;
			}
		}
		connect.connect(null, (win.global.onorientationchange !== undefined && !has('android'))
			? "onorientationchange" : "onresize", null, f);

		// avoid use of query
		/*
		var list = query('[__dojoType]', null);
		list.forEach(function(node, index, nodeList){
			node.setAttribute("dojoType", node.getAttribute("__dojoType"));
			node.removeAttribute("__dojoType");
		});
		*/

		var nodes = win.body().getElementsByTagName("*");
		var i, len = nodes.length, s;
		for(i = 0; i < len; i++){
			s = nodes[i].getAttribute("__dojoType");
			if(s){
				nodes[i].setAttribute("dojoType", s);
				nodes[i].removeAttribute("__dojoType");
			}
		}

		win.body().style.visibility = "visible";
	});

	// To search _parentNode first.  TODO:1.8 reconsider this redefinition.
	registry.getEnclosingWidget = function(node){
		while(node){
			var id = node.getAttribute && node.getAttribute("widgetId");
			if(id){
				return registry.byId(id);
			}
			node = node._parentNode || node.parentNode;
		}
		return null;
	};

	return dm;
});
<|endoftext|>


<commit_before>/* eslint no-undef: [0]*/

module.exports = {
  title: "mList",
  style: "m-list.less",
  template: 'm-list.html',
  i18n: {
    pt: "lang/pt-BR.json",
    en: "lang/en-US.json"
  },
  link: function() {},
  controller: function(
    $scope,
    $rootScope,
    $filter,
    $timeout,
    $mState,
    $stateParams,
    $mDataLoader
  ) {
    var dadosIniciais = [];
    var dataLoadOptions;
    var list = {
      /**
       * Set the view and update the needed parameters
       * @param  {object} data Data received from Moblets backend
       * @param  {boolean} more If called by "more" function, it will add the
       * data to the items array
       */
      setView: function(data, more) {
        if (isDefined(data)) {
          console.log(data, "data do m-list");
          $scope.error = false;
          $scope.emptyData = false;
          $scope.itemStyle = data.itemStyle;

          if (data.search === true) {
            $rootScope.$broadcast('hideShowSearch' , {data});
          }

          $scope.isCard = data.listStyle === "layout-2";
          $scope.isList = isDefined(data.listStyle) ? data.listStyle === "layout-1" : true;

          // If it was called from the "more" function, concatenate the items
          $scope.items = (more) ? $scope.items.concat(data.items) : data.items;

          // Set "noContent" if the items lenght = 0
          $scope.moblet.noContent = $scope.items === undefined ||
                             $scope.items.length === 0;

          // set empty itens if no content
          if ($scope.moblet.noContent) {
            $scope.items = [];
          }

          // Check if the page is loading the list or a detail
          $scope.isDetail = list.isDetail();

          // Disable the "more" function if the API don't have more items
          $scope.more = (data.hasMoreItems) ? list.more : undefined;
        } else {
          $scope.error = true;
          $scope.emptyData = true;
        }

        // Broadcast complete refresh and infinite scroll
        $rootScope.$broadcast('scroll.refreshComplete');
        $rootScope.$broadcast('scroll.infiniteScrollComplete');

        if (!$scope.isDetail) {
          $rootScope.$broadcast('show-search', {data});
        }

        $scope.$on("update-data", function(event, args) {

          console.log(document.getElementById('input-search').style.color);

          if ($scope.items.length < dadosIniciais.length) {
            $scope.items = [];

            for (var i = 0; dadosIniciais[i] !== undefined; i++) {
              $scope.items.push(dadosIniciais[i]);
            }
          }

          var quant_destroy = $scope.items.length - args.response.results.length;

          //popula os itens encontrados
          for (var i = 0; i <= args.response.results.length -1; i++) {
              $scope.items[i].description = args.response.results[i].item.description;
              $scope.items[i].id = args.response.results[i].item.id;
              $scope.items[i].image = args.response.results[i].item.image;
              $scope.items[i].resume = args.response.results[i].item.resume;
              $scope.items[i].title = args.response.results[i].item.title;
          }

          //destroi os itens desnecessarios
          while(quant_destroy > 0) {
            $scope.items.splice(-1,1)
            quant_destroy--;
          }

        });

        // If the view is showing the detail, call showDetail
        if ($scope.items.length === 1) {
          $scope.isDetail = true;
          list.showDetail(0);
        } else if ($scope.isDetail) {
          list.showDetail();
        }

        // Remove the loading animation
        $scope.moblet.isLoading = false;
      },
      /**
       * Check if the view is showing a detail or the list. The function checks
       * if $stateParams.detail is set.
       * @return {boolean} True if the view must show a detail.
       */
      isDetail: function() {
        return $stateParams.detail !== "";
      },
      /**
       * Show the detail getting the index from $stateParams.detail. Set "item"
       * to the selected detail
       */
      showDetail: function(detailIndex) {
        if (isDefined($stateParams.detail) && $stateParams.detail !== "") {
          var itemIndex = _.findIndex($scope.items, function(item) {
            return item.id.toString() === $stateParams.detail;
          });

          if (itemIndex === -1) {
            dataLoadOptions = {
              //offset: $scope.items === undefined ? 0 : $scope.items.length,
              items: 1000,
              cache: false
            };
            list.load(false, function() {
              list.showDetail();
            });
          } else {
            $stateParams.pageTitle = $scope.items[itemIndex].title;
            $scope.detail = $scope.items[itemIndex];
          }
        } else if (isDefined(detailIndex)) {
          $scope.detail = $scope.items[detailIndex];
        }
      },
      /**
       * Load data from the Moblets backend:
       * - show the page loader if it's called by init (sets showLoader to true)
       * - Use $mDataLoader.load to get the moblet data from Moblets backend.
       * 	 The parameters passed to $mDataLoader.load are:
       * 	 - $scope.moblet - the moblet created in the init function
       * 	 - false - A boolean that sets if you want to load data from the
       * 	   device storage or from the Moblets API
       * 	 - dataLoadOptions - An object with parameters for pagination
       * @param  {boolean} showLoader Boolean to determine if the page loader
       * is active
       * @param {function} callback Callback
       */
      load: function(showLoader, callback) {
        $scope.moblet.isLoading = showLoader || false;
        // Reset the pagination
        if (showLoader === true || showLoader === undefined) {
          dataLoadOptions.offset = 0;
        }
        // mDataLoader also saves the response in the local cache. It will be
        // used by the "showDetail" function
        $mDataLoader.load($scope.moblet, dataLoadOptions)
          .then(function(data) {
            list.setView(data);

            $rootScope.$broadcast('hide-search-refresh');

            dadosIniciais = JSON.stringify(data.items);
            dadosIniciais = JSON.parse(dadosIniciais);

            console.log(dadosIniciais, "dadosIniciais");

            if (typeof callback === 'function') {
              callback();
            }
          }
        );
      },
      /**
       * Load more data from the backend if there are more items.
       * - Update the offset summing the number of items
       - Use $mDataLoader.load to get the moblet data from Moblets backend.
       * 	 The parameters passed to $mDataLoader.load are:
       * 	 - $scope.moblet - the moblet created in the init function
       * 	 - false - A boolean that sets if you want to load data from the
       * 	   device storage or from the Moblets API
       * 	 - dataLoadOptions - An object with parameters for pagination
       */
      more: function() {
        // Add the items to the offset
        dataLoadOptions.offset += dataLoadOptions.items;
          $mDataLoader.load($scope.moblet, dataLoadOptions)
            .then(function(data) {
              list.setView(data, true);
            });
      },
      /**
       * Initiate the list moblet:
       * - put the list.load function in the $scope
       * - run list.load function
       */
      /*
       * TODO go to detail if url is called
       */
      init: function() {
        $stateParams.pageTitle = null;
        dataLoadOptions = {
          offset: 0,
          items: 1000,
          listKey: 'items',
          cache: false
        };
        $scope.load(true);
        $scope.reload = function() {
          list.load();
        };
      }
    };

    var listItem = {
      goTo: function(detail) {
        $stateParams.detail = detail.id;
        $mState.go('u-moblets', 'page', {
          detail: detail.id
        });
      }
    };

    $scope.stripHtml = function(str) {
      return str.replace(/<[^>]+>/ig, " ");
    };

    $scope.load = list.load;
    $scope.init = list.init;
    $scope.goTo = listItem.goTo;
    list.init();
  }
};
<commit_msg>refresh list when the search is closed<commit_after>/* eslint no-undef: [0]*/

module.exports = {
  title: "mList",
  style: "m-list.less",
  template: 'm-list.html',
  i18n: {
    pt: "lang/pt-BR.json",
    en: "lang/en-US.json"
  },
  link: function() {},
  controller: function(
    $scope,
    $rootScope,
    $filter,
    $timeout,
    $mState,
      $stateParams,
    $mDataLoader
  ) {
    var dadosIniciais = [];
    var dataLoadOptions;
    var list = {
      /**
       * Set the view and update the needed parameters
       * @param  {object} data Data received from Moblets backend
       * @param  {boolean} more If called by "more" function, it will add the
       * data to the items array
       */
      setView: function(data, more) {
        if (isDefined(data)) {
          console.log(data, "data do m-list");
          $scope.error = false;
          $scope.emptyData = false;
          $scope.itemStyle = data.itemStyle;
          var origData = JSON.stringify(data.items);

          if (data.search === true) {
            $rootScope.$broadcast('hideShowSearch' , {data});
          }

          $scope.isCard = data.listStyle === "layout-2";
          $scope.isList = isDefined(data.listStyle) ? data.listStyle === "layout-1" : true;

          // If it was called from the "more" function, concatenate the items
          $scope.items = (more) ? $scope.items.concat(data.items) : data.items;

          // Set "noContent" if the items lenght = 0
          $scope.moblet.noContent = $scope.items === undefined ||
                             $scope.items.length === 0;

          // set empty itens if no content
          if ($scope.moblet.noContent) {
            $scope.items = [];
          }

          // Check if the page is loading the list or a detail
          $scope.isDetail = list.isDetail();

          // Disable the "more" function if the API don't have more items
          $scope.more = (data.hasMoreItems) ? list.more : undefined;
        } else {
          $scope.error = true;
          $scope.emptyData = true;
        }

        // Broadcast complete refresh and infinite scroll
        $rootScope.$broadcast('scroll.refreshComplete');
        $rootScope.$broadcast('scroll.infiniteScrollComplete');

        if (!$scope.isDetail) {
          $rootScope.$broadcast('show-search', {data});
        }

        $scope.$on("update-data", function(event, args) {

          console.log(document.getElementById('input-search').style.color);

          if ($scope.items.length < dadosIniciais.length) {
            $scope.items = [];

            for (var i = 0; dadosIniciais[i] !== undefined; i++) {
              $scope.items.push(dadosIniciais[i]);
            }
          }

          var quant_destroy = $scope.items.length - args.response.results.length;

          //popula os itens encontrados
          for (var i = 0; i <= args.response.results.length -1; i++) {
              $scope.items[i].description = args.response.results[i].item.description;
              $scope.items[i].id = args.response.results[i].item.id;
              $scope.items[i].image = args.response.results[i].item.image;
              $scope.items[i].resume = args.response.results[i].item.resume;
              $scope.items[i].title = args.response.results[i].item.title;
          }

          //destroi os itens desnecessarios
          while(quant_destroy > 0) {
            $scope.items.splice(-1,1)
            quant_destroy--;
          }

        });

        // If the view is showing the detail, call showDetail
        if ($scope.items.length === 1) {
          $scope.isDetail = true;
          list.showDetail(0);
        } else if ($scope.isDetail) {
          list.showDetail();
        }

        // Remove the loading animation
        $scope.moblet.isLoading = false;

        $scope.$on("check-update-data", function(event, args) {
            if ($scope.items.length < dadosIniciais.length) {
              $scope.items = JSON.parse(origData);
            }
        });
      },
      /**
       * Check if the view is showing a detail or the list. The function checks
       * if $stateParams.detail is set.
       * @return {boolean} True if the view must show a detail.
       */
      isDetail: function() {
        return $stateParams.detail !== "";
      },
      /**
       * Show the detail getting the index from $stateParams.detail. Set "item"
       * to the selected detail
       */
      showDetail: function(detailIndex) {
        if (isDefined($stateParams.detail) && $stateParams.detail !== "") {
          var itemIndex = _.findIndex($scope.items, function(item) {
            return item.id.toString() === $stateParams.detail;
          });

          if (itemIndex === -1) {
            dataLoadOptions = {
              //offset: $scope.items === undefined ? 0 : $scope.items.length,
              items: 1000,
              cache: false
            };
            list.load(false, function() {
              list.showDetail();
            });
          } else {
            $stateParams.pageTitle = $scope.items[itemIndex].title;
            $scope.detail = $scope.items[itemIndex];
          }
        } else if (isDefined(detailIndex)) {
          $scope.detail = $scope.items[detailIndex];
        }
      },
      /**
       * Load data from the Moblets backend:
       * - show the page loader if it's called by init (sets showLoader to true)
       * - Use $mDataLoader.load to get the moblet data from Moblets backend.
       * 	 The parameters passed to $mDataLoader.load are:
       * 	 - $scope.moblet - the moblet created in the init function
       * 	 - false - A boolean that sets if you want to load data from the
       * 	   device storage or from the Moblets API
       * 	 - dataLoadOptions - An object with parameters for pagination
       * @param  {boolean} showLoader Boolean to determine if the page loader
       * is active
       * @param {function} callback Callback
       */
      load: function(showLoader, callback) {
        $scope.moblet.isLoading = showLoader || false;
        // Reset the pagination
        if (showLoader === true || showLoader === undefined) {
          dataLoadOptions.offset = 0;
        }
        // mDataLoader also saves the response in the local cache. It will be
        // used by the "showDetail" function
        $mDataLoader.load($scope.moblet, dataLoadOptions)
          .then(function(data) {
            list.setView(data);

            $rootScope.$broadcast('hide-search-refresh');

            dadosIniciais = JSON.stringify(data.items);
            dadosIniciais = JSON.parse(dadosIniciais);

            console.log(dadosIniciais, "dadosIniciais");

            if (typeof callback === 'function') {
              callback();
            }
          }
        );
      },
      /**
       * Load more data from the backend if there are more items.
       * - Update the offset summing the number of items
       - Use $mDataLoader.load to get the moblet data from Moblets backend.
       * 	 The parameters passed to $mDataLoader.load are:
       * 	 - $scope.moblet - the moblet created in the init function
       * 	 - false - A boolean that sets if you want to load data from the
       * 	   device storage or from the Moblets API
       * 	 - dataLoadOptions - An object with parameters for pagination
       */
      more: function() {
        // Add the items to the offset
        dataLoadOptions.offset += dataLoadOptions.items;
          $mDataLoader.load($scope.moblet, dataLoadOptions)
            .then(function(data) {
              list.setView(data, true);
            });
      },
      /**
       * Initiate the list moblet:
       * - put the list.load function in the $scope
       * - run list.load function
       */
      /*
       * TODO go to detail if url is called
       */
      init: function() {
        $stateParams.pageTitle = null;
        dataLoadOptions = {
          offset: 0,
          items: 1000,
          listKey: 'items',
          cache: false
        };
        $scope.load(true);
        $scope.reload = function() {
          list.load();
        };
      }
    };

    var listItem = {
      goTo: function(detail) {
        $stateParams.detail = detail.id;
        $mState.go('u-moblets', 'page', {
          detail: detail.id
        });
      }
    };

    $scope.stripHtml = function(str) {
      return str.replace(/<[^>]+>/ig, " ");
    };

    $scope.load = list.load;
    $scope.init = list.init;
    $scope.goTo = listItem.goTo;
    list.init();
  }
};
<|endoftext|>


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

const ADDON_ID = 'places-auto-vacuum-on-idle@clear-code.com';
const CLASS_ID = '{4bdc28aa-1031-4f51-881b-1c44fa4d43fd}';

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

function StartupService() {
}
StartupService.prototype = {
	classDescription : ADDON_ID + '_StartupService',
	contractID : '@'+ADDON_ID.split('@').reverse().join('/')+'/startup;1',
	classID : Components.ID(CLASS_ID),
	_xpcom_categories : [
		{ category : 'profile-after-change', service : true }
	],
	QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver]),

	observe : function(aSubject, aTopic, aData)
	{
		switch (aTopic)
		{
			case 'profile-after-change':
				this.onStartup();
				return;
		}
	},

	onStartup : function()
	{
	}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([StartupService]);
<commit_msg>Do VACUUM instead of simple  maintenances on idle<commit_after>/*
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

const ADDON_ID = 'places-auto-vacuum-on-idle@clear-code.com';
const CLASS_ID = '{4bdc28aa-1031-4f51-881b-1c44fa4d43fd}';

const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;

Cu.import('resource://gre/modules/XPCOMUtils.jsm');

function StartupService() {
}
StartupService.prototype = {
	classDescription : ADDON_ID + '_StartupService',
	contractID : '@'+ADDON_ID.split('@').reverse().join('/')+'/startup;1',
	classID : Components.ID(CLASS_ID),
	_xpcom_categories : [
		{ category : 'profile-after-change', service : true }
	],
	QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver]),

	observe : function(aSubject, aTopic, aData)
	{
		switch (aTopic)
		{
			case 'profile-after-change':
				this.onStartup();
				return;
		}
	},

	onStartup : function()
	{
		try {
			var { PlacesDBUtils } = Cu.import('resource://gre/modules/PlacesDBUtils.jsm', {});
			if (!(typeof PlacesDBUtils.maintenanceOnIdle == 'function'))
				return Cu.reportError(new Error(ADDON_ID + ': PlacesDBUtils.maintenanceOnIdle() is not available.'));
			if (!(typeof PlacesDBUtils.maintenanceOnIdle == 'function'))
				return Cu.reportError(new Error(ADDON_ID + ': PlacesDBUtils.checkAndFixDatabase() is not available.'));

			PlacesDBUtils.maintenanceOnIdle = PlacesDBUtils.checkAndFixDatabase;
		}
		catch(error) {
			Cu.reportError(error);
		}
	}
};
var NSGetFactory = XPCOMUtils.generateNSGetFactory([StartupService]);
<|endoftext|>


<commit_before>"use strict";
//为了防止因message而形成环形引用，message文件夹中的内容不允许被外界调用

var cluster = require("cluster");
var fs = require("fs");
var path = require("path");
var isDebug = require("../basic/isDebug");
var message_handlers_path = "../../coms/message";
// message 文件夹中定义主进程的方法
// 子进程可通过message的属性访问主进程中的方法
message_handlers_path = path.join(__dirname, message_handlers_path);
var onmessage = function (msg, then) {
    var index = msg.indexOf(":");
    var run, args, key, stamp;
    if (index < 0) {
        key = msg;
        run = onmessage[key];
    } else {
        key = msg.slice(0, index);
        run = onmessage[key];
        args = msg.slice(index + 1);
    }
    if (run instanceof Function) {
        var notSupport = () => console.info('Not Support', `message[${key}]:${this.id}`);
        if (args) {
            var { params, stamp } = JSON.parse(args);
            if (!then) then = stamp ? (result) => {
                !this.isDead() && (this.state === "online" || cluster.isMaster) && this.send("onresponse:" + JSON.stringify({
                    params: result,
                    stamp
                }), (error) => {
                    if (error) console.error(error, `message[${key}]:${this.id}`);
                });
            } : null;
            run.call(onmessage, params, then, stamp);
        } else {
            run.call(onmessage, null, then || notSupport);
        }
    }
};
var callback_maps = {};
// 发送对主进程方法的访问消息
var send = function (worker, key, params, then) {
    var stamp;
    if (then instanceof Function) {
        do {
            stamp = Math.random().toString("36").slice(2);
        } while (stamp in callback_maps);
        callback_maps[stamp] = function () {
            delete callback_maps[stamp];
            then.apply(null, arguments);
        };
    }
    worker.send([key, JSON.stringify({
        params, stamp
    })].join(":"));
};
if (isDebug) {
    send = function () {
    };
}
//收到主进程的回复
var onresponse = function ({ stamp, params }) {
    var callback = callback_maps[stamp];
    delete callback_maps[stamp];
    if (callback instanceof Function) {
        callback(params);
    }
};
onmessage.onresponse = onresponse;

if (cluster.isMaster && !isDebug) {
    fs.readdirSync(message_handlers_path).forEach(function (name) {
        var match = name.match(/^(.*)\.js$/i);
        if (match && !/^index.js$|\_test\.js$/i.test(name)) {
            var key = match[1];
            onmessage[key] = require(path.join(message_handlers_path, name));
        }
    });

    module.exports = onmessage;
    onmessage.send = send;
} else {
    fs.readdirSync(message_handlers_path).forEach(function (name) {
        var match = name.match(/^(.*).js$/);
        if (match) {
            var key = match[1];
            onmessage[key] = function (data, then) {
                send(key, data, then);
            };
        }
    });
    process.on("message", onmessage);

    module.exports = new Proxy(onmessage, {
        get: function (o, k) {
            if (!(k in o) && ("on" + k) in o) {
                // message文件夹中未定义 并且已定义 on*
                return function (...args) {
                    return send("broadcast", "on" + k + ":" + JSON.stringify(args));
                };
            } else if (k in o) {
                // 自动广播子线程的方法 on*
                return o[k];
            } else {
                throw `message faild with key ${k}!`;
            }
        },
        set: function (o, k, v) {
            if (k in o) throw new Error(`the property '${k}' is already in object [message]!`);
            return o[k] = v;
        }
    });
    onmessage.send = send = send.bind(onmessage, process);
}<commit_msg>解决主副通信无回调的问题<commit_after>"use strict";
//为了防止因message而形成环形引用，message文件夹中的内容不允许被外界调用

var cluster = require("cluster");
var fs = require("fs");
var path = require("path");
var isDebug = require("../basic/isDebug");
var message_handlers_path = "../../coms/message";
// message 文件夹中定义主进程的方法
// 子进程可通过message的属性访问主进程中的方法
message_handlers_path = path.join(__dirname, message_handlers_path);
var onmessage = function (msg, then) {
    var index = msg.indexOf(":");
    var run, args, key, stamp;
    if (index < 0) {
        key = msg;
        run = onmessage[key];
    } else {
        key = msg.slice(0, index);
        run = onmessage[key];
        args = msg.slice(index + 1);
    }
    if (run instanceof Function) {
        var notSupport = () => console.info('Not Support', `message[${key}]:${this.id}`);
        if (args) {
            var { params, stamp } = JSON.parse(args);
            if (!then) then = stamp ? (result) => {
                __send(this, "onresponse", {
                    params: result,
                    stamp
                });
            } : null;
            run.call(onmessage, params, then, stamp);
        } else {
            run.call(onmessage, null, then || notSupport);
        }
    }
};
var callback_maps = {};
// 发送对主进程方法的访问消息
var send, __send = send = function (worker, key, params, then) {
    var stamp;
    if (then instanceof Function) {
        do {
            stamp = Math.random().toString("36").slice(2);
        } while (stamp in callback_maps);
        callback_maps[stamp] = function () {
            delete callback_maps[stamp];
            then.apply(null, arguments);
        };
    }
    worker.send([key, JSON.stringify({
        params, stamp
    })].join(":"));
};
if (isDebug) {
    send = function () {
    };
}
//收到主进程的回复
var onresponse = function ({ stamp, params }) {
    var callback = callback_maps[stamp];
    delete callback_maps[stamp];
    if (callback instanceof Function) {
        callback(params);
    }
};
onmessage.onresponse = onresponse;

if (cluster.isMaster && !isDebug) {
    fs.readdirSync(message_handlers_path).forEach(function (name) {
        var match = name.match(/^(.*)\.js$/i);
        if (match && !/^index.js$|\_test\.js$/i.test(name)) {
            var key = match[1];
            onmessage[key] = require(path.join(message_handlers_path, name));
        }
    });

    module.exports = onmessage;
    onmessage.send = send;
} else {
    fs.readdirSync(message_handlers_path).forEach(function (name) {
        var match = name.match(/^(.*).js$/);
        if (match) {
            var key = match[1];
            onmessage[key] = function (data, then) {
                send(key, data, then);
            };
        }
    });
    process.on("message", onmessage);

    module.exports = new Proxy(onmessage, {
        get: function (o, k) {
            if (!(k in o) && ("on" + k) in o) {
                // message文件夹中未定义 并且已定义 on*
                return function (...args) {
                    return send("broadcast", "on" + k + ":" + JSON.stringify(args));
                };
            } else if (k in o) {
                // 自动广播子线程的方法 on*
                return o[k];
            } else {
                throw `message faild with key ${k}!`;
            }
        },
        set: function (o, k, v) {
            if (k in o) throw new Error(`the property '${k}' is already in object [message]!`);
            return o[k] = v;
        }
    });
    onmessage.send = send = send.bind(onmessage, process);
}<|endoftext|>


<commit_before>'use strict';

const _ = require('lodash');

exports.register = function register(skill) {
  skill.onIntent('LaunchIntent', (alexaEvent) => {
    if (_.get(alexaEvent, 'model.user.email')) {
      return { reply: 'Intent.Launch', to: 'entry' };
    }

    return { reply: 'Intent.NotAuthenticated', to: 'die' };
  });

  skill.onIntent('AMAZON.HelpIntent', () => ({ reply: 'Intent.Help' }));
};
<commit_msg>Add Alexa skill events to account linking sample project<commit_after>'use strict';

const _ = require('lodash');

exports.register = function register(skill) {
  skill.onIntent('LaunchIntent', (alexaEvent) => {
    if (_.get(alexaEvent, 'model.user.email')) {
      return { reply: 'Intent.Launch', to: 'entry' };
    }

    return { reply: 'Intent.NotAuthenticated', to: 'die' };
  });

  skill.onIntent('AMAZON.HelpIntent', () => ({ reply: 'Intent.Help' }));

  skill['onAlexaSkillEvent.SkillEnabled']((alexaEvent, reply) => {
    const userId = alexaEvent.user.userId;
    const timestamp = alexaEvent.request.timestamp;
    console.log(`skill was enabled for user: ${userId} at ${timestamp}`);
    return reply;
  });

  skill['onAlexaSkillEvent.SkillAccountLinked']((alexaEvent, reply) => {
    const userId = alexaEvent.user.userId;
    const accessToken = alexaEvent.request.body.accessToken;
    console.log(`User: ${userId} link his account. Access Token is ${accessToken}`);
    return reply;
  });
};
<|endoftext|>


<commit_before>var
  async = require('async'),
  _ = require('lodash'),
  stringify = require('json-stable-stringify'),
  Router = require('router'),
  bodyParser = require('body-parser'),
  finalhandler = require('finalhandler'),
  RequestObject = require('../core/models/requestObject'),
  ResponseListener = require('../core/responseListener');

module.exports = function RouterController (kuzzle) {
  this.router = null;
  this.controllers = ['write', 'read', 'subscribe', 'admin', 'bulk'];
  this.kuzzle = kuzzle;
  this.listener = new ResponseListener(this.kuzzle, this.kuzzle.config.queues.workerWriteResponseQueue);

  /**
   * Initializes the HTTP routes for the Kuzzle REST API.
   */
  this.initRouterHttp = function () {
    this.router = new Router();

    if (process.env.FEATURE_COVERAGE === 1) {
      var coverage = require('istanbul-middleware');
      this.router.use('/coverage', coverage.createHandler({ resetOnGet: true }));
    }

    // create and mount a new router for our API
    var api = new Router();
    this.router.use('/api/', api);

    // add a body parsing middleware to our API
    api.use(bodyParser.json());

    // Simple hello world to let know to the user that kuzzle is running
    api.get('/', function (request, response) {
      response.writeHead('Access-Control-Allow-Origin', '*');
      response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');
      response.writeHead(200, {'Content-Type': 'application/json'});
      response.end(stringify({error: null, result: 'Hello from Kuzzle :)'}));
    }.bind(this));

    api.post('/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'putMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.get('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'getMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_search', function (request, response) {
      var params = {
        controller: 'read',
        action: 'search'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection/_query', function (request, response) {
      var params = {
        controller: 'write',
        action: 'deleteByQuery'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_count', function (request, response) {
      var params = {
        controller: 'read',
        action: 'count'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/:id/_:action', function (request, response) {
      var params = {
        controller: 'write'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'update'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection', function (request, response) {
      var params = {
        controller: 'write',
        action: 'create'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.get('/:collection/:id', function (request, response) {
      var params = {
        controller: 'read',
        action: 'get'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'delete'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'deleteCollection'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

  };

  /**
   * Forward incoming REST requests to the HTTP routes created by the
   * initRouterHttp function.
   *
   * @param request transmitted through the REST API
   * @param response is the HTTP connection handler
   */
  this.routeHttp = function (request, response) {
    kuzzle.log.silly('Handle HTTP request');
    this.router(request, response, finalhandler(request, response));
  };

  /**
   * Handles requests coming from websocket connections
   *
   * @param {Object} socket client
   */
  this.routeWebsocket = function (socket) {
    var
      routerCtrl = this,
      connection = {type: 'websocket', id: socket.id};

    async.each(routerCtrl.controllers, function recordSocketListener (controller) {
      socket.on(controller, function (data) {
        var requestObject = new RequestObject(data, {controller: controller}, 'websocket');

        kuzzle.log.silly('Handle Websocket', controller, 'request');
        kuzzle.router.listener.add(requestObject, connection);

        // Execute the funnel. Forward any non-empty response to the user.
        kuzzle.funnel.execute(requestObject, connection)
          .then(function onExecuteSuccess (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function onExecuteError(error) {
            kuzzle.emit(requestObject.controller + ':websocket:funnel:reject', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
            kuzzle.log.error(error);
          });
      });
    });

    // handles socket disconnections
    socket.on('disconnect', function () {
      kuzzle.emit('websocket:disconnect');
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });

    // handles socket crashes
    socket.on('error', function (error) {
      kuzzle.emit('websocket:error', error);
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
      kuzzle.log.error(error);
    });
  };

  /**
   * Handles requests coming from MQ protocols: AMQP, MQTT & STOMP
   *
   */
  this.routeMQListener = function () {
    async.each(this.controllers, function recordMQListener (controller) {
      kuzzle.services.list.mqBroker.listenExchange(controller+'.*.*', function handleMQMessage(msg) {
        var
          connection = null,
          data,
          requestObject,
          rawContent,
          routingArray = msg.fields.routingKey.split('.'),
          controller = routingArray[0],
          collection = routingArray[1],
          action = routingArray[2];

        if (!(msg.content instanceof Buffer)) {
          rawContent = msg.content.toString();
        }
        else {
          rawContent = (new Buffer(msg.content)).toString();
        }

        try {
          data = JSON.parse(rawContent);
        }
        catch (e) {
          kuzzle.log.error('Parse error: ', e.message, '\nIncriminated message: ', msg, '\nFaulty content: ', rawContent);
          return false;
        }

        kuzzle.log.silly('Handle MQ input', msg.fields.routingKey, 'message');

        // For MQTT messages, we do not have a replyTo header like with AMQP or STOMP
        // => MQTT client has to send its mqtt client id and subscribe to the topic exchange mqtt.<clientId>
        //    to get feedback from Kuzzle.
        if (msg.properties && msg.properties.replyTo) {
          connection = {type: 'amq', id: data.clientId, replyTo: msg.properties.replyTo};
        }
        else {
          connection = {type: 'mqtt', id: data.clientId, replyTo: 'mqtt.' + data.clientId};
        }

        requestObject = new RequestObject(data, {controller: controller, collection: collection, action: action}, 'mq');

        kuzzle.router.listener.add(requestObject, connection);
        kuzzle.funnel.execute(requestObject, connection)
          .then(function (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          }.bind(this))
          .catch(function (error) {
            kuzzle.emit(requestObject.controller + ':mq:funnel:reject', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      }); // end listenExchange
    }); // end async
  };
};

/**
 * Transmit HTTP requests to the funnel controller and forward its response back to
 * the client
 *
 * @param params contains the request metadata
 * @param request is the original request from the client
 * @param response is the HTTP connection handler
 */
function executeFromRest(params, request, response) {
  var
    requestObject,
    data,
    connection = {type: 'rest', response: response};

  if (!params.controller) {
    response.writeHead(400, {'Content-Type': 'application/json'});
    response.end(stringify({error: 'The "controller" argument is missing', result: null}));
    return false;
  }

  data = {
    controller: params.controller,
    action: params.action || request.params.action,
    collection: request.params.collection
  };

  if (request.params.id) {
    data._id = request.params.id;
  }

  requestObject = new RequestObject(data, request.body, 'rest');

  response.writeHead('Access-Control-Allow-Origin', '*');
  response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');

  this.router.listener.add(requestObject, connection);

  this.funnel.execute(requestObject)
    .then(function (responseObject) {
      response.writeHead(200, {'Content-Type': 'application/json'});
      if (!_.isEmpty(responseObject)) {
        response.end(stringify(responseObject.toJson()));
      }

    }.bind(this))
    .catch(function (error) {
      this.emit(requestObject.controller + ':websocket:funnel:reject', error);
      response.writeHead(400, {'Content-Type': 'application/json'});
      response.end(stringify({error: error.message, result: null}));
    }.bind(this));
}
<commit_msg>reverted a test-broking change<commit_after>var
  async = require('async'),
  _ = require('lodash'),
  stringify = require('json-stable-stringify'),
  Router = require('router'),
  bodyParser = require('body-parser'),
  finalhandler = require('finalhandler'),
  RequestObject = require('../core/models/requestObject'),
  ResponseListener = require('../core/responseListener');

module.exports = function RouterController (kuzzle) {
  this.router = null;
  this.controllers = ['write', 'read', 'subscribe', 'admin', 'bulk'];
  this.kuzzle = kuzzle;
  this.listener = new ResponseListener(this.kuzzle, this.kuzzle.config.queues.workerWriteResponseQueue);

  /**
   * Initializes the HTTP routes for the Kuzzle REST API.
   */
  this.initRouterHttp = function () {
    this.router = new Router();

    if (process.env.FEATURE_COVERAGE == 1) {
      var coverage = require('istanbul-middleware');
      this.router.use('/coverage', coverage.createHandler({ resetOnGet: true }));
    }

    // create and mount a new router for our API
    var api = new Router();
    this.router.use('/api/', api);

    // add a body parsing middleware to our API
    api.use(bodyParser.json());

    // Simple hello world to let know to the user that kuzzle is running
    api.get('/', function (request, response) {
      response.writeHead('Access-Control-Allow-Origin', '*');
      response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');
      response.writeHead(200, {'Content-Type': 'application/json'});
      response.end(stringify({error: null, result: 'Hello from Kuzzle :)'}));
    }.bind(this));

    api.post('/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_bulk', function (request, response) {
      var params = {
        controller: 'bulk',
        action: 'import'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'putMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.get('/:collection/_mapping', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'getMapping'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_search', function (request, response) {
      var params = {
        controller: 'read',
        action: 'search'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection/_query', function (request, response) {
      var params = {
        controller: 'write',
        action: 'deleteByQuery'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection/_count', function (request, response) {
      var params = {
        controller: 'read',
        action: 'count'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/:id/_:action', function (request, response) {
      var params = {
        controller: 'write'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.put('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'update'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.post('/:collection', function (request, response) {
      var params = {
        controller: 'write',
        action: 'create'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.get('/:collection/:id', function (request, response) {
      var params = {
        controller: 'read',
        action: 'get'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection/:id', function (request, response) {
      var params = {
        controller: 'write',
        action: 'delete'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

    api.delete('/:collection', function (request, response) {
      var params = {
        controller: 'admin',
        action: 'deleteCollection'
      };

      executeFromRest.call(kuzzle, params, request, response);
    }.bind(this));

  };

  /**
   * Forward incoming REST requests to the HTTP routes created by the
   * initRouterHttp function.
   *
   * @param request transmitted through the REST API
   * @param response is the HTTP connection handler
   */
  this.routeHttp = function (request, response) {
    kuzzle.log.silly('Handle HTTP request');
    this.router(request, response, finalhandler(request, response));
  };

  /**
   * Handles requests coming from websocket connections
   *
   * @param {Object} socket client
   */
  this.routeWebsocket = function (socket) {
    var
      routerCtrl = this,
      connection = {type: 'websocket', id: socket.id};

    async.each(routerCtrl.controllers, function recordSocketListener (controller) {
      socket.on(controller, function (data) {
        var requestObject = new RequestObject(data, {controller: controller}, 'websocket');

        kuzzle.log.silly('Handle Websocket', controller, 'request');
        kuzzle.router.listener.add(requestObject, connection);

        // Execute the funnel. Forward any non-empty response to the user.
        kuzzle.funnel.execute(requestObject, connection)
          .then(function onExecuteSuccess (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          })
          .catch(function onExecuteError(error) {
            kuzzle.emit(requestObject.controller + ':websocket:funnel:reject', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
            kuzzle.log.error(error);
          });
      });
    });

    // handles socket disconnections
    socket.on('disconnect', function () {
      kuzzle.emit('websocket:disconnect');
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
    });

    // handles socket crashes
    socket.on('error', function (error) {
      kuzzle.emit('websocket:error', error);
      kuzzle.hotelClerk.removeCustomerFromAllRooms(connection);
      kuzzle.log.error(error);
    });
  };

  /**
   * Handles requests coming from MQ protocols: AMQP, MQTT & STOMP
   *
   */
  this.routeMQListener = function () {
    async.each(this.controllers, function recordMQListener (controller) {
      kuzzle.services.list.mqBroker.listenExchange(controller+'.*.*', function handleMQMessage(msg) {
        var
          connection = null,
          data,
          requestObject,
          rawContent,
          routingArray = msg.fields.routingKey.split('.'),
          controller = routingArray[0],
          collection = routingArray[1],
          action = routingArray[2];

        if (!(msg.content instanceof Buffer)) {
          rawContent = msg.content.toString();
        }
        else {
          rawContent = (new Buffer(msg.content)).toString();
        }

        try {
          data = JSON.parse(rawContent);
        }
        catch (e) {
          kuzzle.log.error('Parse error: ', e.message, '\nIncriminated message: ', msg, '\nFaulty content: ', rawContent);
          return false;
        }

        kuzzle.log.silly('Handle MQ input', msg.fields.routingKey, 'message');

        // For MQTT messages, we do not have a replyTo header like with AMQP or STOMP
        // => MQTT client has to send its mqtt client id and subscribe to the topic exchange mqtt.<clientId>
        //    to get feedback from Kuzzle.
        if (msg.properties && msg.properties.replyTo) {
          connection = {type: 'amq', id: data.clientId, replyTo: msg.properties.replyTo};
        }
        else {
          connection = {type: 'mqtt', id: data.clientId, replyTo: 'mqtt.' + data.clientId};
        }

        requestObject = new RequestObject(data, {controller: controller, collection: collection, action: action}, 'mq');

        kuzzle.router.listener.add(requestObject, connection);
        kuzzle.funnel.execute(requestObject, connection)
          .then(function (responseObject) {
            if (!_.isEmpty(responseObject)) {
              kuzzle.notifier.notify(requestObject.requestId, responseObject.toJson(), connection);
            }
          }.bind(this))
          .catch(function (error) {
            kuzzle.emit(requestObject.controller + ':mq:funnel:reject', error);
            kuzzle.notifier.notify(requestObject.requestId, {error: error.message, result: null}, connection);
          });
      }); // end listenExchange
    }); // end async
  };
};

/**
 * Transmit HTTP requests to the funnel controller and forward its response back to
 * the client
 *
 * @param params contains the request metadata
 * @param request is the original request from the client
 * @param response is the HTTP connection handler
 */
function executeFromRest(params, request, response) {
  var
    requestObject,
    data,
    connection = {type: 'rest', response: response};

  if (!params.controller) {
    response.writeHead(400, {'Content-Type': 'application/json'});
    response.end(stringify({error: 'The "controller" argument is missing', result: null}));
    return false;
  }

  data = {
    controller: params.controller,
    action: params.action || request.params.action,
    collection: request.params.collection
  };

  if (request.params.id) {
    data._id = request.params.id;
  }

  requestObject = new RequestObject(data, request.body, 'rest');

  response.writeHead('Access-Control-Allow-Origin', '*');
  response.writeHead('Access-Control-Allow-Headers', 'X-Requested-With');

  this.router.listener.add(requestObject, connection);

  this.funnel.execute(requestObject)
    .then(function (responseObject) {
      response.writeHead(200, {'Content-Type': 'application/json'});
      if (!_.isEmpty(responseObject)) {
        response.end(stringify(responseObject.toJson()));
      }

    }.bind(this))
    .catch(function (error) {
      this.emit(requestObject.controller + ':websocket:funnel:reject', error);
      response.writeHead(400, {'Content-Type': 'application/json'});
      response.end(stringify({error: error.message, result: null}));
    }.bind(this));
}
<|endoftext|>


<commit_before>var redis = require('redis');

exports.redis={
    name: 'redis',
    group: 'redis',
    title:'',
    units: '',
    slope: 'zero',
    type: 'uint32',
    description:'Metrics for Redis Info',
    interval:15000,
    tmax:90,
    dmax:120,
    run:function(api,metric,send){
      var client = redis.createClient(api.config.metrics.redis);
      client.on("error", function (err) {
        api.log(err.message,"error");
        client.end();
      });

      client.on('ready',function(){
          var info = client.server_info;

          var version = api.utils.objClone(metric);
          version.name = 'redis_version';
          version.title = 'Redis Version';
          version.value = info.redis_version;
          version.type='string';
          version.slope='zero';
          send(version);

          var role = api.utils.objClone(metric);
          role.name = 'redis_role';
          role.title = 'Redis role';
          role.value = info.role;
          role.type='string';
          role.slope='zero';
          send(role);

          var sha1 = api.utils.objClone(metric);
          sha1.name = 'redis_git_sha1';
          sha1.title = 'Redis git sha1';
          sha1.value = info[sha1.name];
          sha1.type='string';
          sha1.slope='zero';
          send(sha1);

          var uptime = api.utils.objClone(metric);
          uptime.name = 'redis_uptime_in_seconds';
          uptime.title = 'Redis uptime in seconds';
          uptime.value = info.uptime_in_seconds;
          uptime.type='uint32';
          uptime.slope='zero';
          send(uptime);

          var uptime = api.utils.objClone(metric);
          uptime.name = 'redis_uptime_in_days';
          uptime.title = 'Redis uptime in days';
          uptime.value = info.uptime_in_days;
          uptime.type='uint32';
          uptime.slope='zero';
          send(uptime);

          var memory = api.utils.objClone(metric);
          memory.name = 'redis_used_memory';
          memory.title = 'Used Memory';
          memory.units='KB';
          memory.value = parseFloat(info.used_memory/1000);
          memory.type='float';
          memory.slope='both';
          send(memory);

          var connections = api.utils.objClone(metric);
          connections.name = 'redis_connected_clients';
          connections.title = 'Connected Clients';
          connections.units='clients';
          connections.value = info.connected_clients;
          connections.type='uint16';
          connections.slope='both';
          send(connections);

          var slaves = api.utils.objClone(metric);
          slaves.name = 'redis_connected_slaves';
          slaves.title = 'Connected Slaves';
          slaves.units='slaves';
          slaves.value = info.connected_slaves;
          slaves.type='uint16';
          slaves.slope='both';
          send(slaves);

          var blocked = api.utils.objClone(metric);
          blocked.name = 'redis_blocked_clients';
          blocked.title = 'Blocked Clients';
          blocked.units='clients';
          blocked.value = info.blocked_clients;
          blocked.type='uint32';
          blocked.slope='both';
          send(blocked);

          var channels = api.utils.objClone(metric);
          channels.name = 'redis_pubsub_channels';
          channels.title = 'PUB/SUB Channels';
          channels.units='channels';
          channels.value = info.pubsub_channels;
          channels.type='uint16';
          channels.slope='both';
          send(channels);

          var patterns = api.utils.objClone(metric);
          patterns.name = 'redis_pubsub_patterns';
          patterns.title = 'PUB/SUB Patterns';
          patterns.units='patterns';
          patterns.value = info.pubsub_patterns;
          patterns.type='uint16';
          patterns.slope='both';
          send(patterns);

          var totalConnections = api.utils.objClone(metric);
          totalConnections.name = 'redis_total_connections_received';
          totalConnections.title = 'Redis Total Connections';
          totalConnections.units='connections';
          totalConnections.value = info.total_connections_received;
          totalConnections.type='uint32';
          totalConnections.slope='zero';
          send(totalConnections);

          var totalCommands = api.utils.objClone(metric);
          totalCommands.name = 'redis_total_commands_processed';
          totalCommands.title = 'Redis Total Commands Processed';
          totalCommands.units='commands';
          totalCommands.value = info.total_commands_processed;
          totalCommands.type='uint32';
          totalCommands.slope='zero';
          send(totalCommands);

          var expiredKeys = api.utils.objClone(metric);
          expiredKeys.name = 'redis_expired_keys';
          expiredKeys.title = 'Expired Keys';
          expiredKeys.units='keys';
          expiredKeys.value = info.expired_keys;
          expiredKeys.type='uint32';
          expiredKeys.slope='both';
          send(expiredKeys);

          var keyspaceHits = api.utils.objClone(metric);
          keyspaceHits.name = 'redis_keyspace_hits';
          keyspaceHits.title = 'Keyspace Hits';
          keyspaceHits.units='keys';
          keyspaceHits.value = info.keyspace_hits;
          keyspaceHits.type='uint32';
          keyspaceHits.slope='both';
          send(keyspaceHits);

          var keyspaceMisses = api.utils.objClone(metric);
          keyspaceMisses.name = 'redis_keyspace_misses';
          keyspaceMisses.title = 'Keyspace Misses';
          keyspaceMisses.units='keys';
          keyspaceMisses.value = info.keyspace_misses;
          keyspaceMisses.type='uint32';
          keyspaceMisses.slope='both';
          send(keyspaceMisses);
          client.end();
      });
    }
};<commit_msg>Redis prefix for title<commit_after>var redis = require('redis');

exports.redis={
    name: 'redis',
    group: 'redis',
    title:'Redis',
    units: '',
    slope: 'zero',
    type: 'uint32',
    description:'Metrics for Redis Info',
    interval:15000,
    tmax:90,
    dmax:120,
    run:function(api,metric,send){
      var client = redis.createClient(api.config.metrics.redis);
      client.on('error', function (err) {
        api.log(err.message,'error');
        client.end();
      });

      client.on('ready',function(){
          var info = client.server_info;

          var version = api.utils.objClone(metric);
          version.name = 'redis_version';
          version.title = metric.title + ' Version';
          version.value = info.redis_version;
          version.type='string';
          version.slope='zero';
          send(version);

          var role = api.utils.objClone(metric);
          role.name = 'redis_role';
          role.title = metric.title + ' Redis role';
          role.value = info.role;
          role.type='string';
          role.slope='zero';
          send(role);

          var sha1 = api.utils.objClone(metric);
          sha1.name = 'redis_git_sha1';
          sha1.title = metric.title + ' Redis git sha1';
          sha1.value = info[sha1.name];
          sha1.type='string';
          sha1.slope='zero';
          send(sha1);

          var uptime = api.utils.objClone(metric);
          uptime.name = 'redis_uptime_in_seconds';
          uptime.title = metric.title + ' Uptime In Seconds';
          uptime.value = info.uptime_in_seconds;
          uptime.type='uint32';
          uptime.slope='zero';
          send(uptime);

          var uptime = api.utils.objClone(metric);
          uptime.name = 'redis_uptime_in_days';
          uptime.title = metric.title + ' Uptime In Days';
          uptime.value = info.uptime_in_days;
          uptime.type='uint32';
          uptime.slope='zero';
          send(uptime);

          var memory = api.utils.objClone(metric);
          memory.name = 'redis_used_memory';
          memory.title = metric.title + ' Used Memory';
          memory.units='KB';
          memory.value = parseFloat(info.used_memory/1000);
          memory.type='float';
          memory.slope='both';
          send(memory);

          var connections = api.utils.objClone(metric);
          connections.name = 'redis_connected_clients';
          connections.title = metric.title + ' Connected Clients';
          connections.units='clients';
          connections.value = info.connected_clients;
          connections.type='uint16';
          connections.slope='both';
          send(connections);

          var slaves = api.utils.objClone(metric);
          slaves.name = 'redis_connected_slaves';
          slaves.title = metric.title + ' Connected Slaves';
          slaves.units='slaves';
          slaves.value = info.connected_slaves;
          slaves.type='uint16';
          slaves.slope='both';
          send(slaves);

          var blocked = api.utils.objClone(metric);
          blocked.name = 'redis_blocked_clients';
          blocked.title = metric.title + ' Blocked Clients';
          blocked.units='clients';
          blocked.value = info.blocked_clients;
          blocked.type='uint32';
          blocked.slope='both';
          send(blocked);

          var channels = api.utils.objClone(metric);
          channels.name = 'redis_pubsub_channels';
          channels.title = metric.title + ' PUB/SUB Channels';
          channels.units='channels';
          channels.value = info.pubsub_channels;
          channels.type='uint16';
          channels.slope='both';
          send(channels);

          var patterns = api.utils.objClone(metric);
          patterns.name = 'redis_pubsub_patterns';
          patterns.title = metric.title + ' PUB/SUB Patterns';
          patterns.units='patterns';
          patterns.value = info.pubsub_patterns;
          patterns.type='uint16';
          patterns.slope='both';
          send(patterns);

          var totalConnections = api.utils.objClone(metric);
          totalConnections.name = 'redis_total_connections_received';
          totalConnections.title = metric.title + ' Total Connections';
          totalConnections.units='connections';
          totalConnections.value = info.total_connections_received;
          totalConnections.type='uint32';
          totalConnections.slope='zero';
          send(totalConnections);

          var totalCommands = api.utils.objClone(metric);
          totalCommands.name = 'redis_total_commands_processed';
          totalCommands.title = metric.title + ' Total Commands Processed';
          totalCommands.units='commands';
          totalCommands.value = info.total_commands_processed;
          totalCommands.type='uint32';
          totalCommands.slope='zero';
          send(totalCommands);

          var expiredKeys = api.utils.objClone(metric);
          expiredKeys.name = 'redis_expired_keys';
          expiredKeys.title = metric.title + ' Expired Keys';
          expiredKeys.units='keys';
          expiredKeys.value = info.expired_keys;
          expiredKeys.type='uint32';
          expiredKeys.slope='both';
          send(expiredKeys);

          var keyspaceHits = api.utils.objClone(metric);
          keyspaceHits.name = 'redis_keyspace_hits';
          keyspaceHits.title = metric.title + ' Keyspace Hits';
          keyspaceHits.units='keys';
          keyspaceHits.value = info.keyspace_hits;
          keyspaceHits.type='uint32';
          keyspaceHits.slope='both';
          send(keyspaceHits);

          var keyspaceMisses = api.utils.objClone(metric);
          keyspaceMisses.name = 'redis_keyspace_misses';
          keyspaceMisses.title = metric.title + ' Keyspace Misses';
          keyspaceMisses.units='keys';
          keyspaceMisses.value = info.keyspace_misses;
          keyspaceMisses.type='uint32';
          keyspaceMisses.slope='both';
          send(keyspaceMisses);
          client.end();
      });
    }
};<|endoftext|>


<commit_before>// ==UserScript==
// @name         ktmSaverForBrowser
// @namespace    https://github.com/tatesuke/ktmsaver
// @version      0.1
// @description
// @author       tatesuke
// @match        file:///*/*
// @grant        none
// ==/UserScript==
/* jshint -W097 */
'use strict';

(function() {
    // ローカルファイル以外実行しない
    var url = location.href;
    if (!url.match(/^file:\/\/\//)) {
        return;
    }

    // かんたんMarkdownでなければ実行しない
    var isKantanMarkdown = document.querySelector("#kantanVersion");
    if (!isKantanMarkdown) {
        return;
    }

    // getHTMLForSave関数がないバージョンでは実行しない
    if (!getHTMLForSave) {
        return;
    }

    // UIを構築
    showKtmSaverUI();

    // オリジナルのkeydownイベントを退避させる
    var originalEventListener;
    for (var i = 0; i < eventListeners.length; i++) {
        var el = eventListeners[i];
        if ((el.element == document.body) && (el.eventName == "keydown")) {
            originalEventListener = el.callback;
            break;
        }
    }
    document.body.removeEventListener("keydown", originalEventListener);

    // 新しいkeydownイベントを定義
    document.body.addEventListener("keydown", function(event) {
        var code = (event.keyCode ? event.keyCode : event.which);

        if (!isDrawMode() && (code == 83) && (event.ctrlKey || event.metaKey)) {
            // CTRL+Sのみ新しい処理に書き換える
            event.preventDefault();
            queueOverwriteSave();
            return false;
        } else {
            // CTRL+Sはオリジナルそのまま使う
            originalEventListener(event);
        }
    });

    function showKtmSaverUI() {
        /* 上書き設定ボタン */
        var ktmSaverMenuButton = document.createElement("button");
        var settingButton = document.querySelector("#settingMenuButton");
        ktmSaverMenuButton.innerHTML = "上書き設定";
        ktmSaverMenuButton.style.height = settingButton.offsetHeight;
        ktmSaverMenuButton.style.position = "absolute"
        ktmSaverMenuButton.style.top = settingButton.offsetTop + "px";
        ktmSaverMenuButton.style.left = (settingButton.offsetLeft + settingButton.offsetWidth) + "px";
        on(ktmSaverMenuButton, "click", function() {
            var button = this;
            var menu = document.getElementById("ktmSaverMenu");
            menu.style.top = (this.offsetTop + this.scrollHeight) + "px";
            menu.style.left = this.offsetLeft + "px";
            showBlock(menu);
        });
        document.body.appendChild(ktmSaverMenuButton);

        /* 上書き設定メニュー */
        var ktmSaverMenu = document.createElement("div");
        ktmSaverMenu.classList.add("popupMenu");
        ktmSaverMenu.id  = "ktmSaverMenu";
        ktmSaverMenu.innerHTML =
            '
ポート番号:<input type="text" id="ktmServerPort" value="' + getKtmSaverPort() + '">' +
            '<button id="cliseKtmSaverMenuButton">閉じる</button>
';
        document.body.appendChild(ktmSaverMenu);

        on("#ktmServerPort", "change", function() {
            localStorage.setItem("com.tatesuke.ktmsaver.port", this.value);
        });
        on("#cliseKtmSaverMenuButton", "click", function() {
            hide(ktmSaverMenu);
        });

        /* 接続中メッセージ */
        var connectingMessage = document.createElement("div");
        connectingMessage.innerHTML = "接続中..";
        connectingMessage.id = "ktmSaverConnectingMessage";
        connectingMessage.style.width = "150px";
        connectingMessage.style.height = (settingButton.offsetHeight - 2) + "px";
        connectingMessage.style.position = "absolute";
        connectingMessage.style.top = 0;
        connectingMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        connectingMessage.style.textAlign = "center";
        connectingMessage.style.color = "#c09853";
        connectingMessage.style.backgroundColor = "#fcf8e3";
        connectingMessage.style.border = "1px solid #fbeed5";
        connectingMessage.style.display = "none";
        document.body.appendChild(connectingMessage);

        /* 保存中メッセージ */
        var savingMessage = document.createElement("div");
        savingMessage.innerHTML = "保存中..";
        savingMessage.id = "ktmSaverSavinggMessage";
        savingMessage.style.width = "150px";
        savingMessage.style.height = (settingButton.offsetHeight - 2) + "px";
        savingMessage.style.position = "absolute";
        savingMessage.style.top = 0;
        savingMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        savingMessage.style.textAlign = "center";
        savingMessage.style.color = "#c09853";
        savingMessage.style.backgroundColor = "#fcf8e3";
        savingMessage.style.border = "1px solid #fbeed5";
        savingMessage.style.display = "none";
        document.body.appendChild(savingMessage);

        /* 保存完了メッセージ */
        var sucessMessage = document.createElement("div");
        sucessMessage.innerHTML = "保存完了";
        sucessMessage.id = "ktmSaverSuccessMessage";
        sucessMessage.style.width = "150px";
        sucessMessage.style.height = (settingButton.offsetHeight - 2) + "px";
        sucessMessage.style.position = "absolute";
        sucessMessage.style.top = 0;
        sucessMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        sucessMessage.style.textAlign = "center";
        sucessMessage.style.color = "#3a87ad";
        sucessMessage.style.backgroundColor = "#d9edf7";
        sucessMessage.style.border = "1px solid #bce8f1";
        sucessMessage.style.display = "none";
        document.body.appendChild(sucessMessage);

        /* 保存失敗メッセージ */
        var errorMessage = document.createElement("div");
        errorMessage.innerHTML = "！保存失敗！";
        errorMessage.id = "ktmSaverErrorMessage";
        errorMessage.style.width = "150px";
        errorMessage.style.height = (settingButton.offsetHeight - 2) + "px";
        errorMessage.style.position = "absolute";
        errorMessage.style.top = 0;
        errorMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        errorMessage.style.textAlign = "center";
        errorMessage.style.color = "#b94a48";
        errorMessage.style.backgroundColor = "#f2dede";
        errorMessage.style.border = "1px solid #eed3d7";
        errorMessage.style.display = "none";
        document.body.appendChild(errorMessage);
    }

    function getKtmSaverPort() {
        var port = localStorage.getItem("com.tatesuke.ktmsaver.port");
        if (!port) {
            port = "56565";
        }
        return port;
    }

    // 上書き保存
    var overwriteSaveQueue = null;
    var clearMessageQueue = null;
    var queueWait = 50;
    function queueOverwriteSave() {
        clearTimeout(overwriteSaveQueue);
        clearTimeout(clearMessageQueue);
        overwriteSaveQueue = setTimeout(doOverwriteSave, queueWait);
    }

    function doOverwriteSave() {
        hide(document.querySelector("#ktmSaverSuccessMessage"));
        hide(document.querySelector("#ktmSaverErrorMessage"));
        showBlock(document.querySelector("#ktmSaverConnectingMessage"));

        var data = {
            filePath:decodeURIComponent(location.href.replace(/file:\/\/\//, "")),
            content: getHTMLForSave()
        };

        var port = getKtmSaverPort();
        var ws = new WebSocket('ws://localhost:' + port + '/');
        ws.onopen = function() {
            hide(document.querySelector("#ktmSaverConnectingMessage"));
            showBlock(document.querySelector("#ktmSaverSavinggMessage"));
            ws.send(JSON.stringify(data));
        };

        ws.onmessage = function(event) {
            hide(document.querySelector("#ktmSaverSavinggMessage"));
            showBlock(document.querySelector("#ktmSaverSuccessMessage"));
            clearMessageQueue = setTimeout(function() {
                hide(document.querySelector("#ktmSaverSuccessMessage"));
            }, 3000)
        };

        ws.onerror = function() {
            hide(document.querySelector("#ktmSaverConnectingMessage"));
            hide(document.querySelector("#ktmSaverSavinggMessage"));
            showBlock(document.querySelector("#ktmSaverErrorMessage"));
            alert(data.filePath + "の保存に失敗しました。");
            saved = false;
            doPreview();
        };
    }

})();<commit_msg>設定ボタンがないバージョンに配慮<commit_after>// ==UserScript==
// @name         ktmSaverForBrowser
// @namespace    https://github.com/tatesuke/ktmsaver
// @version      0.1
// @description
// @author       tatesuke
// @match        file:///*/*
// @grant        none
// ==/UserScript==
/* jshint -W097 */
'use strict';

(function() {
    // ローカルファイル以外実行しない
    var url = location.href;
    if (!url.match(/^file:\/\/\//)) {
        return;
    }

    // かんたんMarkdownでなければ実行しない
    var isKantanMarkdown = document.querySelector("#kantanVersion");
    if (!isKantanMarkdown) {
        return;
    }

    // getHTMLForSave関数がないバージョンでは実行しない
    if (!getHTMLForSave) {
        return;
    }

    // UIを構築
    showKtmSaverUI();

    // オリジナルのkeydownイベントを退避させる
    var originalEventListener;
    for (var i = 0; i < eventListeners.length; i++) {
        var el = eventListeners[i];
        if ((el.element == document.body) && (el.eventName == "keydown")) {
            originalEventListener = el.callback;
            break;
        }
    }
    document.body.removeEventListener("keydown", originalEventListener);

    // 新しいkeydownイベントを定義
    document.body.addEventListener("keydown", function(event) {
        var code = (event.keyCode ? event.keyCode : event.which);

        if (!isDrawMode() && (code == 83) && (event.ctrlKey || event.metaKey)) {
            // CTRL+Sのみ新しい処理に書き換える
            event.preventDefault();
            queueOverwriteSave();
            return false;
        } else {
            // CTRL+Sはオリジナルそのまま使う
            originalEventListener(event);
        }
    });

    function showKtmSaverUI() {
        var baseButton = document.querySelector("#settingMenuButton");
        if (!baseButton) {
            baseButton = document.querySelector("#onlineMenuButton");
        }

        /* 上書き設定ボタン */
        var ktmSaverMenuButton = document.createElement("button");
        ktmSaverMenuButton.innerHTML = "上書き設定";
        ktmSaverMenuButton.style.height = baseButton.offsetHeight;
        ktmSaverMenuButton.style.position = "absolute"
        ktmSaverMenuButton.style.top = baseButton.offsetTop + "px";
        ktmSaverMenuButton.style.left = (baseButton.offsetLeft + baseButton.offsetWidth) + "px";
        on(ktmSaverMenuButton, "click", function() {
            var button = this;
            var menu = document.getElementById("ktmSaverMenu");
            menu.style.top = (this.offsetTop + this.scrollHeight) + "px";
            menu.style.left = this.offsetLeft + "px";
            showBlock(menu);
        });
        document.body.appendChild(ktmSaverMenuButton);

        /* 上書き設定メニュー */
        var ktmSaverMenu = document.createElement("div");
        ktmSaverMenu.classList.add("popupMenu");
        ktmSaverMenu.id  = "ktmSaverMenu";
        ktmSaverMenu.innerHTML =
            '
ポート番号:<input type="text" id="ktmServerPort" value="' + getKtmSaverPort() + '">' +
            '<button id="cliseKtmSaverMenuButton">閉じる</button>
';
        document.body.appendChild(ktmSaverMenu);

        on("#ktmServerPort", "change", function() {
            localStorage.setItem("com.tatesuke.ktmsaver.port", this.value);
        });
        on("#cliseKtmSaverMenuButton", "click", function() {
            hide(ktmSaverMenu);
        });

        /* 接続中メッセージ */
        var connectingMessage = document.createElement("div");
        connectingMessage.innerHTML = "接続中..";
        connectingMessage.id = "ktmSaverConnectingMessage";
        connectingMessage.style.width = "150px";
        connectingMessage.style.height = (baseButton.offsetHeight - 2) + "px";
        connectingMessage.style.position = "absolute";
        connectingMessage.style.top = 0;
        connectingMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        connectingMessage.style.textAlign = "center";
        connectingMessage.style.color = "#c09853";
        connectingMessage.style.backgroundColor = "#fcf8e3";
        connectingMessage.style.border = "1px solid #fbeed5";
        connectingMessage.style.display = "none";
        document.body.appendChild(connectingMessage);

        /* 保存中メッセージ */
        var savingMessage = document.createElement("div");
        savingMessage.innerHTML = "保存中..";
        savingMessage.id = "ktmSaverSavinggMessage";
        savingMessage.style.width = "150px";
        savingMessage.style.height = (baseButton.offsetHeight - 2) + "px";
        savingMessage.style.position = "absolute";
        savingMessage.style.top = 0;
        savingMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        savingMessage.style.textAlign = "center";
        savingMessage.style.color = "#c09853";
        savingMessage.style.backgroundColor = "#fcf8e3";
        savingMessage.style.border = "1px solid #fbeed5";
        savingMessage.style.display = "none";
        document.body.appendChild(savingMessage);

        /* 保存完了メッセージ */
        var sucessMessage = document.createElement("div");
        sucessMessage.innerHTML = "保存完了";
        sucessMessage.id = "ktmSaverSuccessMessage";
        sucessMessage.style.width = "150px";
        sucessMessage.style.height = (baseButton.offsetHeight - 2) + "px";
        sucessMessage.style.position = "absolute";
        sucessMessage.style.top = 0;
        sucessMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        sucessMessage.style.textAlign = "center";
        sucessMessage.style.color = "#3a87ad";
        sucessMessage.style.backgroundColor = "#d9edf7";
        sucessMessage.style.border = "1px solid #bce8f1";
        sucessMessage.style.display = "none";
        document.body.appendChild(sucessMessage);

        /* 保存失敗メッセージ */
        var errorMessage = document.createElement("div");
        errorMessage.innerHTML = "！保存失敗！";
        errorMessage.id = "ktmSaverErrorMessage";
        errorMessage.style.width = "150px";
        errorMessage.style.height = (baseButton.offsetHeight - 2) + "px";
        errorMessage.style.position = "absolute";
        errorMessage.style.top = 0;
        errorMessage.style.left = ((document.body.offsetWidth / 2) - (150 / 2)) + "px"
        errorMessage.style.textAlign = "center";
        errorMessage.style.color = "#b94a48";
        errorMessage.style.backgroundColor = "#f2dede";
        errorMessage.style.border = "1px solid #eed3d7";
        errorMessage.style.display = "none";
        document.body.appendChild(errorMessage);
    }

    function getKtmSaverPort() {
        var port = localStorage.getItem("com.tatesuke.ktmsaver.port");
        if (!port) {
            port = "56565";
        }
        return port;
    }

    // 上書き保存
    var overwriteSaveQueue = null;
    var clearMessageQueue = null;
    var queueWait = 50;
    function queueOverwriteSave() {
        clearTimeout(overwriteSaveQueue);
        clearTimeout(clearMessageQueue);
        overwriteSaveQueue = setTimeout(doOverwriteSave, queueWait);
    }

    function doOverwriteSave() {
        hide(document.querySelector("#ktmSaverSuccessMessage"));
        hide(document.querySelector("#ktmSaverErrorMessage"));
        showBlock(document.querySelector("#ktmSaverConnectingMessage"));

        var data = {
            filePath:decodeURIComponent(location.href.replace(/file:\/\/\//, "")),
            content: getHTMLForSave()
        };

        var port = getKtmSaverPort();
        var ws = new WebSocket('ws://localhost:' + port + '/');
        ws.onopen = function() {
            hide(document.querySelector("#ktmSaverConnectingMessage"));
            showBlock(document.querySelector("#ktmSaverSavinggMessage"));
            ws.send(JSON.stringify(data));
        };

        ws.onmessage = function(event) {
            hide(document.querySelector("#ktmSaverSavinggMessage"));
            showBlock(document.querySelector("#ktmSaverSuccessMessage"));
            clearMessageQueue = setTimeout(function() {
                hide(document.querySelector("#ktmSaverSuccessMessage"));
            }, 3000)
        };

        ws.onerror = function() {
            hide(document.querySelector("#ktmSaverConnectingMessage"));
            hide(document.querySelector("#ktmSaverSavinggMessage"));
            showBlock(document.querySelector("#ktmSaverErrorMessage"));
            alert(data.filePath + "の保存に失敗しました。");
            saved = false;
            doPreview();
        };
    }

})();<|endoftext|>