<commit_before>dojo.provide("dijit.Dialog");

dojo.require("dojo.dnd.move");
dojo.require("dojo.fx");

dojo.require("dijit.util.place");
dojo.require("dijit.util.sniff");
dojo.require("dijit.util.popup");			// for BackgroundIFrame
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit._Templated");

dojo.declare(
	"dijit.DialogUnderlay",
	[dijit._Widget, dijit._Templated],
	{
		// summary: the thing that grays out the screen behind the dialog

		// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
		// Inner div has opacity specified in CSS file.
		templateString: "

",

		postCreate: function(){
			var b = dojo.body();
			b.appendChild(this.domNode);
			this.bgIframe = new dijit.util.BackgroundIframe(this.domNode);
		},

		layout: function(){
			// summary
			//		Sets the background to the size of the viewport (rather than the size
			//		of the document) since we need to cover the whole browser window, even
			//		if the document is only a few lines long.

			var viewport = dijit.util.getViewport();
			var is = this.node.style,
				os = this.domNode.style;

			os.top = viewport.t + "px";
			os.left = viewport.l + "px";
			is.width = viewport.w + "px";
			is.height = viewport.h + "px";

			// process twice since the scroll bar may have been removed
			// by the previous resizing
			var viewport2 = dijit.util.getViewport();
			if(viewport.w != viewport2.w){ is.width = viewport2.w + "px"; }
			if(viewport.h != viewport2.h){ is.height = viewport2.h + "px"; }
		},

		show: function(){
			this.domNode.style.display = "block";
			this.layout();
			if(this.bgIframe.iframe){
				this.bgIframe.iframe.style.display = "block";
			}
		},

		hide: function(){
			this.domNode.style.display = "none";
			this.domNode.style.width = this.domNode.style.height = "1px";
			if(this.bgIframe.iframe){
				this.bgIframe.iframe.style.display = "none";
			}
		},

		uninitialize: function(){
			if(this.bgIframe){
				this.bgIframe.destroy();
			}
		}
	}
);

dojo.declare(
	"dijit.Dialog",
	[dijit.layout.ContentPane, dijit._Templated],
	{
		// summary:
		//		Pops up a modal dialog window, blocking access to the screen
		//		and also graying out the screen Dialog is extended from
		//		ContentPane so it supports all the same parameters (href, etc.)

		templatePath: dojo.moduleUrl("dijit", "templates/Dialog.html"),

		// title: String
		//		Title of the dialog
		title: "",

		// closeNode: String
		//	Id of button or other dom node to click to close this dialog
		closeNode: "",

		_duration: 400,

		_lastFocusItem:null,

		postCreate: function(){
			dijit.Dialog.superclass.postCreate.apply(this, arguments);
			this.domNode.style.display="none";
		},

		startup: function(){
			if(this.closeNode){
				var closeNode = dojo.byId(this.closeNode);
				this.connect(closeNode, "onclick", "hide");
			}
		},

		onLoad: function(){
			// when href is specified we need to reposition
			// the dialog after the data is loaded
			this._position();
			dijit.Dialog.superclass.onLoad.call(this);
		},

		_setup: function(){
			// summary:
			//		stuff we need to do before showing the Dialog for the first
			//		time (but we defer it until right beforehand, for
			//		performance reasons)

			this._modalconnects = [];

			if(this.titleBar){
				this._moveable = new dojo.dnd.Moveable(this.domNode, { handle: this.titleBar });
			}

			this._underlay = new dijit.DialogUnderlay();

			var node = this.domNode;
			this._fadeIn = dojo.fx.combine(
				[dojo.fadeIn({
					node: node,
					duration: this._duration
				 }),
				 dojo.fadeIn({
					node: this._underlay.domNode,
					duration: this._duration,
					onBegin: dojo.hitch(this._underlay, "show")
				 })
				]
			);

			this._fadeOut = dojo.fx.combine(
				[dojo.fadeOut({
					node: node,
					duration: this._duration,
					onEnd: function(){
						node.style.display="none";
					}
				 }),
				 dojo.fadeOut({
					node: this._underlay.domNode,
					duration: this._duration,
					onEnd: dojo.hitch(this._underlay, "hide")
				 })
				]
			);
		},

		uninitialize: function(){
			if(this._underlay){
				this._underlay.destroy();
			}
		},

		_position: function(){
			// summary: position modal dialog in center of screen

			var viewport = dijit.util.getViewport();
			var mb = dojo.marginBox(this.domNode);

			var style = this.domNode.style;
			style.left = (viewport.l + (viewport.w - mb.w)/2) + "px";
			style.top = (viewport.t + (viewport.h - mb.h)/2) + "px";
		},

		_findLastFocus: function(/*Event*/ evt){
			// summary:  called from onblur of dialog container to determine the last focusable item
			this._lastFocused = evt.target;
		},

		_cycleFocus: function(/*Event*/ evt){
			// summary: when tabEnd receives focus, advance focus around to titleBar

			// on first focus to tabEnd, store the last focused item in dialog
			if(!this._lastFocusItem){
				this._lastFocusItem = this._lastFocused;
			}
			this.titleBar.focus();
		},

		_onKey: function(/*Event*/ evt){
			if(evt.keyCode){
				var node = evt.target;
				// see if we are shift-tabbing from titleBar
				if(node == this.titleBar && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
					if (this._lastFocusItem){
						this._lastFocusItem.focus(); // send focus to last item in dialog if known
					}
					dojo.stopEvent(evt);
				}else{
					// see if the key is for the dialog
					while (node){
						if(node == this.domNode){
							if (evt.keyCode == dojo.keys.ESCAPE){
								this.hide();
							}else{
								return; // just let it go
							}
						}
						node = node.parentNode;
					}
					// this key is for the disabled document window
					if (evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y
						dojo.stopEvent(evt);
					// opera won't tab to a div
					}else if (!dojo.isOpera){
						try{
							this.titleBar.focus();
						}catch(e){/*squelch*/}
					}
				}
			}
		},

		show: function(){
			// summary: display the dialog

			// first time we show the dialog, there's some initialization stuff to do
			if(!this._alreadyInitialized){
				this._setup();
				this._alreadyInitialized=true;
			}

			if(this._fadeOut.status() == "playing"){
				this._fadeOut.stop();
			}

			this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
			this._modalconnects.push(dojo.connect(document.documentElement, "onkeypress", this, "_onKey"));

			// IE doesn't bubble onblur events - use ondeactivate instead
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
			this._modalconnects.push(dojo.connect(this.containerNode, ev, this, "_findLastFocus"));

			dojo.style(this.domNode, "opacity", 0);
			this.domNode.style.display="block";

			this._loadCheck(); // lazy load trigger

			this._position();

			this._fadeIn.play();

			this._savedFocus = dijit.util.focus.save(this);

			// set timeout to allow the browser to render dialog
			setTimeout(dojo.hitch(this, function(){
				try{
					this.titleBar.focus();
				}catch(e){/*squelch*/}
			}), 50);
		},

		hide: function(){
			// summary
			//		Hide the dialog

			// if we haven't been initialized yet then we aren't showing and we can just return
			if(!this._alreadyInitialized){
				return;
			}

			if(this._fadeIn.status() == "playing"){
				this._fadeIn.stop();
			}
			this._fadeOut.play();

			if (this._scrollConnected){
				this._scrollConnected = false;
			}
			dojo.forEach(this._modalconnects, dojo.disconnect);
			this._modalconnects = [];

			dijit.util.focus.restore(this._savedFocus);
		},

		layout: function() {
			if(this.domNode.style.display == "block"){
				this._underlay.layout();
				this._position();
			}
		}
	}
);

dojo.declare(
	"dijit.TooltipDialog",
	[dijit.layout.ContentPane, dijit._Templated],
	{
		// summary:
		//		Pops up a dialog that appears like a Tooltip

		// closeNode: String
		//	Id of button or other dom node to click to close this dialog
		closeNode: "",

		// title: String
		// Description of tooltip dialog (required for a11Y)
		title: "",

		_lastFocusItem: null,

		templatePath: dojo.moduleUrl("dijit.layout", "templates/TooltipDialog.html"),

		postCreate: function(){
			dijit.TooltipDialog.superclass.postCreate.apply(this, arguments);
			this.connect(this.containerNode, "onkeypress", "_onKey");

			// IE doesn't bubble onblur events - use ondeactivate instead
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
			this.connect(this.containerNode, ev, "_findLastFocus");
			this.containerNode.title=this.title;
	},

	startup: function(){
			if(this.closeNode){
				var closeNode = dojo.byId(this.closeNode);
				this.connect(closeNode, "onclick", "_hide");
			}
		},

		onOpen: function(/*Object*/ pos){
			// summary: called when dialog is displayed, with info on where it's being displayed relative to the button
			this.domNode.className="dijitTooltipDialog dijitTooltip" + (pos.corner=='TL' ? "Below" : "Above");

			this.containerNode.focus();
		},

		_hide: function(){
			// summary: hide the dialog
			dijit.util.popup.closeAll();
		},

		_onKey: function(/*Event*/ evt){
			//summary: keep keyboard focus in dialog; close dialog on escape key
			if (evt.keyCode == dojo.keys.ESCAPE){
				this._hide();
			}else if (evt.target == this.containerNode && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
				if (this._lastFocusItem){
					this._lastFocusItem.focus();
				}
				dojo.stopEvent(evt);
			}
		},

		_findLastFocus: function(/*Event*/ evt){
			// summary:  called from onblur of dialog container to determine the last focusable item
			this._lastFocused = evt.target;
		},

		_cycleFocus: function(/*Event*/ evt){
			// summary: when tabEnd receives focus, advance focus around to containerNode

			// on first focus to tabEnd, store the last focused item in dialog
			if(!this._lastFocusItem){
				this._lastFocusItem = this._lastFocused;
			}
			this.containerNode.focus();
		}
	}
);

<commit_msg>Convert TooltipDialog to issue onExecute()/onCancel() callbacks so parent knows when to close it, rather than closing itself.<commit_after>dojo.provide("dijit.Dialog");

dojo.require("dojo.dnd.move");
dojo.require("dojo.fx");

dojo.require("dijit.util.place");
dojo.require("dijit.util.sniff");
dojo.require("dijit.util.popup");			// for BackgroundIFrame
dojo.require("dijit.layout.ContentPane");
dojo.require("dijit._Templated");

dojo.declare(
	"dijit.DialogUnderlay",
	[dijit._Widget, dijit._Templated],
	{
		// summary: the thing that grays out the screen behind the dialog

		// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
		// Inner div has opacity specified in CSS file.
		templateString: "

",

		postCreate: function(){
			var b = dojo.body();
			b.appendChild(this.domNode);
			this.bgIframe = new dijit.util.BackgroundIframe(this.domNode);
		},

		layout: function(){
			// summary
			//		Sets the background to the size of the viewport (rather than the size
			//		of the document) since we need to cover the whole browser window, even
			//		if the document is only a few lines long.

			var viewport = dijit.util.getViewport();
			var is = this.node.style,
				os = this.domNode.style;

			os.top = viewport.t + "px";
			os.left = viewport.l + "px";
			is.width = viewport.w + "px";
			is.height = viewport.h + "px";

			// process twice since the scroll bar may have been removed
			// by the previous resizing
			var viewport2 = dijit.util.getViewport();
			if(viewport.w != viewport2.w){ is.width = viewport2.w + "px"; }
			if(viewport.h != viewport2.h){ is.height = viewport2.h + "px"; }
		},

		show: function(){
			this.domNode.style.display = "block";
			this.layout();
			if(this.bgIframe.iframe){
				this.bgIframe.iframe.style.display = "block";
			}
		},

		hide: function(){
			this.domNode.style.display = "none";
			this.domNode.style.width = this.domNode.style.height = "1px";
			if(this.bgIframe.iframe){
				this.bgIframe.iframe.style.display = "none";
			}
		},

		uninitialize: function(){
			if(this.bgIframe){
				this.bgIframe.destroy();
			}
		}
	}
);

dojo.declare(
	"dijit.Dialog",
	[dijit.layout.ContentPane, dijit._Templated],
	{
		// summary:
		//		Pops up a modal dialog window, blocking access to the screen
		//		and also graying out the screen Dialog is extended from
		//		ContentPane so it supports all the same parameters (href, etc.)

		templatePath: dojo.moduleUrl("dijit", "templates/Dialog.html"),

		// title: String
		//		Title of the dialog
		title: "",

		// closeNode: String
		//	Id of button or other dom node to click to close this dialog
		closeNode: "",

		_duration: 400,

		_lastFocusItem:null,

		postCreate: function(){
			dijit.Dialog.superclass.postCreate.apply(this, arguments);
			this.domNode.style.display="none";
		},

		startup: function(){
			if(this.closeNode){
				var closeNode = dojo.byId(this.closeNode);
				this.connect(closeNode, "onclick", "hide");
			}
		},

		onLoad: function(){
			// when href is specified we need to reposition
			// the dialog after the data is loaded
			this._position();
			dijit.Dialog.superclass.onLoad.call(this);
		},

		_setup: function(){
			// summary:
			//		stuff we need to do before showing the Dialog for the first
			//		time (but we defer it until right beforehand, for
			//		performance reasons)

			this._modalconnects = [];

			if(this.titleBar){
				this._moveable = new dojo.dnd.Moveable(this.domNode, { handle: this.titleBar });
			}

			this._underlay = new dijit.DialogUnderlay();

			var node = this.domNode;
			this._fadeIn = dojo.fx.combine(
				[dojo.fadeIn({
					node: node,
					duration: this._duration
				 }),
				 dojo.fadeIn({
					node: this._underlay.domNode,
					duration: this._duration,
					onBegin: dojo.hitch(this._underlay, "show")
				 })
				]
			);

			this._fadeOut = dojo.fx.combine(
				[dojo.fadeOut({
					node: node,
					duration: this._duration,
					onEnd: function(){
						node.style.display="none";
					}
				 }),
				 dojo.fadeOut({
					node: this._underlay.domNode,
					duration: this._duration,
					onEnd: dojo.hitch(this._underlay, "hide")
				 })
				]
			);
		},

		uninitialize: function(){
			if(this._underlay){
				this._underlay.destroy();
			}
		},

		_position: function(){
			// summary: position modal dialog in center of screen

			var viewport = dijit.util.getViewport();
			var mb = dojo.marginBox(this.domNode);

			var style = this.domNode.style;
			style.left = (viewport.l + (viewport.w - mb.w)/2) + "px";
			style.top = (viewport.t + (viewport.h - mb.h)/2) + "px";
		},

		_findLastFocus: function(/*Event*/ evt){
			// summary:  called from onblur of dialog container to determine the last focusable item
			this._lastFocused = evt.target;
		},

		_cycleFocus: function(/*Event*/ evt){
			// summary: when tabEnd receives focus, advance focus around to titleBar

			// on first focus to tabEnd, store the last focused item in dialog
			if(!this._lastFocusItem){
				this._lastFocusItem = this._lastFocused;
			}
			this.titleBar.focus();
		},

		_onKey: function(/*Event*/ evt){
			if(evt.keyCode){
				var node = evt.target;
				// see if we are shift-tabbing from titleBar
				if(node == this.titleBar && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
					if (this._lastFocusItem){
						this._lastFocusItem.focus(); // send focus to last item in dialog if known
					}
					dojo.stopEvent(evt);
				}else{
					// see if the key is for the dialog
					while (node){
						if(node == this.domNode){
							if (evt.keyCode == dojo.keys.ESCAPE){
								this.hide();
							}else{
								return; // just let it go
							}
						}
						node = node.parentNode;
					}
					// this key is for the disabled document window
					if (evt.keyCode != dojo.keys.TAB){ // allow tabbing into the dialog for a11y
						dojo.stopEvent(evt);
					// opera won't tab to a div
					}else if (!dojo.isOpera){
						try{
							this.titleBar.focus();
						}catch(e){/*squelch*/}
					}
				}
			}
		},

		show: function(){
			// summary: display the dialog

			// first time we show the dialog, there's some initialization stuff to do
			if(!this._alreadyInitialized){
				this._setup();
				this._alreadyInitialized=true;
			}

			if(this._fadeOut.status() == "playing"){
				this._fadeOut.stop();
			}

			this._modalconnects.push(dojo.connect(window, "onscroll", this, "layout"));
			this._modalconnects.push(dojo.connect(document.documentElement, "onkeypress", this, "_onKey"));

			// IE doesn't bubble onblur events - use ondeactivate instead
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
			this._modalconnects.push(dojo.connect(this.containerNode, ev, this, "_findLastFocus"));

			dojo.style(this.domNode, "opacity", 0);
			this.domNode.style.display="block";

			this._loadCheck(); // lazy load trigger

			this._position();

			this._fadeIn.play();

			this._savedFocus = dijit.util.focus.save(this);

			// set timeout to allow the browser to render dialog
			setTimeout(dojo.hitch(this, function(){
				try{
					this.titleBar.focus();
				}catch(e){/*squelch*/}
			}), 50);
		},

		hide: function(){
			// summary
			//		Hide the dialog

			// if we haven't been initialized yet then we aren't showing and we can just return
			if(!this._alreadyInitialized){
				return;
			}

			if(this._fadeIn.status() == "playing"){
				this._fadeIn.stop();
			}
			this._fadeOut.play();

			if (this._scrollConnected){
				this._scrollConnected = false;
			}
			dojo.forEach(this._modalconnects, dojo.disconnect);
			this._modalconnects = [];

			dijit.util.focus.restore(this._savedFocus);
		},

		layout: function() {
			if(this.domNode.style.display == "block"){
				this._underlay.layout();
				this._position();
			}
		}
	}
);

dojo.declare(
	"dijit.TooltipDialog",
	[dijit.layout.ContentPane, dijit._Templated],
	{
		// summary:
		//		Pops up a dialog that appears like a Tooltip

		// submitNode: String
		//	Id of button or other dom node to click to submit this dialog
		submitNode: "",

		// cancelNode: String
		//	Id of button or other dom node to click to cancel this dialog
		cancelNode: "",

		// title: String
		// Description of tooltip dialog (required for a11Y)
		title: "",

		// onCancel: Function
		//	Callback when user has canceled dialog
		onCancel: function(){},

		// onExecute: Function
		//	Callback when user has executed dialog
		onExecute: function(){},

		_lastFocusItem: null,

		templatePath: dojo.moduleUrl("dijit.layout", "templates/TooltipDialog.html"),

		postCreate: function(){
			dijit.TooltipDialog.superclass.postCreate.apply(this, arguments);
			this.connect(this.containerNode, "onkeypress", "_onKey");

			// IE doesn't bubble onblur events - use ondeactivate instead
			var ev = typeof(document.ondeactivate) == "object" ? "ondeactivate" : "onblur";
			this.connect(this.containerNode, ev, "_findLastFocus");
			this.containerNode.title=this.title;
		},

		startup: function(){
			if(this.cancelNode){
				var cancelNode = dojo.byId(this.cancelNode);
				this.connect(cancelNode, "onclick", "onCancel");
			}
			if(this.submitNode){
				var submitNode = dojo.byId(this.submitNode);
				this.connect(submitNode, "onclick", "onExecute");
			}
		},

		onOpen: function(/*Object*/ pos){
			// summary: called when dialog is displayed, with info on where it's being displayed relative to the button
			this.domNode.className="dijitTooltipDialog dijitTooltip" + (pos.corner=='TL' ? "Below" : "Above");

			this.containerNode.focus();
		},

		_onKey: function(/*Event*/ evt){
			//summary: keep keyboard focus in dialog; close dialog on escape key
			if (evt.keyCode == dojo.keys.ESCAPE){
				this.onCancel();
			}else if (evt.target == this.containerNode && evt.shiftKey && evt.keyCode == dojo.keys.TAB){
				if (this._lastFocusItem){
					this._lastFocusItem.focus();
				}
				dojo.stopEvent(evt);
			}
		},

		_findLastFocus: function(/*Event*/ evt){
			// summary:  called from onblur of dialog container to determine the last focusable item
			this._lastFocused = evt.target;
		},

		_cycleFocus: function(/*Event*/ evt){
			// summary: when tabEnd receives focus, advance focus around to containerNode

			// on first focus to tabEnd, store the last focused item in dialog
			if(!this._lastFocusItem){
				this._lastFocusItem = this._lastFocused;
			}
			this.containerNode.focus();
		}
	}
);

<|endoftext|>


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

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'build/src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2019 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google-cloud/security-center',
    theme: 'lumen',
    default: {
      "outputSourceFiles": false
    }
  },
  markdown: {
    idInHeadings: true
  }
};
<commit_msg>chore: match jsdoc with macro license (#225)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// ** This file is automatically generated by gapic-generator-typescript. **
// ** https://github.com/googleapis/gapic-generator-typescript **
// ** All changes to this file may be overwritten. **

'use strict';

module.exports = {
  opts: {
    readme: './README.md',
    package: './package.json',
    template: './node_modules/jsdoc-fresh',
    recurse: true,
    verbose: true,
    destination: './docs/'
  },
  plugins: [
    'plugins/markdown',
    'jsdoc-region-tag'
  ],
  source: {
    excludePattern: '(^|\\/|\\\\)[._]',
    include: [
      'build/src'
    ],
    includePattern: '\\.js$'
  },
  templates: {
    copyright: 'Copyright 2019 Google, LLC.',
    includeDate: false,
    sourceFiles: false,
    systemName: '@google-cloud/security-center',
    theme: 'lumen',
    default: {
      "outputSourceFiles": false
    }
  },
  markdown: {
    idInHeadings: true
  }
};
<|endoftext|>


<commit_before>/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/

'use strict';

// Older UI
togglbutton.render('.details-pane-body:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.sticky-view-placeholder', elem),
    description = $('#details_property_sheet_title', elem),
    project = $('#details_pane_project_tokenizer .token_name', elem);

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.ancestor-projects', elem) && $('.ancestor-projects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana',
    description: descFunc,
    projectName: projectFunc
  });

  container.parentNode.insertBefore(link, container.nextSibling);
});

// New UI v1
togglbutton.render('#right_pane__contents .SingleTaskPane:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.SingleTaskTitleRow', elem),
    description = $('.SingleTaskTitleRow .simpleTextarea', elem),
    project = $('.TaskProjectPill-projectName div', elem);

  if (!container) {
    return;
  }

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.TaskAncestry-ancestorProjects', elem) && $('.TaskAncestry-ancestorProjects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana-new',
    description: descFunc,
    projectName: projectFunc
  });

  container.after(link);
});

// New UI v2
togglbutton.render('#right_pane__contents .SingleTaskPane-body:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.TaskPaneAssigneeDueDateRowStructure', elem),
    description = $('.SingleTaskPane-titleRow .simpleTextarea', elem),
    project = $('.TaskProjectPill-projectName div', elem);

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.TaskAncestry-ancestorProjects', elem) && $('.TaskAncestry-ancestorProjects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana-new',
    description: descFunc,
    projectName: projectFunc
  });

  container.appendChild(link);
});

// New UI Board view v1 and v2
togglbutton.render('.BoardColumnCardsContainer-item:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.BoardCardWithCustomProperties-assigneeAndDueDate', elem),
    description = $('.BoardCardWithCustomProperties-name', elem).textContent,
    project = $('.SidebarItemRow.is-selected').textContent;

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: project,
    buttonType: 'minimal'
  });

  container.appendChild(link);
});

// New UI Board task detail view v1
togglbutton.render('.SingleTaskTitleRow:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.SingleTaskPaneToolbar', elem.parentNode),
    description = $('.SingleTaskTitleRow textarea', elem.parentNode).textContent,
    projectElement = $('.SingleTaskPane-projects .TaskProjectPill-projectName', elem.parentNode);

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: projectElement ? projectElement.textContent : '',
    buttonType: 'minimal'
  });

  container.appendChild(link);
});

// New UI Board task detail view v2
togglbutton.render('.SingleTaskPane-titleRow:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.SingleTaskPaneToolbarEasyCompletion', elem.parentNode),
    description = $('.SingleTaskPane-titleRow .simpleTextarea', elem.parentNode).textContent,
    projectElement = $('.SingleTaskPane-projects .TaskProjectPill-projectName', elem.parentNode);

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: projectElement ? projectElement.textContent : '',
    buttonType: 'minimal'
  });

  container.appendChild(link);
});

<commit_msg>Fix asana board task view closes #1068<commit_after>/*jslint indent: 2 */
/*global $: false, document: false, togglbutton: false*/

'use strict';

// Older UI
togglbutton.render('.details-pane-body:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.sticky-view-placeholder', elem),
    description = $('#details_property_sheet_title', elem),
    project = $('#details_pane_project_tokenizer .token_name', elem);

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.ancestor-projects', elem) && $('.ancestor-projects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana',
    description: descFunc,
    projectName: projectFunc
  });

  container.parentNode.insertBefore(link, container.nextSibling);
});

// New UI v1
togglbutton.render('#right_pane__contents .SingleTaskPane:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.SingleTaskTitleRow', elem),
    description = $('.SingleTaskTitleRow .simpleTextarea', elem),
    project = $('.TaskProjectPill-projectName div', elem);

  if (!container) {
    return;
  }

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.TaskAncestry-ancestorProjects', elem) && $('.TaskAncestry-ancestorProjects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana-new',
    description: descFunc,
    projectName: projectFunc
  });

  container.after(link);
});

// New UI v2
togglbutton.render('#right_pane__contents .SingleTaskPane-body:not(.toggl)', {observe: true}, function (elem) {

  var link, descFunc, projectFunc,
    container = $('.TaskPaneAssigneeDueDateRowStructure', elem),
    description = $('.SingleTaskPane-titleRow .simpleTextarea', elem),
    project = $('.TaskProjectPill-projectName div', elem);

  descFunc = function () {
    return !!description ? description.value : "";
  };

  projectFunc = function () {
    return (project && project.textContent) || ($('.TaskAncestry-ancestorProjects', elem) && $('.TaskAncestry-ancestorProjects', elem).textContent) || "";
  };

  link = togglbutton.createTimerLink({
    className: 'asana-new',
    description: descFunc,
    projectName: projectFunc
  });

  container.appendChild(link);
});

// New UI Board view v1 and v2
togglbutton.render('.BoardColumnCardsContainer-item:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.BoardCardWithCustomProperties-assigneeAndDueDate', elem),
    description = $('.BoardCardWithCustomProperties-name', elem).textContent,
    project = $('.SidebarItemRow.is-selected').textContent;

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: project,
    buttonType: 'minimal'
  });

  container.appendChild(link);
});

// New UI Board task detail view v1
togglbutton.render('.SingleTaskTitleRow:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.SingleTaskPaneToolbar', elem.parentNode),
    description = $('.SingleTaskTitleRow textarea', elem.parentNode).textContent,
    projectElement = $('.SingleTaskPane-projects .TaskProjectPill-projectName', elem.parentNode);

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: projectElement ? projectElement.textContent : '',
    buttonType: 'minimal'
  });

  container.appendChild(link);
});

// New UI Board task detail view v2
togglbutton.render('.SingleTaskPane-titleRow:not(.toggl)', {observe: true}, function (elem) {
  if (!!$('.toggl-button', elem)) {
    return;
  }
  var link,
    container = $('.SingleTaskPaneToolbar'),
    description = $('.SingleTaskPane-titleRow .simpleTextarea', elem.parentNode).textContent,
    projectElement = $('.SingleTaskPane-projects .TaskProjectPill-projectName', elem.parentNode);

  link = togglbutton.createTimerLink({
    className: 'asana-board',
    description: description,
    projectName: projectElement ? projectElement.textContent : '',
    buttonType: 'minimal'
  });

  link.style.marginRight = '5px';

  if (container) {
    var closeButton = container.lastElementChild;
    container.insertBefore(link, closeButton);
  }
});

<|endoftext|>


<commit_before>/*global ServiceRegistry, ProjectConfig, ares, ComponentsRegistry, async, enyo, Phonegap */
/**
 * This kind is the top kind of project handling. It contains:
 * - The project list
 * - the interface towards the user's file (harmonia)
 * - Popups to manage projects (create, scan, error ...)
 */
enyo.kind({
	name: "ProjectView",
	kind: "FittableColumns",
	classes: "enyo-unselectable",
	debug: false,
	published: {
		// harcoded until ENYO-2755 is fixed
		panelIndex: 0
	},
	components: [
		{kind: "ProjectList",
			onModifySettings: "modifySettingsAction",
			onCreateProject: "createProjectAction",
			onOpenProject: "openProjectAction",
			onSearchProjects: "searchProjectsAction",
			onDuplicateProject: "duplicateProjectAction",
			onProjectRemoved: "projectRemoved",
			name: "projectList"
		},
		{kind: "ProjectWizardCreate", canGenerate: false, name: "projectWizardCreate", classes:"ares-masked-content-popup"},
		{kind: "ProjectWizardScan", canGenerate: false, name: "projectWizardScan", classes:"ares-masked-content-popup"},
		{kind: "ProjectWizardModify", canGenerate: false,
			name: "projectWizardModify"
		},
		{kind: "ProjectWizardCopy", name: "projectWizardCopy", classes:"ares-masked-content-popup"}
	],
	handlers: {
		onAddProjectInList: "addProjectInList",
		onPreview: "previewAction",
		onBuild: "buildAction",
		onInstall: "installAction",
		onRun: "runAction",
		onRunDebug: "runDebugAction"
	},
	events: {
		onHideWaitPopup: "",
		onShowWaitPopup: "",
		onError: "",
		onProjectSelected: "",
		onProjectSave: "",
		onRegisterMe: ""
	},
	create: function() {
		ares.setupTraceLogger(this);
		this.inherited(arguments);
		this.doRegisterMe({name:"projectView", reference:this});
	},
	/**
	 * Refresh the {ProjectView} (if relevant), following a change of the given file
	 * @param {Object} toSelectId. Asynchronous
	 */
	refreshFileTree: function(toSelectId,next) {
		ComponentsRegistry.getComponent("harmonia").refreshFileTree(toSelectId,next);
	},
	searchProjectsAction: function(inSender, inEvent) {
		this.$.projectWizardScan.setHeaderText('Select a folder hierarchy');
		this.$.projectWizardScan.setRecurse(true);
		this.$.projectWizardScan.show();
		return true; //Stop event propagation
	},
	openProjectAction: function(inSender, inEvent) {
		this.$.projectWizardScan.setHeaderText('Select an existing Ares application folder (contains a project.json file)');
		this.$.projectWizardScan.setRecurse(false);
		this.$.projectWizardScan.show();
		return true; //Stop event propagation
	},
	duplicateProjectAction: function(InSender, inEvent) {
		if(InSender.selected == null){
			this.doError({msg: "Please project list select item."});
			return false;
		}

		this.$.projectWizardCopy.start( this.currentProject() );
		return true; //Stop event propagation
	},
	createProjectAction: function(inSender, inEvent) {
		this.$.projectWizardCreate.start();
		return true; //Stop event propagation
	},
	modifySettingsAction: function(inSender, inEvent) {
		this.$.projectWizardModify.start( this.currentProject() );

		return true; //Stop event propagation
	},

	addProjectInList: function(inSender, inEvent) {
		this.doHideWaitPopup();
		try {
			// Add an entry into the project list
			this.$.projectList.addProject(inEvent.name, inEvent.folderId, inEvent.service, inEvent.dontSelect);
		} catch(e) {
			var msg = e.toString();
			this.error(msg);
			this.doError({msg: msg});
			return false;
		}
		return true; //Stop event propagation
	},
	currentProject: function() {
		return this.$.projectList.getSelectedProject() ;
	},
	/**
	 *
	 * @param {Object} project
	 * @param {Function} next
	 */
	setupProjectConfig: function(project, next) {
		ares.assertCb(next);

		var pname = project.getName();
		if ( this.currentProject() && pname === this.currentProject().getName() ) {
			this.trace("skip setup of already selected project" + pname);
			next();
			return;
		}

		// FIXME: temporary hack to create config.json on the
		// fly if needed... would be better to create/load it
		// when the workspace is loaded & when a new project
		// is created that would save per-click HTTP traffic
		// to the FileSystemService.
		var self = this;
		var config = new ProjectConfig();
		this.trace("setup project config init on "+ project.getName() );
		var initData = {
			service: project.getService(),
			folderId: project.getFolderId()
		};

		async.series(
			[
				// read default config from remote server
				config.init.bind(config, initData) ,
				function (next) {
					self.trace("ProjectView: setup project set config on "+ project.getName() );
					project.setConfig(config);

					self.initializeDownloadStatus(project, config.data.providers.phonegap.enabled);
					self.initializeValidPgbConf(project, config.data.providers.phonegap.enabled);
					next();
				},
				function (next) {
					// Pass service definition & configuration to Harmonia
					// & consequently to HermesFileTree
					self.trace("ProjectView: setup project on harmonia "+ project.getName() );
					ComponentsRegistry.getComponent("harmonia").setProject(project, next);
				}

			],
			function (err) {
				self.trace("ProjectView: setup project config done on " + project.getName() + " err is ",err );
				if (err) {
					self.doError({msg: err.toString(), err: err});
				}
				next(err);
			}
		);
	},
	/**
	 * Initialize the attribute "downloadStatus" of the project
	 *
	 * this attribute is used only by Phonegap Build.
	 * @private
	 */
	initializeDownloadStatus: function(inProject, inPhonegapEnabled) {
		if (inProject.getDownloadStatus() === undefined && inPhonegapEnabled) {
			var downloadStatus = {};
			var index = 0;

			for (index in Phonegap.UIConfiguration.platformDrawersContent) {
				downloadStatus[Phonegap.UIConfiguration.platformDrawersContent[index].id] = "Ready for download";
			}

			inProject.setDownloadStatus(downloadStatus);
		}
	},

	initializeValidPgbConf: function(inProject, inPhonegapEnabled) {
		if (inProject.getValidPgbConf() === undefined && inPhonegapEnabled) {
			var pgbValidation = {};
			var pgbUiData = Phonegap.UIConfiguration.commonDrawersContent.concat(Phonegap.UIConfiguration.platformDrawersContent);
			var index =0;

			for (index in pgbUiData) {
				//The creation of the pgbValidation drawer attribute and its initialization are done in the same time.
				pgbValidation[pgbUiData[index].id] = {};

				for (var i=0, maxLength = pgbUiData[index].rows.length; i<maxLength; i++) {
					//The creation of the pgbValidation row attribute and its initialization are done in the same time.
					pgbValidation[pgbUiData[index].id][pgbUiData[index].rows[i].name] = true;
				}
				pgbValidation[pgbUiData[index].id]["validDrawer"] = true;
			}

			inProject.setValidPgbConf(pgbValidation);
		}
	},

	projectRemoved: function(inSender, inEvent) {
		ComponentsRegistry.getComponent("harmonia").setProject(null, ares.noNext);
	},
	/**
	 * Event handler: handle build project action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	buildAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectAction(project, 'build', 'build');
		}
		return true; // stop bubble-up
	},

	/**
	 * Event handler: handle install application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	installAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectAction(project, 'test', 'install');
		}
		return true; // stop bubble-up
	},
	/**
	 * Event handler: handle run application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	runAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectAction(project, 'test', 'run');
		}
		return true; // stop bubble-up
	},
	/**
	 * Event handler: handle debug application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	runDebugAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectAction(project, 'test', 'runDebug');
		}
		return true; // stop bubble-up
	},
	/**
	 * @private
	 */
	projectAction: function(project, serviceType, action) {
		var self = this;
		this.doShowWaitPopup({msg: "Starting: " + action, service: serviceType});
		// TODO: Must be reworked to allow the selection of builder/tester in the UI - ENYO-2049
		var services = ServiceRegistry.instance.getServicesByType(serviceType);
		var provider =	services[services.length - 1];
		if (!provider) {
			this.doError({msg: 'No ' + serviceType + ' service available'});
		} else if (typeof provider[action] !== 'function') {
			this.doError({msg: 'Service ' + provider.name + ' does not provide action: ' + action});
		} else {
			provider[action](project, function(inError) {
				self.doHideWaitPopup();
				self.refreshFileTree(project.getFolderId());
				if (inError) {
					self.doError({msg: inError.toString(), err: inError});
				}
			});
		}
	},

	launchPreview: function (project) {
		var cb = function (err) {
			if (err) {
				this.trace(err);
			} else {
				this._launchPreview(project);
			}
		};
		this.doProjectSave({ project: project, callback: cb.bind(this) });
	},

	_launchPreview: function (project) {
		var config = project.getConfig() ;
		var topFile = config.data.preview.top_file ;
		var projectUrl = project.getProjectUrl() + '/' + topFile ;

		// the last replace method is needed for test environment only
		var winLoc = window.location.toString().replace('ares','preview').replace('test', 'index') ;
		var previewUrl = winLoc
				+ ( winLoc.indexOf('?') != -1 ? '&' : '?' )
				+ 'url=' + encodeURIComponent(projectUrl)+'&name=' + project.id;

		this.trace("preview on URL ", previewUrl) ;

		window.open(
			previewUrl,
			'_blank', // ensure that a new window is created each time preview is tapped
			'scrollbars=0,menubar=1,resizable=1',
			false
		);
	},

	/**
	 * Event handler: Launch a preview widget of the selected project in a separate frame
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	previewAction: function(inSender, inEvent) {
		var project = inEvent.project;
		if ( project) {
			this.launchPreview(project);
		}
		return true; // stop the bubble
	}
});
<commit_msg>ENYO-3244: propose to save file before build/run/install actions<commit_after>/*global ServiceRegistry, ProjectConfig, ares, ComponentsRegistry, async, enyo, Phonegap */
/**
 * This kind is the top kind of project handling. It contains:
 * - The project list
 * - the interface towards the user's file (harmonia)
 * - Popups to manage projects (create, scan, error ...)
 */
enyo.kind({
	name: "ProjectView",
	kind: "FittableColumns",
	classes: "enyo-unselectable",
	debug: false,
	published: {
		// harcoded until ENYO-2755 is fixed
		panelIndex: 0
	},
	components: [
		{kind: "ProjectList",
			onModifySettings: "modifySettingsAction",
			onCreateProject: "createProjectAction",
			onOpenProject: "openProjectAction",
			onSearchProjects: "searchProjectsAction",
			onDuplicateProject: "duplicateProjectAction",
			onProjectRemoved: "projectRemoved",
			name: "projectList"
		},
		{kind: "ProjectWizardCreate", canGenerate: false, name: "projectWizardCreate", classes:"ares-masked-content-popup"},
		{kind: "ProjectWizardScan", canGenerate: false, name: "projectWizardScan", classes:"ares-masked-content-popup"},
		{kind: "ProjectWizardModify", canGenerate: false,
			name: "projectWizardModify"
		},
		{kind: "ProjectWizardCopy", name: "projectWizardCopy", classes:"ares-masked-content-popup"}
	],
	handlers: {
		onAddProjectInList: "addProjectInList",
		onPreview: "previewAction",
		onBuild: "buildAction",
		onInstall: "installAction",
		onRun: "runAction",
		onRunDebug: "runDebugAction"
	},
	events: {
		onHideWaitPopup: "",
		onShowWaitPopup: "",
		onError: "",
		onProjectSelected: "",
		onProjectSave: "",
		onRegisterMe: ""
	},
	create: function() {
		ares.setupTraceLogger(this);
		this.inherited(arguments);
		this.doRegisterMe({name:"projectView", reference:this});
	},
	/**
	 * Refresh the {ProjectView} (if relevant), following a change of the given file
	 * @param {Object} toSelectId. Asynchronous
	 */
	refreshFileTree: function(toSelectId,next) {
		ComponentsRegistry.getComponent("harmonia").refreshFileTree(toSelectId,next);
	},
	searchProjectsAction: function(inSender, inEvent) {
		this.$.projectWizardScan.setHeaderText('Select a folder hierarchy');
		this.$.projectWizardScan.setRecurse(true);
		this.$.projectWizardScan.show();
		return true; //Stop event propagation
	},
	openProjectAction: function(inSender, inEvent) {
		this.$.projectWizardScan.setHeaderText('Select an existing Ares application folder (contains a project.json file)');
		this.$.projectWizardScan.setRecurse(false);
		this.$.projectWizardScan.show();
		return true; //Stop event propagation
	},
	duplicateProjectAction: function(InSender, inEvent) {
		if(InSender.selected == null){
			this.doError({msg: "Please project list select item."});
			return false;
		}

		this.$.projectWizardCopy.start( this.currentProject() );
		return true; //Stop event propagation
	},
	createProjectAction: function(inSender, inEvent) {
		this.$.projectWizardCreate.start();
		return true; //Stop event propagation
	},
	modifySettingsAction: function(inSender, inEvent) {
		this.$.projectWizardModify.start( this.currentProject() );

		return true; //Stop event propagation
	},

	addProjectInList: function(inSender, inEvent) {
		this.doHideWaitPopup();
		try {
			// Add an entry into the project list
			this.$.projectList.addProject(inEvent.name, inEvent.folderId, inEvent.service, inEvent.dontSelect);
		} catch(e) {
			var msg = e.toString();
			this.error(msg);
			this.doError({msg: msg});
			return false;
		}
		return true; //Stop event propagation
	},
	currentProject: function() {
		return this.$.projectList.getSelectedProject() ;
	},
	/**
	 *
	 * @param {Object} project
	 * @param {Function} next
	 */
	setupProjectConfig: function(project, next) {
		ares.assertCb(next);

		var pname = project.getName();
		if ( this.currentProject() && pname === this.currentProject().getName() ) {
			this.trace("skip setup of already selected project" + pname);
			next();
			return;
		}

		// FIXME: temporary hack to create config.json on the
		// fly if needed... would be better to create/load it
		// when the workspace is loaded & when a new project
		// is created that would save per-click HTTP traffic
		// to the FileSystemService.
		var self = this;
		var config = new ProjectConfig();
		this.trace("setup project config init on "+ project.getName() );
		var initData = {
			service: project.getService(),
			folderId: project.getFolderId()
		};

		async.series(
			[
				// read default config from remote server
				config.init.bind(config, initData) ,
				function (next) {
					self.trace("ProjectView: setup project set config on "+ project.getName() );
					project.setConfig(config);

					self.initializeDownloadStatus(project, config.data.providers.phonegap.enabled);
					self.initializeValidPgbConf(project, config.data.providers.phonegap.enabled);
					next();
				},
				function (next) {
					// Pass service definition & configuration to Harmonia
					// & consequently to HermesFileTree
					self.trace("ProjectView: setup project on harmonia "+ project.getName() );
					ComponentsRegistry.getComponent("harmonia").setProject(project, next);
				}

			],
			function (err) {
				self.trace("ProjectView: setup project config done on " + project.getName() + " err is ",err );
				if (err) {
					self.doError({msg: err.toString(), err: err});
				}
				next(err);
			}
		);
	},
	/**
	 * Initialize the attribute "downloadStatus" of the project
	 *
	 * this attribute is used only by Phonegap Build.
	 * @private
	 */
	initializeDownloadStatus: function(inProject, inPhonegapEnabled) {
		if (inProject.getDownloadStatus() === undefined && inPhonegapEnabled) {
			var downloadStatus = {};
			var index = 0;

			for (index in Phonegap.UIConfiguration.platformDrawersContent) {
				downloadStatus[Phonegap.UIConfiguration.platformDrawersContent[index].id] = "Ready for download";
			}

			inProject.setDownloadStatus(downloadStatus);
		}
	},

	initializeValidPgbConf: function(inProject, inPhonegapEnabled) {
		if (inProject.getValidPgbConf() === undefined && inPhonegapEnabled) {
			var pgbValidation = {};
			var pgbUiData = Phonegap.UIConfiguration.commonDrawersContent.concat(Phonegap.UIConfiguration.platformDrawersContent);
			var index =0;

			for (index in pgbUiData) {
				//The creation of the pgbValidation drawer attribute and its initialization are done in the same time.
				pgbValidation[pgbUiData[index].id] = {};

				for (var i=0, maxLength = pgbUiData[index].rows.length; i<maxLength; i++) {
					//The creation of the pgbValidation row attribute and its initialization are done in the same time.
					pgbValidation[pgbUiData[index].id][pgbUiData[index].rows[i].name] = true;
				}
				pgbValidation[pgbUiData[index].id]["validDrawer"] = true;
			}

			inProject.setValidPgbConf(pgbValidation);
		}
	},

	projectRemoved: function(inSender, inEvent) {
		ComponentsRegistry.getComponent("harmonia").setProject(null, ares.noNext);
	},
	/**
	 * Event handler: handle build project action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	buildAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectSaveAndAction(project, 'build', 'build');
		}
		return true; // stop bubble-up
	},

	/**
	 * Event handler: handle install application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	installAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectSaveAndAction(project, 'test', 'install');
		}
		return true; // stop bubble-up
	},
	/**
	 * Event handler: handle run application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	runAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectSaveAndAction(project, 'test', 'run');
		}
		return true; // stop bubble-up
	},
	/**
	 * Event handler: handle debug application action (select provider & run action)
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	runDebugAction: function(inSender, inEvent) {
		var project = inEvent && inEvent.project;
		if (project) {
			this.projectSaveAndAction(project, 'test', 'runDebug');
		}
		return true; // stop bubble-up
	},

	/**
	 * Request to save project and perform action
	 * @param {Ares.Model.Project} project
	 * @param {String} serviceType
	 * @param {String} action
	 */
	projectSaveAndAction: function(project, serviceType, action) {
		var cb = function (err) {
			if (err) {
				this.trace(err);
			} else {
				this.projectAction( project, serviceType, action);
			}
		};
		if (project) {
			this.doProjectSave({ project: project, callback: cb });
		}
	},

	/**
	 * @private
	 */
	projectAction: function(project, serviceType, action) {
		var self = this;
		this.doShowWaitPopup({msg: "Starting: " + action, service: serviceType});
		// TODO: Must be reworked to allow the selection of builder/tester in the UI - ENYO-2049
		var services = ServiceRegistry.instance.getServicesByType(serviceType);
		var provider =	services[services.length - 1];
		if (!provider) {
			this.doError({msg: 'No ' + serviceType + ' service available'});
		} else if (typeof provider[action] !== 'function') {
			this.doError({msg: 'Service ' + provider.name + ' does not provide action: ' + action});
		} else {
			provider[action](project, function(inError) {
				self.doHideWaitPopup();
				self.refreshFileTree(project.getFolderId());
				if (inError) {
					self.doError({msg: inError.toString(), err: inError});
				}
			});
		}
	},

	launchPreview: function (project) {
		var cb = function (err) {
			if (err) {
				this.trace(err);
			} else {
				this._launchPreview(project);
			}
		};
		this.doProjectSave({ project: project, callback: cb.bind(this) });
	},

	_launchPreview: function (project) {
		var config = project.getConfig() ;
		var topFile = config.data.preview.top_file ;
		var projectUrl = project.getProjectUrl() + '/' + topFile ;

		// the last replace method is needed for test environment only
		var winLoc = window.location.toString().replace('ares','preview').replace('test', 'index') ;
		var previewUrl = winLoc
				+ ( winLoc.indexOf('?') != -1 ? '&' : '?' )
				+ 'url=' + encodeURIComponent(projectUrl)+'&name=' + project.id;

		this.trace("preview on URL ", previewUrl) ;

		window.open(
			previewUrl,
			'_blank', // ensure that a new window is created each time preview is tapped
			'scrollbars=0,menubar=1,resizable=1',
			false
		);
	},

	/**
	 * Event handler: Launch a preview widget of the selected project in a separate frame
	 * @param {enyo.Component} inSender
	 * @param {Object} inEvent
	 * @property inEvent {Ares.Model.Project} project
	 * @private
	 */
	previewAction: function(inSender, inEvent) {
		var project = inEvent.project;
		if ( project) {
			this.launchPreview(project);
		}
		return true; // stop the bubble
	}
});
<|endoftext|>


<commit_before>/**
 *
 * Process monitor sensor
 *
 * (c) 2014 James Hall
 */

var os = require('os'),
	fs = require('fs'),
	child_process = require('child_process');

var plugin = {
	/**
	 * This appears in the title of the graph
	 */
	title: 'Process List',
	description:
		'This returns a process list, grouped by executable name. CPU % is divided by the number of cores. ' +
		'100% CPU Usage is all cores being maxed out. Unlike other tools that define the maximum as 800% for 8 cores for example.',
	/**
	 * The type of sensor
	 * @type {String}
	 */
	type: 'table',
	/**
	 * The default interval time in ms that this plugin should be polled.
	 * More costly benchmarks should be polled less frequently.
	 */
	interval: 2000,

	columns: ['Command', 'CPU %', 'Count', 'Memory %'],
	currentValue: [{
		'Command': 'Google Chrome',
		'Count': '4',
		'CPU %': '0.4',
		'Memory %': '1'
	}, {
		'Command': 'Sublime Text 2',
		'Count': '1',
		'CPU %': '0.1',
		'Memory': '5'
	}],

	// comm, count calced, cp, pmem

	/**
	 * Grab the current value for the table
	 */
	poll: function() {
		var stats = {};
		// @todo If you can think of a better way of getting process stats,
		// then please feel free to send me a pull request. This is version 0.1
		// and needs some love.
		var ps = child_process.exec('ps -ewwwo %cpu,%mem,comm', function (error, stdout, stderr) {
			var lines = stdout.split("\n");
			// Ditch the first line
			lines[0] = '';
			for (var line in lines) {
				var currentLine = lines[line].trim().replace('  ', ' ');
				//console.log(currentLine);
				var words = currentLine.split(" ");
				if (typeof words[0] !== 'undefined' && typeof words[1] !== 'undefined' ) {
					var cpu = words[0];
					var mem = words[1];
					var offset = cpu.length + mem.length + 2;
					var comm = currentLine.slice(offset);
					// If we're on Mac then remove the path
					if (/^darwin/.test(process.platform)) {
						comm = comm.split('/');
						comm = comm[comm.length - 1];
					} else {
						// Otherwise assume linux and remove the unnecessary /1 info like
						// you get on kworker
						comm = comm.split('/');
						comm = comm[0];
					}
					// If already exists, then add them together
					if (typeof stats[comm] !== 'undefined') {
						stats[comm] = {
							cpu: parseFloat(stats[comm].cpu, 10) + parseFloat(cpu),
							mem: parseFloat(stats[comm].mem, 10) + parseFloat(mem),
							comm: comm,
							count: parseInt(stats[comm].count, 10) + 1
						};
					} else {
						stats[comm] = {
							cpu: cpu,
							mem: mem,
							comm: comm,
							count: 1
						};
					}
				}
			}
			var statsArray = [];
			for (var stat in stats) {
				// Divide by nuber of CPU cores
				var cpuRounded = parseFloat(stats[stat].cpu / os.cpus().length).toFixed(1);
				var memRounded = parseFloat(stats[stat].mem).toFixed(1);
				statsArray.push({
					'Command': stats[stat].comm,
					'Count': stats[stat].count,
					'CPU %': cpuRounded,
					'Memory %':  memRounded,
					'cpu': stats[stat].cpu // exact cpu for comparison
				});
			}
			statsArray.sort(function(a, b) {
				return b.cpu - a.cpu;
			});
			plugin.currentValue = statsArray;
		});
	}
};

module.exports = exports = plugin;<commit_msg>Replace ',' on '.' in cpu and mem. Fix #6<commit_after>/**
 *
 * Process monitor sensor
 *
 * (c) 2014 James Hall
 */

var os = require('os'),
	fs = require('fs'),
	child_process = require('child_process');

var plugin = {
	/**
	 * This appears in the title of the graph
	 */
	title: 'Process List',
	description:
		'This returns a process list, grouped by executable name. CPU % is divided by the number of cores. ' +
		'100% CPU Usage is all cores being maxed out. Unlike other tools that define the maximum as 800% for 8 cores for example.',
	/**
	 * The type of sensor
	 * @type {String}
	 */
	type: 'table',
	/**
	 * The default interval time in ms that this plugin should be polled.
	 * More costly benchmarks should be polled less frequently.
	 */
	interval: 2000,

	columns: ['Command', 'CPU %', 'Count', 'Memory %'],
	currentValue: [{
		'Command': 'Google Chrome',
		'Count': '4',
		'CPU %': '0.4',
		'Memory %': '1'
	}, {
		'Command': 'Sublime Text 2',
		'Count': '1',
		'CPU %': '0.1',
		'Memory': '5'
	}],

	// comm, count calced, cp, pmem

	/**
	 * Grab the current value for the table
	 */
	poll: function() {
		var stats = {};
		// @todo If you can think of a better way of getting process stats,
		// then please feel free to send me a pull request. This is version 0.1
		// and needs some love.
		var ps = child_process.exec('ps -ewwwo %cpu,%mem,comm', function (error, stdout, stderr) {
			var lines = stdout.split("\n");
			// Ditch the first line
			lines[0] = '';
			for (var line in lines) {
				var currentLine = lines[line].trim().replace('  ', ' ');
				//console.log(currentLine);
				var words = currentLine.split(" ");
				if (typeof words[0] !== 'undefined' && typeof words[1] !== 'undefined' ) {
					var cpu = words[0].replace(',', '.');
					var mem = words[1].replace(',', '.');
					var offset = cpu.length + mem.length + 2;
					var comm = currentLine.slice(offset);
					// If we're on Mac then remove the path
					if (/^darwin/.test(process.platform)) {
						comm = comm.split('/');
						comm = comm[comm.length - 1];
					} else {
						// Otherwise assume linux and remove the unnecessary /1 info like
						// you get on kworker
						comm = comm.split('/');
						comm = comm[0];
					}
					// If already exists, then add them together
					if (typeof stats[comm] !== 'undefined') {
						stats[comm] = {
							cpu: parseFloat(stats[comm].cpu, 10) + parseFloat(cpu),
							mem: parseFloat(stats[comm].mem, 10) + parseFloat(mem),
							comm: comm,
							count: parseInt(stats[comm].count, 10) + 1
						};
					} else {
						stats[comm] = {
							cpu: cpu,
							mem: mem,
							comm: comm,
							count: 1
						};
					}
				}
			}
			var statsArray = [];
			for (var stat in stats) {
				// Divide by nuber of CPU cores
				var cpuRounded = parseFloat(stats[stat].cpu / os.cpus().length).toFixed(1);
				var memRounded = parseFloat(stats[stat].mem).toFixed(1);
				statsArray.push({
					'Command': stats[stat].comm,
					'Count': stats[stat].count,
					'CPU %': cpuRounded,
					'Memory %':  memRounded,
					'cpu': stats[stat].cpu // exact cpu for comparison
				});
			}
			statsArray.sort(function(a, b) {
				return b.cpu - a.cpu;
			});
			plugin.currentValue = statsArray;
		});
	}
};

module.exports = exports = plugin;
<|endoftext|>


<commit_before>window.addEventListener('DOMContentLoaded', function () {
	'use strict';
	/* global d3, queue, topojson */

	window.debug = {};

	// Graph setup
	var MARGIN = 10;
	var WIDTH = 750;
	var HEIGHT = 750;
	var MAX_SQUARE = WIDTH / 3.5;

	// Additional data
	var schengenCountries = [
		'AT', 'BE', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR',
		'HU', 'IS', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'NL',
		'NO', 'PL', 'PT', 'SK', 'SI', 'ES', 'SE', 'CH'];
	function isSchengenCountry(feature) {
		return schengenCountries.indexOf(feature.id) !== -1;
	}

	// D3 helpers
	function translate(x, y) {
		return 'translate(' + x + ', ' + y + ')';
	}
	// Returns a function that retrieves the specified properties of an object
	// or array, or an arbitrarily nested hierarchy thereof.
	// When invoked without arguments, returns the identity function.
	function ƒ(/* args ... */) {
		var args = arguments, l = arguments.length;
		return function (item) {
			for (var i = 0; i < l; i++) { item = item[args[i]]; }
			return item;
		}
	}
	// Returns the projected centroid for the largest polygon of a MultiPolygon
	// (the polygon with the most coordinates is assumed to be the largest).
	// This is very useful for France whose overseas departments drag its
	// centroid out into the Atlantic Ocean.
	function mainCentroid(path, feature) {
		var coordinates = feature.geometry.coordinates;
		if (coordinates.length > 1) {
			// This could be written more efficiently.
			coordinates = coordinates.slice().sort(function (a, b) {
				return b[0].length - a[0].length;
			}).slice(0, 1);
			feature = { type: feature.type, geometry: {
				type: feature.geometry.type,
				coordinates: coordinates
			} };
		}
		return path.centroid(feature);
	}

	queue()
		.defer(d3.json, 'data/world.topojson')
		.defer(d3.tsv, 'data/applications.tsv')
		.await(function (error, geo, applications) {
			var container = d3.select('.refugee-quota');
			var svg = container.append('svg')
				.attr('class', 'visualization')
				.attr('preserveAspectRatio', 'xMidYMid')
				.attr('viewBox', [ 0, 0, WIDTH, HEIGHT ].join(' '));
			var stage = svg.append('g');
			applications = applications.map(function (row) {
				return {
					country:       row.country,
					count:        +row.count,
					gdp:          +row.gdp,
					gdpPerCapita: +row['gdp/capita'],
					applications: +row.nsum,
					population:	  +row.population,
					markerX:      +row.markerX || 0,
					markerY:      +row.markerY || 0,
				};
			}).sort(function (a, b) {
				return b.gdpPerCapita - a.gdpPerCapita;
			});
			var byCountry = {};
			var totals = {
				applications: d3.sum(applications, ƒ('applications')),
				population:   d3.sum(applications, ƒ('population')),
				gdp:          d3.sum(applications, ƒ('gdp')),
			};
			applications.forEach(function (a) {
				// Calculate relative values
				a.relative = {
					applications: a.applications/totals.applications,
					population:   a.population/totals.population,
					gdp:          a.gdp/totals.gdp,
				};
				// Fill byCountry object
				byCountry[a.country] = a;
			});

			// Scales and formats
			var rPop = d3.scale.sqrt()
				.domain([ 0, totals.population ])
				.range([ 0, MAX_SQUARE ]);
			var rApp = d3.scale.sqrt()
				.domain([ 0, totals.applications ])
				.range([ 0, MAX_SQUARE ]);
			var rGDP = d3.scale.sqrt()
				.domain([ 0, totals.gdp ])
				.range([ 0, MAX_SQUARE ]);
			var projection = d3.geo.conicConformal()
				.parallels([ 65, 35 ])
				.center([ 0, 52 ])
				.rotate([ -10, 0 ])
				.translate([ WIDTH/2, HEIGHT/2 ])
				.clipExtent([ [ -WIDTH, -50 ], [ 3 * WIDTH, HEIGHT + 100 ] ])
				.scale(1300);
			var path = d3.geo.path()
				.projection(projection);
			var numberFormat = d3.format(',f');
			var percentFormat = d3.format('.1%');

			// State
			var metric = 'population';
			var rectSide = {
				population: function (f) { return rPop(f.data.population); },
				gdp:        function (f) { return rGDP(f.data.gdp); },
			};

			// Tooltip
			var tooltip = (function () {
				var tt = container.append('aside')
					.datum(null)
					.attr('class', 'tooltip hidden');
				var countryName = tt.append('header').append('h3');
				var values = tt.append('div').attr('class', 'values');
				var stats = [
					{ name: 'applications', label: 'Applications', relative: true },
					{ name: 'by-metric',
						label: function () { return 'Applications if they were distributed by ' + metric; },
						value: function (f) { return totals.applications * f.data.relative[metric]; }
					},
					{ name: 'gdp', label: 'GDP', unit: '€ _ M', relative: true },
					{ name: 'population', label: 'Population', relative: true },
				];
				var statsP = values.selectAll('.statistic')
					.data(stats)
					.enter()
					.append('p')
						.attr('class', 'statistic');
				var statsSpan = statsP.append('span').attr('class', 'number')
				var statLabels = statsP.append('span');

				var statsRelative = statsP.filter(ƒ('relative')).append('small');
				var statsRelativeSpan = statsRelative.append('span');
				statsRelative.append('span').text(' of EU total');

				function show(f) {
					if (tt.datum() === f) return;
					if (!f.data) return;
					tt.datum(f);
					tt.classed('hidden', false);

					// Set position
					var group = countryGroups.filter(function (g) { return g === f; });
					var gRect = group.node().getBoundingClientRect();
					var cRect = container.node().getBoundingClientRect();
					var left, right;
					if (gRect.left + gRect.width / 2 > cRect.width / 2) {
						left = 'auto';
						right = cRect.width - gRect.left + 5 + 'px';
					} else {
						left = (gRect.left - cRect.left) + gRect.width + 5 + 'px';
						right = 'auto';
					}
					tt.style({
						left: left, right: right,
						top: gRect.top - cRect.top + 'px' });

					// Fill elements
					countryName.text(f.properties.name);
					statLabels.text(function (s) {
						return (typeof s.label === 'function') ? s.label() : s.label; });
					statsSpan.text(function (s) {
						var unit = s.unit || '_';
						if (s.value) return numberFormat(s.value(f))
						else return unit.replace('_', numberFormat(f.data[s.name])); });
					statsRelativeSpan.text(function (s) { return percentFormat(f.data.relative[s.name]); });
					//adjustedCount.text(numberFormat(totals.applications * f.data.relative[metric]));
				}
				function hide() {
					if (!tt.datum()) return;
					tt.datum(null);
					tt.classed('hidden', true);
				}
				return { show: show, hide: hide };
			})();

			var world = topojson.feature(geo, geo.objects.ne_50m_admin_0_countries);
			var borders = topojson.mesh(geo, geo.objects.ne_50m_admin_0_countries,
				function(a, b) { return a !== b; });
			var schengen = topojson.merge(geo, geo.objects.ne_50m_admin_0_countries.geometries.filter(isSchengenCountry));
			world.features.forEach(function (f) {
				f.data = byCountry[f.id];
				if (f.data) f.data.feature = f;
			});

			// Project the squares' coordinates
			applications.forEach(function (a) {
				var feature = a.feature;
				if (!feature) return;
				var centroid = mainCentroid(path, feature);
				feature.x = centroid[0];
				feature.y = centroid[1];
				feature.w = Math.max( rPop(a.population), rGDP(a.gdp), rApp(a.applications) );
			});
			debug.world = world;

			var paths = stage.selectAll('path')
				.data(world.features)
				.enter()
				.append('path')
				.attr('class', 'country')
				.classed('no-data', function (f) { return !f.data; })
				.attr('d', path)
				.on('mouseenter', tooltip.show)
				.on('mouseleave', tooltip.hide);
			var borderPaths = stage.append('path')
				.datum(borders)
				.attr('class', 'border')
				.attr('d', path);
			var schengenPath = stage.append('path')
				.datum(schengen)
				.attr('class', 'schengen')
				.attr('d', path);

			var countryGroups = stage.selectAll('g.country')
				.data(world.features.filter(ƒ('data')))
				.enter()
				.append('g')
				.attr('class', 'country')
				.attr('transform', function (f) {
					return translate(f.x - f.w / 2 + f.data.markerX, f.y + f.w / 2 + f.data.markerY) + ' scale(1, -1)';
				})
				.on('mouseenter', tooltip.show)
				.on('mouseleave', tooltip.hide);

			var metricRects = countryGroups.append('rect')
				.attr('class', 'metric')
				.attr('width', function (f) { return rPop(f.data.population); })
				.attr('height', function (f) { return rPop(f.data.population); });

			var applicationRects = countryGroups.append('rect')
				.attr('class', 'application-count')
				.attr('width', function (f) { return rApp(f.data.applications); })
				.attr('height', function (f) { return rApp(f.data.applications); });

			var metricRectOutlines = countryGroups.append('rect')
				.attr('class', 'metric-outline')
				.attr('width', function (f) { return rPop(f.data.population); })
				.attr('height', function (f) { return rPop(f.data.population); });

			// Legend
			var legend = container.append('div')
				.attr('class', 'legend');
			var dl = legend.append('dl');
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 30 })
					.append('rect')
						.attr({ 'class': 'application-count', x: 3, y: 3, width: 24, height: 24 });
			dl.append('dd')
				.text('Number of asylum applications by Syrians, Eritreans, and Iraqis (April—June 2015*)')
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 30 })
					.selectAll('rect')
					.data([ 'metric', 'metric-outline' ])
					.enter()
					.append('rect')
						.attr({ 'class': ƒ(), x: 3, y: 3, width: 24, height: 24 });
			var metricControl = dl.append('dd')
				.attr('class', 'controls')
				.text('Number of applications if they were equally distributed by ')
				.selectAll('button')
					.data([ 'population', 'GDP' ])
					.enter()
					.append('button')
					.text(ƒ())
					.on('click', setMetric);
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 4 })
					.append('path')
						.attr('class', 'schengen')
						.attr('d', 'M 3 2 L 27 2');
			dl.append('dd')
				.text('Schengen Area');

			legend.append('p').append('small').text('* June data estimated for Cyprus')

			// As a sanity check, calculate the total area of all three sets of rects
			var totalAreas = { population: 0, gdp: 0, applications: 0 };
			applications.forEach(function (d) {
				totalAreas.population   += Math.pow(rPop(d.population), 2);
				totalAreas.gdp          += Math.pow(rGDP(d.gdp), 2);
				totalAreas.applications += Math.pow(rApp(d.applications), 2);
			});
			window.debug.totalAreas = totalAreas;

			function resize() {
				var rect = svg.node().getBoundingClientRect();
				var width = Math.min(rect.width - 2 * MARGIN, WIDTH);
				var height = rect.height - 2 * MARGIN;

				var fontSize = 400/Math.sqrt(width);

				svg.selectAll('text').attr('font-size', fontSize);
			}
			function setMetric(m) {
				d3.selectAll('.js-metric-name').text(m);
				metric = m.toLowerCase();
				metricControl.classed('active', function (m) { return m.toLowerCase() === metric; });
				metricRects.transition().attr('width', rectSide[metric]).attr('height', rectSide[metric]);
				metricRectOutlines.transition().attr('width', rectSide[metric]).attr('height', rectSide[metric]);
			}
			setMetric('population');

			window.debug.setMetric = setMetric;
			window.addEventListener('resize', resize);
			resize();
		});
});
<commit_msg>Allow defining custom data directory<commit_after>window.addEventListener('DOMContentLoaded', function () {
	'use strict';
	/* global d3, queue, topojson */

	window.debug = {};

	var container = d3.select('.refugee-quota');
	var DATA_DIR = container.attr('data-datadir') || '';

	// Graph setup
	var MARGIN = 10;
	var WIDTH = 750;
	var HEIGHT = 750;
	var MAX_SQUARE = WIDTH / 3.5;

	// Additional data
	var schengenCountries = [
		'AT', 'BE', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR',
		'HU', 'IS', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'NL',
		'NO', 'PL', 'PT', 'SK', 'SI', 'ES', 'SE', 'CH'];
	function isSchengenCountry(feature) {
		return schengenCountries.indexOf(feature.id) !== -1;
	}

	// D3 helpers
	function translate(x, y) {
		return 'translate(' + x + ', ' + y + ')';
	}
	// Returns a function that retrieves the specified properties of an object
	// or array, or an arbitrarily nested hierarchy thereof.
	// When invoked without arguments, returns the identity function.
	function ƒ(/* args ... */) {
		var args = arguments, l = arguments.length;
		return function (item) {
			for (var i = 0; i < l; i++) { item = item[args[i]]; }
			return item;
		}
	}
	// Returns the projected centroid for the largest polygon of a MultiPolygon
	// (the polygon with the most coordinates is assumed to be the largest).
	// This is very useful for France whose overseas departments drag its
	// centroid out into the Atlantic Ocean.
	function mainCentroid(path, feature) {
		var coordinates = feature.geometry.coordinates;
		if (coordinates.length > 1) {
			// This could be written more efficiently.
			coordinates = coordinates.slice().sort(function (a, b) {
				return b[0].length - a[0].length;
			}).slice(0, 1);
			feature = { type: feature.type, geometry: {
				type: feature.geometry.type,
				coordinates: coordinates
			} };
		}
		return path.centroid(feature);
	}

	queue()
		.defer(d3.json, DATA_DIR + 'data/world.topojson')
		.defer(d3.tsv, DATA_DIR + 'data/applications.tsv')
		.await(function (error, geo, applications) {
			var svg = container.append('svg')
				.attr('class', 'visualization')
				.attr('preserveAspectRatio', 'xMidYMid')
				.attr('viewBox', [ 0, 0, WIDTH, HEIGHT ].join(' '));
			var stage = svg.append('g');
			applications = applications.map(function (row) {
				return {
					country:       row.country,
					count:        +row.count,
					gdp:          +row.gdp,
					gdpPerCapita: +row['gdp/capita'],
					applications: +row.nsum,
					population:	  +row.population,
					markerX:      +row.markerX || 0,
					markerY:      +row.markerY || 0,
				};
			}).sort(function (a, b) {
				return b.gdpPerCapita - a.gdpPerCapita;
			});
			var byCountry = {};
			var totals = {
				applications: d3.sum(applications, ƒ('applications')),
				population:   d3.sum(applications, ƒ('population')),
				gdp:          d3.sum(applications, ƒ('gdp')),
			};
			applications.forEach(function (a) {
				// Calculate relative values
				a.relative = {
					applications: a.applications/totals.applications,
					population:   a.population/totals.population,
					gdp:          a.gdp/totals.gdp,
				};
				// Fill byCountry object
				byCountry[a.country] = a;
			});

			// Scales and formats
			var rPop = d3.scale.sqrt()
				.domain([ 0, totals.population ])
				.range([ 0, MAX_SQUARE ]);
			var rApp = d3.scale.sqrt()
				.domain([ 0, totals.applications ])
				.range([ 0, MAX_SQUARE ]);
			var rGDP = d3.scale.sqrt()
				.domain([ 0, totals.gdp ])
				.range([ 0, MAX_SQUARE ]);
			var projection = d3.geo.conicConformal()
				.parallels([ 65, 35 ])
				.center([ 0, 52 ])
				.rotate([ -10, 0 ])
				.translate([ WIDTH/2, HEIGHT/2 ])
				.clipExtent([ [ -WIDTH, -50 ], [ 3 * WIDTH, HEIGHT + 100 ] ])
				.scale(1300);
			var path = d3.geo.path()
				.projection(projection);
			var numberFormat = d3.format(',f');
			var percentFormat = d3.format('.1%');

			// State
			var metric = 'population';
			var rectSide = {
				population: function (f) { return rPop(f.data.population); },
				gdp:        function (f) { return rGDP(f.data.gdp); },
			};

			// Tooltip
			var tooltip = (function () {
				var tt = container.append('aside')
					.datum(null)
					.attr('class', 'tooltip hidden');
				var countryName = tt.append('header').append('h3');
				var values = tt.append('div').attr('class', 'values');
				var stats = [
					{ name: 'applications', label: 'Applications', relative: true },
					{ name: 'by-metric',
						label: function () { return 'Applications if they were distributed by ' + metric; },
						value: function (f) { return totals.applications * f.data.relative[metric]; }
					},
					{ name: 'gdp', label: 'GDP', unit: '€ _ M', relative: true },
					{ name: 'population', label: 'Population', relative: true },
				];
				var statsP = values.selectAll('.statistic')
					.data(stats)
					.enter()
					.append('p')
						.attr('class', 'statistic');
				var statsSpan = statsP.append('span').attr('class', 'number')
				var statLabels = statsP.append('span');

				var statsRelative = statsP.filter(ƒ('relative')).append('small');
				var statsRelativeSpan = statsRelative.append('span');
				statsRelative.append('span').text(' of EU total');

				function show(f) {
					if (tt.datum() === f) return;
					if (!f.data) return;
					tt.datum(f);
					tt.classed('hidden', false);

					// Set position
					var group = countryGroups.filter(function (g) { return g === f; });
					var gRect = group.node().getBoundingClientRect();
					var cRect = container.node().getBoundingClientRect();
					var left, right;
					if (gRect.left + gRect.width / 2 > cRect.width / 2) {
						left = 'auto';
						right = cRect.width - gRect.left + 5 + 'px';
					} else {
						left = (gRect.left - cRect.left) + gRect.width + 5 + 'px';
						right = 'auto';
					}
					tt.style({
						left: left, right: right,
						top: gRect.top - cRect.top + 'px' });

					// Fill elements
					countryName.text(f.properties.name);
					statLabels.text(function (s) {
						return (typeof s.label === 'function') ? s.label() : s.label; });
					statsSpan.text(function (s) {
						var unit = s.unit || '_';
						if (s.value) return numberFormat(s.value(f))
						else return unit.replace('_', numberFormat(f.data[s.name])); });
					statsRelativeSpan.text(function (s) { return percentFormat(f.data.relative[s.name]); });
					//adjustedCount.text(numberFormat(totals.applications * f.data.relative[metric]));
				}
				function hide() {
					if (!tt.datum()) return;
					tt.datum(null);
					tt.classed('hidden', true);
				}
				return { show: show, hide: hide };
			})();

			var world = topojson.feature(geo, geo.objects.ne_50m_admin_0_countries);
			var borders = topojson.mesh(geo, geo.objects.ne_50m_admin_0_countries,
				function(a, b) { return a !== b; });
			var schengen = topojson.merge(geo, geo.objects.ne_50m_admin_0_countries.geometries.filter(isSchengenCountry));
			world.features.forEach(function (f) {
				f.data = byCountry[f.id];
				if (f.data) f.data.feature = f;
			});

			// Project the squares' coordinates
			applications.forEach(function (a) {
				var feature = a.feature;
				if (!feature) return;
				var centroid = mainCentroid(path, feature);
				feature.x = centroid[0];
				feature.y = centroid[1];
				feature.w = Math.max( rPop(a.population), rGDP(a.gdp), rApp(a.applications) );
			});
			debug.world = world;

			var paths = stage.selectAll('path')
				.data(world.features)
				.enter()
				.append('path')
				.attr('class', 'country')
				.classed('no-data', function (f) { return !f.data; })
				.attr('d', path)
				.on('mouseenter', tooltip.show)
				.on('mouseleave', tooltip.hide);
			var borderPaths = stage.append('path')
				.datum(borders)
				.attr('class', 'border')
				.attr('d', path);
			var schengenPath = stage.append('path')
				.datum(schengen)
				.attr('class', 'schengen')
				.attr('d', path);

			var countryGroups = stage.selectAll('g.country')
				.data(world.features.filter(ƒ('data')))
				.enter()
				.append('g')
				.attr('class', 'country')
				.attr('transform', function (f) {
					return translate(f.x - f.w / 2 + f.data.markerX, f.y + f.w / 2 + f.data.markerY) + ' scale(1, -1)';
				})
				.on('mouseenter', tooltip.show)
				.on('mouseleave', tooltip.hide);

			var metricRects = countryGroups.append('rect')
				.attr('class', 'metric')
				.attr('width', function (f) { return rPop(f.data.population); })
				.attr('height', function (f) { return rPop(f.data.population); });

			var applicationRects = countryGroups.append('rect')
				.attr('class', 'application-count')
				.attr('width', function (f) { return rApp(f.data.applications); })
				.attr('height', function (f) { return rApp(f.data.applications); });

			var metricRectOutlines = countryGroups.append('rect')
				.attr('class', 'metric-outline')
				.attr('width', function (f) { return rPop(f.data.population); })
				.attr('height', function (f) { return rPop(f.data.population); });

			// Legend
			var legend = container.append('div')
				.attr('class', 'legend');
			var dl = legend.append('dl');
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 30 })
					.append('rect')
						.attr({ 'class': 'application-count', x: 3, y: 3, width: 24, height: 24 });
			dl.append('dd')
				.text('Number of asylum applications by Syrians, Eritreans, and Iraqis (April—June 2015*)')
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 30 })
					.selectAll('rect')
					.data([ 'metric', 'metric-outline' ])
					.enter()
					.append('rect')
						.attr({ 'class': ƒ(), x: 3, y: 3, width: 24, height: 24 });
			var metricControl = dl.append('dd')
				.attr('class', 'controls')
				.text('Number of applications if they were equally distributed by ')
				.selectAll('button')
					.data([ 'population', 'GDP' ])
					.enter()
					.append('button')
					.text(ƒ())
					.on('click', setMetric);
			dl.append('dt')
				.append('svg')
					.attr({ width: 30, height: 4 })
					.append('path')
						.attr('class', 'schengen')
						.attr('d', 'M 3 2 L 27 2');
			dl.append('dd')
				.text('Schengen Area');

			legend.append('p').append('small').text('* June data estimated for Cyprus')

			// As a sanity check, calculate the total area of all three sets of rects
			var totalAreas = { population: 0, gdp: 0, applications: 0 };
			applications.forEach(function (d) {
				totalAreas.population   += Math.pow(rPop(d.population), 2);
				totalAreas.gdp          += Math.pow(rGDP(d.gdp), 2);
				totalAreas.applications += Math.pow(rApp(d.applications), 2);
			});
			window.debug.totalAreas = totalAreas;

			function resize() {
				var rect = svg.node().getBoundingClientRect();
				var width = Math.min(rect.width - 2 * MARGIN, WIDTH);
				var height = rect.height - 2 * MARGIN;

				var fontSize = 400/Math.sqrt(width);

				svg.selectAll('text').attr('font-size', fontSize);
			}
			function setMetric(m) {
				d3.selectAll('.js-metric-name').text(m);
				metric = m.toLowerCase();
				metricControl.classed('active', function (m) { return m.toLowerCase() === metric; });
				metricRects.transition().attr('width', rectSide[metric]).attr('height', rectSide[metric]);
				metricRectOutlines.transition().attr('width', rectSide[metric]).attr('height', rectSide[metric]);
			}
			setMetric('population');

			window.debug.setMetric = setMetric;
			window.addEventListener('resize', resize);
			resize();
		});
});
<|endoftext|>


<commit_before>'use strict'

module.exports = function (req, res) {
};<commit_msg>send json with null properties<commit_after>'use strict'

module.exports = function (req, res) {
    var timestamp = {
        unix: null,
        natural: null
    }
    res.json(timestamp);
};<|endoftext|>


<commit_before>/*
 * loquat-combinators / combinators.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module combinators
 */

"use strict";

module.exports = _core => {
    function end() {
        return Object.freeze({
            choice,
            option,
            optionMaybe,
            optional,
            between,
            many1,
            skipMany1
        });
    }

    const _prim = require("loquat-prim")(_core);
    const map      = _prim.map;
    const pure     = _prim.pure;
    const bind     = _prim.bind;
    const then     = _prim.then;
    const mzero    = _prim.mzero;
    const mplus    = _prim.mplus;
    const many     = _prim.many;
    const skipMany = _prim.skipMany;

    /**
     * @function module:combinators.choice
     * @static
     * @param {Array.<AbstractParser>} parsers
     * @returns {AbstractParser}
     */
    function choice(parsers) {
        return parsers.reduceRight((accum, parser) => mplus(parser, accum), mzero);
    }

    /**
     * @function module:combinators.option
     * @static
     * @param {*} val
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function option(val, parser) {
        return mplus(parser, pure(val));
    }

    /**
     * @function module:combinators.optionmaybe
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function optionMaybe(parser) {
        return mplus(
            map(parser, val => ({ empty: false, value: val })),
            map(pure(undefined), () => ({ empty: true }))
        );
    }

    /**
     * @function module:combinators.optional
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function optional(parser) {
        return mplus(then(parser, pure(undefined)), pure(undefined));
    }

    /**
     * @function module:combinators.between
     * @static
     * @param {AbstractParser} open
     * @param {AbstractParser} close
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function between(open, close, parser) {
        return then(open,
            bind(parser, val =>
                then(close,
                    pure(val)
                )
            )
        );
    }

    /**
     * @function module:combinators.many1
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function many1(parser) {
        return bind(parser, head =>
            bind(many(parser), tail =>
                pure([head].concat(tail))
            )
        );
    }

    /**
     * @function module:combinators.skipMany1
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function skipMany1(parser) {
        return then(parser, skipMany(parser));
    }

    return end();
};
<commit_msg>[combinators] Add `sepBy()` and `sepBy1()`<commit_after>/*
 * loquat-combinators / combinators.js
 * copyright (c) 2016 Susisu
 */

/**
 * @module combinators
 */

"use strict";

module.exports = _core => {
    function end() {
        return Object.freeze({
            choice,
            option,
            optionMaybe,
            optional,
            between,
            many1,
            skipMany1,
            sepBy,
            sepBy1
        });
    }

    const _prim = require("loquat-prim")(_core);
    const map      = _prim.map;
    const pure     = _prim.pure;
    const bind     = _prim.bind;
    const then     = _prim.then;
    const mzero    = _prim.mzero;
    const mplus    = _prim.mplus;
    const many     = _prim.many;
    const skipMany = _prim.skipMany;

    /**
     * @function module:combinators.choice
     * @static
     * @param {Array.<AbstractParser>} parsers
     * @returns {AbstractParser}
     */
    function choice(parsers) {
        return parsers.reduceRight((accum, parser) => mplus(parser, accum), mzero);
    }

    /**
     * @function module:combinators.option
     * @static
     * @param {*} val
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function option(val, parser) {
        return mplus(parser, pure(val));
    }

    /**
     * @function module:combinators.optionmaybe
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function optionMaybe(parser) {
        return mplus(
            map(parser, val => ({ empty: false, value: val })),
            map(pure(undefined), () => ({ empty: true }))
        );
    }

    /**
     * @function module:combinators.optional
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function optional(parser) {
        return mplus(then(parser, pure(undefined)), pure(undefined));
    }

    /**
     * @function module:combinators.between
     * @static
     * @param {AbstractParser} open
     * @param {AbstractParser} close
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function between(open, close, parser) {
        return then(open,
            bind(parser, val =>
                then(close,
                    pure(val)
                )
            )
        );
    }

    /**
     * @function module:combinators.many1
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function many1(parser) {
        return bind(parser, head =>
            bind(many(parser), tail =>
                pure([head].concat(tail))
            )
        );
    }

    /**
     * @function module:combinators.skipMany1
     * @static
     * @param {AbstractParser} parser
     * @returns {AbstractParser}
     */
    function skipMany1(parser) {
        return then(parser, skipMany(parser));
    }

    /**
     * @function module:combinators.sepBy
     * @static
     * @param {AbstractParser} parser
     * @param {AbstractParser} sep
     * @returns {AbstractParser}
     */
    function sepBy(parser, sep) {
        return mplus(
            sepBy1(parser, sep),
            map(pure(undefined), () => [])
        );
    }

    /**
     * @function module:combinators.sepBy1
     * @static
     * @param {AbstractParser} parser
     * @param {AbstractParser} sep
     * @returns {AbstractParser}
     */
    function sepBy1(parser, sep) {
        return bind(parser, head =>
            bind(many(then(sep, parser)), tail =>
                pure([head].concat(tail))
            )
        );
    }

    return end();
};
<|endoftext|>


<commit_before>const path = require("path");
const webpack = require("webpack");
const babel = require("./config/babel");
const uglify = require("./config/uglify");

const env = process.env.NODE_ENV || "development";
const useCordova = (process.env.USE_CORDOVA || "false") === "true";
const isProd = env === "production";
const hotReload = process.env.LIVERELOAD === "true" && !isProd;
const out = path.resolve(__dirname, "dist");
const exclusions = /node_modules/;

const ExtractText = require("extract-text-webpack-plugin");
const extractShellCss = new ExtractText("shell.[hash].css");
const extractOtherCss = new ExtractText("styles.[hash].css");

process.stderr.write(`Building with env = ${env}\n`);

// plugin management
const HTML = require("html-webpack-plugin");
const Clean = require("clean-webpack-plugin");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
const plugins = [
  ...require("maji/lib/webpack").plugins,
  new HTML({
    template: "src/index.html",
    useCordova,
    inject: false,
    minify: false
  }),
  new Clean(["dist"], { verbose: false, exclude: [".keep"] }),
  extractShellCss,
  extractOtherCss,
  new SpriteLoaderPlugin()
];

const optimization = {
  splitChunks: {
    name: "vendor"
  },
  minimize: isProd,
  // prints more readable module names in the browser console on HMR updates, in dev
  namedModules: !isProd,
  // prevent emitting assets with errors, in dev
  noEmitOnErrors: !isProd
};

if (!isProd && hotReload) {
  plugins.push(
    // enable HMR globally
    new webpack.HotModuleReplacementPlugin()
  );
}
// end of plugin management

// optionally live-reloadable entry points
const entryPoints = function() {
  const items = hotReload
    ? ["webpack-hot-middleware/client?noInfo=true&reload=true"]
    : [];
  items.push(...arguments);
  return items;
};

const postcssLoader = {
  loader: "postcss-loader",
  options: {
    plugins: () => [require("autoprefixer")]
  }
};

/**
 * All of Maji's peerDependencies will be aliased to the versions
 * in this app's node_modules folder. This prevents building with
 * multiple versions of the same dependency.
 *
 * See: https://medium.com/@penx/managing-dependencies-in-a-node-package-so-that-they-are-compatible-with-npm-link-61befa5aaca7
 */
const majiAliases = (function() {
  const dependencies = require("maji/package.json").peerDependencies || {};
  return Object.keys(dependencies).reduce((aliases, packageName) => {
    aliases[packageName] = path.resolve(
      __dirname,
      `./node_modules/${packageName}`
    );
    return aliases;
  }, {});
})();

module.exports = {
  mode: isProd ? "production" : "development",
  entry: {
    app: entryPoints("./src/index.js"),
    vendor: ["preact", "preact-router"]
  },
  output: {
    path: out,
    filename: "[name].[hash].js",
    publicPath: "./"
  },
  module: {
    rules: [
      {
        test: /.*/,
        include: path.resolve(__dirname, "src/assets"),
        exclude: path.resolve(__dirname, "src/assets/icons"),
        options: {
          name: "[name]-[hash].[ext]"
        },
        loader: "file-loader"
      },
      {
        test: /\.jsx?$/,
        exclude: exclusions,
        loader: "babel-loader",
        options: babel
      },
      { test: /\.yml$/, loader: "json-loader!yaml-loader" },
      {
        test: /\.scss$/,
        loader: isProd
          ? extractOtherCss.extract({
              use: ["css-loader?modules", postcssLoader, "sass-loader"]
            })
          : [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  modules: true,
                  localIdentName: "[path][name]__[local]--[hash:base64:5]"
                }
              },
              postcssLoader,
              "sass-loader"
            ],
        exclude: /shell.scss$/
      },
      {
        test: /shell.scss$/,
        loader: isProd
          ? extractShellCss.extract({
              use: ["css-loader", postcssLoader, "sass-loader"]
            })
          : ["style-loader", "css-loader", postcssLoader, "sass-loader"]
      },
      {
        test: /\.svg$/,
        include: path.resolve(__dirname, "src/assets/icons"),
        loader: "svg-sprite-loader",
        options: {
          extract: true
        }
      }
    ]
  },
  resolve: {
    alias: Object.assign(
      {
        src: path.resolve(__dirname, "./src"),
        config: path.resolve(__dirname, "./config")
      },
      majiAliases
    ),
    symlinks: false
  },
  devtool: isProd ? "source-map" : "eval",
  plugins,
  optimization
};
<commit_msg>Get rid of explicit vendor chunk<commit_after>const path = require("path");
const webpack = require("webpack");
const babel = require("./config/babel");
const uglify = require("./config/uglify");

const env = process.env.NODE_ENV || "development";
const useCordova = (process.env.USE_CORDOVA || "false") === "true";
const isProd = env === "production";
const hotReload = process.env.LIVERELOAD === "true" && !isProd;
const out = path.resolve(__dirname, "dist");
const exclusions = /node_modules/;

const ExtractText = require("extract-text-webpack-plugin");
const extractShellCss = new ExtractText("shell.[hash].css");
const extractOtherCss = new ExtractText("styles.[hash].css");

process.stderr.write(`Building with env = ${env}\n`);

// plugin management
const HTML = require("html-webpack-plugin");
const Clean = require("clean-webpack-plugin");
const SpriteLoaderPlugin = require("svg-sprite-loader/plugin");
const plugins = [
  ...require("maji/lib/webpack").plugins,
  new HTML({
    template: "src/index.html",
    useCordova,
    inject: false,
    minify: false
  }),
  new Clean(["dist"], { verbose: false, exclude: [".keep"] }),
  extractShellCss,
  extractOtherCss,
  new SpriteLoaderPlugin()
];

const optimization = {
  splitChunks: {
    chunks: "all"
  },
  minimize: isProd,
  // prints more readable module names in the browser console on HMR updates, in dev
  namedModules: !isProd,
  // prevent emitting assets with errors, in dev
  noEmitOnErrors: !isProd
};

if (!isProd && hotReload) {
  plugins.push(
    // enable HMR globally
    new webpack.HotModuleReplacementPlugin()
  );
}
// end of plugin management

// optionally live-reloadable entry points
const entryPoints = function() {
  const items = hotReload
    ? ["webpack-hot-middleware/client?noInfo=true&reload=true"]
    : [];
  items.push(...arguments);
  return items;
};

const postcssLoader = {
  loader: "postcss-loader",
  options: {
    plugins: () => [require("autoprefixer")]
  }
};

/**
 * All of Maji's peerDependencies will be aliased to the versions
 * in this app's node_modules folder. This prevents building with
 * multiple versions of the same dependency.
 *
 * See: https://medium.com/@penx/managing-dependencies-in-a-node-package-so-that-they-are-compatible-with-npm-link-61befa5aaca7
 */
const majiAliases = (function() {
  const dependencies = require("maji/package.json").peerDependencies || {};
  return Object.keys(dependencies).reduce((aliases, packageName) => {
    aliases[packageName] = path.resolve(
      __dirname,
      `./node_modules/${packageName}`
    );
    return aliases;
  }, {});
})();

module.exports = {
  mode: isProd ? "production" : "development",
  entry: {
    app: entryPoints("./src/index.js")
  },
  output: {
    path: out,
    filename: "[name].[hash].js",
    publicPath: "./"
  },
  module: {
    rules: [
      {
        test: /.*/,
        include: path.resolve(__dirname, "src/assets"),
        exclude: path.resolve(__dirname, "src/assets/icons"),
        options: {
          name: "[name]-[hash].[ext]"
        },
        loader: "file-loader"
      },
      {
        test: /\.jsx?$/,
        exclude: exclusions,
        loader: "babel-loader",
        options: babel
      },
      { test: /\.yml$/, loader: "json-loader!yaml-loader" },
      {
        test: /\.scss$/,
        loader: isProd
          ? extractOtherCss.extract({
              use: ["css-loader?modules", postcssLoader, "sass-loader"]
            })
          : [
              "style-loader",
              {
                loader: "css-loader",
                options: {
                  modules: true,
                  localIdentName: "[path][name]__[local]--[hash:base64:5]"
                }
              },
              postcssLoader,
              "sass-loader"
            ],
        exclude: /shell.scss$/
      },
      {
        test: /shell.scss$/,
        loader: isProd
          ? extractShellCss.extract({
              use: ["css-loader", postcssLoader, "sass-loader"]
            })
          : ["style-loader", "css-loader", postcssLoader, "sass-loader"]
      },
      {
        test: /\.svg$/,
        include: path.resolve(__dirname, "src/assets/icons"),
        loader: "svg-sprite-loader",
        options: {
          extract: true
        }
      }
    ]
  },
  resolve: {
    alias: Object.assign(
      {
        src: path.resolve(__dirname, "./src"),
        config: path.resolve(__dirname, "./config")
      },
      majiAliases
    ),
    symlinks: false
  },
  devtool: isProd ? "source-map" : "eval",
  plugins,
  optimization
};
<|endoftext|>