<commit_before>import {
  GET_PROJECTS,
  ADD_PROJECT,
  RENAME_PROJECT,
  CLEAR_PROJECTS,
  DELETE_PROJECT,
  TOGGLE_PROJECT_DONE,
  FETCH_PROJECTS,
  SUBTRACT_PROJECT_TIME
} from '../actions/actionTypes';

export function projects(state = { list: [] }, action) {
  switch (action.type) {
    case FETCH_PROJECTS:
      return { ...state, isFetching: action.response };
    case ADD_PROJECT: {
      return { ...state, list: [...state.list, action.project] };
    }
    case SUBTRACT_PROJECT_TIME: {
      const list = state.list.map(item => {
        if (
          item._id === action.params.currentProject &&
          action.params.deleteTime === true
        ) {
          item.timeSpent -= action.params.timeSpent || 0; // eslint-disable-line no-param-reassign
        }
        if (
          item._id === action.params.newProject &&
          action.params.moveTime === true
        ) {
          item.timeSpent += action.params.timeSpent || 0; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case RENAME_PROJECT: {
      const list = state.list.map(item => {
        if (item._id === action.project.id) {
          item.name = action.project.name; // eslint-disable-line no-param-reassign
          item.parent_id = action.project.parentID; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case TOGGLE_PROJECT_DONE: {
      const list = state.list.map(item => {
        if (item._id === action.id) {
          item.done = action.done; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case GET_PROJECTS:
      return {
        ...state,
        list: action.response.data.projectsList,
        isFetching: false
      };
    case CLEAR_PROJECTS:
      return action.response;
    case DELETE_PROJECT: {
      const list = state.list.filter(item => item._id !== action.id);
      return { ...state, list };
    }
    default:
      return state;
  }
}

export default projects;
<commit_msg>dont forget about parents<commit_after>import deepClone from 'lodash.clonedeep';
import {
  GET_PROJECTS,
  ADD_PROJECT,
  RENAME_PROJECT,
  CLEAR_PROJECTS,
  DELETE_PROJECT,
  TOGGLE_PROJECT_DONE,
  FETCH_PROJECTS,
  ADD_TIME_TO_PROJECTS,
  SUBTRACT_PROJECT_TIME
} from '../actions/actionTypes';
import { findParents } from '../helpers';

export function projects(state = { list: [] }, action) {
  switch (action.type) {
    case FETCH_PROJECTS:
      return { ...state, isFetching: action.response };
    case ADD_PROJECT: {
      return { ...state, list: [...state.list, action.project] };
    }
    case SUBTRACT_PROJECT_TIME: {
      const currentParents = findParents(
        state.list,
        action.params.currentProject
      );
      const newParents = findParents(state.list, action.params.newProject);
      const list = state.list.map(item => {
        if (
          currentParents.includes(item._id) &&
          action.params.deleteTime === true
        ) {
          item.timeSpent -= action.params.timeSpent || 0; // eslint-disable-line no-param-reassign
        }
        if (newParents.includes(item._id) && action.params.moveTime === true) {
          item.timeSpent += action.params.timeSpent || 0; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case RENAME_PROJECT: {
      const list = state.list.map(item => {
        if (item._id === action.project.id) {
          item.name = action.project.name; // eslint-disable-line no-param-reassign
          item.parent_id = action.project.parentID; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case ADD_TIME_TO_PROJECTS: {
      const projectsList = action.data.projects.map(item => item.id);
      const list = state.list.map(item => {
        const newItem = deepClone(item);
        if (projectsList.includes(item._id)) {
          newItem.timeSpent += action.seconds;
        }
        return newItem;
      });
      return { ...state, list };
    }
    case TOGGLE_PROJECT_DONE: {
      const list = state.list.map(item => {
        if (item._id === action.id) {
          item.done = action.done; // eslint-disable-line no-param-reassign
        }
        return item;
      });
      return { ...state, list };
    }
    case GET_PROJECTS:
      return {
        ...state,
        list: action.response.data.projectsList,
        isFetching: false
      };
    case CLEAR_PROJECTS:
      return action.response;
    case DELETE_PROJECT: {
      const list = state.list.filter(item => item._id !== action.id);
      return { ...state, list };
    }
    default:
      return state;
  }
}

export default projects;
<|endoftext|>


<commit_before>		var currentTime = new Date();
		currentTime.setUTCMinutes(0, 0, 0);
		var endDate = new Date(currentTime.getTime());
//L.TimeDimension.Util.addTimeDuration(endDate, "P1D", true);

		var map = L.map('map', {
			center: [45.25, -73.6159],
			zoom: 9,
			timeDimension: true,
			timeDimensionControl: true,
			timeDimensionOptions: {
				timeInterval: currentTime.toISOString() + "/P2D",
				period: "PT1H",
				currentTime: currentTime.getTime(),
			},
			timeDimensionControlOptions: {
				styleNS: "leaflet-control-timecontrol",
				position: "bottomleft",
				autoPlay: false,
				loopButton: true,
				playerOptions: {
					buffer: 10,
					transitionTime: 250,
				},
				timeZones: ['Local'],
			},

		});

		var basemaps = {
			Google: L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}')
		};

		var temperatureLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_TT',
			opacity: '0.75',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var windLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_UU',
			//opacity: '0.9',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var precipLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_PR',
			opacity: '0.6',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var dewpointLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_TD',
			opacity: '0.75',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var radarLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'RADAR_RDBS',
			transparent: true,
			format: 'image/png'
			});

		var markers = [{
			name: 'Mas',
			position: [45.177046, -73.614294],
		}, {
			name: 'Leclair',
			position: [45.149207, -73.622507]
		}, {
			name: 'JML',
			position: [45.197038, -73.639380],
		}, {
			name: 'Village',
			position: [45.236798, -73.574530]
		}, {
			name: 'Hamelin',
			position: [45.230818, -73.55349],
		}];

	    //var proxy = 'proxy.php';

		var hrrrTemperatureTimeLayer = L.timeDimension.layer.wms.timeseries(temperatureLayer, {
			//proxy: proxy,
			//updateTimeDimension: true,
			//updateTimeDimensionMode: "replace",
			//requestTimeFromCapabilities: true,
			markers: markers,
			name: "Surface Temperature",
			units: "\xBA C"//,
			//enableNewMarkers: true
		});

		var tdTemp = L.timeDimension.layer.wms(temperatureLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
			markers: markers,
			name: "Surface Temperature",
			//units: "\xBA C",
		});

		var tdDew = L.timeDimension.layer.wms(dewpointLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		var tdWind = L.timeDimension.layer.wms(windLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		var tdPrecip = L.timeDimension.layer.wms(precipLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		// add Temp Legend
		var legend1 = L.control({position: 'bottomright'});
		legend1.onAdd = function(map) {
			var src1 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_TT&format=image/png&STYLE=TEMPSUMMER"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add Wind Legend
		var legend2 = L.control({position: 'bottomright'});
		legend2.onAdd = function(map) {
			var src2 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_UU&format=image/png&STYLE=WINDARROWKMH"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add dewpoint Legend
		var legend3 = L.control({position: 'bottomright'});
		legend3.onAdd = function(map) {
			var src3 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_TD&format=image/png&STYLE=DEWPOINT"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add Precip Legend
		var legend4 = L.control({position: 'bottomright'});
		legend4.onAdd = function(map) {
			var src4 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_PR&format=image/png&STYLE=PRECIPMM"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		legend1.addTo(map);

		var envcan = {
			'Temperature': tdTemp,
			'Dewpoint': tdDew,
			'Cumulative Precipitation (mm)': tdPrecip,
			'Windspeed Arrows in km/h': tdWind,
		};

		L.control.layers(basemaps, envcan).addTo(map);

		envcan.Temperature.addTo(map);
		basemaps.Google.addTo(map);

		// Remove Legends
		map.on('overlayremove', function (eventLayer) {
			if (eventLayer.name === 'Temperature') {
				this.removeControl(legend1);
			} else if (eventLayer.name === 'Dewpoint') {
				this.removeControl(legend3);
			} else if (eventLayer.name === 'Windspeed Arrows in km/h') {
				this.removeControl(legend2);
			} else {
				this.removeControl(legend4);
			}
		});
		map.on('overlayadd', function (eventLayer) {
		// Add Legends
			if (eventLayer.name === 'Temperature') {
				legend1.addTo(this);
			} else if (eventLayer.name === 'Dewpoint') {
				legend3.addTo(this);
			} else if (eventLayer.name === 'Windspeed Arrows in km/h') {
				legend2.addTo(this);
			} else {
				legend4.addTo(this);
			}
		});
		<commit_msg>Update<commit_after>		var currentTime = new Date();
		currentTime.setUTCMinutes(0, 0, 0);
		var endDate = new Date(currentTime.getTime());
//L.TimeDimension.Util.addTimeDuration(endDate, "P1D", true);

		var map = L.map('map', {
			center: [45.25, -73.6159],
			zoom: 9,
			timeDimension: true,
			timeDimensionControl: true,
			timeDimensionOptions: {
				timeInterval: currentTime.toISOString() + "/P2D",
				period: "PT1H",
				currentTime: currentTime.getTime(),
			},
			timeDimensionControlOptions: {
				styleNS: "leaflet-control-timecontrol",
				position: "bottomleft",
				autoPlay: false,
				loopButton: true,
				playerOptions: {
					buffer: 10,
					transitionTime: 250,
				},
				//timeZones: ['Local'],
			},

		});

		var basemaps = {
			Google: L.tileLayer('https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z}')
		};

		var temperatureLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_TT',
			opacity: '0.75',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var windLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_UU',
			//opacity: '0.9',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var precipLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_PR',
			opacity: '0.6',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var dewpointLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'HRDPS.CONTINENTAL_TD',
			opacity: '0.75',
			transparent: true,
			format: 'image/png',
			attribution: 'Data Source: Environment and Climate Change Canada'
			});

		var radarLayer = L.tileLayer.wms('http://geo.weather.gc.ca/geomet/?lang=E&service=WMS&request=GetCapabilities', {
			layers: 'RADAR_RDBS',
			transparent: true,
			format: 'image/png'
			});

		var markers = [{
			name: 'Mas',
			position: [45.177046, -73.614294],
		}, {
			name: 'Leclair',
			position: [45.149207, -73.622507]
		}, {
			name: 'JML',
			position: [45.197038, -73.639380],
		}, {
			name: 'Village',
			position: [45.236798, -73.574530]
		}, {
			name: 'Hamelin',
			position: [45.230818, -73.55349],
		}];

	    //var proxy = 'proxy.php';

		var hrrrTemperatureTimeLayer = L.timeDimension.layer.wms.timeseries(temperatureLayer, {
			//proxy: proxy,
			//updateTimeDimension: true,
			//updateTimeDimensionMode: "replace",
			//requestTimeFromCapabilities: true,
			markers: markers,
			name: "Surface Temperature",
			units: "\xBA C"//,
			//enableNewMarkers: true
		});

		var tdTemp = L.timeDimension.layer.wms(temperatureLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
			markers: markers,
			name: "Surface Temperature",
			//units: "\xBA C",
		});

		var tdDew = L.timeDimension.layer.wms(dewpointLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		var tdWind = L.timeDimension.layer.wms(windLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		var tdPrecip = L.timeDimension.layer.wms(precipLayer, {
			wmsVersion: "1.3.0",
			//proxy: proxy,
		});

		// add Temp Legend
		var legend1 = L.control({position: 'bottomright'});
		legend1.onAdd = function(map) {
			var src1 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_TT&format=image/png&STYLE=TEMPSUMMER"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add Wind Legend
		var legend2 = L.control({position: 'bottomright'});
		legend2.onAdd = function(map) {
			var src2 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_UU&format=image/png&STYLE=WINDARROWKMH"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add dewpoint Legend
		var legend3 = L.control({position: 'bottomright'});
		legend3.onAdd = function(map) {
			var src3 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_TD&format=image/png&STYLE=DEWPOINT"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		// add Precip Legend
		var legend4 = L.control({position: 'bottomright'});
		legend4.onAdd = function(map) {
			var src4 = "http://geo.weather.gc.ca/geomet?version=1.3.0&service=WMS&request=GetLegendGraphic&sld_version=1.1.0&layer=HRDPS.CONTINENTAL_PR&format=image/png&STYLE=PRECIPMM"
			var div = L.DomUtil.create('div', 'info legend');
			div.innerHTML +=
				'';
			return div;
		};

		legend1.addTo(map);

		var envcan = {
			'Temperature': tdTemp,
			'Dewpoint': tdDew,
			'Cumulative Precipitation (mm)': tdPrecip,
			'Windspeed Arrows in km/h': tdWind,
		};

		L.control.layers(basemaps, envcan).addTo(map);

		envcan.Temperature.addTo(map);
		basemaps.Google.addTo(map);

		// Remove Legends
		map.on('overlayremove', function (eventLayer) {
			if (eventLayer.name === 'Temperature') {
				this.removeControl(legend1);
			} else if (eventLayer.name === 'Dewpoint') {
				this.removeControl(legend3);
			} else if (eventLayer.name === 'Windspeed Arrows in km/h') {
				this.removeControl(legend2);
			} else {
				this.removeControl(legend4);
			}
		});
		map.on('overlayadd', function (eventLayer) {
		// Add Legends
			if (eventLayer.name === 'Temperature') {
				legend1.addTo(this);
			} else if (eventLayer.name === 'Dewpoint') {
				legend3.addTo(this);
			} else if (eventLayer.name === 'Windspeed Arrows in km/h') {
				legend2.addTo(this);
			} else {
				legend4.addTo(this);
			}
		});
		<|endoftext|>


<commit_before>// jshint devel:true

(function (global) {
  'use strict';

  let Feature = global.WWCD.Feature || require('./feature').WWCD.Feature;

  let features = {
    localNotifications: new Feature({
      id: 'local-notifications',
      icon: 'mdi-notification-system-update',
      name: 'Local notifications',
      description: `Local notifications, available through Notifications API, allow the authorized web applications to draw users attention
       in a bold but standardized fashion. Notifications are generated by the web application running in a browsers' tab to be presented
       to the user outside of the browser tab area.`,
      caniuse: 'notifications',
      supported: Feature.windowContains('Notification'),
      demoPen: 'yYJdWO',
      links: [
        {url: 'https://addons.mozilla.org/en-us/firefox/addon/221523/', ignore: true},
        {url: 'http://www.html5rocks.com/tutorials/notifications/quick/', ignore: true},
        {url: 'http://aurelio.audero.it/demo/web-notifications-api-demo.html', ignore: true}
      ]
    }),

    pushNotifications: new Feature({
      id: 'push-notifications',
      icon: 'mdi-notification-tap-and-play',
      name: 'Push notifications',
      description: [`Remote Push Notifications allow web applications to subscribe the user to the re-engagement mechanism
       that can show a message to the subscriber even if the web application is not currently opened in the browser. This utilizes the powerful concept
       of Service Workers, code units installable by the web app that execute separately in the background.`,
        `Currently works in Chrome only and requires <a href="https://developers.google.com/cloud-messaging/">GCM</a> setup.`],
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker') && Feature.windowContains('PushManager') && Feature.containedIn(ServiceWorkerRegistration.prototype, 'showNotification'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/service-worker/introduction/', ignore: true},
        {url: 'https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API', ignore: true},
        {url: 'https://jakearchibald.github.io/isserviceworkerready/resources.html', ignore: true},
        {
          url: 'https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web',
          title: 'Push Notifications on the Open Web - Chrome tutorial'
        },
        {url: 'https://goroost.com/try-chrome-push-notifications', title: 'Chrome Push Notifications demo'}
      ]
    }),

    backgroundTasks: new Feature({
      id: 'background-tasks',
      icon: 'mdi-action-settings-applications',
      name: 'Background processing',
      description: `Web applications running on HTTPS can request the browser to install the separate code unit called Service Worker.
       This unit is then run in a separate thread off the owning web application, communicating via events and messages.
       It provides a possibility to execute code regardless of the main application state, i.e. when it is not opened.
       Use cases might include prefetching and caching the data, long processing etc.`,
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker'),
      links: [
        {url: 'https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API', title: 'ServiceWorker API - MDN docs'},
        {
          url: 'https://googlechrome.github.io/samples/service-worker/prefetch/index.html',
          title: 'Service Worker Sample: Pre-fetching Resources During Registration'
        }
      ]
    }),

    touch: new Feature({
      id: 'touch',
      icon: 'mdi-content-gesture',
      name: 'Touch gestures',
      description: [`Traditionally, web relies on mouse and keyboard as the only input devices, while mobile devices are mostly controlled by touch.
        Mobile web started with a bit touchy solution of translating touch events to mouse events like mousedown.`,
        `Newer HTML5 approach is to embrace touch as the first-class input mean, allowing web applications to intercept and identify complex multitouch gestures,
        free-hand drawing etc. Unfortunately, the support is twofold - either via touch events that were first introduced by Apple and standardized later
        as a de-facto solution, when other vendors went the same route, or via the newer, more general Pointer Events specification.`],
      caniuse: 'touch',
      demoPen: 'LpbVoV',
      supported: Feature.windowContains('ontouchstart'),
      links: [
        {url: 'http://www.html5rocks.com/en/mobile/touch/', title: 'Multi-touch Web Development'}
      ]
    }),

    manifest: new Feature({
      id: 'manifest',
      icon: 'mdi-content-archive',
      name: 'Homescreen installation',
      links: [
        {
          url: 'https://developers.google.com/web/updates/2014/11/Support-for-installable-web-apps-with-webapp-manifest-in-chrome-38-for-Android',
          title: 'Installable Web Apps with the WebApp Manifest in Chrome for Android'
        },
        {url: 'http://html5doctor.com/web-manifest-specification/', title: 'The Web Manifest specification'}
      ]
    }),
    foregroundDetection: new Feature({
      id: 'foreground-detection',
      icon: 'mdi-action-flip-to-front',
      name: 'Foreground detection',
      caniuse: 'pagevisibility',
      supported: Feature.containedIn(global.document, 'visibilityState')
    }),
    geolocation: new Feature({
      id: 'geolocation',
      icon: 'mdi-device-gps-fixed',
      name: 'Geolocation',
      caniuse: 'geolocation',
      supported: Feature.navigatorContains('geolocation')
    }),
    bluetooth: new Feature({
      id: 'bluetooth',
      icon: 'mdi-device-bluetooth',
      name: 'Bluetooth',
      supported: Feature.navigatorContains('bluetooth'),
      links: [
        {url: 'https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web', title: 'Interact with BLE devices on the Web'}
      ]
    }),
    nfc: new Feature({
      id: 'nfc',
      icon: 'mdi-device-nfc',
      name: 'NFC',
      supported: Feature.navigatorContains('nfc')
    }),
    proximity: new Feature({
      id: 'proximity',
      icon: 'mdi-image-leak-add',
      name: 'Proximity sensors',
      caniuse: 'proximity',
      supported: Feature.windowContains('ondeviceproximity')
    }),
    networkInfo: new Feature({
      id: 'network-information',
      icon: 'mdi-device-wifi-tethering',
      name: 'Network information',
      supported: Feature.navigatorContains('connection')
    }),
    online: new Feature({
      id: 'online-state',
      icon: 'mdi-device-signal-cellular-connected-no-internet-3-bar',
      name: 'On-line state',
      caniuse: 'online-status',
      supported: Feature.navigatorContains('onLine')
    }),
    mediaCapture: new Feature({
      id: 'media-capture',
      icon: 'mdi-image-camera-alt',
      name: 'Media capturing',
      caniuse: 'stream',
      supported: Feature.navigatorContains('getUserMedia'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/getusermedia/intro/', title: 'Capturing Audio & Video in HTML5'}
      ]
    }),
    wakeLock: new Feature({
      id: 'wake-lock',
      icon: 'mdi-action-lock',
      name: 'Wake lock',
      supported: Feature.navigatorContains('requestWakeLock')
    }),
    vibration: new Feature({
      id: 'vibration',
      icon: 'mdi-notification-vibration',
      name: 'Vibration',
      caniuse: 'vibration',
      supported: Feature.navigatorContains('vibrate')
    }),
    accelerometer: new Feature({
      id: 'accelerometer',
      icon: 'mdi-action-3d-rotation',
      name: 'Accelerometer',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceMotionEvent')
    }),
    batteryStatus: new Feature({
      id: 'battery-status',
      icon: 'mdi-device-battery-80',
      name: 'Battery status',
      caniuse: 'battery-status',
      supported: Feature.navigatorContains('getBattery')
    }),
    ambientLight: new Feature({
      id: 'ambient-light',
      icon: 'mdi-device-brightness-low',
      name: 'Ambient light',
      caniuse: 'ambient-light',
      supported: Feature.windowContains('ondevicelight')
    }),
    permissions: new Feature({
      id: 'permissions',
      icon: 'mdi-action-lock-open',
      name: 'Permissions',
      caniuse: 'permissions-api',
      supported: Feature.navigatorContains('permissions')
    }),
    files: new Feature({
      id: 'files',
      icon: 'mdi-device-sd-storage',
      name: 'File access',
      caniuse: 'fileapi',
      supported: Feature.windowContains('File'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/file/dndfiles/', title: 'Reading files in JavaScript using the File APIs'}
      ]
    }),
    storage: new Feature({
      id: 'storage',
      icon: 'mdi-notification-folder-special',
      name: 'Offline storage',
      caniuse: 'namevalue-storage',
      supported: Feature.windowContains('indexedDB') || Feature.windowContains('localStorage')
    }),
    contacts: new Feature({
      id: 'contacts',
      icon: 'mdi-action-account-box',
      name: 'Contacts',
      supported: Feature.navigatorContains('contacts')
    }),
    quota: new Feature({
      id: 'quota',
      icon: 'mdi-notification-sim-card-alert',
      name: 'Quota management',
      supported: Feature.windowContains('storageInfo') || Feature.navigatorContains('temporaryStorageQuota'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/offline/quota-research/', title: 'Working with quota on mobile browsers'}
      ]
    }),
    deviceOrientation: new Feature({
      id: 'device-orientation',
      icon: 'mdi-device-screen-rotation',
      name: 'Device orientation',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceOrientationEvent')
    }),
    rotationLock: new Feature({
      id: 'rotation-lock',
      icon: 'mdi-device-screen-lock-rotation',
      name: 'Rotation lock',
      supported: Feature.containedIn(global.screen, 'lockOrientation')
    }),
    presentation: new Feature({
      id: 'presentation',
      icon: 'mdi-hardware-tv',
      name: 'Presentation features',
      supported: Feature.navigatorContains('presentation')
    }),
    viewports: new Feature({
      id: 'viewports',
      icon: 'mdi-hardware-phonelink',
      name: 'Viewport adaptation',
      links: [
        {url: 'https://dev.opera.com/articles/an-introduction-to-meta-viewport-and-viewport/', title: 'An Introduction to Meta Viewport and @viewport'}
      ]
    }),
    speech: new Feature({
      id: 'speech-recognition',
      icon: 'mdi-av-mic',
      name: 'Speech recognition',
      caniuse: 'speech-recognition',
      supported: Feature.windowContains('SpeechRecognition'),
      links: [
        {url: 'https://shaungallagher.github.io/say_restyle/', title: 'Demo - Edit the webpage with your voice'}
      ]
    }),
    clipboard: new Feature({
      id: 'clipboard',
      icon: 'mdi-content-content-paste',
      name: 'Clipboard (copy & paste)',
      caniuse: 'clipboard',
      supported: Feature.windowContains('ClipboardEvent'),
      links: [
        {url: 'https://github.com/GoogleChrome/samples/tree/gh-pages/cut-and-copy', title: 'Sample code from Google Chrome'}
      ]
    }),
    inputModality: new Feature({
      id: 'input-modality',
      icon: 'mdi-hardware-mouse',
      name: 'Input modality',
      caniuse: 'css-media-interaction',
      links: [
        {url: 'http://radar.oreilly.com/2015/08/proposing-css-input-modailty.html', title: 'Proposing CSS input modality'}
      ]
    }),
    autofill: new Feature({
      id: 'forms-autofill',
      icon: 'mdi-action-assignment-turned-in',
      name: 'Forms autofill',
      links: [
        {url: 'https://html.spec.whatwg.org/multipage/forms.html#autofill', title: 'Specification draft'}
      ]
    })
  };

  let featuresGroups = [
    {
      heading: 'Behave like a native app',
      features: [features.localNotifications, features.pushNotifications, features.backgroundTasks, features.manifest, features.foregroundDetection]
    },
    {
      heading: 'Access what\'s around',
      features: [features.geolocation, features.bluetooth, features.nfc, features.proximity, features.ambientLight]
    },
    {
      heading: 'Access device features',
      features: [features.mediaCapture, features.networkInfo, features.online, features.vibration, features.batteryStatus]
    },
    {
      heading: 'Access the system',
      features: [features.storage, features.files, features.permissions, features.contacts, features.quota]
    },
    {
      heading: 'Input',
      features: [features.touch, features.speech, features.accelerometer, features.clipboard, features.inputModality]
    },
    {
      heading: 'Screen & output',
      features: [features.deviceOrientation, features.rotationLock, features.wakeLock, features.viewports, features.presentation]
    }
  ];

  if (global.WWCD.container) { // web run
    global.WWCD.container.configure(register => register.singletons({
      features: features,
      featuresGroups: featuresGroups
    }));
  } else { // build run
    global.features = features;
  }

})(function () {
  let global = typeof exports === 'object' ? exports : window;
  global.WWCD = global.WWCD || {};
  return global;
}());
<commit_msg>manifest, page visibility descriptions<commit_after>// jshint devel:true

(function (global) {
  'use strict';

  let Feature = global.WWCD.Feature || require('./feature').WWCD.Feature;

  let features = {
    localNotifications: new Feature({
      id: 'local-notifications',
      icon: 'mdi-notification-system-update',
      name: 'Local notifications',
      description: `Local notifications, available through Notifications API, allow the authorized web applications to draw users attention
       in a bold but standardized fashion. Notifications are generated by the web application running in a browsers' tab to be presented
       to the user outside of the browser tab area.`,
      caniuse: 'notifications',
      supported: Feature.windowContains('Notification'),
      demoPen: 'yYJdWO',
      links: [
        {url: 'https://addons.mozilla.org/en-us/firefox/addon/221523/', ignore: true},
        {url: 'http://www.html5rocks.com/tutorials/notifications/quick/', ignore: true},
        {url: 'http://aurelio.audero.it/demo/web-notifications-api-demo.html', ignore: true}
      ]
    }),

    pushNotifications: new Feature({
      id: 'push-notifications',
      icon: 'mdi-notification-tap-and-play',
      name: 'Push notifications',
      description: [`Remote Push Notifications allow web applications to subscribe the user to the re-engagement mechanism
       that can show a message to the subscriber even if the web application is not currently opened in the browser. This utilizes the powerful concept
       of Service Workers, code units installable by the web app that execute separately in the background.`,
        `Currently works in Chrome only and requires <a href="https://developers.google.com/cloud-messaging/">GCM</a> setup.`],
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker') && Feature.windowContains('PushManager') && Feature.containedIn(ServiceWorkerRegistration.prototype, 'showNotification'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/service-worker/introduction/', ignore: true},
        {url: 'https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API', ignore: true},
        {url: 'https://jakearchibald.github.io/isserviceworkerready/resources.html', ignore: true},
        {
          url: 'https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web',
          title: 'Push Notifications on the Open Web - Chrome tutorial'
        },
        {url: 'https://goroost.com/try-chrome-push-notifications', title: 'Chrome Push Notifications demo'}
      ]
    }),

    backgroundTasks: new Feature({
      id: 'background-tasks',
      icon: 'mdi-action-settings-applications',
      name: 'Background processing',
      description: `Web applications running on HTTPS can request the browser to install the separate code unit called Service Worker.
       This unit is then run in a separate thread off the owning web application, communicating via events and messages.
       It provides a possibility to execute code regardless of the main application state, i.e. when it is not opened.
       Use cases might include prefetching and caching the data, long processing etc.`,
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker'),
      links: [
        {url: 'https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorker_API', title: 'ServiceWorker API - MDN docs'},
        {
          url: 'https://googlechrome.github.io/samples/service-worker/prefetch/index.html',
          title: 'Service Worker Sample: Pre-fetching Resources During Registration'
        }
      ]
    }),

    touch: new Feature({
      id: 'touch',
      icon: 'mdi-content-gesture',
      name: 'Touch gestures',
      description: [`Traditionally, web relies on mouse and keyboard as the only input devices, while mobile devices are mostly controlled by touch.
        Mobile web started with a bit touchy solution of translating touch events to mouse events like mousedown.`,
        `Newer HTML5 approach is to embrace touch as the first-class input mean, allowing web applications to intercept and identify complex multitouch
         gestures, free-hand drawing etc. Unfortunately, the support is twofold - either via touch events like touchstart that were first
         introduced by Apple and standardized later as a de-facto solution, when other vendors went the same route, or via the newer,
         more general Pointer Events specification.`],
      caniuse: 'touch',
      demoPen: 'LpbVoV',
      supported: Feature.windowContains('ontouchstart'),
      links: [
        {url: 'http://www.html5rocks.com/en/mobile/touch/', title: 'Multi-touch Web Development'}
      ]
    }),

    manifest: new Feature({
      id: 'manifest',
      icon: 'mdi-content-archive',
      name: 'Home screen installation',
      description: `Web applications can provide the manifest.json file, standarized as the Web Manifest, specifying the features
       and behaviors needed on order to treat the application as a first-class citizen on the mobile platform, i.e. adding ("installing") to home screen
       with the relevant icon, full screen behaviors, standalone appearance etc.`,
      links: [
        {
          url: 'https://developers.google.com/web/fundamentals/device-access/stickyness/?hl=en',
          title: 'Device Access & Integration: Add To Home Screen'
        },
        {url: 'http://html5doctor.com/web-manifest-specification/', title: 'The Web Manifest specification'}
      ]
    }),

    foregroundDetection: new Feature({
      id: 'foreground-detection',
      icon: 'mdi-action-flip-to-front',
      name: 'Foreground detection',
      description: `Page Visibility API is useful for the web application to know whether it is currently displayed on the front or not, especially to stop
        resource-intensive UI animations or data refreshing when it's not needed. On the mobile devices, the primary reason for that is to reduce battery usage.`,
      caniuse: 'pagevisibility',
      demoPen: 'avBpOb',
      supported: Feature.containedIn(global.document, 'visibilityState')
    }),

    geolocation: new Feature({
      id: 'geolocation',
      icon: 'mdi-device-gps-fixed',
      name: 'Geolocation',
      caniuse: 'geolocation',
      supported: Feature.navigatorContains('geolocation')
    }),
    bluetooth: new Feature({
      id: 'bluetooth',
      icon: 'mdi-device-bluetooth',
      name: 'Bluetooth',
      supported: Feature.navigatorContains('bluetooth'),
      links: [
        {url: 'https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web', title: 'Interact with BLE devices on the Web'}
      ]
    }),
    nfc: new Feature({
      id: 'nfc',
      icon: 'mdi-device-nfc',
      name: 'NFC',
      supported: Feature.navigatorContains('nfc')
    }),
    proximity: new Feature({
      id: 'proximity',
      icon: 'mdi-image-leak-add',
      name: 'Proximity sensors',
      caniuse: 'proximity',
      supported: Feature.windowContains('ondeviceproximity')
    }),
    networkInfo: new Feature({
      id: 'network-information',
      icon: 'mdi-device-wifi-tethering',
      name: 'Network information',
      supported: Feature.navigatorContains('connection')
    }),
    online: new Feature({
      id: 'online-state',
      icon: 'mdi-device-signal-cellular-connected-no-internet-3-bar',
      name: 'On-line state',
      caniuse: 'online-status',
      supported: Feature.navigatorContains('onLine')
    }),
    mediaCapture: new Feature({
      id: 'media-capture',
      icon: 'mdi-image-camera-alt',
      name: 'Media capturing',
      caniuse: 'stream',
      supported: Feature.navigatorContains('getUserMedia'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/getusermedia/intro/', title: 'Capturing Audio & Video in HTML5'}
      ]
    }),
    wakeLock: new Feature({
      id: 'wake-lock',
      icon: 'mdi-action-lock',
      name: 'Wake lock',
      supported: Feature.navigatorContains('requestWakeLock')
    }),
    vibration: new Feature({
      id: 'vibration',
      icon: 'mdi-notification-vibration',
      name: 'Vibration',
      caniuse: 'vibration',
      supported: Feature.navigatorContains('vibrate')
    }),
    accelerometer: new Feature({
      id: 'accelerometer',
      icon: 'mdi-action-3d-rotation',
      name: 'Accelerometer',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceMotionEvent')
    }),
    batteryStatus: new Feature({
      id: 'battery-status',
      icon: 'mdi-device-battery-80',
      name: 'Battery status',
      caniuse: 'battery-status',
      supported: Feature.navigatorContains('getBattery')
    }),
    ambientLight: new Feature({
      id: 'ambient-light',
      icon: 'mdi-device-brightness-low',
      name: 'Ambient light',
      caniuse: 'ambient-light',
      supported: Feature.windowContains('ondevicelight')
    }),
    permissions: new Feature({
      id: 'permissions',
      icon: 'mdi-action-lock-open',
      name: 'Permissions',
      caniuse: 'permissions-api',
      supported: Feature.navigatorContains('permissions')
    }),
    files: new Feature({
      id: 'files',
      icon: 'mdi-device-sd-storage',
      name: 'File access',
      caniuse: 'fileapi',
      supported: Feature.windowContains('File'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/file/dndfiles/', title: 'Reading files in JavaScript using the File APIs'}
      ]
    }),
    storage: new Feature({
      id: 'storage',
      icon: 'mdi-notification-folder-special',
      name: 'Offline storage',
      caniuse: 'namevalue-storage',
      supported: Feature.windowContains('indexedDB') || Feature.windowContains('localStorage')
    }),
    contacts: new Feature({
      id: 'contacts',
      icon: 'mdi-action-account-box',
      name: 'Contacts',
      supported: Feature.navigatorContains('contacts')
    }),
    quota: new Feature({
      id: 'quota',
      icon: 'mdi-notification-sim-card-alert',
      name: 'Quota management',
      supported: Feature.windowContains('storageInfo') || Feature.navigatorContains('temporaryStorageQuota'),
      links: [
        {url: 'http://www.html5rocks.com/en/tutorials/offline/quota-research/', title: 'Working with quota on mobile browsers'}
      ]
    }),
    deviceOrientation: new Feature({
      id: 'device-orientation',
      icon: 'mdi-device-screen-rotation',
      name: 'Device orientation',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceOrientationEvent')
    }),
    rotationLock: new Feature({
      id: 'rotation-lock',
      icon: 'mdi-device-screen-lock-rotation',
      name: 'Rotation lock',
      supported: Feature.containedIn(global.screen, 'lockOrientation')
    }),
    presentation: new Feature({
      id: 'presentation',
      icon: 'mdi-hardware-tv',
      name: 'Presentation features',
      supported: Feature.navigatorContains('presentation')
    }),
    viewports: new Feature({
      id: 'viewports',
      icon: 'mdi-hardware-phonelink',
      name: 'Viewport adaptation',
      links: [
        {url: 'https://dev.opera.com/articles/an-introduction-to-meta-viewport-and-viewport/', title: 'An Introduction to Meta Viewport and @viewport'}
      ]
    }),
    speech: new Feature({
      id: 'speech-recognition',
      icon: 'mdi-av-mic',
      name: 'Speech recognition',
      caniuse: 'speech-recognition',
      supported: Feature.windowContains('SpeechRecognition'),
      links: [
        {url: 'https://shaungallagher.github.io/say_restyle/', title: 'Demo - Edit the webpage with your voice'}
      ]
    }),
    clipboard: new Feature({
      id: 'clipboard',
      icon: 'mdi-content-content-paste',
      name: 'Clipboard (copy & paste)',
      caniuse: 'clipboard',
      supported: Feature.windowContains('ClipboardEvent'),
      links: [
        {url: 'https://github.com/GoogleChrome/samples/tree/gh-pages/cut-and-copy', title: 'Sample code from Google Chrome'}
      ]
    }),
    inputModality: new Feature({
      id: 'input-modality',
      icon: 'mdi-hardware-mouse',
      name: 'Input modality',
      caniuse: 'css-media-interaction',
      links: [
        {url: 'http://radar.oreilly.com/2015/08/proposing-css-input-modailty.html', title: 'Proposing CSS input modality'}
      ]
    }),
    autofill: new Feature({
      id: 'forms-autofill',
      icon: 'mdi-action-assignment-turned-in',
      name: 'Forms autofill',
      links: [
        {url: 'https://html.spec.whatwg.org/multipage/forms.html#autofill', title: 'Specification draft'}
      ]
    })
  };

  let featuresGroups = [
    {
      heading: 'Behave like a native app',
      features: [features.localNotifications, features.pushNotifications, features.backgroundTasks, features.manifest, features.foregroundDetection]
    },
    {
      heading: 'Access what\'s around',
      features: [features.geolocation, features.bluetooth, features.nfc, features.proximity, features.ambientLight]
    },
    {
      heading: 'Access device features',
      features: [features.mediaCapture, features.networkInfo, features.online, features.vibration, features.batteryStatus]
    },
    {
      heading: 'Access the system',
      features: [features.storage, features.files, features.permissions, features.contacts, features.quota]
    },
    {
      heading: 'Input',
      features: [features.touch, features.speech, features.accelerometer, features.clipboard, features.inputModality]
    },
    {
      heading: 'Screen & output',
      features: [features.deviceOrientation, features.rotationLock, features.wakeLock, features.viewports, features.presentation]
    }
  ];

  if (global.WWCD.container) { // web run
    global.WWCD.container.configure(register => register.singletons({
      features: features,
      featuresGroups: featuresGroups
    }));
  } else { // build run
    global.features = features;
  }

})(function () {
  let global = typeof exports === 'object' ? exports : window;
  global.WWCD = global.WWCD || {};
  return global;
}());
<|endoftext|>


<commit_before>var route = require('./helpers/utils').route,
    workspace = require('./helpers/workspace'),
    content = require('./helpers/pages').content,
    authoring = require('./helpers/pages').authoring,
    monitoring = require('./helpers/monitoring');

describe('Package', function() {
    'use strict';

    beforeEach(route('/workspace/content'));

    it('increment package version', function() {
        workspace.switchToDesk('PERSONAL');
        content.setListView();
        content.actionOnItem('Edit package', 0);
        authoring.showSearch();
        authoring.addToGroup(0, 'MAIN');
        authoring.save();
        authoring.showVersions();
        expect(element.all(by.repeater('version in versions')).count()).toBe(2);
    });

    it('reorder item on package', function() {
        workspace.switchToDesk('Personal');
        content.setListView();
        content.actionOnItem('Edit package', 0);
        authoring.showSearch();
        authoring.addToGroup(0, 'MAIN');
        authoring.addToGroup(1, 'STORY');
        authoring.addToGroup(2, 'STORY');
        authoring.addToGroup(3, 'SIDEBARS');
        authoring.showSearch();
        authoring.moveToGroup('MAIN', 0, 'STORY', 1);
        expect(authoring.getGroupItems('MAIN').count()).toBe(0);
        expect(authoring.getGroupItems('STORY').count()).toBe(3);
    });

    it('add multiple items to package', function() {
        addItemsToPackage();
        expect(authoring.getGroupItems('MAIN').count()).toBe(3);
    });

    it('can preview package in a package', function() {
        // populate package
        addItemsToPackage();

        // package existing package
        workspace.openContent();
        workspace.switchToDesk('Personal');
        content.setListView();
        content.actionOnItem('Package item', 0);

        // select package
        workspace.openContent();
        workspace.switchToDesk('Personal');
        element.all(by.repeater('item in items')).first().click();

        // preview package via preview
        browser.sleep(100);
        element.all(by.repeater('child in item')).first().click();
        browser.sleep(100);
        expect(element(by.css('h5.lightbox-title')).getText()).toBe('package1');

        expect(element(by.css('.condensed-preview')).all(by.repeater('child in item')).count()).toBe(3);
    });

    it('create package from multiple items', function() {
        workspace.switchToDesk('SPORTS DESK');
        content.setListView();
        content.selectItem(0);
        content.selectItem(1);
        content.createPackageFromItems();
        expect(authoring.getGroupItems('MAIN').count()).toBe(2);
    });

    it('can add items to an existing package', function() {
        monitoring.openMonitoring();
        monitoring.showMonitoringSettings();
        monitoring.togglePersonal();
        monitoring.nextStages();
        monitoring.nextSearches();
        monitoring.nextReorder();
        monitoring.saveSettings();
        monitoring.openAction(2, 0);
        browser.sleep(500);
        expect(authoring.getGroupItems('MAIN').count()).toBe(0);
        monitoring.actionOnItem('Add to package', 0, 0);
        expect(authoring.getGroupItems('MAIN').count()).toBe(1);
    });

    it('can add items to a package only once', function() {
        monitoring.openMonitoring();
        monitoring.showMonitoringSettings();
        monitoring.togglePersonal();
        monitoring.nextStages();
        monitoring.nextSearches();
        monitoring.nextReorder();
        monitoring.saveSettings();
        monitoring.openAction(2, 0);
        browser.sleep(1000);
        monitoring.actionOnItem('Add to package', 0, 0);
        browser.sleep(1000);
        authoring.save();
        browser.refresh();
        monitoring.openAction(2, 0);
        browser.sleep(500);
        var menu = monitoring.openItemMenu(0, 0);
        expect(menu.element(by.partialLinkText('Add to package')).isPresent()).toBe(false);
    });

    it('create package from published item', function() {
        workspace.open();
        workspace.switchToDesk('SPORTS DESK');
        workspace.editItem('item5', 'Politic');
        authoring.writeText('some text');
        authoring.save();
        authoring.publish();
        browser.sleep(500);
        workspace.selectStage('Published');
        browser.sleep(500);
        workspace.filterItems('text');
        content.actionOnItem('Package item', 0);
        expect(authoring.getGroupItems('MAIN').count()).toBe(1);
    });

    function addItemsToPackage() {
        workspace.switchToDesk('Personal').then(
            content.setListView
        ).then(function() {
            content.actionOnItem('Edit package', 0);
        });
        authoring.showSearch();
        authoring.selectSearchItem(0);
        authoring.selectSearchItem(1);
        authoring.selectSearchItem(2);
        authoring.addMultiToGroup('MAIN');
        authoring.save();
    }
});
<commit_msg>Fixed tests after rebase.<commit_after>var route = require('./helpers/utils').route,
    workspace = require('./helpers/workspace'),
    content = require('./helpers/pages').content,
    authoring = require('./helpers/pages').authoring,
    monitoring = require('./helpers/monitoring');

describe('Package', function() {
    'use strict';

    beforeEach(route('/workspace/content'));

    it('increment package version', function() {
        workspace.switchToDesk('PERSONAL');
        content.setListView();
        content.actionOnItem('Edit package', 0);
        authoring.showSearch();
        authoring.addToGroup(0, 'MAIN');
        authoring.save();
        authoring.showVersions();
        expect(element.all(by.repeater('version in versions')).count()).toBe(2);
    });

    it('reorder item on package', function() {
        workspace.switchToDesk('Personal');
        content.setListView();
        content.actionOnItem('Edit package', 0);
        authoring.showSearch();
        authoring.addToGroup(0, 'MAIN');
        authoring.addToGroup(1, 'STORY');
        authoring.addToGroup(2, 'STORY');
        authoring.addToGroup(3, 'SIDEBARS');
        authoring.showSearch();
        authoring.moveToGroup('MAIN', 0, 'STORY', 1);
        expect(authoring.getGroupItems('MAIN').count()).toBe(0);
        expect(authoring.getGroupItems('STORY').count()).toBe(3);
    });

    it('add multiple items to package', function() {
        addItemsToPackage();
        expect(authoring.getGroupItems('MAIN').count()).toBe(3);
    });

    it('can preview package in a package', function() {
        // populate package
        addItemsToPackage();

        // package existing package
        workspace.openContent();
        workspace.switchToDesk('Personal');
        content.setListView();
        content.actionOnItem('Package item', 0);

        // select package
        workspace.openContent();
        workspace.switchToDesk('Personal');
        element.all(by.repeater('item in items')).first().click();

        // preview package via preview
        browser.sleep(100);
        element.all(by.repeater('child in item')).first().click();
        browser.sleep(100);
        expect(element(by.css('h5.lightbox-title')).getText()).toBe('package1');

        expect(element(by.css('.condensed-preview')).all(by.repeater('child in item')).count()).toBe(3);
    });

    it('create package from multiple items', function() {
        workspace.switchToDesk('SPORTS DESK');
        content.setListView();
        content.selectItem(0);
        content.selectItem(1);
        content.createPackageFromItems();
        expect(authoring.getGroupItems('MAIN').count()).toBe(2);
    });

    fit('can add items to an existing package', function() {
        monitoring.openMonitoring();
        monitoring.showMonitoringSettings();
        monitoring.togglePersonal();
        monitoring.nextStages();
        monitoring.nextSearches();
        monitoring.nextReorder();
        monitoring.saveSettings();
        monitoring.openAction(4, 0);
        browser.sleep(500);
        expect(authoring.getGroupItems('MAIN').count()).toBe(0);
        monitoring.actionOnItem('Add to package', 1, 0);
        expect(authoring.getGroupItems('MAIN').count()).toBe(1);
    });

    fit('can add items to a package only once', function() {
        monitoring.openMonitoring();
        monitoring.showMonitoringSettings();
        monitoring.togglePersonal();
        monitoring.nextStages();
        monitoring.nextSearches();
        monitoring.nextReorder();
        monitoring.saveSettings();
        monitoring.openAction(4, 0);
        browser.sleep(1000);
        monitoring.actionOnItem('Add to package', 1, 0);
        browser.sleep(1000);
        authoring.save();
        browser.refresh();
        monitoring.openAction(4, 0);
        browser.sleep(500);
        var menu = monitoring.openItemMenu(1, 0);
        expect(menu.element(by.partialLinkText('Add to package')).isPresent()).toBe(false);
    });

    it('create package from published item', function() {
        workspace.open();
        workspace.switchToDesk('SPORTS DESK');
        workspace.editItem('item5', 'Politic');
        authoring.writeText('some text');
        authoring.save();
        authoring.publish();
        browser.sleep(500);
        workspace.selectStage('Published');
        browser.sleep(500);
        workspace.filterItems('text');
        content.actionOnItem('Package item', 0);
        expect(authoring.getGroupItems('MAIN').count()).toBe(1);
    });

    function addItemsToPackage() {
        workspace.switchToDesk('Personal').then(
            content.setListView
        ).then(function() {
            content.actionOnItem('Edit package', 0);
        });
        authoring.showSearch();
        authoring.selectSearchItem(0);
        authoring.selectSearchItem(1);
        authoring.selectSearchItem(2);
        authoring.addMultiToGroup('MAIN');
        authoring.save();
    }
});
<|endoftext|>


<commit_before>// jshint devel:true

(function (global) {
  'use strict';

  let Feature = global.WWCD.Feature;

  let features = {
    localNotifications: new Feature({
      id: 'local-notifications',
      icon: 'mdi-notification-system-update',
      name: 'Local notifications',
      caniuse: 'notifications',
      supported: Feature.windowContains('Notification')
    }),
    pushNotifications: new Feature({
      id: 'push-notifications',
      icon: 'mdi-notification-tap-and-play',
      name: 'Push notifications',
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker') && Feature.windowContains('PushManager') && Feature.containedIn(ServiceWorkerRegistration.prototype, 'showNotification'),
      urls: [
        {
          url: 'https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web',
          title: 'Push Notifications on the Open Web - Chrome tutorial'
        }
      ]
    }),
    backgroundTasks: new Feature({
      id: 'background-tasks',
      icon: 'mdi-action-settings-applications',
      name: 'Background processing',
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker')
    }),
    touch: new Feature({
      id: 'touch',
      icon: 'mdi-content-gesture',
      name: 'Touch gestures',
      caniuse: 'touch',
      supported: Feature.windowContains('ontouchstart'),
      urls: [
        {url: 'http://www.html5rocks.com/en/mobile/touch/', title: 'Multi-touch Web Development'}
      ]
    }),
    manifest: new Feature({
      id: 'manifest',
      icon: 'mdi-content-archive',
      name: 'Homescreen installation',
      urls: [
        {
          url: 'https://developers.google.com/web/updates/2014/11/Support-for-installable-web-apps-with-webapp-manifest-in-chrome-38-for-Android',
          title: 'Installable Web Apps with the WebApp Manifest in Chrome for Android'
        },
        {url: 'http://html5doctor.com/web-manifest-specification/', title: 'The Web Manifest specification'}
      ]
    }),
    foregroundDetection: new Feature({
      id: 'foreground-detection',
      icon: 'mdi-action-flip-to-front',
      name: 'Foreground detection',
      caniuse: 'pagevisibility',
      supported: Feature.containedIn(global.document, 'visibilityState')
    }),
    geolocation: new Feature({
      id: 'geolocation',
      icon: 'mdi-device-gps-fixed',
      name: 'Geolocation',
      caniuse: 'geolocation',
      supported: Feature.navigatorContains('geolocation')
    }),
    bluetooth: new Feature({
      id: 'bluetooth',
      icon: 'mdi-device-bluetooth',
      name: 'Bluetooth',
      supported: Feature.navigatorContains('bluetooth'),
      urls: [
        {url: 'https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web', title: 'Interact with BLE devices on the Web'}
      ]
    }),
    nfc: new Feature({
      id: 'nfc',
      icon: 'mdi-device-nfc',
      name: 'NFC',
      supported: Feature.navigatorContains('nfc')
    }),
    proximity: new Feature({
      id: 'proximity',
      icon: 'mdi-image-leak-add',
      name: 'Proximity sensors',
      caniuse: 'proximity',
      supported: Feature.windowContains('ondeviceproximity')
    }),
    networkInfo: new Feature({
      id: 'network-information',
      icon: 'mdi-device-wifi-tethering',
      name: 'Network information',
      supported: Feature.navigatorContains('connection')
    }),
    online: new Feature({
      id: 'online-state',
      icon: 'mdi-device-signal-cellular-connected-no-internet-3-bar',
      name: 'On-line state',
      caniuse: 'online-status',
      supported: Feature.navigatorContains('onLine')
    }),
    mediaCapture: new Feature({
      id: 'media-capture',
      icon: 'mdi-image-camera-alt',
      name: 'Media capturing',
      caniuse: 'stream',
      supported: Feature.navigatorContains('getUserMedia'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/getusermedia/intro/', title: 'Capturing Audio & Video in HTML5'}
      ]
    }),
    wakeLock: new Feature({
      id: 'wake-lock',
      icon: 'mdi-action-lock',
      name: 'Wake lock',
      supported: Feature.navigatorContains('requestWakeLock')
    }),
    vibration: new Feature({
      id: 'vibration',
      icon: 'mdi-notification-vibration',
      name: 'Vibration',
      caniuse: 'vibration',
      supported: Feature.navigatorContains('vibrate')
    }),
    accelerometer: new Feature({
      id: 'accelerometer',
      icon: 'mdi-action-3d-rotation',
      name: 'Accelerometer',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceMotionEvent')
    }),
    batteryStatus: new Feature({
      id: 'battery-status',
      icon: 'mdi-device-battery-80',
      name: 'Battery status',
      caniuse: 'battery-status',
      supported: Feature.navigatorContains('getBattery')
    }),
    ambientLight: new Feature({
      id: 'ambient-light',
      icon: 'mdi-device-brightness-low',
      name: 'Ambient light',
      caniuse: 'ambient-light',
      supported: Feature.windowContains('ondevicelight')
    }),
    permissions: new Feature({
      id: 'permissions',
      icon: 'mdi-action-lock-open',
      name: 'Permissions',
      caniuse: 'permissions-api',
      supported: Feature.navigatorContains('permissions')
    }),
    files: new Feature({
      id: 'files',
      icon: 'mdi-device-sd-storage',
      name: 'File access',
      caniuse: 'fileapi',
      supported: Feature.windowContains('File'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/file/dndfiles/', title: 'Reading files in JavaScript using the File APIs'}
      ]
    }),
    storage: new Feature({
      id: 'storage',
      icon: 'mdi-notification-folder-special',
      name: 'Offline storage',
      caniuse: 'namevalue-storage',
      supported: Feature.windowContains('indexedDB') || Feature.windowContains('localStorage')
    }),
    contacts: new Feature({
      id: 'contacts',
      icon: 'mdi-action-account-box',
      name: 'Contacts',
      supported: Feature.navigatorContains('contacts')
    }),
    quota: new Feature({
      id: 'quota',
      icon: 'mdi-notification-sim-card-alert',
      name: 'Quota management',
      supported: Feature.windowContains('storageInfo') || Feature.navigatorContains('temporaryStorageQuota'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/offline/quota-research/', title: 'Working with quota on mobile browsers'}
      ]
    }),
    deviceOrientation: new Feature({
      id: 'device-orientation',
      icon: 'mdi-device-screen-rotation',
      name: 'Device orientation',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceOrientationEvent')
    }),
    rotationLock: new Feature({
      id: 'rotation-lock',
      icon: 'mdi-device-screen-lock-rotation',
      name: 'Rotation lock',
      supported: Feature.containedIn(global.screen, 'lockOrientation')
    }),
    presentation: new Feature({
      id: 'presentation',
      icon: 'mdi-hardware-tv',
      name: 'Presentation features',
      supported: Feature.navigatorContains('presentation')
    }),
    viewports: new Feature({
      id: 'viewports',
      icon: 'mdi-hardware-phonelink',
      name: 'Viewport adaptation',
      urls: [
        {url: 'https://dev.opera.com/articles/an-introduction-to-meta-viewport-and-viewport/', title: 'An Introduction to Meta Viewport and @viewport'}
      ]
    }),
    speech: new Feature({
      id: 'speech-recognition',
      icon: 'mdi-av-mic',
      name: 'Speech recognition',
      caniuse: 'speech-recognition',
      supported: Feature.windowContains('SpeechRecognition'),
      urls: [
        {url: 'https://shaungallagher.github.io/say_restyle/', title: 'Demo - Edit the webpage with your voice'}
      ]
    }),
    clipboard: new Feature({
      id: 'clipboard',
      icon: 'mdi-content-content-paste',
      name: 'Clipboard (copy & paste)',
      caniuse: 'clipboard',
      supported: Feature.windowContains('ClipboardEvent'),
      urls: [
        {url: 'https://github.com/GoogleChrome/samples/tree/gh-pages/cut-and-copy', title: 'Sample code from Google Chrome'}
      ]
    }),
    inputModality: new Feature({
      id: 'input-modality',
      icon: 'mdi-hardware-mouse',
      name: 'Input modality',
      caniuse: 'css-media-interaction',
      urls: [
        {url: 'http://radar.oreilly.com/2015/08/proposing-css-input-modailty.html', title: 'Proposing CSS input modality'},
      ]
    }),
    autofill: new Feature({
      id: 'forms-autofill',
      icon: 'mdi-action-assignment-turned-in',
      name: 'Forms autofill',
      urls: [
        {url: 'https://html.spec.whatwg.org/multipage/forms.html#autofill', title: 'Specification draft'}
      ]
    })
  };

  let featuresGroups = [
    {
      heading: 'Behave like a native app',
      features: [features.localNotifications, features.pushNotifications, features.backgroundTasks, features.manifest, features.foregroundDetection]
    },
    {
      heading: 'Access what\'s around',
      features: [features.geolocation, features.bluetooth, features.nfc, features.proximity, features.ambientLight]
    },
    {
      heading: 'Access device features',
      features: [features.mediaCapture, features.networkInfo, features.online, features.vibration, features.batteryStatus]
    },
    {
      heading: 'Access the system',
      features: [features.storage, features.files, features.permissions, features.contacts, features.quota]
    },
    {
      heading: 'Input',
      features: [features.touch, features.speech, features.accelerometer, features.clipboard, features.inputModality]
    },
    {
      heading: 'Screen & output',
      features: [features.deviceOrientation, features.rotationLock, features.wakeLock, features.viewports, features.presentation]
    }
  ];

  if (global.WWCD.container) { // web run
    global.WWCD.container.configure(register => register.singletons({
      features: features,
      featuresGroups: featuresGroups
    }));
  } else { // build run
    global.features = features;
  }

})(function () {
  let global = typeof exports === 'object' ? exports : window;
  global.WWCD = global.WWCD || {};
  return global;
}());
<commit_msg>fix for grunt duplicate, to rethink...<commit_after>// jshint devel:true

(function (global) {
  'use strict';

  let Feature = global.WWCD.Feature || require('./feature').WWCD.Feature;

  let features = {
    localNotifications: new Feature({
      id: 'local-notifications',
      icon: 'mdi-notification-system-update',
      name: 'Local notifications',
      caniuse: 'notifications',
      supported: Feature.windowContains('Notification')
    }),
    pushNotifications: new Feature({
      id: 'push-notifications',
      icon: 'mdi-notification-tap-and-play',
      name: 'Push notifications',
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker') && Feature.windowContains('PushManager') && Feature.containedIn(ServiceWorkerRegistration.prototype, 'showNotification'),
      urls: [
        {
          url: 'https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web',
          title: 'Push Notifications on the Open Web - Chrome tutorial'
        }
      ]
    }),
    backgroundTasks: new Feature({
      id: 'background-tasks',
      icon: 'mdi-action-settings-applications',
      name: 'Background processing',
      caniuse: 'serviceworkers',
      supported: Feature.navigatorContains('serviceWorker')
    }),
    touch: new Feature({
      id: 'touch',
      icon: 'mdi-content-gesture',
      name: 'Touch gestures',
      caniuse: 'touch',
      supported: Feature.windowContains('ontouchstart'),
      urls: [
        {url: 'http://www.html5rocks.com/en/mobile/touch/', title: 'Multi-touch Web Development'}
      ]
    }),
    manifest: new Feature({
      id: 'manifest',
      icon: 'mdi-content-archive',
      name: 'Homescreen installation',
      urls: [
        {
          url: 'https://developers.google.com/web/updates/2014/11/Support-for-installable-web-apps-with-webapp-manifest-in-chrome-38-for-Android',
          title: 'Installable Web Apps with the WebApp Manifest in Chrome for Android'
        },
        {url: 'http://html5doctor.com/web-manifest-specification/', title: 'The Web Manifest specification'}
      ]
    }),
    foregroundDetection: new Feature({
      id: 'foreground-detection',
      icon: 'mdi-action-flip-to-front',
      name: 'Foreground detection',
      caniuse: 'pagevisibility',
      supported: Feature.containedIn(global.document, 'visibilityState')
    }),
    geolocation: new Feature({
      id: 'geolocation',
      icon: 'mdi-device-gps-fixed',
      name: 'Geolocation',
      caniuse: 'geolocation',
      supported: Feature.navigatorContains('geolocation')
    }),
    bluetooth: new Feature({
      id: 'bluetooth',
      icon: 'mdi-device-bluetooth',
      name: 'Bluetooth',
      supported: Feature.navigatorContains('bluetooth'),
      urls: [
        {url: 'https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web', title: 'Interact with BLE devices on the Web'}
      ]
    }),
    nfc: new Feature({
      id: 'nfc',
      icon: 'mdi-device-nfc',
      name: 'NFC',
      supported: Feature.navigatorContains('nfc')
    }),
    proximity: new Feature({
      id: 'proximity',
      icon: 'mdi-image-leak-add',
      name: 'Proximity sensors',
      caniuse: 'proximity',
      supported: Feature.windowContains('ondeviceproximity')
    }),
    networkInfo: new Feature({
      id: 'network-information',
      icon: 'mdi-device-wifi-tethering',
      name: 'Network information',
      supported: Feature.navigatorContains('connection')
    }),
    online: new Feature({
      id: 'online-state',
      icon: 'mdi-device-signal-cellular-connected-no-internet-3-bar',
      name: 'On-line state',
      caniuse: 'online-status',
      supported: Feature.navigatorContains('onLine')
    }),
    mediaCapture: new Feature({
      id: 'media-capture',
      icon: 'mdi-image-camera-alt',
      name: 'Media capturing',
      caniuse: 'stream',
      supported: Feature.navigatorContains('getUserMedia'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/getusermedia/intro/', title: 'Capturing Audio & Video in HTML5'}
      ]
    }),
    wakeLock: new Feature({
      id: 'wake-lock',
      icon: 'mdi-action-lock',
      name: 'Wake lock',
      supported: Feature.navigatorContains('requestWakeLock')
    }),
    vibration: new Feature({
      id: 'vibration',
      icon: 'mdi-notification-vibration',
      name: 'Vibration',
      caniuse: 'vibration',
      supported: Feature.navigatorContains('vibrate')
    }),
    accelerometer: new Feature({
      id: 'accelerometer',
      icon: 'mdi-action-3d-rotation',
      name: 'Accelerometer',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceMotionEvent')
    }),
    batteryStatus: new Feature({
      id: 'battery-status',
      icon: 'mdi-device-battery-80',
      name: 'Battery status',
      caniuse: 'battery-status',
      supported: Feature.navigatorContains('getBattery')
    }),
    ambientLight: new Feature({
      id: 'ambient-light',
      icon: 'mdi-device-brightness-low',
      name: 'Ambient light',
      caniuse: 'ambient-light',
      supported: Feature.windowContains('ondevicelight')
    }),
    permissions: new Feature({
      id: 'permissions',
      icon: 'mdi-action-lock-open',
      name: 'Permissions',
      caniuse: 'permissions-api',
      supported: Feature.navigatorContains('permissions')
    }),
    files: new Feature({
      id: 'files',
      icon: 'mdi-device-sd-storage',
      name: 'File access',
      caniuse: 'fileapi',
      supported: Feature.windowContains('File'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/file/dndfiles/', title: 'Reading files in JavaScript using the File APIs'}
      ]
    }),
    storage: new Feature({
      id: 'storage',
      icon: 'mdi-notification-folder-special',
      name: 'Offline storage',
      caniuse: 'namevalue-storage',
      supported: Feature.windowContains('indexedDB') || Feature.windowContains('localStorage')
    }),
    contacts: new Feature({
      id: 'contacts',
      icon: 'mdi-action-account-box',
      name: 'Contacts',
      supported: Feature.navigatorContains('contacts')
    }),
    quota: new Feature({
      id: 'quota',
      icon: 'mdi-notification-sim-card-alert',
      name: 'Quota management',
      supported: Feature.windowContains('storageInfo') || Feature.navigatorContains('temporaryStorageQuota'),
      urls: [
        {url: 'http://www.html5rocks.com/en/tutorials/offline/quota-research/', title: 'Working with quota on mobile browsers'}
      ]
    }),
    deviceOrientation: new Feature({
      id: 'device-orientation',
      icon: 'mdi-device-screen-rotation',
      name: 'Device orientation',
      caniuse: 'deviceorientation',
      supported: Feature.windowContains('DeviceOrientationEvent')
    }),
    rotationLock: new Feature({
      id: 'rotation-lock',
      icon: 'mdi-device-screen-lock-rotation',
      name: 'Rotation lock',
      supported: Feature.containedIn(global.screen, 'lockOrientation')
    }),
    presentation: new Feature({
      id: 'presentation',
      icon: 'mdi-hardware-tv',
      name: 'Presentation features',
      supported: Feature.navigatorContains('presentation')
    }),
    viewports: new Feature({
      id: 'viewports',
      icon: 'mdi-hardware-phonelink',
      name: 'Viewport adaptation',
      urls: [
        {url: 'https://dev.opera.com/articles/an-introduction-to-meta-viewport-and-viewport/', title: 'An Introduction to Meta Viewport and @viewport'}
      ]
    }),
    speech: new Feature({
      id: 'speech-recognition',
      icon: 'mdi-av-mic',
      name: 'Speech recognition',
      caniuse: 'speech-recognition',
      supported: Feature.windowContains('SpeechRecognition'),
      urls: [
        {url: 'https://shaungallagher.github.io/say_restyle/', title: 'Demo - Edit the webpage with your voice'}
      ]
    }),
    clipboard: new Feature({
      id: 'clipboard',
      icon: 'mdi-content-content-paste',
      name: 'Clipboard (copy & paste)',
      caniuse: 'clipboard',
      supported: Feature.windowContains('ClipboardEvent'),
      urls: [
        {url: 'https://github.com/GoogleChrome/samples/tree/gh-pages/cut-and-copy', title: 'Sample code from Google Chrome'}
      ]
    }),
    inputModality: new Feature({
      id: 'input-modality',
      icon: 'mdi-hardware-mouse',
      name: 'Input modality',
      caniuse: 'css-media-interaction',
      urls: [
        {url: 'http://radar.oreilly.com/2015/08/proposing-css-input-modailty.html', title: 'Proposing CSS input modality'}
      ]
    }),
    autofill: new Feature({
      id: 'forms-autofill',
      icon: 'mdi-action-assignment-turned-in',
      name: 'Forms autofill',
      urls: [
        {url: 'https://html.spec.whatwg.org/multipage/forms.html#autofill', title: 'Specification draft'}
      ]
    })
  };

  let featuresGroups = [
    {
      heading: 'Behave like a native app',
      features: [features.localNotifications, features.pushNotifications, features.backgroundTasks, features.manifest, features.foregroundDetection]
    },
    {
      heading: 'Access what\'s around',
      features: [features.geolocation, features.bluetooth, features.nfc, features.proximity, features.ambientLight]
    },
    {
      heading: 'Access device features',
      features: [features.mediaCapture, features.networkInfo, features.online, features.vibration, features.batteryStatus]
    },
    {
      heading: 'Access the system',
      features: [features.storage, features.files, features.permissions, features.contacts, features.quota]
    },
    {
      heading: 'Input',
      features: [features.touch, features.speech, features.accelerometer, features.clipboard, features.inputModality]
    },
    {
      heading: 'Screen & output',
      features: [features.deviceOrientation, features.rotationLock, features.wakeLock, features.viewports, features.presentation]
    }
  ];

  if (global.WWCD.container) { // web run
    global.WWCD.container.configure(register => register.singletons({
      features: features,
      featuresGroups: featuresGroups
    }));
  } else { // build run
    global.features = features;
  }

})(function () {
  let global = typeof exports === 'object' ? exports : window;
  global.WWCD = global.WWCD || {};
  return global;
}());
<|endoftext|>


<commit_before>// @flow

import React from 'react';
import { NavLink } from 'react-router-dom';
import logoOkf from "../Graphics/logo-okfn.svg";
import logoBmbf from "../Graphics/logo-bmbf.svg";
import {grey500, blue500} from 'material-ui/styles/colors';
 import RaisedButton from 'material-ui/RaisedButton';
 import EmailIcon from 'material-ui/svg-icons/communication/email';

import ml1 from "../Graphics/ml1.png";
import ml2 from "../Graphics/ml2.png";
import ml3 from "../Graphics/ml3.png";
import ml3a from "../Graphics/ml3a.png";
import ml4 from "../Graphics/ml4.png";
import ml5 from "../Graphics/ml5.png";
import ml6 from "../Graphics/ml6.png";

const style = {
  container: {
    marginTop: 50
  },
  img: {
    width: "100%"
  },
  impressum: {
    color: grey500,
    textDecoration: "none",
    fontSize: 14
  },
  link: {
    color: blue500,
    textDecoration: "none",
  }
};

const Preview = () =>

Ein Assistent für eine einfachere Mietpreisbremse

Es sollte einfacher sein, mit der Mietpreisbremse auf eigene Faust eine Mietsenkung
  zu erwirken und dadurch das mietlimbo zu verhindern, bei welchem die Mieten jedes Mal weiter steigen,
  wenn eine Wohnung weitergegeben wurde, ohne, dass gegen zu hohe Mieten angefochten
  wurde.

Hierbei hilft der mietlimbo-Assistent, indem er alle Sonderfälle überprüft und für eine
  bestimmte Wohnung den kritischen Wert ortsübliche Vergleichsmiete + 10 Prozent berechnet,
  auf den die Miete gesenkt werden kann.

Obwohl die Software bereits fertig entwickelt ist, kann ich mietlimbo im Moment noch nicht veröffentlichen, da das Rechtsdienstleistungsgesetz
  dies nur zulässt, wenn ich vorher alle Inhalte von einem Anwalt habe prüfen lassen. Daher suche
  ich im Moment nach einem Kooperationspartner, der mir hiermit hilft. Hast du eine Idee, wer das sein könnte?
  Dann schreibe mir gerne an hallo@mietlimbo.de

Wenn du (einmalig) von mir hören möchtest, sobald mietlimbo offiziell startet, klick den Button um dich mit deiner Email-Adresse dafür anzumelden:

  <RaisedButton
      href="http://github.us3.list-manage.com/subscribe/post?u=ccc89b05626e50b935568e6ec&id=090381b944"
      target="_blank"
      label="Sag mir Bescheid"
      primary={true}
      icon={<EmailIcon />}
    />

Das kann mietlimbo

Mietspiegelabfrage über eine moderne, schnelle und nutzerfreundlich gestaltete Webseite

Überprüfung aller wichtigen Ausnahmeregelungen für die Mietpreisbremse

Einordnung im Mietspiegelfeld über Abfrage der Merkmale in den fünf Kategorien Bad, Küche, Wohnung, Gebäude und Umfeld

Berechnung der maximal zulässigen Miete unter Berücksichtigung aller Angaben

Speicherung aller Eingaben, so dass bei Rückkehr zur Seite nicht alles erneut eingegeben werden muss

Ergebnisübersicht zum Ausdrucken, die zu einer Rechtsberatung bei Mieterverein oder Anwalt mitgenommen werden kann

Englische Übersetzung aller Inhalte und Abfragen (weitere Übersetzungen können hinzugefügt werden)

Und so sieht's aus

Realisiert wurde dieses Projekt von Vincent Ahrend mit Förderung und Unterstützung von Prototype Fund, dem
  Deutschen Zentrum für Luft- und Raumfahrttechnik Projektträger und
  Bundesministerium für Bildung und Forschung.

  <NavLink to="/about/" style={style.impressum} >Impressum</NavLink>

;

export default Preview;<commit_msg>Add Twitter link<commit_after>// @flow

import React from 'react';
import { NavLink } from 'react-router-dom';
import logoOkf from "../Graphics/logo-okfn.svg";
import logoBmbf from "../Graphics/logo-bmbf.svg";
import {grey500, blue500} from 'material-ui/styles/colors';
 import RaisedButton from 'material-ui/RaisedButton';
 import EmailIcon from 'material-ui/svg-icons/communication/email';

import ml1 from "../Graphics/ml1.png";
import ml2 from "../Graphics/ml2.png";
import ml3 from "../Graphics/ml3.png";
import ml3a from "../Graphics/ml3a.png";
import ml4 from "../Graphics/ml4.png";
import ml5 from "../Graphics/ml5.png";
import ml6 from "../Graphics/ml6.png";

const style = {
  container: {
    marginTop: 50
  },
  img: {
    width: "100%"
  },
  impressum: {
    color: grey500,
    textDecoration: "none",
    fontSize: 14
  },
  link: {
    color: blue500,
    textDecoration: "none",
  }
};

const Preview = () =>

Ein Assistent für eine einfachere Mietpreisbremse

Es sollte einfacher sein, mit der Mietpreisbremse auf eigene Faust eine Mietsenkung
  zu erwirken und dadurch das mietlimbo zu verhindern, bei welchem die Mieten jedes Mal weiter steigen,
  wenn eine Wohnung weitergegeben wurde, ohne, dass gegen zu hohe Mieten angefochten
  wurde.

Hierbei hilft der mietlimbo-Assistent, indem er alle Sonderfälle überprüft und für eine
  bestimmte Wohnung den kritischen Wert ortsübliche Vergleichsmiete + 10 Prozent berechnet,
  auf den die Miete gesenkt werden kann.

Obwohl die Software bereits fertig entwickelt ist, kann ich mietlimbo im Moment noch nicht veröffentlichen, da das Rechtsdienstleistungsgesetz
  dies nur zulässt, wenn ich vorher alle Inhalte von einem Anwalt habe prüfen lassen. Daher suche
  ich im Moment nach einem Kooperationspartner, der mir hiermit hilft. Hast du eine Idee, wer das sein könnte?
  Dann schreibe mir gerne an hallo@mietlimbo.de oder @ciex

Wenn du (einmalig) von mir hören möchtest, sobald mietlimbo offiziell startet, klick den Button um dich mit deiner Email-Adresse dafür anzumelden:

  <RaisedButton
      href="http://github.us3.list-manage.com/subscribe/post?u=ccc89b05626e50b935568e6ec&id=090381b944"
      target="_blank"
      label="Sag mir Bescheid"
      primary={true}
      icon={<EmailIcon />}
    />

Das kann mietlimbo

Mietspiegelabfrage über eine moderne, schnelle und nutzerfreundlich gestaltete Webseite

Überprüfung aller wichtigen Ausnahmeregelungen für die Mietpreisbremse

Einordnung im Mietspiegelfeld über Abfrage der Merkmale in den fünf Kategorien Bad, Küche, Wohnung, Gebäude und Umfeld

Berechnung der maximal zulässigen Miete unter Berücksichtigung aller Angaben

Speicherung aller Eingaben, so dass bei Rückkehr zur Seite nicht alles erneut eingegeben werden muss

Ergebnisübersicht zum Ausdrucken, die zu einer Rechtsberatung bei Mieterverein oder Anwalt mitgenommen werden kann

Englische Übersetzung aller Inhalte und Abfragen (weitere Übersetzungen können hinzugefügt werden)

Und so sieht's aus

Realisiert wurde dieses Projekt von Vincent Ahrend mit Förderung und Unterstützung von Prototype Fund, dem
  Deutschen Zentrum für Luft- und Raumfahrttechnik Projektträger und
  Bundesministerium für Bildung und Forschung.

  <NavLink to="/about/" style={style.impressum} >Impressum</NavLink>

;

export default Preview;<|endoftext|>


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

foam.LIB({
  name: 'foam.java.Util',
  methods:[
    {
      /** Splits strings when they are too long, used to split documentation in multiple lines */
      name: 'limitSplit',
      code: function(o, str, maxWords){
          var res = '';
          var arr = str.split(' ', 150);
          o.indent();
          for( i = 0 ; i < arr.length ; i++){
            res += arr[i] + ' ';
            if( i % maxWords == 0 && i > 0 ){
              res += '\n';
              o.indent();
            }
          }
          return res;
      }
    }
  ]
});

<commit_msg>Fix of library declaration used in documentation generation<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

foam.LIB({
  name: 'foam.java.Util',
  methods:[
    {
      /** Splits strings when they are too long, used to split documentation in multiple lines */
      name: 'limitSplit',
      code: function(str, maxWords){
          res = [];
          var arr = str.split(' ', 150);
          for( i = 0 ; i < arr.length ; i++){
            line = arr[i] + ' ';
            if( i % maxWords == 0 && i > 0 ){
              line += '\n';
            }
            res.push(line);
          }
          return res;
      }
    }
  ]
});
<|endoftext|>


<commit_before>const debug = require('debug')('windshaft:dataview:histogram');
const NumericHistogram = require('./numeric-histogram');
const DateHistogram = require('./date-histogram');

const TYPE = 'histogram';
const DATE_HISTOGRAM = 'DateHistogram';
const NUMERIC_HISTOGRAM = 'NumericHistogram';

module.exports = class Histogram {
    constructor (query, options, queries) {
        switch (this._getHistogramSubtype(options)) {
            case DATE_HISTOGRAM:
                debug('Delegating to DateHistogram with options: %j', options)
                this.dataview = new DateHistogram(query, options, queries);
                break;
            case NUMERIC_HISTOGRAM:
                debug('Delegating to NumericHistogram with options: %j', options)
                this.dataview = new NumericHistogram(query, options, queries);
                break;

            default:
                throw new Error('Unsupported Histogram type');
        }
    }

    _getHistogramSubtype (options) {
        if(options.bins && !options.aggregation) {
            return NUMERIC_HISTOGRAM
        } else if(options.aggregation && !options.bins) {
            return DATE_HISTOGRAM
        }
    }

    getResult (psql, override, callback) {
        this.dataview.getResult(psql, override, callback);
    }

    // In order to keep previous behaviour with overviews,
    // we have to expose the following methods to bypass
    // the concrete overview implementation

    sql (psql, override, callback) {
        this.dataview.sql(psql, override, callback);
    }

    format (result, override) {
        return this.dataview.format(result, override);
    }

    getType () {
        return this.dataview.getType();
    }

    toString () {
        return this.dataview.toString();
    }
};
<commit_msg>Do not choose histogram implementation until getResult()<commit_after>const debug = require('debug')('windshaft:dataview:histogram');
const NumericHistogram = require('./numeric-histogram');
const DateHistogram = require('./date-histogram');

const TYPE = 'histogram';
const DATE_HISTOGRAM = 'DateHistogram';
const NUMERIC_HISTOGRAM = 'NumericHistogram';

module.exports = class Histogram {
    constructor (query, options = {}, queries) {
        this.query = query;
        this.options = options;
        this.queries = queries;

        this.dataview = this._getHistogramImplemetation();
    }

    _getHistogramImplemetation (override) {
        switch (this._getHistogramSubtype(override)) {
            case DATE_HISTOGRAM:
                debug('Delegating to DateHistogram with options: %j and overriding: %j', this.options, override)
                return new DateHistogram(this.query, this.options, this.queries);
                break;
            case NUMERIC_HISTOGRAM:
                debug('Delegating to NumericHistogram with options: %j and overriding: %j', this.options, override)
                return new NumericHistogram(this.query, this.options, this.queries);
                break;
            default:
                throw new Error('Unsupported Histogram type');
        }
    }

    _getHistogramSubtype (override = {}) {
        if(this.options.aggregation !== undefined || override.aggregation !== undefined) {
            return DATE_HISTOGRAM;
        }
        return NUMERIC_HISTOGRAM;
    }

    getResult (psql, override, callback) {
        this.dataview = this._getHistogramImplemetation(override);
        this.dataview.getResult(psql, override, callback);
    }

    // In order to keep previous behaviour with overviews,
    // we have to expose the following methods to bypass
    // the concrete overview implementation

    sql (psql, override, callback) {
        this.dataview.sql(psql, override, callback);
    }

    format (result, override) {
        return this.dataview.format(result, override);
    }

    getType () {
        return this.dataview.getType();
    }

    toString () {
        return this.dataview.toString();
    }
};
<|endoftext|>


<commit_before>
var itemHeadline = document.querySelectorAll('.item-headline');
function payWall(){
    var xhrpw = new XMLHttpRequest();
    xhrpw.open('get', '/index.php/jsapi/paywall');
    xhrpw.setRequestHeader('Content-Type', 'application/text');
    xhrpw.onload = function() {
        if (xhrpw.status === 200) {
            var data = xhrpw.responseText;
            var dataObj = JSON.parse(data);
            if (dataObj.paywall === 1) {
                console.log('get paywall:'+data);
                updateLockClass();
            }
        } else {
            // updateLockClass();
            console.log('fail to get paywall');
        }
    };
    xhrpw.send(null);
}

var userId1 = GetCookie('USER_ID') || GetCookie('uniqueVisitorId');
if (userId1 !== null) {
    payWall();
    var interval = setInterval(function(){
      payWall();
    },3000);
    setTimeout(function( ) {
       clearInterval(interval);
    }, 15000);
}

var getPayHeadline = [];
// 过滤出包含locked的item-headline数组
function getPayStory(){
  // 循环itemHeadline长度数量
  for (var i = 0; i < itemHeadline.length; i++) {
        var childNodes = itemHeadline[i].children;
        // 循环childNodes长度数量
        for (var j = 0; j < childNodes.length; j++) {
          if (hasClass(childNodes[j],'locked')){
            getPayHeadline.push(childNodes[j]);
          }
        }
  }
}
getPayStory();

function updateLockClass(){
    if (getPayHeadline.length>0){
      for (var k = 0; k < getPayHeadline.length; k++) {
        removeClass(getPayHeadline[k], 'locked');
        addClass(getPayHeadline[k], 'unlocked');
      }
    }
}

function hasClass(ele, cls) {
  cls = cls || '';
  if (cls.replace(/\s/g, '').length === 0) {
    return false;
  }else{
    return new RegExp(' ' + cls + ' ').test(' ' + ele.className + ' ');
  }

}

function addClass(ele, cls) {
  if (!hasClass(ele, cls)) {
    ele.className = ele.className === '' ? cls : ele.className + ' ' + cls;
  }
}

function removeClass(ele, cls) {
  if (hasClass(ele, cls)) {
    var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' ';
    while (newClass.indexOf(' ' + cls + ' ') >= 0) {
      newClass = newClass.replace(' ' + cls + ' ', ' ');
    }
    ele.className = newClass.replace(/^\s+|\s+$/g, '');
  }
}

// 过滤出包含a的子节点，然后用过滤出的节点增加class
// function getHeadlineA(ele,clsName){
//     var childNodes = ele.children;   //HTMLCollection
//     for (let i = 0; i < childNodes.length; i++) {
//       if (childNodes[i].tagName.toLowerCase()==='a'){
//         addClass(childNodes[i], clsName);
//       }
//     }
// }<commit_msg>delete pay-wall console<commit_after>
var itemHeadline = document.querySelectorAll('.item-headline');
function payWall(){
    var xhrpw = new XMLHttpRequest();
    xhrpw.open('get', '/index.php/jsapi/paywall');
    xhrpw.setRequestHeader('Content-Type', 'application/text');
    xhrpw.onload = function() {
        if (xhrpw.status === 200) {
            var data = xhrpw.responseText;
            var dataObj = JSON.parse(data);
            if (dataObj.paywall === 1) {
                // console.log('get paywall:'+data);
                updateLockClass();
            }
        } else {
            // updateLockClass();
            console.log('fail to get pw');
        }
    };
    xhrpw.send(null);
}

var userId1 = GetCookie('USER_ID') || GetCookie('uniqueVisitorId');
if (userId1 !== null) {
    payWall();
    var interval = setInterval(function(){
      payWall();
    },3000);
    setTimeout(function( ) {
       clearInterval(interval);
    }, 15000);
}

var getPayHeadline = [];
// 过滤出包含locked的item-headline数组
function getPayStory(){
  // 循环itemHeadline长度数量
  for (var i = 0; i < itemHeadline.length; i++) {
        var childNodes = itemHeadline[i].children;
        // 循环childNodes长度数量
        for (var j = 0; j < childNodes.length; j++) {
          if (hasClass(childNodes[j],'locked')){
            getPayHeadline.push(childNodes[j]);
          }
        }
  }
}
getPayStory();

function updateLockClass(){
    if (getPayHeadline.length>0){
      for (var k = 0; k < getPayHeadline.length; k++) {
        removeClass(getPayHeadline[k], 'locked');
        addClass(getPayHeadline[k], 'unlocked');
      }
    }
}

function hasClass(ele, cls) {
  cls = cls || '';
  if (cls.replace(/\s/g, '').length === 0) {
    return false;
  }else{
    return new RegExp(' ' + cls + ' ').test(' ' + ele.className + ' ');
  }

}

function addClass(ele, cls) {
  if (!hasClass(ele, cls)) {
    ele.className = ele.className === '' ? cls : ele.className + ' ' + cls;
  }
}

function removeClass(ele, cls) {
  if (hasClass(ele, cls)) {
    var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' ';
    while (newClass.indexOf(' ' + cls + ' ') >= 0) {
      newClass = newClass.replace(' ' + cls + ' ', ' ');
    }
    ele.className = newClass.replace(/^\s+|\s+$/g, '');
  }
}

// 过滤出包含a的子节点，然后用过滤出的节点增加class
// function getHeadlineA(ele,clsName){
//     var childNodes = ele.children;   //HTMLCollection
//     for (let i = 0; i < childNodes.length; i++) {
//       if (childNodes[i].tagName.toLowerCase()==='a'){
//         addClass(childNodes[i], clsName);
//       }
//     }
// }<|endoftext|>