<commit_before>/* ****** MODEL ******** */

var neighborhoodSpots = {
		filters: ["None", "Restaurant", "Gym", "Dry Cleaners"],
		spots: [
			{
				title: "Saggio",
				phone: "212-795-3080",
				location: {lat: 40.851473, lng: -73.939588},
			type: "Restaurant",
//			label: "Restaurant"
		},
    	{
    		title: "Mambi Steakhouse",
    		phone: "212-928-9796",
    		location: {lat: 40.84772, lng: -73.938251},
    		type: "Restaurant"
    	},
    	{
    		title: "Planet Fitness",
    		phone: "646-216-3150",
    		location: {lat: 40.847326, lng: -73.937638},
    		type: "Gym",
    //		label:"Gym"
    	},
    	{
    		title: "Le Cheile",
    		phone: "212-740-3111",
    		location: {lat: 40.851495, lng: -73.939988},
    		type: "Restaurant"
    	},
    	{
    		title: "Elite Cleaners",
    		phone: "212-927-5872",
    		location: {lat: 40.843097, lng: -73.939438},
    		type: "Dry Cleaners",
    //		label: "Dry Cleaners"
    	},
    	{
    		title: "Columbia Wine Co",
    		phone: "212-543-2633",
    		location: {lat: 40.842125, lng: -73.939272},
    		type: "Liquor Store",
    //		label: "Liquor Store"
		}
    	]
	}

	//****** VIEW ******** //

var listView = function(data) {
	this.title = ko.observable(data.title);
	this.location = ko.observable(data.location);
	this.type = ko.observable(data.type);
}

var filterView = function(data) {
	this.filters = ko.observable(data.filters);
}

// ****** VIEWMODEL ******** //

var ViewModel = function() {
	var self = this;

	this.places = ko.observableArray([]);

	// Push data to observable array.
	neighborhoodSpots.spots.forEach(function(locationItem) {
		self.places.push(new listView(locationItem));
	});

	neighborhoodSpots.filters.forEach(function(filterItem) {
		self.filters.push(new filterView(filterItem));
	});

	self.filters = ko.observableArray([]);
	self.filter = ko.observable('');
	self.places = ko.observableArray(neighborhoodSpots.spots);
	self.filteredSpots = ko.computed(function() {
		var filter = self.filter();
		if (!filter || filter == "None") {
			return self.places();
		} else {
			return ko.utils.arrayFilter(self.spots(), function(i) {
				return i.type == filter;
			});
		}
	});

	//neighborhoodSpots.filters.forEach(function(filterItem) {
	//	self.filters.push(new filterView(filterItem));
	//});

	this.selectedType = ko.observable();
	this.clearFilter = function() {
		this.selectedType(null)
	}

	//this.filterPlaces = function() {
	//	if ()
	//}

}

ko.applyBindings(new ViewModel());

// ******** MAP ******** //

var map;

// Create empty arry to push markers into.
var markers = [];

// Get that map going in the map div!
// Thanks to Google Maps API for the super helpful documentation, course, and instructions!
var initMap = function() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 40.843436, lng: -73.934794},
		zoom: 14
	});

	// Close infowindow when any other portion of the map is clicked.
	google.maps.event.addListener(map, "click", function(event) {
		mapInfoWindow.close();
		marker.setIcon(defaultIcon);
	});

	var mapInfoWindow = new google.maps.InfoWindow();

	var defaultIcon = makeMarkerIcon('ff2d00');

	var highlightedIcon = makeMarkerIcon('631504');

	var clickedIcon = makeMarkerIcon('46e91f');

	// loop through neighborhoodSpots array to make markers.
	for (var i = 0; i < neighborhoodSpots.length; i++) {
		var title = neighborhoodSpots[i].title;
		var phone = neighborhoodSpots[i].phone;
		var position = neighborhoodSpots[i].location;

		var marker = new google.maps.Marker({
			map: map,
			position: position,
			title: title,
			phone: phone,
			// markers drop in when page loads.
			animation: google.maps.Animation.DROP,
			icon: defaultIcon,
			id: i
		});

/*	// Make marker bounce on click.
	marker.addListener("click", makeBounce); */

	// Push markers to markers[] array.
	markers.push(marker);

	//Open info window on click.
	marker.addListener("click", function() {
		fillInfoWindow(this, mapInfoWindow);
		// change clicked icon's color.
		this.setIcon(clickedIcon);
	});

	// Change marker color when mouse hovers over marker.
	marker.addListener("mouseover", function() {
		this.setIcon(highlightedIcon);
	});

	// Use default color when not hovering over marker.
	marker.addListener("mouseout", function() {
		this.setIcon(defaultIcon);

		// Make sure icon hasn't been clicked.
		//if (this.setIcon(clickedIcon) = true) {
		//this.setIcon(clickedIcon);
		//} else {
		//	this.setIcon(defaultIcon);
		//}
	});
}

/*	function makeBounce() {
		var bounce = setInterval(function() {
			marker.setAnimation(google.maps.Animation.BOUNCE);
		}, 100);

		setTimeout(function() {
			clearInterval(bounce);
		}, 1000);
	} */

	function fillInfoWindow(marker, infowindow) {
		// Check if marker's window is already open.
		if (infowindow.marker != marker) {
			// Clear infowindow
			infowindow.setContent('');
			infowindow.marker = marker;
			// Open  infowinow
			infowindow.open(map, marker)

			// Close infowindow on click.
			infowindow.addListener('closeclick', function() {
				infowindow.marker(null);
				// Change icon color back to default.
				marker.setIcon(defaultIcon);
			});

			infowindow.setContent("
" + marker.title + "

" + marker.phone + "
")

			}
		}

		// This function takes in a color, and then creates a new marker
        // icon of that color. The icon will be 21px wide by 34px high, have an
        // origin of 0, 0 and be anchored at 10, 34. Again, thank you Google API for the help.
      function makeMarkerIcon(markerColor) {
        var markerImage = new google.maps.MarkerImage(
          "https://chart.googleapis.com/chart?chst=d_map_spin&chld=1.15|0|" + markerColor +
          "|40|_|%E2%80%A2",
          new google.maps.Size(21, 34),
          new google.maps.Point(0, 0),
          new google.maps.Point(10, 34),
          new google.maps.Size(21,34));
        return markerImage;
      }

};<commit_msg>move item<commit_after>/* ****** MODEL ******** */

var neighborhoodSpots = {
		filters: ["None", "Restaurant", "Gym", "Dry Cleaners"],
		spots: [
			{
				title: "Saggio",
				phone: "212-795-3080",
				location: {lat: 40.851473, lng: -73.939588},
			type: "Restaurant",
//			label: "Restaurant"
		},
    	{
    		title: "Mambi Steakhouse",
    		phone: "212-928-9796",
    		location: {lat: 40.84772, lng: -73.938251},
    		type: "Restaurant"
    	},
    	{
    		title: "Planet Fitness",
    		phone: "646-216-3150",
    		location: {lat: 40.847326, lng: -73.937638},
    		type: "Gym",
    //		label:"Gym"
    	},
    	{
    		title: "Le Cheile",
    		phone: "212-740-3111",
    		location: {lat: 40.851495, lng: -73.939988},
    		type: "Restaurant"
    	},
    	{
    		title: "Elite Cleaners",
    		phone: "212-927-5872",
    		location: {lat: 40.843097, lng: -73.939438},
    		type: "Dry Cleaners",
    //		label: "Dry Cleaners"
    	},
    	{
    		title: "Columbia Wine Co",
    		phone: "212-543-2633",
    		location: {lat: 40.842125, lng: -73.939272},
    		type: "Liquor Store",
    //		label: "Liquor Store"
		}
    	]
	}

	//****** VIEW ******** //

var listView = function(data) {
	this.title = ko.observable(data.title);
	this.location = ko.observable(data.location);
	this.type = ko.observable(data.type);
}

var filterView = function(data) {
	this.filters = ko.observable(data.filters);
}

// ****** VIEWMODEL ******** //

var ViewModel = function() {
	var self = this;

	this.places = ko.observableArray([]);

	// Push data to observable array.
	neighborhoodSpots.spots.forEach(function(locationItem) {
		self.places.push(new listView(locationItem));
	});

	self.filters = ko.observableArray([]);
	self.filter = ko.observable('');
	self.places = ko.observableArray(neighborhoodSpots.spots);
	self.filteredSpots = ko.computed(function() {
		var filter = self.filter();
		if (!filter || filter == "None") {
			return self.places();
		} else {
			return ko.utils.arrayFilter(self.spots(), function(i) {
				return i.type == filter;
			});
		}
	});

neighborhoodSpots.filters.forEach(function(filterItem) {
		self.filters.push(new filterView(filterItem));
	});
	//neighborhoodSpots.filters.forEach(function(filterItem) {
	//	self.filters.push(new filterView(filterItem));
	//});

	this.selectedType = ko.observable();
	this.clearFilter = function() {
		this.selectedType(null)
	}

	//this.filterPlaces = function() {
	//	if ()
	//}

}

ko.applyBindings(new ViewModel());

// ******** MAP ******** //

var map;

// Create empty arry to push markers into.
var markers = [];

// Get that map going in the map div!
// Thanks to Google Maps API for the super helpful documentation, course, and instructions!
var initMap = function() {
	map = new google.maps.Map(document.getElementById('map'), {
		center: {lat: 40.843436, lng: -73.934794},
		zoom: 14
	});

	// Close infowindow when any other portion of the map is clicked.
	google.maps.event.addListener(map, "click", function(event) {
		mapInfoWindow.close();
		marker.setIcon(defaultIcon);
	});

	var mapInfoWindow = new google.maps.InfoWindow();

	var defaultIcon = makeMarkerIcon('ff2d00');

	var highlightedIcon = makeMarkerIcon('631504');

	var clickedIcon = makeMarkerIcon('46e91f');

	// loop through neighborhoodSpots array to make markers.
	for (var i = 0; i < neighborhoodSpots.length; i++) {
		var title = neighborhoodSpots[i].title;
		var phone = neighborhoodSpots[i].phone;
		var position = neighborhoodSpots[i].location;

		var marker = new google.maps.Marker({
			map: map,
			position: position,
			title: title,
			phone: phone,
			// markers drop in when page loads.
			animation: google.maps.Animation.DROP,
			icon: defaultIcon,
			id: i
		});

/*	// Make marker bounce on click.
	marker.addListener("click", makeBounce); */

	// Push markers to markers[] array.
	markers.push(marker);

	//Open info window on click.
	marker.addListener("click", function() {
		fillInfoWindow(this, mapInfoWindow);
		// change clicked icon's color.
		this.setIcon(clickedIcon);
	});

	// Change marker color when mouse hovers over marker.
	marker.addListener("mouseover", function() {
		this.setIcon(highlightedIcon);
	});

	// Use default color when not hovering over marker.
	marker.addListener("mouseout", function() {
		this.setIcon(defaultIcon);

		// Make sure icon hasn't been clicked.
		//if (this.setIcon(clickedIcon) = true) {
		//this.setIcon(clickedIcon);
		//} else {
		//	this.setIcon(defaultIcon);
		//}
	});
}

/*	function makeBounce() {
		var bounce = setInterval(function() {
			marker.setAnimation(google.maps.Animation.BOUNCE);
		}, 100);

		setTimeout(function() {
			clearInterval(bounce);
		}, 1000);
	} */

	function fillInfoWindow(marker, infowindow) {
		// Check if marker's window is already open.
		if (infowindow.marker != marker) {
			// Clear infowindow
			infowindow.setContent('');
			infowindow.marker = marker;
			// Open  infowinow
			infowindow.open(map, marker)

			// Close infowindow on click.
			infowindow.addListener('closeclick', function() {
				infowindow.marker(null);
				// Change icon color back to default.
				marker.setIcon(defaultIcon);
			});

			infowindow.setContent("
" + marker.title + "

" + marker.phone + "
")

			}
		}

		// This function takes in a color, and then creates a new marker
        // icon of that color. The icon will be 21px wide by 34px high, have an
        // origin of 0, 0 and be anchored at 10, 34. Again, thank you Google API for the help.
      function makeMarkerIcon(markerColor) {
        var markerImage = new google.maps.MarkerImage(
          "https://chart.googleapis.com/chart?chst=d_map_spin&chld=1.15|0|" + markerColor +
          "|40|_|%E2%80%A2",
          new google.maps.Size(21, 34),
          new google.maps.Point(0, 0),
          new google.maps.Point(10, 34),
          new google.maps.Size(21,34));
        return markerImage;
      }

};<|endoftext|>


<commit_before>'use strict';

var http = require('http');
var config = require('./common');

/**
 * Activiate Session with UrQA Api Key
 *
 * Body :
 *    {
 *      "apikey" : "apikey", "appversion" : "1.0"
 *    }
 *
 * Header :
 *    Content-Type : 'application/json'
 *    Content-Lenght : data.length
 **/
exports.activate = function (message) {
  try {
    var data = new Buffer(JSON.stringify(message), 'utf8');
    var httpOptions = {
      host: config.UrQA.host,
      path: config.UrQA.init,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': data.length
      }
    };

    var req = http.request(httpOptions, function (res) {
      console.log(res.statusCode)
      console.log(httpOptions)
    });

    req.write(data);
    req.end();

  } catch (e) {
    console.log('UrQA Error Occured : ' + e + ' -> while activate session');
  }
}

/**
 * Send nodejs exceptions data
 *
 * Body :
 *    message : Error data
 *
 * Headers :
 *    Content-Type : application/json
 *    Content-Length : data.length
 **/
exports.exception = function (message) {

  console.log(message);

  try {
    var data = new Buffer(JSON.stringify(message), 'utf8');
    var httpOptions = {
      host: config.UrQA.host,
      path: config.UrQA.exception,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': data.length
      }
    };

    var req = http.request(httpOptions, function (res) {
      console.log(res.statusCode)
      console.log(httpOptions)
    });

    req.write(data);
    req.end();

  } catch (e) {
    console.log('UrQA Error Occured : ' + e + ' -> while activate session');
  }
}<commit_msg>code convention<commit_after>'use strict';

var http = require('http');
var config = require('./common');

/**
 * Activiate Session with UrQA Api Key
 *
 * Body :
 *    {
 *      "apikey" : "apikey", "appversion" : "1.0"
 *    }
 *
 * Header :
 *    Content-Type : 'application/json'
 *    Content-Lenght : data.length
 **/
exports.activate = function (message) {
  try {
    var data = new Buffer(JSON.stringify(message), 'utf8');
    var httpOptions = {
      host: config.UrQA.host,
      path: config.UrQA.init,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': data.length
      }
    };

    var req = http.request(httpOptions, function (res) {
      console.log(res.statusCode)
      console.log(httpOptions)
    });

    req.write(data);
    req.end();

  } catch (e) {
    console.log('UrQA Error Occured : ' + e + ' -> while activate session');
  }
}

/**
 * Send nodejs exceptions data
 *
 * Body :
 *    message : Error data
 *
 * Headers :
 *    Content-Type : application/json
 *    Content-Length : data.length
 **/
exports.exception = function (message) {
  try {
    var data = new Buffer(JSON.stringify(message), 'utf8');
    var httpOptions = {
      host: config.UrQA.host,
      path: config.UrQA.exception,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Content-Length': data.length
      }
    };

    var req = http.request(httpOptions, function (res) {
      console.log(res.statusCode)
      console.log(httpOptions)
    });

    req.write(data);
    req.end();

  } catch (e) {
    console.log('UrQA Error Occured : ' + e + ' -> while sending error data');
  }
}<|endoftext|>


<commit_before>/**
 * @class Oskari.mapframework.bundle.backendstatus.BackendStatusBundleInstance
 *
 * Utility bundle to manage updating backend status information.
 * Updates information only when LayerSelector2 is being opened.
 *
 */
Oskari.clazz.define("Oskari.mapframework.bundle.backendstatus.BackendStatusBundleInstance",

/**
 * @method create called automatically on construction
 * @static
 */
function() {
	this._sandbox = null;
	this._started = false;
	this._pendingAjaxQuery = {
		busy : false,
		jqhr : null,
		timestamp : null
	};
	this.timeInterval = this.ajaxSettings.defaultTimeThreshold;
	this.backendStatus = {};
	this.backendExtendedStatus = {};
}, {
	ajaxSettings: {
		defaultTimeThreshold: 30000
	},
	/**
	 * @static
	 * @property __name
	 */
	__name : 'BackendStatus',
	/**
	 * @method getName
	 * @return {String} the name for the component
	 */
	"getName" : function() {
		return this.__name;
	},
	/**
	 * @method setSandbox
	 * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
	 * Sets the sandbox reference to this component
	 */
	setSandbox : function(sandbox) {
		this._sandbox = sandbox;
	},
	/**
	 * @method getSandbox
	 * @return {Oskari.mapframework.sandbox.Sandbox}
	 */
	getSandbox : function() {
		return this._sandbox;
	},

	getAjaxUrl: function(key) {
		var ajaxUrl = this.getSandbox().getAjaxUrl();
		var url = ajaxUrl + 'action_route=GetBackendStatus';

		return url;
		/*return 'GetBackendStatus.json';*/
	},

	/**
	 * @method start
	 * implements BundleInstance protocol start method
	 */
	"start" : function() {
		var me = this;

		if(me._started) {
			return;
		}

		me._started = true;

		var sandbox = Oskari.$("sandbox");
		me._sandbox = sandbox;

		sandbox.register(me);
		for(p in me.eventHandlers) {
			sandbox.registerForEventByName(me, p);
		}
	},
	/**
	 * @method init
	 * implements Module protocol init method - does nothing atm
	 */
	"init" : function() {
		return null;
	},
	/**
	 * @method update
	 * implements BundleInstance protocol update method - does nothing atm
	 */
	"update" : function() {

	},
	/**
	 * @method onEvent
	 * @param {Oskari.mapframework.event.Event} event a Oskari event object
	 * Event is handled forwarded to correct #eventHandlers if found or discarded if not.
	 */
	onEvent : function(event) {

		var handler = this.eventHandlers[event.getName()];
		if(!handler)
			return;

		return handler.apply(this, [event]);

	},

	/**
	 * @property extensionsByName
	 * @static
	 * extensions that trigger update
	 *
	 */
	extensionsByName : {
		'LayerSelector' : true
	},

	/**
	 * @property extensionStatesByName
	 * @static
	 * extension states that trigger update
	 *
	 */
	extensionStatesByName : {
		'attach' : true,
		'detach' : true
	},

	/**
	 * @property {Object} eventHandlers
	 * @static
	 */
	eventHandlers : {

		/**
		 * @method ExtensionUpdatedEvent
		 */
		'userinterface.ExtensionUpdatedEvent' : function(event) {

			var extension = event.getExtension();
			if(!extension) {
				return;
			}
			var extensionName = extension.getName();
			if(!extensionName) {
				return;
			}
			if(!this.extensionsByName[extensionName]) {
				return;
			}
			var extensionViewState = event.getViewState();

			if(!this.extensionStatesByName[extensionViewState]) {
				return;
			}

			this.updateBackendStatus();
		},

		/**
		 * @method AfterShowMapLayerInfoEvent
		 */

		'AfterShowMapLayerInfoEvent' : function(event) {

			var mapLayer = event.getMapLayer();
			var mapLayerId = mapLayer.getId();
			var mapLayerBackendStatus = mapLayer.getBackendStatus();
			console.log("ABOUT to show information for "+mapLayerId,mapLayer,mapLayerBackendStatus);

			if( !mapLayerBackendStatus ) {
				return;
			}

			var backendExtendedStatusForLayer =
				this.backendExtendedStatus[mapLayerId];

			console.log("MIGHT show information for "+mapLayerId,mapLayer,backendExtendedStatusForLayer);

			if( !backendExtendedStatusForLayer) {
				return;
			}

			var infoUrl = backendExtendedStatusForLayer.infourl;
			if( !infoUrl ) {
				return;
			}

			console.log("WOULD show information for "+mapLayerId,mapLayer,infoUrl);

			this.openURLinWindow(infoUrl);
		}
	},

	openURLinWindow: function(infoUrl) {
		var wopParm = "location=1," + "status=1," + "scrollbars=1," + "width=850," + "height=1200";
		var link = infoUrl;
		window.open(link, "BackendStatus", wopParm);
	},

	/**
	 * @method stop
	 * implements BundleInstance protocol stop method
	 */
	"stop" : function() {

		var me = this;
		var sandbox = me._sandbox;

		me._cancelAjaxRequest();

		for(p in me.eventHandlers) {
			sandbox.unregisterFromEventByName(this, p);
		}

		sandbox.unregister(this);
		this._started = false;
		this._sandbox = null;
	},
	/**
	 * @method getTitle
	 * @return {String} localized text for the title of the component
	 */
	getTitle : function() {
		return 'Backend Status';
	},
	/**
	 * @method getDescription
	 * @return {String} localized text for the description of the component
	 */
	getDescription : function() {
		return 'Backend Status';
	},
	_cancelAjaxRequest : function() {
		var me = this;
		if(!me._pendingAjaxQuery.busy) {
			return;
		}
		var jqhr = me._pendingAjaxQuery.jqhr;
		me._pendingAjaxQuery.jqhr = null;
		if(!jqhr) {
			return;
		}
		this._sandbox.printDebug("[BackendStatus] Abort jqhr ajax request");
		jqhr.abort();
		jqhr = null;
		me._pendingAjaxQuery.busy = false;
	},
	_startAjaxRequest : function(dteMs) {
		var me = this;
		me._pendingAjaxQuery.busy = true;
		me._pendingAjaxQuery.timestamp = dteMs;

	},
	_finishAjaxRequest : function() {
		var me = this;
		me._pendingAjaxQuery.busy = false;
		me._pendingAjaxQuery.jqhr = null;
		this._sandbox.printDebug("[BackendStatus] finished jqhr ajax request");
	},
	_notifyAjaxFailure : function() {
		var me = this;
		me._sandbox.printDebug("[BackendStatus] BackendStatus AJAX failed");
		me._processResponse({ backendstatus: []});
	},
	_isAjaxRequestBusy : function() {
		var me = this;
		return me._pendingAjaxQuery.busy;
	},
	updateBackendStatus : function() {
		var me = this;
		var sandbox = me._sandbox;
		if(me._pendingAjaxQuery.busy) {
			sandbox.printDebug("[BackendStatus] updateBackendStatus NOT SENT previous query is busy");
			return;
		}
		var dte = new Date();
		var dteMs = dte.getTime();

		if(me._pendingAjaxQuery.timestamp && dteMs - me._pendingAjaxQuery.timestamp < me.timeInterval) {
			sandbox.printDebug("[BackendStatus] updateBackendStatus NOT SENT (time difference < " + me.timeInterval + "ms)");
			return;
		}

		me._cancelAjaxRequest();
		me._startAjaxRequest(dteMs);

		var ajaxUrl = me.getAjaxUrl();

		jQuery.ajax({
			beforeSend : function(x) {
				me._pendingAjaxQuery.jqhr = x;
				if(x && x.overrideMimeType) {
					x.overrideMimeType("application/j-son;charset=UTF-8");
				}
			},
			success : function(resp) {
				me._finishAjaxRequest();
				me._processResponse(resp);
			},
			error : function() {
				me._finishAjaxRequest();
				me._notifyAjaxFailure();
			},
			always : function() {
				me._finishAjaxRequest();
			},
			complete : function() {
				me._finishAjaxRequest();
			},
			data : {
			},
			type : 'POST',
			dataType : 'json',
			url : ajaxUrl
			/*url : 'GetBackendStatus.json'*/
		});
	},
	_processResponse : function(resp) {
		var sandbox = this._sandbox;
		var evtBuilder = sandbox.getEventBuilder('MapLayerEvent');

		var backendStatusArr = resp.backendstatus;
		if(!backendStatusArr || backendStatusArr.length == undefined ) {
			sandbox.printDebug("[BackendStatus] backendStatus NO data");
			return;
		}

		var changeNotifications = {};
		var extendedStatuses = {};

		for(var n = 0; n < backendStatusArr.length; n++) {
			var data = backendStatusArr[n];
			var layerId = data.maplayer_id;
			if( !this.backendStatus[layerId] ) {
				changeNotifications[layerId] = {
						status: data.status };
				extendedStatuses[layerId] = data;
				/*sandbox.printDebug("[BackendStatus] "+layerId+" new alert");*/
			} else if( this.backendStatus[layerId].status != data.status ) {
				changeNotifications[layerId] = { status: data.status };
				extendedStatuses[layerId] = data;
				/*sandbox.printDebug("[BackendStatus] "+layerId+" changed alert");*/
			}
		}

		for( p in this.backendStatus ) {
			if( !changeNotifications[p] && this.backendStatus[p].status != null ) {
				changeNotifications[p] = { status: null };
				/*sandbox.printDebug("[BackendStatus] "+layerId+" alert closed");*/
			}
		}

		this.backendExtendedStatus = extendedStatuses;

		var maplayers = {};

		for( p in changeNotifications ) {
			this.backendStatus[p] = changeNotifications[p];

			var maplayer = sandbox.findMapLayerFromAllAvailable(p);
			if (!maplayer) {
				continue;
			}
			maplayers[p] = maplayer;
			maplayer.setBackendStatus(this.backendStatus[p].status);
		}

		for( p in maplayers ) {
			var evt = evtBuilder(p,'update');
			sandbox.notifyAll(evt);
		}
	}
}, {
	/**
	 * @property {String[]} protocol
	 * @static
	 */
	"protocol" : ["Oskari.bundle.BundleInstance", 'Oskari.mapframework.module.Module']
});
<commit_msg>[PORTTISK-1131] removed console.log to enable IE8 to work<commit_after>/**
 * @class Oskari.mapframework.bundle.backendstatus.BackendStatusBundleInstance
 *
 * Utility bundle to manage updating backend status information.
 * Updates information only when LayerSelector2 is being opened.
 *
 */
Oskari.clazz.define("Oskari.mapframework.bundle.backendstatus.BackendStatusBundleInstance",

/**
 * @method create called automatically on construction
 * @static
 */
function() {
	this._sandbox = null;
	this._started = false;
	this._pendingAjaxQuery = {
		busy : false,
		jqhr : null,
		timestamp : null
	};
	this.timeInterval = this.ajaxSettings.defaultTimeThreshold;
	this.backendStatus = {};
	this.backendExtendedStatus = {};
}, {
	ajaxSettings: {
		defaultTimeThreshold: 30000
	},
	/**
	 * @static
	 * @property __name
	 */
	__name : 'BackendStatus',
	/**
	 * @method getName
	 * @return {String} the name for the component
	 */
	"getName" : function() {
		return this.__name;
	},
	/**
	 * @method setSandbox
	 * @param {Oskari.mapframework.sandbox.Sandbox} sandbox
	 * Sets the sandbox reference to this component
	 */
	setSandbox : function(sandbox) {
		this._sandbox = sandbox;
	},
	/**
	 * @method getSandbox
	 * @return {Oskari.mapframework.sandbox.Sandbox}
	 */
	getSandbox : function() {
		return this._sandbox;
	},

	getAjaxUrl: function(key) {
		var ajaxUrl = this.getSandbox().getAjaxUrl();
		var url = ajaxUrl + 'action_route=GetBackendStatus';

		return url;
		/*return 'GetBackendStatus.json';*/
	},

	/**
	 * @method start
	 * implements BundleInstance protocol start method
	 */
	"start" : function() {
		var me = this;

		if(me._started) {
			return;
		}

		me._started = true;

		var sandbox = Oskari.$("sandbox");
		me._sandbox = sandbox;

		sandbox.register(me);
		for(p in me.eventHandlers) {
			sandbox.registerForEventByName(me, p);
		}
	},
	/**
	 * @method init
	 * implements Module protocol init method - does nothing atm
	 */
	"init" : function() {
		return null;
	},
	/**
	 * @method update
	 * implements BundleInstance protocol update method - does nothing atm
	 */
	"update" : function() {

	},
	/**
	 * @method onEvent
	 * @param {Oskari.mapframework.event.Event} event a Oskari event object
	 * Event is handled forwarded to correct #eventHandlers if found or discarded if not.
	 */
	onEvent : function(event) {

		var handler = this.eventHandlers[event.getName()];
		if(!handler)
			return;

		return handler.apply(this, [event]);

	},

	/**
	 * @property extensionsByName
	 * @static
	 * extensions that trigger update
	 *
	 */
	extensionsByName : {
		'LayerSelector' : true
	},

	/**
	 * @property extensionStatesByName
	 * @static
	 * extension states that trigger update
	 *
	 */
	extensionStatesByName : {
		'attach' : true,
		'detach' : true
	},

	/**
	 * @property {Object} eventHandlers
	 * @static
	 */
	eventHandlers : {

		/**
		 * @method ExtensionUpdatedEvent
		 */
		'userinterface.ExtensionUpdatedEvent' : function(event) {

			var extension = event.getExtension();
			if(!extension) {
				return;
			}
			var extensionName = extension.getName();
			if(!extensionName) {
				return;
			}
			if(!this.extensionsByName[extensionName]) {
				return;
			}
			var extensionViewState = event.getViewState();

			if(!this.extensionStatesByName[extensionViewState]) {
				return;
			}

			this.updateBackendStatus();
		},

		/**
		 * @method AfterShowMapLayerInfoEvent
		 */

		'AfterShowMapLayerInfoEvent' : function(event) {

			var mapLayer = event.getMapLayer();
			var mapLayerId = mapLayer.getId();
			var mapLayerBackendStatus = mapLayer.getBackendStatus();
			/*console.log("ABOUT to show information for "+mapLayerId,mapLayer,mapLayerBackendStatus);*/

			if( !mapLayerBackendStatus ) {
				return;
			}

			var backendExtendedStatusForLayer =
				this.backendExtendedStatus[mapLayerId];

			/*console.log("MIGHT show information for "+mapLayerId,mapLayer,backendExtendedStatusForLayer);*/

			if( !backendExtendedStatusForLayer) {
				return;
			}

			var infoUrl = backendExtendedStatusForLayer.infourl;
			if( !infoUrl ) {
				return;
			}

			/*console.log("WOULD show information for "+mapLayerId,mapLayer,infoUrl);*/

			this.openURLinWindow(infoUrl);
		}
	},

	openURLinWindow: function(infoUrl) {
		var wopParm = "location=1," + "status=1," + "scrollbars=1," + "width=850," + "height=1200";
		var link = infoUrl;
		window.open(link, "BackendStatus", wopParm);
	},

	/**
	 * @method stop
	 * implements BundleInstance protocol stop method
	 */
	"stop" : function() {

		var me = this;
		var sandbox = me._sandbox;

		me._cancelAjaxRequest();

		for(p in me.eventHandlers) {
			sandbox.unregisterFromEventByName(this, p);
		}

		sandbox.unregister(this);
		this._started = false;
		this._sandbox = null;
	},
	/**
	 * @method getTitle
	 * @return {String} localized text for the title of the component
	 */
	getTitle : function() {
		return 'Backend Status';
	},
	/**
	 * @method getDescription
	 * @return {String} localized text for the description of the component
	 */
	getDescription : function() {
		return 'Backend Status';
	},
	_cancelAjaxRequest : function() {
		var me = this;
		if(!me._pendingAjaxQuery.busy) {
			return;
		}
		var jqhr = me._pendingAjaxQuery.jqhr;
		me._pendingAjaxQuery.jqhr = null;
		if(!jqhr) {
			return;
		}
		this._sandbox.printDebug("[BackendStatus] Abort jqhr ajax request");
		jqhr.abort();
		jqhr = null;
		me._pendingAjaxQuery.busy = false;
	},
	_startAjaxRequest : function(dteMs) {
		var me = this;
		me._pendingAjaxQuery.busy = true;
		me._pendingAjaxQuery.timestamp = dteMs;

	},
	_finishAjaxRequest : function() {
		var me = this;
		me._pendingAjaxQuery.busy = false;
		me._pendingAjaxQuery.jqhr = null;
		this._sandbox.printDebug("[BackendStatus] finished jqhr ajax request");
	},
	_notifyAjaxFailure : function() {
		var me = this;
		me._sandbox.printDebug("[BackendStatus] BackendStatus AJAX failed");
		me._processResponse({ backendstatus: []});
	},
	_isAjaxRequestBusy : function() {
		var me = this;
		return me._pendingAjaxQuery.busy;
	},
	updateBackendStatus : function() {
		var me = this;
		var sandbox = me._sandbox;
		if(me._pendingAjaxQuery.busy) {
			sandbox.printDebug("[BackendStatus] updateBackendStatus NOT SENT previous query is busy");
			return;
		}
		var dte = new Date();
		var dteMs = dte.getTime();

		if(me._pendingAjaxQuery.timestamp && dteMs - me._pendingAjaxQuery.timestamp < me.timeInterval) {
			sandbox.printDebug("[BackendStatus] updateBackendStatus NOT SENT (time difference < " + me.timeInterval + "ms)");
			return;
		}

		me._cancelAjaxRequest();
		me._startAjaxRequest(dteMs);

		var ajaxUrl = me.getAjaxUrl();

		jQuery.ajax({
			beforeSend : function(x) {
				me._pendingAjaxQuery.jqhr = x;
				if(x && x.overrideMimeType) {
					x.overrideMimeType("application/j-son;charset=UTF-8");
				}
			},
			success : function(resp) {
				me._finishAjaxRequest();
				me._processResponse(resp);
			},
			error : function() {
				me._finishAjaxRequest();
				me._notifyAjaxFailure();
			},
			always : function() {
				me._finishAjaxRequest();
			},
			complete : function() {
				me._finishAjaxRequest();
			},
			data : {
			},
			type : 'POST',
			dataType : 'json',
			url : ajaxUrl
			/*url : 'GetBackendStatus.json'*/
		});
	},
	_processResponse : function(resp) {
		var sandbox = this._sandbox;
		var evtBuilder = sandbox.getEventBuilder('MapLayerEvent');

		var backendStatusArr = resp.backendstatus;
		if(!backendStatusArr || backendStatusArr.length == undefined ) {
			sandbox.printDebug("[BackendStatus] backendStatus NO data");
			return;
		}

		var changeNotifications = {};
		var extendedStatuses = {};

		for(var n = 0; n < backendStatusArr.length; n++) {
			var data = backendStatusArr[n];
			var layerId = data.maplayer_id;
			if( !this.backendStatus[layerId] ) {
				changeNotifications[layerId] = {
						status: data.status };
				extendedStatuses[layerId] = data;
				/*sandbox.printDebug("[BackendStatus] "+layerId+" new alert");*/
			} else if( this.backendStatus[layerId].status != data.status ) {
				changeNotifications[layerId] = { status: data.status };
				extendedStatuses[layerId] = data;
				/*sandbox.printDebug("[BackendStatus] "+layerId+" changed alert");*/
			}
		}

		for( p in this.backendStatus ) {
			if( !changeNotifications[p] && this.backendStatus[p].status != null ) {
				changeNotifications[p] = { status: null };
				/*sandbox.printDebug("[BackendStatus] "+layerId+" alert closed");*/
			}
		}

		this.backendExtendedStatus = extendedStatuses;

		var maplayers = {};

		for( p in changeNotifications ) {
			this.backendStatus[p] = changeNotifications[p];

			var maplayer = sandbox.findMapLayerFromAllAvailable(p);
			if (!maplayer) {
				continue;
			}
			maplayers[p] = maplayer;
			maplayer.setBackendStatus(this.backendStatus[p].status);
		}

		for( p in maplayers ) {
			var evt = evtBuilder(p,'update');
			sandbox.notifyAll(evt);
		}
	}
}, {
	/**
	 * @property {String[]} protocol
	 * @static
	 */
	"protocol" : ["Oskari.bundle.BundleInstance", 'Oskari.mapframework.module.Module']
});
<|endoftext|>


<commit_before>// function includeJavaScriptFile(path) {{{
//
// Creates a JavaScript <script> element and appends it
// to the <head>.
//
function includeJavaScriptFile(path) {
  // Ensure that we have a non-empty string.'
  if ((typeof(path) != 'string') || (!path))
    {return;}

  // Ensure that we can find the <head>.
  var head = jQuery('head');
  if (!head)
    {return;}

  var element   = document.createElement('script');
  element.type  = 'text/javascript';
  element.src   = path;

  head.append(element);

  return true;
} // }}}

// function buildTheDashboard() {{{
//
// Determines which dashboard widgets to build,
// builds them, and adds them to the DOM.
//
function buildTheDashboard() {
  var parsed_widgets = jQuery.parseJSON(json_widgets);

  jQuery.each(parsed_widgets, function(index, widget) {
    if (widget.type == 'number')
      {dashboard.addNumberWidget(widget);}
    else if (widget.type == 'short_messages')
      {dashboard.addShortMessagesWidget(widget);}
    else if (widget.type == 'line_graph')
      {dashboard.addLineGraphWidget(widget);}
  });
} // }}}

function createUpdateTimerFor(widget) { // {{{
  $(this).everyTime(widget.update_interval, widget.name, function() {
    widget.updateData();
  });
} // }}}

// Begin Dashboard class. {{{
var Dashboard = new JS.Class({
  initialize: function(options) { // {{{
    this.basePath             = options.basePath;

    this.numberWidgets        = new Array();
    this.shortMessagesWidgets = new Array();
    this.lineGraphWidgets     = new Array();

    this.div                      = jQuery('#dashboard');
    this.numberWidgetsDiv         = this.div.find('.dashboard-numbers');
    this.shortMessagesWidgetsDiv  = this.div.find('.dashboard-short-messages');
    this.lineGraphWidgetsDiv      = this.div.find('.dashboard-line-graphs');
  }, // }}}

  addNumberWidget: function(options) { // {{{
    var widget      = new NumberWidget(options);
    var templateDiv = this.numberWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('number-widget')
      .attr('id', widget.divID())
      .appendTo(this.numberWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    widget.setDataDivTo(widgetDiv.find('.widget-data'));
    widget.updateData();

    createUpdateTimerFor(widget);

    this.numberWidgets.push(widget);

    widgetDiv.show();
  }, // }}}

  addShortMessagesWidget: function(options) { // {{{
    var widget      = new ShortMessagesWidget(options);
    var templateDiv = this.shortMessagesWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('short-messages-widget')
      .attr('id', widget.divID())
      .appendTo(this.shortMessagesWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    widget.setDataDivTo(widgetDiv.find('.widget-data'));
    widget.updateData();

    createUpdateTimerFor(widget);

    this.shortMessagesWidgets.push(widget);

    widgetDiv.show();
  }, // }}}

  addLineGraphWidget: function(options) { // {{{
    var widget      = new LineGraphWidget(options);
    var templateDiv = this.lineGraphWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('line-graph-widget')
      .attr('id', widget.divID())
      .appendTo(this.lineGraphWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    var dataDiv = widgetDiv.find('.widget-data')
      .attr('id', widget.divID() + '-data');

    widget.setDataDivTo(dataDiv);

    widget.updateData();

    // I don't know why, but setting these properties when dataDiv
    // is initialized fails to work. They work here, though...
    dataDiv.css('height',  widget.height)
    dataDiv.css('width',   widget.width)

    createUpdateTimerFor(widget);

    this.lineGraphWidgets.push(widget);

    widgetDiv.show();
  } // }}}
});
// End Dashboard class. }}}

// Begin DashboardWidget class. {{{
var DashboardWidget = new JS.Class({
  initialize: function(options) {
    this.type             = options.type;
    this.name             = options.name;
    this.title            = options.title;
    this.update_interval  = options.update_interval;
  },

  dataURL: function() {
    return dashboard.basePath + this.name;
  },

  divID: function() {
    return this.name + '-' + this.type + '-widget';
  },

  setDataDivTo: function(element) {
    this.dataDiv = element;
  }
});
// End DashboardWidget class. }}}

// Begin NumberWidget class. {{{
var NumberWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    options['type'] = 'number';
    this.callSuper(options);
  },

  updateData: function() {
    if (!this.dataDiv)
      {return false;}

    // This is needed so that the dataDiv property is accessible with jQuery.get() .
    var element = this.dataDiv;

    jQuery.get(this.dataURL(), function(data) {
      element.fadeOut(400, function() {
        element.html(data);
        element.fadeIn();
      });
    }, 'text');
  }
});
// End NumberWidget class. }}}

// Begin ShortMessagesWidget class. {{{
var ShortMessagesWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    options['type'] = 'short_messages';
    this.callSuper(options);
    this.maxDataItems = 5;
  },

  firstDataItem: function() {
    return this.dataDiv.find('li.widget-data-item:first');
  },

  dataItemsCount: function() {
    return this.dataDiv.find('li.widget-data-item').length;
  },

  createDataItem: function() {
    return this.dataDiv.find('li.widget-data-template')
      .clone(false)
      .removeClass('widget-data-template')
      .addClass('widget-data-item');
  },

  updateData: function() {
    if (!this.dataDiv)
      {return false;}

    var new_data  = '';
    var new_li    = this.createDataItem();

    // We use ajax() instead of get() because the "async" option must
    // be false. If it isn't, we're unable to determine if data was
    // obtained.
    var get_result = jQuery.ajax({
      url:      this.dataURL(),
      type:     'GET',
      async:    false,
      cache:    false,
      dataType: 'text',
      timeout:  this.updateInterval,
      success:  function(data) {
        new_li.html(data);
        new_data = data;
      }
    });

    // If no data was obtained, return. Otherwise, an empty list item
    // will be shown.
    if (new_data == '')
      {return false;}

    new_li.appendTo(this.dataDiv.find('ul'));

    // Hide the first list item if we've reached the maximum number of
    // list items to show in this widget.
    if (this.dataItemsCount() > this.maxDataItems) {
      var firstDataItem = this.firstDataItem();

      firstDataItem.slideUp(400, function() {
        firstDataItem.remove();
      });
    }

    new_li.slideDown();
  }
});
// End ShortMessagesWidget class. }}}

// Begin LineGraphWidget class. {{{
var LineGraphWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    this.height         = options.height;
    this.width          = options.width;
    this.seriesColours  = options.line_colours;

    options.type  = 'line_graph';
    this.callSuper(options);
  },

  updateData: function() {
    if (!this.graph)
      {this.createGraph();}

    // This is needed so that the dataDiv property is accessible with jQuery.get() .
    var graph = this.graph;

    jQuery.get(this.dataURL(), function(data) {
      graph.series[0].data = data;
      graph.replot({resetAxes: true});
    }, 'json');
  },

  createGraph: function() {
    this.graph = jQuery.jqplot(this.dataDiv.attr('id'), [ [] ], {
      title:        this.title,
      seriesColors: this.seriesColours
    });
  }
});
// End LineGraphWidget class. }}}

<commit_msg>The x-axis can be formatted as dates now.<commit_after>// function includeJavaScriptFile(path) {{{
//
// Creates a JavaScript <script> element and appends it
// to the <head>.
//
function includeJavaScriptFile(path) {
  // Ensure that we have a non-empty string.'
  if ((typeof(path) != 'string') || (!path))
    {return;}

  // Ensure that we can find the <head>.
  var head = jQuery('head');
  if (!head)
    {return;}

  var element   = document.createElement('script');
  element.type  = 'text/javascript';
  element.src   = path;

  head.append(element);

  return true;
} // }}}

// function buildTheDashboard() {{{
//
// Determines which dashboard widgets to build,
// builds them, and adds them to the DOM.
//
function buildTheDashboard() {
  var parsed_widgets = jQuery.parseJSON(json_widgets);

  jQuery.each(parsed_widgets, function(index, widget) {
    if (widget.type == 'number')
      {dashboard.addNumberWidget(widget);}
    else if (widget.type == 'short_messages')
      {dashboard.addShortMessagesWidget(widget);}
    else if (widget.type == 'line_graph')
      {dashboard.addLineGraphWidget(widget);}
  });
} // }}}

function createUpdateTimerFor(widget) { // {{{
  $(this).everyTime(widget.update_interval, widget.name, function() {
    widget.updateData();
  });
} // }}}

// Begin Dashboard class. {{{
var Dashboard = new JS.Class({
  initialize: function(options) { // {{{
    this.basePath             = options.basePath;

    this.numberWidgets        = new Array();
    this.shortMessagesWidgets = new Array();
    this.lineGraphWidgets     = new Array();

    this.div                      = jQuery('#dashboard');
    this.numberWidgetsDiv         = this.div.find('.dashboard-numbers');
    this.shortMessagesWidgetsDiv  = this.div.find('.dashboard-short-messages');
    this.lineGraphWidgetsDiv      = this.div.find('.dashboard-line-graphs');
  }, // }}}

  addNumberWidget: function(options) { // {{{
    var widget      = new NumberWidget(options);
    var templateDiv = this.numberWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('number-widget')
      .attr('id', widget.divID())
      .appendTo(this.numberWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    widget.setDataDivTo(widgetDiv.find('.widget-data'));
    widget.updateData();

    createUpdateTimerFor(widget);

    this.numberWidgets.push(widget);

    widgetDiv.show();
  }, // }}}

  addShortMessagesWidget: function(options) { // {{{
    var widget      = new ShortMessagesWidget(options);
    var templateDiv = this.shortMessagesWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('short-messages-widget')
      .attr('id', widget.divID())
      .appendTo(this.shortMessagesWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    widget.setDataDivTo(widgetDiv.find('.widget-data'));
    widget.updateData();

    createUpdateTimerFor(widget);

    this.shortMessagesWidgets.push(widget);

    widgetDiv.show();
  }, // }}}

  addLineGraphWidget: function(options) { // {{{
    var widget      = new LineGraphWidget(options);
    var templateDiv = this.lineGraphWidgetsDiv.find('.widget-template');

    var widgetDiv   = templateDiv.clone(false)
      .removeClass('widget-template')
      .addClass('line-graph-widget')
      .attr('id', widget.divID())
      .appendTo(this.lineGraphWidgetsDiv);

    // Set the new div's title.
    widgetDiv.find('.widget-title').html(widget.title);

    var dataDiv = widgetDiv.find('.widget-data')
      .attr('id', widget.divID() + '-data');

    widget.setDataDivTo(dataDiv);

    widget.updateData();

    // I don't know why, but setting these properties when dataDiv
    // is initialized fails to work. They work here, though...
    dataDiv.css('height',  widget.height)
    dataDiv.css('width',   widget.width)

    createUpdateTimerFor(widget);

    this.lineGraphWidgets.push(widget);

    widgetDiv.show();
  } // }}}
});
// End Dashboard class. }}}

// Begin DashboardWidget class. {{{
var DashboardWidget = new JS.Class({
  initialize: function(options) {
    this.type             = options.type;
    this.name             = options.name;
    this.title            = options.title;
    this.update_interval  = options.update_interval;
  },

  dataURL: function() {
    return dashboard.basePath + this.name;
  },

  divID: function() {
    return this.name + '-' + this.type + '-widget';
  },

  setDataDivTo: function(element) {
    this.dataDiv = element;
  }
});
// End DashboardWidget class. }}}

// Begin NumberWidget class. {{{
var NumberWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    options['type'] = 'number';
    this.callSuper(options);
  },

  updateData: function() {
    if (!this.dataDiv)
      {return false;}

    // This is needed so that the dataDiv property is accessible with jQuery.get() .
    var element = this.dataDiv;

    jQuery.get(this.dataURL(), function(data) {
      element.fadeOut(400, function() {
        element.html(data);
        element.fadeIn();
      });
    }, 'text');
  }
});
// End NumberWidget class. }}}

// Begin ShortMessagesWidget class. {{{
var ShortMessagesWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    options['type'] = 'short_messages';
    this.callSuper(options);
    this.maxDataItems = 5;
  },

  firstDataItem: function() {
    return this.dataDiv.find('li.widget-data-item:first');
  },

  dataItemsCount: function() {
    return this.dataDiv.find('li.widget-data-item').length;
  },

  createDataItem: function() {
    return this.dataDiv.find('li.widget-data-template')
      .clone(false)
      .removeClass('widget-data-template')
      .addClass('widget-data-item');
  },

  updateData: function() {
    if (!this.dataDiv)
      {return false;}

    var new_data  = '';
    var new_li    = this.createDataItem();

    // We use ajax() instead of get() because the "async" option must
    // be false. If it isn't, we're unable to determine if data was
    // obtained.
    var get_result = jQuery.ajax({
      url:      this.dataURL(),
      type:     'GET',
      async:    false,
      cache:    false,
      dataType: 'text',
      timeout:  this.updateInterval,
      success:  function(data) {
        new_li.html(data);
        new_data = data;
      }
    });

    // If no data was obtained, return. Otherwise, an empty list item
    // will be shown.
    if (new_data == '')
      {return false;}

    new_li.appendTo(this.dataDiv.find('ul'));

    // Hide the first list item if we've reached the maximum number of
    // list items to show in this widget.
    if (this.dataItemsCount() > this.maxDataItems) {
      var firstDataItem = this.firstDataItem();

      firstDataItem.slideUp(400, function() {
        firstDataItem.remove();
      });
    }

    new_li.slideDown();
  }
});
// End ShortMessagesWidget class. }}}

// Begin LineGraphWidget class. {{{
var LineGraphWidget = new JS.Class(DashboardWidget, {
  initialize: function(options) {
    this.height         = options.height;
    this.width          = options.width;
    this.seriesColours  = options.line_colours;
    this.x_axis         = options.x_axis;

    options.type  = 'line_graph';
    this.callSuper(options);
  },

  updateData: function() {
    if (!this.graph)
      {this.createGraph();}

    // This is needed so that the dataDiv property is accessible with jQuery.get() .
    var graph = this.graph;

    jQuery.get(this.dataURL(), function(data) {
      graph.series[0].data = data;
      graph.replot({resetAxes: true});
    }, 'json');
  },

  createGraph: function() {
    includeJavaScriptFile('/javascripts/jqplot-0.9.7/plugins/jqplot.dateAxisRenderer.min.js');

    var x_axis_format = {};
    if (this.x_axis === 'dates') {
      var x_axis_format = {
        renderer:     jQuery.jqplot.DateAxisRenderer,
        tickOptions:  {formatString: '%Y-%m-%d'}
      }
    }

    this.graph = jQuery.jqplot(this.dataDiv.attr('id'), [ [] ], {
      title:        this.title,
      seriesColors: this.seriesColours,
      axes:         {
                      xaxis: x_axis_format
                    }
    });
  }
});
// End LineGraphWidget class. }}}

<|endoftext|>


<commit_before>import Widget from "./base_widget.js";
import { generate_route } from "./utils";

export default class OnboardingWidget extends Widget {
	constructor(opts) {
		super(opts);
	}

	make_body() {
		this.steps.forEach((step) => {
			this.add_step(step);
		});
	}

	add_step(step) {
		// Make Step
		let status = "";
		let icon_class = "fa-circle-o";

		if (step.is_skipped) {
			status = "skipped";
			icon_class = "fa-check-circle-o";
		}

		if (step.is_complete) {
			status = "complete";
			icon_class = "fa-check-circle-o";
		}

		let $step = $(`

				${step.title}

`);

		step.$step = $step;

		// Add skip button
		if (!step.is_mandatory && !step.is_complete) {
			let skip_html = $(
				`<span class="ml-5 small text-muted step-skip">Skip</span>`
			);

			skip_html.appendTo($step);
			skip_html.on("click", () => {
				this.skip_step(step);
				event.stopPropagation();
			});
		}

		// Setup actions
		let actions = {
			"Watch Video": () => this.show_video(step),
			"Create Entry": () => {
				if (step.is_complete) {
					frappe.set_route(`#List/${step.reference_document}`);
				} else {
					if (step.show_full_form) {
						this.create_entry(step);
					} else {
						this.show_quick_entry(step);
					}
				}
			},
			"Show Form Tour": () => this.show_form_tour(step),
			"Update Settings": () => this.update_settings(step),
			"View Report": () => this.open_report(step),
			"Go to Page": () => this.go_to_page(step),
		};

		$step.find("#title").on("click", actions[step.action]);

		$step.appendTo(this.body);
		return $step;
	}

	go_to_page(step) {
		frappe.set_route(step.path).then(() => {
			if (step.callback_message) {
				let msg_dialog = frappe.msgprint({
					message: __(step.callback_message),
					title: __(step.callback_title),
					primary_action: {
						action: () => {
							msg_dialog.hide();
						},
						label: () => __("Continue"),
					},
					wide: true,
				});
			}
		});
	}

	open_report(step) {
		let route = generate_route({
			name: step.reference_report,
			type: "report",
			is_query_report: ["Query Report", "Script Report"].includes(
				step.report_type
			),
			doctype: step.report_reference_doctype,
		});

		let current_route = frappe.get_route();

		frappe.set_route(route).then(() => {
			let msg_dialog = frappe.msgprint({
				message: __(step.report_description),
				title: __(step.reference_report),
				primary_action: {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
						msg_dialog.hide();
					},
					label: () => __("Continue"),
				},
				secondary_action: {
					action: () => {
						msg_dialog.hide();
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Go Back"),
				},
			});

			frappe.msg_dialog.custom_onhide = () => this.mark_complete(step);
		});
	}

	show_form_tour(step) {
		let route;
		if (step.is_single) {
			route = `Form/${step.reference_document}`;
		} else {
			route = `Form/${step.reference_document}/${__('New')} ${__(step.reference_document)}`;
		}

		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		frappe.route_hooks.after_load = (frm) => {
			frm.show_tour(() => {
				let msg_dialog = frappe.msgprint({
					message: __("Let's take you back to onboarding"),
					title: __("Great Job"),
					primary_action: {
						action: () => {
							frappe.set_route(current_route).then(() => {
								this.mark_complete(step);
							});
							msg_dialog.hide();
						},
						label: () => __("Continue"),
					},
				});
			});
		};

		frappe.set_route(route);
	}

	update_settings(step) {
		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		frappe.route_hooks.after_load = (frm) => {
			frm.scroll_to_field(step.field);
			frm.doc.__unsaved = true;
		};

		frappe.route_hooks.after_save = (frm) => {
			let success = false;
			let args = {};

			let value = frm.doc[step.field];
			let custom_onhide = null;

			if (value && step.value_to_validate == "%") success = true;
			if (value == step.value_to_validate) success = true;
			if (cstr(value) == cstr(step.value_to_validate)) success = true;

			if (success) {
				args.message = __("Let's take you back to onboarding");
				args.title = __("Looks Great");
				args.primary_action = {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Continue"),
				};

				custom_onhide = () => args.primary_action.action();
			} else {
				args.message = __("Looks like you didn't change the value");
				args.title = __("Oops");
				args.secondary_action = {
					action: () => frappe.set_route(current_route),
					label: __("Go Back"),
				};

				if (!step.is_mandatory) {
					args.primary_action = {
						action: () => {
							frappe.set_route(current_route).then(() => {
								setTimeout(() => {
									this.skip_step(step);
								}, 300);
							});
						},
						label: __("Skip Step"),
					};
				}

				custom_onhide = () => args.secondary_action.action();
			}

			frappe.msgprint(args);
			frappe.msg_dialog.custom_onhide = () => custom_onhide();
		};

		frappe.set_route("Form", step.reference_document);
	}

	create_entry(step) {
		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		let callback = () => {
			frappe.msgprint({
				message: __("You're doing great, let's take you back to the onboarding page."),
				title: __("Good Work 🎉"),
				primary_action: {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Continue"),
				},
			});

			frappe.msg_dialog.custom_onhide = () => {
				this.mark_complete(step);
			};
		};

		if (step.is_submittable) {
			frappe.route_hooks.after_save = () => {
				frappe.msgprint({
					message: __("Submit this document to complete this step."),
					title: __("Great")
				});
			};
			frappe.route_hooks.after_submit = callback;
		} else {
			frappe.route_hooks.after_save = callback;
		}

		frappe.set_route(`Form/${step.reference_document}/${__('New')} ${__(step.reference_document)}`);
	}

	show_quick_entry(step) {
		let current_route = frappe.get_route_str();
		frappe.ui.form.make_quick_entry(
			step.reference_document,
			() => {
				if (frappe.get_route_str() != current_route) {
					let success_dialog = frappe.msgprint({
						message: __("Let's take you back to onboarding"),
						title: __("Looks Great"),
						primary_action: {
							action: () => {
								success_dialog.hide();
								frappe.set_route(current_route).then(() => {
									this.mark_complete(step);
								});
							},
							label: __("Continue"),
						},
					});

					frappe.msg_dialog.custom_onhide = () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					};
				} else {
					frappe.msgprint({
						message: __("You may continue with onboarding"),
						title: __("Looks Great")
					});
					this.mark_complete(step);
				}
			},
			null,
			null,
			true
		);
	}

	show_video(step) {
		frappe.help.show_video(step.video_url, step.title);
		this.mark_complete(step);
	}

	mark_complete(step) {
		let $step = step.$step;

		let callback = () => {
			step.is_complete = true;
			$step.removeClass("skipped");
			$step.addClass("complete");
		};

		this.update_step_status(step, "is_complete", 1, callback);
	}

	skip_step(step) {
		let $step = step.$step;

		let callback = () => {
			step.is_skipped = true;
			$step.removeClass("complete");
			$step.addClass("skipped");
		};

		this.update_step_status(step, "is_skipped", 1, callback);
	}

	update_step_status(step, status, value, callback) {
		let icon_class = {
			is_complete: "fa-check-circle-o",
			is_skipped: "fa-check-circle-o",
		};
		//  Clear any hooks
		frappe.route_hooks = {};

		frappe
			.call("frappe.desk.desktop.update_onboarding_step", {
				name: step.name,
				field: status,
				value: value,
			})
			.then(() => {
				callback();

				let icon = step.$step.find("i.fa");
				icon.removeClass();
				icon.addClass("fa");
				icon.addClass(icon_class[status]);

				let pending = this.steps.filter((step) => {
					return !(step.is_complete || step.is_skipped);
				});

				if (pending.length == 0) {
					this.show_success();
				}
			});
	}

	show_success() {
		let success_message = this.success || __("You seem good to go!");
		let success_state_image =
			this.success_state_image ||
			"/assets/frappe/images/ui-states/success-color.png";
		let documentation = "";
		if (this.docs_url) {
			documentation = __(
				'Congratulations on completing the module setup. If you want to learn more you can refer to the documentation here.',
				[this.docs_url]
			);
		}

		let success = $(`

${success_message}

${documentation}

		`);

		if (!this.success_dialog) {
			this.success_dialog = new frappe.ui.Dialog({
				primary_action: () => {
					this.success_dialog.hide();
					// Wait for modal to close before removing widget
					setTimeout(() => {
						this.delete();
					}, 300);
				},
				primary_action_label: __("Continue"),
			});

			this.success_dialog.set_title(__("Onboarding Complete"));
			this.success_dialog.header
				.find(".indicator")
				.removeClass("hidden")
				.addClass("green");

			success.appendTo(this.success_dialog.$body);
			this.success_dialog.show();
		}
	}

	set_body() {
		this.widget.addClass("onboarding-widget-box");
		if (this.is_dismissed()) {
			this.widget.hide();
		} else {
			this.make_body();
		}
	}

	is_dismissed() {
		let dismissed = JSON.parse(
			localStorage.getItem("dismissed-onboarding") || "{}"
		);
		if (Object.keys(dismissed).includes(this.label)) {
			let last_hidden = new Date(dismissed[this.label]);
			let today = new Date();
			let diff = frappe.datetime.get_hour_diff(today, last_hidden);
			return diff < 24;
		}
		return false;
	}

	set_title(title) {
		super.set_title(title);
		if (this.subtitle) {
			let subtitle = $(
				`<div class="widget-subtitle">${this.subtitle}</div>`
			);
			subtitle.appendTo(this.title_field);
		}
	}

	set_actions() {
		this.action_area.empty();
		const dismiss = $(
			`<div class="small" style="cursor:pointer;">Dismiss</div>`
		);
		dismiss.on("click", () => {
			let dismissed = JSON.parse(
				localStorage.getItem("dismissed-onboarding") || "{}"
			);
			dismissed[this.label] = frappe.datetime.now_datetime();

			localStorage.setItem(
				"dismissed-onboarding",
				JSON.stringify(dismissed)
			);
			this.delete();
		});
		dismiss.appendTo(this.action_area);
	}
}
<commit_msg>fix: route to new doc 1 explicitly<commit_after>import Widget from "./base_widget.js";
import { generate_route } from "./utils";

export default class OnboardingWidget extends Widget {
	constructor(opts) {
		super(opts);
	}

	make_body() {
		this.steps.forEach((step) => {
			this.add_step(step);
		});
	}

	add_step(step) {
		// Make Step
		let status = "";
		let icon_class = "fa-circle-o";

		if (step.is_skipped) {
			status = "skipped";
			icon_class = "fa-check-circle-o";
		}

		if (step.is_complete) {
			status = "complete";
			icon_class = "fa-check-circle-o";
		}

		let $step = $(`

				${step.title}

`);

		step.$step = $step;

		// Add skip button
		if (!step.is_mandatory && !step.is_complete) {
			let skip_html = $(
				`<span class="ml-5 small text-muted step-skip">Skip</span>`
			);

			skip_html.appendTo($step);
			skip_html.on("click", () => {
				this.skip_step(step);
				event.stopPropagation();
			});
		}

		// Setup actions
		let actions = {
			"Watch Video": () => this.show_video(step),
			"Create Entry": () => {
				if (step.is_complete) {
					frappe.set_route(`#List/${step.reference_document}`);
				} else {
					if (step.show_full_form) {
						this.create_entry(step);
					} else {
						this.show_quick_entry(step);
					}
				}
			},
			"Show Form Tour": () => this.show_form_tour(step),
			"Update Settings": () => this.update_settings(step),
			"View Report": () => this.open_report(step),
			"Go to Page": () => this.go_to_page(step),
		};

		$step.find("#title").on("click", actions[step.action]);

		$step.appendTo(this.body);
		return $step;
	}

	go_to_page(step) {
		frappe.set_route(step.path).then(() => {
			if (step.callback_message) {
				let msg_dialog = frappe.msgprint({
					message: __(step.callback_message),
					title: __(step.callback_title),
					primary_action: {
						action: () => {
							msg_dialog.hide();
						},
						label: () => __("Continue"),
					},
					wide: true,
				});
			}
		});
	}

	open_report(step) {
		let route = generate_route({
			name: step.reference_report,
			type: "report",
			is_query_report: ["Query Report", "Script Report"].includes(
				step.report_type
			),
			doctype: step.report_reference_doctype,
		});

		let current_route = frappe.get_route();

		frappe.set_route(route).then(() => {
			let msg_dialog = frappe.msgprint({
				message: __(step.report_description),
				title: __(step.reference_report),
				primary_action: {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
						msg_dialog.hide();
					},
					label: () => __("Continue"),
				},
				secondary_action: {
					action: () => {
						msg_dialog.hide();
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Go Back"),
				},
			});

			frappe.msg_dialog.custom_onhide = () => this.mark_complete(step);
		});
	}

	show_form_tour(step) {
		let route;
		if (step.is_single) {
			route = `Form/${step.reference_document}`;
		} else {
			route = `Form/${step.reference_document}/${__('New')} ${__(step.reference_document)} 1`;
		}

		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		frappe.route_hooks.after_load = (frm) => {
			frm.show_tour(() => {
				let msg_dialog = frappe.msgprint({
					message: __("Let's take you back to onboarding"),
					title: __("Great Job"),
					primary_action: {
						action: () => {
							frappe.set_route(current_route).then(() => {
								this.mark_complete(step);
							});
							msg_dialog.hide();
						},
						label: () => __("Continue"),
					},
				});
			});
		};

		frappe.set_route(route);
	}

	update_settings(step) {
		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		frappe.route_hooks.after_load = (frm) => {
			frm.scroll_to_field(step.field);
			frm.doc.__unsaved = true;
		};

		frappe.route_hooks.after_save = (frm) => {
			let success = false;
			let args = {};

			let value = frm.doc[step.field];
			let custom_onhide = null;

			if (value && step.value_to_validate == "%") success = true;
			if (value == step.value_to_validate) success = true;
			if (cstr(value) == cstr(step.value_to_validate)) success = true;

			if (success) {
				args.message = __("Let's take you back to onboarding");
				args.title = __("Looks Great");
				args.primary_action = {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Continue"),
				};

				custom_onhide = () => args.primary_action.action();
			} else {
				args.message = __("Looks like you didn't change the value");
				args.title = __("Oops");
				args.secondary_action = {
					action: () => frappe.set_route(current_route),
					label: __("Go Back"),
				};

				if (!step.is_mandatory) {
					args.primary_action = {
						action: () => {
							frappe.set_route(current_route).then(() => {
								setTimeout(() => {
									this.skip_step(step);
								}, 300);
							});
						},
						label: __("Skip Step"),
					};
				}

				custom_onhide = () => args.secondary_action.action();
			}

			frappe.msgprint(args);
			frappe.msg_dialog.custom_onhide = () => custom_onhide();
		};

		frappe.set_route("Form", step.reference_document);
	}

	create_entry(step) {
		let current_route = frappe.get_route();

		frappe.route_hooks = {};
		let callback = () => {
			frappe.msgprint({
				message: __("You're doing great, let's take you back to the onboarding page."),
				title: __("Good Work 🎉"),
				primary_action: {
					action: () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					},
					label: __("Continue"),
				},
			});

			frappe.msg_dialog.custom_onhide = () => {
				this.mark_complete(step);
			};
		};

		if (step.is_submittable) {
			frappe.route_hooks.after_save = () => {
				frappe.msgprint({
					message: __("Submit this document to complete this step."),
					title: __("Great")
				});
			};
			frappe.route_hooks.after_submit = callback;
		} else {
			frappe.route_hooks.after_save = callback;
		}

		frappe.set_route(`Form/${step.reference_document}/${__('New')} ${__(step.reference_document)} 1`);
	}

	show_quick_entry(step) {
		let current_route = frappe.get_route_str();
		frappe.ui.form.make_quick_entry(
			step.reference_document,
			() => {
				if (frappe.get_route_str() != current_route) {
					let success_dialog = frappe.msgprint({
						message: __("Let's take you back to onboarding"),
						title: __("Looks Great"),
						primary_action: {
							action: () => {
								success_dialog.hide();
								frappe.set_route(current_route).then(() => {
									this.mark_complete(step);
								});
							},
							label: __("Continue"),
						},
					});

					frappe.msg_dialog.custom_onhide = () => {
						frappe.set_route(current_route).then(() => {
							this.mark_complete(step);
						});
					};
				} else {
					frappe.msgprint({
						message: __("You may continue with onboarding"),
						title: __("Looks Great")
					});
					this.mark_complete(step);
				}
			},
			null,
			null,
			true
		);
	}

	show_video(step) {
		frappe.help.show_video(step.video_url, step.title);
		this.mark_complete(step);
	}

	mark_complete(step) {
		let $step = step.$step;

		let callback = () => {
			step.is_complete = true;
			$step.removeClass("skipped");
			$step.addClass("complete");
		};

		this.update_step_status(step, "is_complete", 1, callback);
	}

	skip_step(step) {
		let $step = step.$step;

		let callback = () => {
			step.is_skipped = true;
			$step.removeClass("complete");
			$step.addClass("skipped");
		};

		this.update_step_status(step, "is_skipped", 1, callback);
	}

	update_step_status(step, status, value, callback) {
		let icon_class = {
			is_complete: "fa-check-circle-o",
			is_skipped: "fa-check-circle-o",
		};
		//  Clear any hooks
		frappe.route_hooks = {};

		frappe
			.call("frappe.desk.desktop.update_onboarding_step", {
				name: step.name,
				field: status,
				value: value,
			})
			.then(() => {
				callback();

				let icon = step.$step.find("i.fa");
				icon.removeClass();
				icon.addClass("fa");
				icon.addClass(icon_class[status]);

				let pending = this.steps.filter((step) => {
					return !(step.is_complete || step.is_skipped);
				});

				if (pending.length == 0) {
					this.show_success();
				}
			});
	}

	show_success() {
		let success_message = this.success || __("You seem good to go!");
		let success_state_image =
			this.success_state_image ||
			"/assets/frappe/images/ui-states/success-color.png";
		let documentation = "";
		if (this.docs_url) {
			documentation = __(
				'Congratulations on completing the module setup. If you want to learn more you can refer to the documentation here.',
				[this.docs_url]
			);
		}

		let success = $(`

${success_message}

${documentation}

		`);

		if (!this.success_dialog) {
			this.success_dialog = new frappe.ui.Dialog({
				primary_action: () => {
					this.success_dialog.hide();
					// Wait for modal to close before removing widget
					setTimeout(() => {
						this.delete();
					}, 300);
				},
				primary_action_label: __("Continue"),
			});

			this.success_dialog.set_title(__("Onboarding Complete"));
			this.success_dialog.header
				.find(".indicator")
				.removeClass("hidden")
				.addClass("green");

			success.appendTo(this.success_dialog.$body);
			this.success_dialog.show();
		}
	}

	set_body() {
		this.widget.addClass("onboarding-widget-box");
		if (this.is_dismissed()) {
			this.widget.hide();
		} else {
			this.make_body();
		}
	}

	is_dismissed() {
		let dismissed = JSON.parse(
			localStorage.getItem("dismissed-onboarding") || "{}"
		);
		if (Object.keys(dismissed).includes(this.label)) {
			let last_hidden = new Date(dismissed[this.label]);
			let today = new Date();
			let diff = frappe.datetime.get_hour_diff(today, last_hidden);
			return diff < 24;
		}
		return false;
	}

	set_title(title) {
		super.set_title(title);
		if (this.subtitle) {
			let subtitle = $(
				`<div class="widget-subtitle">${this.subtitle}</div>`
			);
			subtitle.appendTo(this.title_field);
		}
	}

	set_actions() {
		this.action_area.empty();
		const dismiss = $(
			`<div class="small" style="cursor:pointer;">Dismiss</div>`
		);
		dismiss.on("click", () => {
			let dismissed = JSON.parse(
				localStorage.getItem("dismissed-onboarding") || "{}"
			);
			dismissed[this.label] = frappe.datetime.now_datetime();

			localStorage.setItem(
				"dismissed-onboarding",
				JSON.stringify(dismissed)
			);
			this.delete();
		});
		dismiss.appendTo(this.action_area);
	}
}
<|endoftext|>