<commit_before>$(function() {

	$.fn.googleMap = function(params) {
		params = $.extend( {
			zoom : 10,
			coords : [48.895651, 2.290569],
			type : "ROADMAP",
			debug : false,
			langage : "english",
			overviewMapControl: false,
			streetViewControl: false,
			scrollwheel: false,
			mapTypeControl: false
		}, params);

		switch(params.type) {
			case 'ROADMAP':
			case 'SATELLITE':
			case 'HYBRID':
			case 'TERRAIN':
				params.type = google.maps.MapTypeId[params.type];
				break;
			default:
				params.type = google.maps.MapTypeId.ROADMAP;
				break;
		}

		this.each(function() {

			var map = new google.maps.Map(this, {
				zoom: params.zoom,
				center: new google.maps.LatLng(params.coords[0], params.coords[1]),
				mapTypeId: params.type,
				scrollwheel: params.scrollwheel,
				streetViewControl: params.streetViewControl,
				overviewMapControl: params.overviewMapControl,
				mapTypeControl: params.mapTypeControl

			});

			$(this).data('googleMap', map);
			$(this).data('googleLang', params.langage);
			$(this).data('googleDebug', params.debug);
			$(this).data('googleMarker', new Array());
			$(this).data('googleBound', new google.maps.LatLngBounds());
		});

		return this;
	}

	$.fn.addMarker = function(params) {
		params = $.extend( {
			coords : false,
			address : false,
			url : false,
			id : false,
			icon : false,
			draggable : false,
			title : "",
			text : "",
			success : function() {}
		}, params);

		this.each(function() {
			$this = $(this);

			if(!$this.data('googleMap')) {
				if($this.data('googleDebug'))
					console.error("jQuery googleMap : Unable to add a marker where there is no map !");

				return false;
			}

			if(!params.coords && !params.address) {
				if($this.data('googleDebug'))
					console.error("jQuery googleMap : Unable to add a marker if you don't tell us where !");

				return false;
			}

			if(params.address && typeof params.address == "string") {

				var geocodeAsync = function($that) {

					var geocoder = new google.maps.Geocoder();

					geocoder.geocode({
						address : params.address,
						bounds : $that.data('googleBound'),
						language : $that.data('googleLang')
					}, function(results, status) {

						if (status == google.maps.GeocoderStatus.OK) {
							$that.data('googleBound').extend(results[0].geometry.location);

							if(params.icon) {
								var marker = new google.maps.Marker({
									map: $this.data('googleMap'),
									position: results[0].geometry.location,
									title: params.title,
									icon: params.icon,
									draggable: params.draggable
								});
							} else {
								var marker = new google.maps.Marker({
									map: $that.data('googleMap'),
									position: results[0].geometry.location,
									title: params.title,
									draggable: params.draggable
								});
							}

							if(params.draggable) {
								google.maps.event.addListener(marker, 'dragend', function() {
									var location = marker.getPosition();

									var coords = {};

									coords.lat = location.lat();
									coords.lon = location.lng();

									params.success(coords, $this);
								});
							}

							if(params.title != "" && params.text != "" && !params.url) {
								var infowindow = new google.maps.InfoWindow({
									content: "
"+params.title+"
"+params.text
								});

								var map = $that.data('googleMap');

								google.maps.event.addListener(marker, 'click', function() {
									infowindow.open(map, marker);
								});
							} else if(params.url) {
								google.maps.event.addListener(marker, 'click', function() {
									document.location = params.url;
								});
							}

							if(!params.id) {
								$that.data('googleMarker').push(marker);
							} else {
								$that.data('googleMarker')[params.id] = marker;
							}

							if($that.data('googleMarker').length == 1) {
								$that.data('googleMap').setCenter(results[0].geometry.location);
								$that.data('googleMap').setZoom($that.data('googleMap').getZoom());
							} else {
								$that.data('googleMap').fitBounds($that.data('googleBound'));
							}

							var coords = {};
							coords.lat = results[0].geometry.location.lat();
							coords.lon = results[0].geometry.location.lng();

							params.success(coords, $this);

						} else {
							if($this.data('googleDebug'))
								console.error("jQuery googleMap : Unable to find the place asked for the marker ("+status+")");
						}
					});
				}($this);
			} else {
				$this.data('googleBound').extend(new google.maps.LatLng(params.coords[0], params.coords[1]));

        			if(params.icon) {
					var marker = new google.maps.Marker({
						map: $this.data('googleMap'),
						position: new google.maps.LatLng(params.coords[0], params.coords[1]),
						title: params.title,
						icon: params.icon,
						draggable: params.draggable
					});
				} else {
					var marker = new google.maps.Marker({
						map: $this.data('googleMap'),
						position: new google.maps.LatLng(params.coords[0], params.coords[1]),
						title: params.title,
						draggable: params.draggable
					});
				}

        			if(params.title != "" && params.text != "" && !params.url) {
          				var infowindow = new google.maps.InfoWindow({
						content: "
"+params.title+"
"+params.text
					});

					var map = $this.data('googleMap');

	        			google.maps.event.addListener(marker, 'click', function() {
		        			infowindow.open(map, marker);
	        			});
				} else if(params.url) {
          				google.maps.event.addListener(marker, 'click', function() {
              					document.location = params.url;
        				});
				}

				if(params.draggable) {
					google.maps.event.addListener(marker, 'dragend', function() {
						var location = marker.getPosition();

						var coords = {};

						coords.lat = location.lat();
						coords.lon = location.lng();

						params.success(coords, $this);
					});
				}

				if(!params.id) {
       					$this.data('googleMarker').push(marker);
        			} else {
        				$this.data('googleMarker')[params.id] = marker;
        			}

				if($this.data('googleMarker').length == 1) {
					$this.data('googleMap').setCenter(new google.maps.LatLng(params.coords[0], params.coords[1]));
					$this.data('googleMap').setZoom($this.data('googleMap').getZoom());
				} else {
					$this.data('googleMap').fitBounds($this.data('googleBound'));
				}

				params.success({
					lat: params.coords[0],
					lon: params.coords[1]
				}, $this);
			}
		});

		return this;
	}

	$.fn.removeMarker = function(id) {
		this.each(function() {
			var $this = $(this);

    			if(!$this.data('googleMap')) {
    				if($this.data('googleDebug'))
      					console.log("jQuery googleMap : Unable to delete a marker where there is no map !");

      				return false;
    			}

    			var $markers = $this.data('googleMarker');

    			if(typeof $markers[id] != 'undefined') {
    				$markers[id].setMap(null);

      				if($this.data('googleDebug'))
      					console.log('jQuery googleMap : marker deleted');

      				return true;
    			} else {
      				if($this.data('googleDebug'))
      					console.error("jQuery googleMap : Unable to delete a marker if it not exists !");

      				return false;
    			}
		});
	}

	$.fn.addWay = function(params) {
		params = $.extend( {
			start : false,
			end : false,
			step : [],
			route : false,
			langage : 'english'
		}, params);

		var direction = new google.maps.DirectionsService({
			region: "fr"
		});

		var way = new google.maps.DirectionsRenderer({
			draggable: true,
			map: $(this).data('googleMap'),
			panel: document.getElementById(params.route),
			provideTripAlternatives: true
		});

		document.getElementById.innerHTML = "";

		var waypoints = [];

    		for(var i in params.step) {
    			var step;
      			if(typeof params.step[i] == "object") {
        			step = new google.maps.LatLng(params.step[i][0], params.step[i][1]);
      			} else {
        			step = params.step[i];
      			}

      			waypoints.push({
      				location: step,
      				stopover: true
      			});
		}

		if(typeof params.end != "object") {
			var geocodeAsync = function($that) {
				var geocoder = new google.maps.Geocoder();

		    		geocoder.geocode({
		    			address  : params.end,
		    			bounds   : $that.data('googleBound'),
		    			language : params.langage
		    		}, function(results, status) {
	        			if (status == google.maps.GeocoderStatus.OK) {
	        				var request = {
	            					origin: params.start,
	            					destination: results[0].geometry.location,
	            					travelMode: google.maps.DirectionsTravelMode.DRIVING,
	            					region: "fr",
	            					waypoints: waypoints
		        			};

		        			direction.route(request, function(response, status) {
	            					if (status == google.maps.DirectionsStatus.OK) {
	              						way.setDirections(response);
	            					} else {
	              						if($that.data('googleDebug'))
	            							console.error("jQuery googleMap : Unable to find the place asked for the route ("+response+")");
	            					}
		        			});

	        			} else {
	          				if($that.data('googleDebug'))
	          					console.error("jQuery googleMap : Address not found");
	        			}
		    		});
	    		}($(this));
		} else {
			var request = {
				origin: params.start,
				destination: new google.maps.LatLng(params.end[0], params.end[1]),
				travelMode: google.maps.DirectionsTravelMode.DRIVING,
				region: "fr",
				waypoints: waypoints
			};

			direction.route(request, function(response, status) {
				if (status == google.maps.DirectionsStatus.OK) {
					way.setDirections(response);
				} else {
					if($(this).data('googleDebug'))
          					console.error("jQuery googleMap : Address not found");
				}
			});
		}

		return this;
	}
});
<commit_msg>Decided not using google map api<commit_after><|endoftext|>


<commit_before>import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
import { compose, withState, withHandlers } from 'recompact'
import { ReactiveVar } from 'meteor/reactive-var'
import _ from 'lodash'

import { withCampaignData, withCampaignId } from '../data/campaign'
import { CampaignSplash } from '../visual/splash'
import Title from '../utils/title'
import CardList from '../collection/card-list'

import {
	SplashToolbar,
	MenuItem,
	MenuLink,
	Space,
	Center,
} from '../visual/menu'
import Icon from '../visual/icon'
import { Input } from '../visual/form'
import HistoryList from '../collection/card-history'
import { Main, Aside } from '../visual/grid'
import { Button } from '../visual/primitives'
import { Card } from '../../shared/methods'
import { go } from '../utils/router'

const searchVar = new ReactiveVar('')
const debouncedSetSearch = _.debounce(searchVar.set.bind(searchVar), 300)

const withCampaignSearch = withTracker(() => ({
		search: searchVar.get(),
	setSearch: debouncedSetSearch
}))

const withSearchState = withState('search', 'setSearch', '')
const withSearchActions = withHandlers({
	createCard: ({ search, campaignId }) => async ev => {
		const { _id } = await Card.create({ title: search, campaignId })
		go(`/${campaignId}/${_id}`)
	}
})

const connectSearch = compose(
	withCampaignId,
	withSearchState,
	withSearchActions
)

const Search = connectSearch(({ search, setSearch, onChange, createCard }) => (
	<>
		<Input
			type='search'
			placeholder='Search…'
			value={search}
			onChange={ev => {
				setSearch(ev.target.value)
				onChange(ev.target.value)
			}}
		/>
		{search && (
			<Button onClick={createCard}>
				<Icon icon='plus' />
				Quick add…
			</Button>
		)}
	</>
))

const connectCampaignPage = compose(
	withCampaignData,
	withCampaignSearch
)

export default connectCampaignPage(({ campaign, search, setSearch }) => (
		<>
			<Title>{campaign.title}</Title>

			<CampaignSplash />

			<SplashToolbar>

					<MenuItem flush>
					<Search onChange={setSearch} />
					</MenuItem>

					<Space />

					<MenuLink href={`/${campaign._id}/new`}>
						<Icon icon='file-text-o' /> New
					</MenuLink>

			</SplashToolbar>

				<CardList search={search} />

				<HistoryList />

		</>
))
<commit_msg>nicer looking<commit_after>import React from 'react'
import { withTracker } from 'meteor/react-meteor-data'
import { compose, withState, withHandlers } from 'recompact'
import { ReactiveVar } from 'meteor/reactive-var'
import _ from 'lodash'

import { withCampaignData, withCampaignId } from '../data/campaign'
import { CampaignSplash } from '../visual/splash'
import Title from '../utils/title'
import CardList from '../collection/card-list'

import {
	SplashToolbar,
	MenuItem,
	MenuLink,
	Space,
	Center,
	MenuButton
} from '../visual/menu'
import Icon from '../visual/icon'
import { Input } from '../visual/form'
import HistoryList from '../collection/card-history'
import { Main, Aside } from '../visual/grid'
import { Card } from '../../shared/methods'
import { go } from '../utils/router'

const searchVar = new ReactiveVar('')
const debouncedSetSearch = _.debounce(searchVar.set.bind(searchVar), 300)

const withCampaignSearch = withTracker(() => ({
	search: searchVar.get(),
	setSearch: debouncedSetSearch
}))

const withSearchState = withState('search', 'setSearch', '')
const withSearchActions = withHandlers({
	createCard: ({ search, campaignId }) => async ev => {
		const { _id } = await Card.create({ title: search, campaignId })
		go(`/${campaignId}/${_id}`)
	}
})

const connectSearch = compose(
	withCampaignId,
	withSearchState,
	withSearchActions
)

const Search = connectSearch(({ search, setSearch, onChange, createCard }) => (
	<>
		<MenuItem flush>
		<Input
			type='search'
			placeholder='Search…'
			value={search}
			onChange={ev => {
				setSearch(ev.target.value)
				onChange(ev.target.value)
			}}
		/>
		</MenuItem>
		{search && (
			<MenuButton onClick={createCard}>
				<Icon icon='plus' />
				Quick add…
			</MenuButton>
		)}
	</>
))

const connectCampaignPage = compose(
	withCampaignData,
	withCampaignSearch
)

export default connectCampaignPage(({ campaign, search, setSearch }) => (
	<>
		<Title>{campaign.title}</Title>

		<CampaignSplash />

		<SplashToolbar>

					<Search onChange={setSearch} />

				<Space />

				<MenuLink href={`/${campaign._id}/new`}>
					<Icon icon='file-text-o' /> New
				</MenuLink>

		</SplashToolbar>

			<CardList search={search} />

			<HistoryList />

	</>
))
<|endoftext|>


<commit_before>var path = require('path')
  , Matcher
  , rule
  , Rule;

// Define some static functions.
function Matcher() {}

// Split a task to two parts, name space and task name.
// For example, given 'foo:bin/a%.c', return an object with
// - 'ns'     : foo
// - 'name'   : bin/a%.c
Matcher.split = function(task) {
  var parts = task.split(':')
  , name  = parts.pop()
  , ns    = Matcher.resolveNS( parts );
  return {
    'name' : name,
    'ns'   : ns
  };
}

// Return the namespace based on an array of names.
// For example, given ['foo', 'baz' ], return the namespace
//
//   default -> foo -> baz
//
// where default is the global root namespace
// and -> means child namespace.
Matcher.resolveNS = function(parts) {
  var  ns    = jake.defaultNamespace;
  for(var i = 0, l = parts.length; ns && i < l; i++) {
    ns = ns.childNamespaces[parts[i]];
  }
  return ns;
};

// Given a pattern p, say 'foo:bin/a%.c'
// Return an object with
// - 'ns'     : foo
// - 'dir'    : bin
// - 'prefix' : a
// - 'suffix' : .c
Matcher.resolve = function(p) {
  var task = Matcher.split(p),
      name  = task.name,
      ns    = task.ns;
  var split = path.basename(name).split('%');
  return {
    'ns'  : ns,
    'dir' : path.dirname(name),
    'prefix' : split[0],
    'suffix' : split[1],
  };
}

// Test whether string a is a suffix of string b
Matcher.stringEndWith = function (a,b) {
  var l;
  return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
}

// Replace the suffix a of the string s with b.
// Note that, it is assumed a is a suffix of s.
Matcher.stringReplaceSuffix = function (s,a,b) {
  return s.slice(0,s.lastIndexOf(a)) + b;
}

// Test wether the a prerequisite matchs the pattern.
// The arg 'pattern' does not have namespace as prefix.
// For example, the following tests are true
//
//   pattern      |    prereq
//   bin/%.o      |    bin/main.o
//   bin/%.o      |    foo:bin/main.o
//
// The following tests are false (trivally)
//
//   pattern      |    prereq
//   bin/%.o      |    foobin/main.o
//   bin/%.o      |    bin/main.oo

Matcher.match = function(pattern, prereq) {
  if (pattern.indexOf('%') == -1) {
    // No Pattern. No Folder. No Namespace.
    // A Simple Suffix Rule. Just test suffix
    return Matcher.stringEndWith(pattern, prereq);
  } else {
    // Resolve the dir, prefix and suffix of pattern
    var p = Matcher.resolve(pattern);

    // Resolve the namespace and task name of prereq
    var task = Matcher.split(prereq),
        name  = task.name,
        ns    = task.ns;

    // Set the objective as the task name of prereq
    var obj = name;

    // Namespace is already matched.

    // Check dir
    if (path.dirname(obj) != p.dir)
      return false;

    var filename = path.basename(obj);

    // Check file name length.
    if (p.prefix.length + p.suffix.length + 1
          > filename.length) {
      // Length does not match.
      return false;
    }

    // Check prefix
    if (filename.indexOf(p.prefix) != 0) {
      return false;
    }

    // Check suffix
    if (! Matcher.stringEndWith( p.suffix, filename)) {
      return false;
    }

    // OK. Find a match.
    return true;
  }
}

// Generate the source based on
//  - prereq   prerequisite
//  - objP    pattern for the objective
//  - srcP    pattern for the source
//
// Return the source with properties
//  - dep      the prerequisite of source
//             (with the namespace)
//
//  - file     the file name of source
//             (without the namespace)
//
// For example, given
//
//  - prereq   foo:bin/main.o
//  - objP    bin/%.o
//  - srcP    src/%.c
//
// return {
//    'dep' : 'foo:src/main.c',
//    'file': 'src/main.c'
//  };
//
Matcher.source = function(prereq, objP, srcP) {
  if (objP.indexOf('%') == -1) {
    // A Simple Suffix
    var src = Matcher.stringReplaceSuffix( prereq, objP, srcP );
    return {
      'dep'  : src
    , 'file' : src
    };

  } else {
    // Resolve the dir, prefix and suffix of pattern
    var p = Matcher.resolve(objP);

    // Resolve the namespace and task name
    var parts = prereq.split(':')
      , obj   = parts.pop()
      , ns    = Matcher.resolveNS( parts );

    var filename = path.basename(obj);

    // Resolve the matching pattern for the symbol '%'
    var fill = filename.slice(
      p.prefix.length,
      filename.length - p.suffix.length
    );

    // Replace the % with the mathcing pattern
    // found in last step
    var src = srcP.replace('%', fill);

    // Construct the return object
    if (parts.length == 0) {
      // no name space
      return {
        'dep'  : src
      , 'file' : src };
    } else {
      // Add namespace to the prerequisite of source file
      var dep = parts.join(':') + ':' + src;
      return {
          'dep'  : dep
        , 'file' : src
      };
    }
  }
}

Rule = function (opts) {
  this.args = opts.args;
  this.desc =  opts.desc;
  this.ns = opts.ns;
};

rule = new (function () {
  // The steps are
  // 1. Resolve the task name and namespace for the prerequisite.
  // 2. Fetch all the rules belonging to the namespace.
  // 3. Test each pattern.
  this.match = function(prereq, args) {
    // 1.
    var task = Matcher.split(prereq),
       name  = task.name,
       ns    = task.ns;

    if (ns == null) return null;

    // 2.
    var rules = ns.rules;

    // 3.
    for (var p in rules) {
      if (rules.hasOwnProperty(p)
          && typeof rules[p] == 'object'
          && Matcher.match(p, prereq)
        ) {
          return p;
        }
    }
    return null;
  }

  // Create a file task based on the Rule.
  this.createTask = function(pattern, prereq, args) {
    var objP
    , srcP
    , prereqs = []
    , action
    , opts;

    // 1. Resolve the task name and namespace for the prerequisite.
    var task = Matcher.split(prereq)
    ,  name  = task.name
    ,  ns    = task.ns;

    // 2. Obtain the matching rule.
    var rule = ns.rules[pattern].args.slice(0);  // We need a copy.

    // 3. Obtain the pattern for the objective and source.
    objP = rule.shift();
    srcP = rule.shift();

    // 4. Resolve the prerequisite and file name representing the source
    var src = Matcher.source( prereq, objP, srcP );
    // - src.dep  is the prerequisite with namespace.
    // - src.file is the source file name.

    // 5. Generate the prerequisite for the matching task.
    //    It is the original prerequisites plus the prerequisite
    //    representing source file, i.e.,
    //
    //      rule( '%.o', '%.c', ['some.h'] ...
    //
    //    If the objective is main.o, then new task should be
    //
    //      file( 'main.o', ['main.c', 'some.h' ] ...
    if (Array.isArray(rule[0])) {
      prereqs = rule.shift().slice(0); // again, a copy
    }
    prereqs.unshift( src.dep );

    // 6. Fetch the action and opts.
    while ((arg = rule.shift())) {
      if (typeof arg == 'function') {
        action = arg;
      }
      else {
        opts = arg;
      }
    }

    // 7. Create a new action with obj and src replaced.
    var newAction = function() {
      action(name, src.file, args);
    };

    // 8. Now, create the file task.
    var newArgs = [name, prereqs, newAction, opts];
    newArgs.unshift('file');

    // 9. Insert the file task into Jake
    //
    // Since createTask function stores the task as a child task
    // of currentNamespace. Here we temporariliy switch the namespace.
    var tNs = jake.currentNamespace;
    jake.currentNamespace = ns;
    var createdTask = jake.createTask.apply(global, newArgs);
    jake.currentNamespace = tNs;

    return createdTask;
  };

})();

rule.Rule = Rule;

module.exports = rule;
<commit_msg>Make Matcher a constructed singleton helper<commit_after>var path = require('path')
  , Matcher
  , rule
  , Rule;

// Define a helper object with some utility functions
Matcher = new (function () {
  // Split a task to two parts, name space and task name.
  // For example, given 'foo:bin/a%.c', return an object with
  // - 'ns'     : foo
  // - 'name'   : bin/a%.c
  this.split = function(task) {
    var parts = task.split(':')
    , name  = parts.pop()
    , ns    = Matcher.resolveNS( parts );
    return {
      'name' : name,
      'ns'   : ns
    };
  }

  // Return the namespace based on an array of names.
  // For example, given ['foo', 'baz' ], return the namespace
  //
  //   default -> foo -> baz
  //
  // where default is the global root namespace
  // and -> means child namespace.
  this.resolveNS = function(parts) {
    var  ns    = jake.defaultNamespace;
    for(var i = 0, l = parts.length; ns && i < l; i++) {
      ns = ns.childNamespaces[parts[i]];
    }
    return ns;
  };

  // Given a pattern p, say 'foo:bin/a%.c'
  // Return an object with
  // - 'ns'     : foo
  // - 'dir'    : bin
  // - 'prefix' : a
  // - 'suffix' : .c
  this.resolve = function(p) {
    var task = Matcher.split(p),
        name  = task.name,
        ns    = task.ns;
    var split = path.basename(name).split('%');
    return {
      'ns'  : ns,
      'dir' : path.dirname(name),
      'prefix' : split[0],
      'suffix' : split[1],
    };
  }

  // Test whether string a is a suffix of string b
  this.stringEndWith = function (a,b) {
    var l;
    return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
  }

  // Replace the suffix a of the string s with b.
  // Note that, it is assumed a is a suffix of s.
  this.stringReplaceSuffix = function (s,a,b) {
    return s.slice(0,s.lastIndexOf(a)) + b;
  }

  // Test wether the a prerequisite matchs the pattern.
  // The arg 'pattern' does not have namespace as prefix.
  // For example, the following tests are true
  //
  //   pattern      |    prereq
  //   bin/%.o      |    bin/main.o
  //   bin/%.o      |    foo:bin/main.o
  //
  // The following tests are false (trivally)
  //
  //   pattern      |    prereq
  //   bin/%.o      |    foobin/main.o
  //   bin/%.o      |    bin/main.oo

  this.match = function(pattern, prereq) {
    if (pattern.indexOf('%') == -1) {
      // No Pattern. No Folder. No Namespace.
      // A Simple Suffix Rule. Just test suffix
      return this.stringEndWith(pattern, prereq);
    } else {
      // Resolve the dir, prefix and suffix of pattern
      var p = this.resolve(pattern);

      // Resolve the namespace and task name of prereq
      var task = this.split(prereq),
          name  = task.name,
          ns    = task.ns;

      // Set the objective as the task name of prereq
      var obj = name;

      // Namespace is already matched.

      // Check dir
      if (path.dirname(obj) != p.dir)
        return false;

      var filename = path.basename(obj);

      // Check file name length.
      if (p.prefix.length + p.suffix.length + 1
            > filename.length) {
        // Length does not match.
        return false;
      }

      // Check prefix
      if (filename.indexOf(p.prefix) != 0) {
        return false;
      }

      // Check suffix
      if (! this.stringEndWith( p.suffix, filename)) {
        return false;
      }

      // OK. Find a match.
      return true;
    }
  }

  // Generate the source based on
  //  - prereq   prerequisite
  //  - objP    pattern for the objective
  //  - srcP    pattern for the source
  //
  // Return the source with properties
  //  - dep      the prerequisite of source
  //             (with the namespace)
  //
  //  - file     the file name of source
  //             (without the namespace)
  //
  // For example, given
  //
  //  - prereq   foo:bin/main.o
  //  - objP    bin/%.o
  //  - srcP    src/%.c
  //
  // return {
  //    'dep' : 'foo:src/main.c',
  //    'file': 'src/main.c'
  //  };
  //
  this.source = function(prereq, objP, srcP) {
    if (objP.indexOf('%') == -1) {
      // A Simple Suffix
      var src = this.stringReplaceSuffix( prereq, objP, srcP );
      return {
        'dep'  : src
      , 'file' : src
      };

    } else {
      // Resolve the dir, prefix and suffix of pattern
      var p = this.resolve(objP);

      // Resolve the namespace and task name
      var parts = prereq.split(':')
        , obj   = parts.pop()
        , ns    = this.resolveNS( parts );

      var filename = path.basename(obj);

      // Resolve the matching pattern for the symbol '%'
      var fill = filename.slice(
        p.prefix.length,
        filename.length - p.suffix.length
      );

      // Replace the % with the mathcing pattern
      // found in last step
      var src = srcP.replace('%', fill);

      // Construct the return object
      if (parts.length == 0) {
        // no name space
        return {
          'dep'  : src
        , 'file' : src };
      } else {
        // Add namespace to the prerequisite of source file
        var dep = parts.join(':') + ':' + src;
        return {
            'dep'  : dep
          , 'file' : src
        };
      }
    }
  }

})();

Rule = function (opts) {
  this.args = opts.args;
  this.desc =  opts.desc;
  this.ns = opts.ns;
};

rule = new (function () {
  // The steps are
  // 1. Resolve the task name and namespace for the prerequisite.
  // 2. Fetch all the rules belonging to the namespace.
  // 3. Test each pattern.
  this.match = function(prereq, args) {
    // 1.
    var task = Matcher.split(prereq),
       name  = task.name,
       ns    = task.ns;

    if (ns == null) return null;

    // 2.
    var rules = ns.rules;

    // 3.
    for (var p in rules) {
      if (rules.hasOwnProperty(p)
          && typeof rules[p] == 'object'
          && Matcher.match(p, prereq)
        ) {
          return p;
        }
    }
    return null;
  }

  // Create a file task based on the Rule.
  this.createTask = function(pattern, prereq, args) {
    var objP
    , srcP
    , prereqs = []
    , action
    , opts;

    // 1. Resolve the task name and namespace for the prerequisite.
    var task = Matcher.split(prereq)
    ,  name  = task.name
    ,  ns    = task.ns;

    // 2. Obtain the matching rule.
    var rule = ns.rules[pattern].args.slice(0);  // We need a copy.

    // 3. Obtain the pattern for the objective and source.
    objP = rule.shift();
    srcP = rule.shift();

    // 4. Resolve the prerequisite and file name representing the source
    var src = Matcher.source( prereq, objP, srcP );
    // - src.dep  is the prerequisite with namespace.
    // - src.file is the source file name.

    // 5. Generate the prerequisite for the matching task.
    //    It is the original prerequisites plus the prerequisite
    //    representing source file, i.e.,
    //
    //      rule( '%.o', '%.c', ['some.h'] ...
    //
    //    If the objective is main.o, then new task should be
    //
    //      file( 'main.o', ['main.c', 'some.h' ] ...
    if (Array.isArray(rule[0])) {
      prereqs = rule.shift().slice(0); // again, a copy
    }
    prereqs.unshift( src.dep );

    // 6. Fetch the action and opts.
    while ((arg = rule.shift())) {
      if (typeof arg == 'function') {
        action = arg;
      }
      else {
        opts = arg;
      }
    }

    // 7. Create a new action with obj and src replaced.
    var newAction = function() {
      action(name, src.file, args);
    };

    // 8. Now, create the file task.
    var newArgs = [name, prereqs, newAction, opts];
    newArgs.unshift('file');

    // 9. Insert the file task into Jake
    //
    // Since createTask function stores the task as a child task
    // of currentNamespace. Here we temporariliy switch the namespace.
    var tNs = jake.currentNamespace;
    jake.currentNamespace = ns;
    var createdTask = jake.createTask.apply(global, newArgs);
    jake.currentNamespace = tNs;

    return createdTask;
  };

})();

rule.Rule = Rule;

module.exports = rule;
<|endoftext|>


<commit_before>'use strict';

const logger = require('winston');

const BlinkRetryError = require('../errors/BlinkRetryError');
const MessageParsingBlinkError = require('../errors/MessageParsingBlinkError');
const MessageValidationBlinkError = require('../errors/MessageValidationBlinkError');
const DelayLogic = require('./DelayLogic');

class Dequeuer {
  constructor(queue, callback, retryDelayLogic) {
    this.queue = queue;
    this.callback = callback;

    // Expose function by binding it to object context.
    this.dequeue = this.dequeue.bind(this);

    // Retry delay logic.
    if (!retryDelayLogic || typeof retryDelayLogic !== 'function') {
      // Default exponential backoff logic
      this.retryDelay = DelayLogic.exponentialBackoff;
    } else {
      this.retryDelay = retryDelayLogic;
    }

    // Retry limit is a hardcoded const now.
    this.retryLimit = 100;
  }

  async dequeue(rabbitMessage) {
    const message = this.extractOrDiscard(rabbitMessage);
    if (message) {
      await this.executeCallback(message);
    }
  }

  async executeCallback(message) {
    // Make sure nothing is thrown from here, it will kill the channel.
    let result;
    try {
      result = await this.callback(message);
    } catch (error) {
      // Got retry request.
      if (error instanceof BlinkRetryError) {
        this.retry(message, error);
        return false;
      }

      // Unexpected error, no retry requested.
      this.log(
        'warning',
        error.toString(),
        message,
        'message_processing_error',
      );
      // TODO: send to dead letters?
      this.queue.nack(message);
      return false;
    }

    // Acknowledge message and log result.
    this.queue.ack(message);
    if (result) {
      this.log(
        'debug',
        'Message acknowledged, processed true',
        message,
        'acknowledged_true_result',
      );
    } else {
      this.log(
        'debug',
        'Message acknowledged, processed false',
        message,
        'success_message_ack_false_result',
      );
    }
    return true;
  }

  extractOrDiscard(rabbitMessage) {
    const message = this.unpack(rabbitMessage);
    if (!message || !this.validate(message)) {
      return false;
    }
    return message;
  }

  unpack(rabbitMessage) {
    let message;

    // Transform raw to Message object.
    try {
      message = this.queue.messageClass.fromRabbitMessage(rabbitMessage);
    } catch (error) {
      if (error instanceof MessageParsingBlinkError) {
        this.log(
          'warning',
          `payload='${error.badPayload}' Can't parse payload: ${error}`,
          null,
          'error_cant_parse_message',
        );
      } else {
        this.log(
          'warning',
          `Unknown message parsing error: ${error}`,
          null,
          'error_cant_parse_message_unknown',
        );
      }
      this.queue.nack(rabbitMessage);
      return false;
    }
    return message;
  }

  validate(message) {
    // Validate payload.
    try {
      message.validate();
    } catch (error) {
      if (error instanceof MessageValidationBlinkError) {
        this.log(
          'warning',
          error.toString(),
          message,
          'error_queue_message_validation',
        );
      } else {
        this.log(
          'warning',
          `Unexpected message validation error: ${error}`,
          null,
          'error_queue_unexpected_message_validation',
        );
      }
      this.queue.nack(message);
      return false;
    }

    this.log(
      'debug',
      `Message valid ${message.toString()}`,
      message,
      'success_message_valid',
    );

    return true;
  }

  retry(message, error) {
    // Checked if retry limit reached.
    const retryNumber = message.getMeta().retry || 0;
    if (retryNumber > this.retryLimit) {
      // Retry limit reached
      this.queue.nack(message);
      this.log(
        'warning',
        `Got error ${error}, retry limit reached, rejecting`,
        message,
        'error_got_retry_limit_reached',
      );
      return false;
    }

    // Calculate wait time until the redelivery.
    const delayMilliseconds = this.retryDelay(retryNumber);

    // Log retry information.
    this.log(
      'warning',
      `Got error ${error}, retry ${retryNumber}, retrying after ${delayMilliseconds}ms`,
      message,
      'error_got_retry_request',
    );

    // Delay the redelivery.
    this.scheduleRedeliveryIn(delayMilliseconds, message, error.toString());
    return true;
  }

  scheduleRedeliveryIn(delayMilliseconds, message, reason) {
    setTimeout(() => this.redeliver(message, reason), delayMilliseconds);
  }

  redeliver(message, reason = 'unknown') {
    let retry = 0;
    if (message.getMeta().retry) {
      retry = message.getMeta().retry;
    }
    retry += 1;
    const retryMessage = message;
    retryMessage.payload.meta.retry = retry;
    retryMessage.payload.meta.retryReason = reason;
    // Republish modified message.
    this.queue.nack(message);
    this.queue.publish(retryMessage);
  }

  log(level, logMessage, message = {}, code = 'unexpected_code') {
    const meta = {
      // Todo: log env
      code,
      queue: this.queue.name,
      request_id: message ? message.getRequestId() : 'not_parsed',
    };

    logger.log(level, logMessage, meta);
  }
}

module.exports = Dequeuer;
<commit_msg>Modularize callback error/result processing<commit_after>'use strict';

const logger = require('winston');

const BlinkRetryError = require('../errors/BlinkRetryError');
const MessageParsingBlinkError = require('../errors/MessageParsingBlinkError');
const MessageValidationBlinkError = require('../errors/MessageValidationBlinkError');
const DelayLogic = require('./DelayLogic');

class Dequeuer {
  constructor(queue, callback, retryDelayLogic) {
    this.queue = queue;
    this.callback = callback;

    // Expose function by binding it to object context.
    this.dequeue = this.dequeue.bind(this);

    // Retry delay logic.
    if (!retryDelayLogic || typeof retryDelayLogic !== 'function') {
      // Default exponential backoff logic
      this.retryDelay = DelayLogic.exponentialBackoff;
    } else {
      this.retryDelay = retryDelayLogic;
    }

    // Retry limit is a hardcoded const now.
    this.retryLimit = 100;
  }

  async dequeue(rabbitMessage) {
    const message = this.extractOrDiscard(rabbitMessage);
    if (message) {
      await this.executeCallback(message);
    }
  }

  async executeCallback(message) {
    // Make sure nothing is thrown from here, it will kill the channel.
    let result;
    try {
      result = await this.callback(message);
    } catch (error) {
      return this.processCallbackError(message, error);
    }

    return this.processCallbackResult(message, result);
  }

  processCallbackResult(message, result) {
    // Acknowledge message and log result.
    this.queue.ack(message);
    if (result) {
      this.log(
        'debug',
        'Message acknowledged, processed true',
        message,
        'acknowledged_true_result',
      );
    } else {
      this.log(
        'debug',
        'Message acknowledged, processed false',
        message,
        'success_message_ack_false_result',
      );
    }
    return true;
  }

  processCallbackError(message, error) {
    // Got retry request.
    if (error instanceof BlinkRetryError) {
      this.retry(message, error);
      return;
    }

    // Unexpected error, no retry requested.
    this.log(
      'warning',
      error.toString(),
      message,
      'message_processing_error',
    );
    // TODO: send to dead letters?
    this.queue.nack(message);
  }

  extractOrDiscard(rabbitMessage) {
    const message = this.unpack(rabbitMessage);
    if (!message || !this.validate(message)) {
      return false;
    }
    return message;
  }

  unpack(rabbitMessage) {
    let message;

    // Transform raw to Message object.
    try {
      message = this.queue.messageClass.fromRabbitMessage(rabbitMessage);
    } catch (error) {
      if (error instanceof MessageParsingBlinkError) {
        this.log(
          'warning',
          `payload='${error.badPayload}' Can't parse payload: ${error}`,
          null,
          'error_cant_parse_message',
        );
      } else {
        this.log(
          'warning',
          `Unknown message parsing error: ${error}`,
          null,
          'error_cant_parse_message_unknown',
        );
      }
      this.queue.nack(rabbitMessage);
      return false;
    }
    return message;
  }

  validate(message) {
    // Validate payload.
    try {
      message.validate();
    } catch (error) {
      if (error instanceof MessageValidationBlinkError) {
        this.log(
          'warning',
          error.toString(),
          message,
          'error_queue_message_validation',
        );
      } else {
        this.log(
          'warning',
          `Unexpected message validation error: ${error}`,
          null,
          'error_queue_unexpected_message_validation',
        );
      }
      this.queue.nack(message);
      return false;
    }

    this.log(
      'debug',
      `Message valid ${message.toString()}`,
      message,
      'success_message_valid',
    );

    return true;
  }

  retry(message, error) {
    // Checked if retry limit reached.
    const retryNumber = message.getMeta().retry || 0;
    if (retryNumber > this.retryLimit) {
      // Retry limit reached
      this.queue.nack(message);
      this.log(
        'warning',
        `Got error ${error}, retry limit reached, rejecting`,
        message,
        'error_got_retry_limit_reached',
      );
      return false;
    }

    // Calculate wait time until the redelivery.
    const delayMilliseconds = this.retryDelay(retryNumber);

    // Log retry information.
    this.log(
      'warning',
      `Got error ${error}, retry ${retryNumber}, retrying after ${delayMilliseconds}ms`,
      message,
      'error_got_retry_request',
    );

    // Delay the redelivery.
    this.scheduleRedeliveryIn(delayMilliseconds, message, error.toString());
    return true;
  }

  scheduleRedeliveryIn(delayMilliseconds, message, reason) {
    setTimeout(() => this.redeliver(message, reason), delayMilliseconds);
  }

  redeliver(message, reason = 'unknown') {
    let retry = 0;
    if (message.getMeta().retry) {
      retry = message.getMeta().retry;
    }
    retry += 1;
    const retryMessage = message;
    retryMessage.payload.meta.retry = retry;
    retryMessage.payload.meta.retryReason = reason;
    // Republish modified message.
    this.queue.nack(message);
    this.queue.publish(retryMessage);
  }

  log(level, logMessage, message = {}, code = 'unexpected_code') {
    const meta = {
      // Todo: log env
      code,
      queue: this.queue.name,
      request_id: message ? message.getRequestId() : 'not_parsed',
    };

    logger.log(level, logMessage, meta);
  }
}

module.exports = Dequeuer;
<|endoftext|>


<commit_before>var electron = require('electron')
var Convert = require('ansi-to-html')
var convert = new Convert()
var Cucumber = require('cucumber')

// HACK:
// window.Gherkin !== require('gherkin') in browsers, so parsing fails unless:
require('gherkin').Parser = window.Gherkin.Parser
require('gherkin').Compiler = window.Gherkin.Compiler
require('gherkin').DIALECTS = window.Gherkin.DIALECTS

var cli = require('./cli')(electron.remote.process.argv)

var stdout = electron.remote.process.stdout

function log() {
  var args = [].slice.apply(arguments)
  stdout.write.apply(null, args)
  var output = convert.toHtml(args[0].toString(), { fg: '#000', bg: '#fff' })
  var pre = document.createElement('pre')
  pre.innerHTML = output
  document.body.appendChild(pre)
}

var PrettyFormatter = Cucumber.Listener.PrettyFormatter
Cucumber.Listener.PrettyFormatter = function(options) {
  options.logToFunction = log
  return PrettyFormatter(options)
}

var args = JSON.parse(decodeURIComponent(window.location.hash.substr(1)))

var cucumberCli = Cucumber.Cli([`${__dirname}/node_modules/.bin/cucumberjs`, __dirname].concat(args));

function exitWithCode(code) {
  if (cli.electronDebug) return
  electron.remote.process.exit(code)
}

try {
  cucumberCli.run(function(succeeded) {
    exitWithCode(succeeded ? 0 : 1)
  });
} catch (err) {
  log(err.stack)
  exitWithCode(2)
}
<commit_msg>Exit process on ctrl + c<commit_after>var electron = require('electron')
var Convert = require('ansi-to-html')
var convert = new Convert()
var Cucumber = require('cucumber')

// HACK:
// window.Gherkin !== require('gherkin') in browsers, so parsing fails unless:
require('gherkin').Parser = window.Gherkin.Parser
require('gherkin').Compiler = window.Gherkin.Compiler
require('gherkin').DIALECTS = window.Gherkin.DIALECTS

var cli = require('./cli')(electron.remote.process.argv)

var stdout = electron.remote.process.stdout

function log() {
  var args = [].slice.apply(arguments)
  stdout.write.apply(null, args)
  var output = convert.toHtml(args[0].toString(), { fg: '#000', bg: '#fff' })
  var pre = document.createElement('pre')
  pre.innerHTML = output
  document.body.appendChild(pre)
}

var PrettyFormatter = Cucumber.Listener.PrettyFormatter
Cucumber.Listener.PrettyFormatter = function(options) {
  options.logToFunction = log
  return PrettyFormatter(options)
}

var args = JSON.parse(decodeURIComponent(window.location.hash.substr(1)))

var cucumberCli = Cucumber.Cli([`${__dirname}/node_modules/.bin/cucumberjs`, __dirname].concat(args));

function exitWithCode(code) {
  if (cli.electronDebug) return
  electron.remote.process.exit(code)
}

try {
  cucumberCli.run(function(succeeded) {
    exitWithCode(succeeded ? 0 : 1)
  });
} catch (err) {
  log(err.stack)
  exitWithCode(2)
}

document.body.addEventListener("keydown", function(e) {
  e = e || window.event;
  var key = e.which || e.keyCode;
  var ctrl = e.ctrlKey ? e.ctrlKey : ((key === 17) ? true : false);
  if ( key == 67 && ctrl ) { electron.remote.process.exit(0) }
}, false);
<|endoftext|>


<commit_before>import SqlCommands from 'src/database/sql-related/sqlCommands';
import SqlArray from 'src/database/generated-data/sqlArray';
import exceptions from 'src/database/exceptions';

const testDatabaseName = 'testSQL';
const sqlCommands = new SqlCommands();
const alasqlArray = new SqlArray();

function isDatabaseExists(databaseName, callback) {
  const req = indexedDB.open(databaseName);
  let existed = true;
  req.onsuccess = function onSuccess() {
    req.result.close();
    if (!existed) {
      indexedDB.deleteDatabase(databaseName);
    }
    callback(existed);
  };
  req.onupgradeneeded = function onUpgradeNeeded() {
    existed = false;
  };
}

function isTableExistsInDatabase(tableName, callback) {
  const req = indexedDB.open(testDatabaseName);
  req.onsuccess = function onSuccess(event) {
    const tableNames = event.target.result.objectStoreNames;
    if (tableNames.contains(tableName) === false) {
      req.result.close();
      callback(false);
    } else {
      req.result.close();
      callback(true);
    }
  };
  req.onupgradeneeded = function onUpgradeNeeded(event) {
    callback(false);
  };
}

function deleteTable(tableNames, databaseName, callback) {
  const thisDatabaseName = databaseName || testDatabaseName;
  const request = indexedDB.open(thisDatabaseName);
  let database = null;

  request.onsuccess = function onSuccess(event) {
    database = request.result;
    callback();
  };

  request.onupgradeneeded = function onUpgradeNeeded(event) {
    database = event.target.result;
    for (let tableToDeleteIndex = 0; tableToDeleteIndex < tableNames.length; tableToDeleteIndex+=1) {
      if (database.objectStoreNames.contains(tableNames[tableToDeleteIndex])) {
        database.deleteObjectStore(tableNames[tableToDeleteIndex]);
      }
    }
  };
}

function deleteDatabase(databaseName) {
  return new Promise((resolve, reject) => {
    const thisDatabaseName = databaseName || testDatabaseName;
    if (!window.indexedDB) {
      reject(exceptions.IndexedDBNotSupported);
    }
    resolve(window.indexedDB.deleteDatabase(thisDatabaseName));
  });
}

function cleanTestCase() {
  return deleteDatabase(testDatabaseName);
}

describe('sqlCommands', () => {
  before(() =>
    sqlCommands.linkDatabaseToIndexedDB(testDatabaseName)
  );

  after((done) => {
    cleanTestCase()
    .then(() =>
      done()
    );
  });

  describe('link to indexeddb database', () => {
    it('should link to indexeddb database', (done) => {
      if (!window.indexedDB) {
        throw new exceptions.IndexedDBNotSupported();
      }
      sqlCommands.linkDatabaseToIndexedDB(testDatabaseName)
      .then(() => {
        isDatabaseExists(testDatabaseName, (isDBExists) => {
          expect(isDBExists).to.be.true;
          done();
        });
      })
      .catch(err => done(err));
    });
  });

  describe('creation', () => {
    after((doneAfter) => {
      const tablesToDelete = ['a', 'b'];
      deleteTable(tablesToDelete, testDatabaseName, () => {
        doneAfter();
      });
    });

    it('should create table', (done) => {
      if (!window.indexedDB) {
        done(exceptions.IndexedDBNotSupported);
      }
      const createTableString = 'a(a NUMBER, b STRING, c DATE)';
      sqlCommands.createTable(createTableString)
      .then(() => { // need to insert something before table exists
        sqlCommands.insertContent('a', [{ a: 123, b: 'hello', c: '2017.03.07 15:52:33' }])
        .catch(sqlErr => done(sqlErr));
      })
      .then(() => { // test for table exists in database
        isTableExistsInDatabase('abc', (isTableExist) => {
          expect(isTableExist).to.be.true;
          done();
        });
      })
      .catch(sqlErr => done(sqlErr));
    });

    it('should insert content with correct datatype', (done) => {
      let correctArray;
      let incorrectArray;

      before((doneBefore) => {
        const createTableString = 'b(a NUMBER, b STRING, c DATE)';
        sqlCommands.createTable(createTableString)
        .then(() => { // insert data
          alasqlArray.initializeAlasqlArray();
          alasqlArray.addKeyBasePair('a', 123);
          alasqlArray.addKeyBasePair('b', 'hello string here');
          alasqlArray.addKeyBasePair('c', '2017.03.07 15:52:33');
          correctArray = alasqlArray.endAlasqlArray();
          alasqlArray.initializeAlasqlArray();
          alasqlArray.addKeyBasePair('a', 123);
          alasqlArray.addKeyBasePair('b', '2017.03.07 15:52:33');
          alasqlArray.addKeyBasePair('c', 'hello string here');
          incorrectArray = alasqlArray.endAlasqlArray();
          sqlCommands.insertContent('b', correctArray)
          .then(() => doneBefore())
          .catch(sqlErr => doneBefore(sqlErr));
        })
        .catch(sqlErr => doneBefore(sqlErr));
      });
      sqlCommands.getFullTableData('b')
      .then((data) => {
        expect(data).to.deep.equal(correctArray);
        expect(data).to.not.deep.equal(incorrectArray);
      })
      .then(done())
      .catch(err => done(err));
    }).timeout(20000);
  });
});
<commit_msg>Update closedatabase and tablename in sqlCommands test case<commit_after>import SqlCommands from 'src/database/sql-related/sqlCommands';
import SqlArray from 'src/database/generated-data/sqlArray';
import exceptions from 'src/database/exceptions';

const testDatabaseName = 'testSQL';
const sqlCommands = new SqlCommands();
const alasqlArray = new SqlArray();

function isDatabaseExists(databaseName, callback) {
  const req = indexedDB.open(databaseName);
  let existed = true;
  req.onsuccess = function onSuccess() {
    req.result.close();
    if (!existed) {
      indexedDB.deleteDatabase(databaseName);
    }
    callback(existed);
  };
  req.onupgradeneeded = function onUpgradeNeeded() {
    existed = false;
  };
}

function isTableExistsInDatabase(tableName, callback) {
  const req = indexedDB.open(testDatabaseName);
  req.onsuccess = function onSuccess(event) {
    const tableNames = event.target.result.objectStoreNames;
    if (tableNames.contains(tableName) === false) {
      req.result.close();
      callback(false);
    } else {
      req.result.close();
      callback(true);
    }
  };
  req.onupgradeneeded = function onUpgradeNeeded(event) {
    callback(false);
  };
}

function deleteTable(tableNames, databaseName, callback) {
  const thisDatabaseName = databaseName || testDatabaseName;
  const request = indexedDB.open(thisDatabaseName);
  let database = null;

  request.onsuccess = function onSuccess(event) {
    database = request.result;
    database.close();
    callback();
  };

  request.onupgradeneeded = function onUpgradeNeeded(event) {
    database = event.target.result;
    for (let tableToDeleteIndex = 0; tableToDeleteIndex < tableNames.length; tableToDeleteIndex+=1) {
      if (database.objectStoreNames.contains(tableNames[tableToDeleteIndex])) {
        database.deleteObjectStore(tableNames[tableToDeleteIndex]);
      }
    }
  };
}

function deleteDatabase(databaseName) {
  return new Promise((resolve, reject) => {
    const thisDatabaseName = databaseName || testDatabaseName;
    if (!window.indexedDB) {
      reject(exceptions.IndexedDBNotSupported);
    }
    resolve(window.indexedDB.deleteDatabase(thisDatabaseName));
  });
}

function cleanTestCase() {
  return deleteDatabase(testDatabaseName);
}

describe('sqlCommands', () => {
  before(() =>
    sqlCommands.linkDatabaseToIndexedDB(testDatabaseName)
  );

  after((done) => {
    cleanTestCase()
    .then(() =>
      done()
    );
  });

  describe('link to indexeddb database', () => {
    it('should link to indexeddb database', (done) => {
      if (!window.indexedDB) {
        throw new exceptions.IndexedDBNotSupported();
      }
      sqlCommands.linkDatabaseToIndexedDB(testDatabaseName)
      .then(() => {
        isDatabaseExists(testDatabaseName, (isDBExists) => {
          expect(isDBExists).to.be.true;
          done();
        });
      })
      .catch(err => done(err));
    });
  });

  describe('creation', () => {
    after((doneAfter) => {
      const tablesToDelete = ['a', 'b'];
      deleteTable(tablesToDelete, testDatabaseName, () => {
        doneAfter();
      });
    });

    it('should create table', (done) => {
      if (!window.indexedDB) {
        done(exceptions.IndexedDBNotSupported);
      }
      const createTableString = 'a(a NUMBER, b STRING, c DATE)';
      sqlCommands.createTable(createTableString)
      .then(() => { // need to insert something before table exists
        sqlCommands.insertContent('a', [{ a: 123, b: 'hello', c: '2017.03.07 15:52:33' }])
        .catch(sqlErr => done(sqlErr));
      })
      .then(() => { // test for table exists in database
        isTableExistsInDatabase('a', (isTableExist) => {
          expect(isTableExist).to.be.true;
          done();
        });
      })
      .catch(sqlErr => done(sqlErr));
    });

    it('should insert content with correct datatype', (done) => {
      let correctArray;
      let incorrectArray;

      before((doneBefore) => {
        const createTableString = 'b(a NUMBER, b STRING, c DATE)';
        sqlCommands.createTable(createTableString)
        .then(() => { // insert data
          alasqlArray.initializeAlasqlArray();
          alasqlArray.addKeyBasePair('a', 123);
          alasqlArray.addKeyBasePair('b', 'hello string here');
          alasqlArray.addKeyBasePair('c', '2017.03.07 15:52:33');
          correctArray = alasqlArray.endAlasqlArray();
          alasqlArray.initializeAlasqlArray();
          alasqlArray.addKeyBasePair('a', 123);
          alasqlArray.addKeyBasePair('b', '2017.03.07 15:52:33');
          alasqlArray.addKeyBasePair('c', 'hello string here');
          incorrectArray = alasqlArray.endAlasqlArray();
          sqlCommands.insertContent('b', correctArray)
          .then(() => doneBefore())
          .catch(sqlErr => doneBefore(sqlErr));
        })
        .catch(sqlErr => doneBefore(sqlErr));
      });
      sqlCommands.getFullTableData('b')
      .then((data) => {
        expect(data).to.deep.equal(correctArray);
        expect(data).to.not.deep.equal(incorrectArray);
      })
      .then(done())
      .catch(err => done(err));
    }).timeout(20000);
  });
});
<|endoftext|>


<commit_before>SharkGame.Save = {

    saveFileName: "sharkGameSave",

    saveGame: function(suppressSavingToStorage, dontFlat) {
        // populate save data object
        var saveData = {};
        saveData.version = SharkGame.VERSION;
        saveData.resources = {};
        saveData.tabs = {};
        saveData.settings = {};
        saveData.upgrades = {};
        saveData.gateCostsMet = {};

        $.each(SharkGame.ResourceTable, function(k, v) {
            saveData.resources[k] = {
                amount: v.amount,
                totalAmount: v.totalAmount
            };
        });

        $.each(SharkGame.Upgrades, function(k, v) {
            saveData.upgrades[k] = v.purchased;
        });

        $.each(SharkGame.Tabs, function(k, v) {
            saveData.tabs[k] = {
                discovered: v.discovered
            }
        });
        saveData.tabs.current = SharkGame.Tabs.current;

        $.each(SharkGame.Gate.costsMet, function(k, v) {
            saveData.gateCostsMet[k] = v;
        });

        $.each(SharkGame.Settings, function(k, v) {
            if(i !== "current") {
                saveData.settings[k] = SharkGame.Settings.current[k];
            }
        });

        // add timestamp
        //saveData.timestamp = (new Date()).getTime();
        saveData.timestampLastSave = (new Date()).getTime();
        saveData.timestampGameStart = SharkGame.timestampGameStart;
        saveData.timestampRunStart = SharkGame.timestampRunStart;

        if(dontFlat) {
            saveData.saveVersion = SharkGame.Save.saveUpdaters.length - 1;
        } else {
            //make a current-version template
            var saveVersion = SharkGame.Save.saveUpdaters.length - 1;
            var template = {};
            for(var i = 0; i <= saveVersion; i++) {
                var updater = SharkGame.Save.saveUpdaters[i];
                template = updater(template);
            }
            //flatten
            var flatData = SharkGame.Save.flattenData(template, saveData);
            flatData.unshift(saveVersion);
            var saveString = pako.deflate(JSON.stringify(flatData), {to: 'string'});
        }

        if(!suppressSavingToStorage) {
            try {
                localStorage.setItem(SharkGame.Save.saveFileName, saveString);
            } catch(err) {
                throw new Error("Couldn't save to local storage. Reason: " + err.message);
            }
        }
        return saveString;
    },

    loadGame: function(importSaveData) {
        var saveData;
        var saveDataString = importSaveData || localStorage.getItem(SharkGame.Save.saveFileName);

        if(!saveDataString) {
            throw new Error("Tried to load game, but no game to load.");
        }

        // if first letter of string is x, data is compressed and needs uncompressing.
        if(saveDataString.charAt(0) === 'x') {
            // decompress string
            try {
                saveDataString = pako.inflate(saveDataString, {to: 'string'});
            } catch(err) {
                throw new Error("Saved data is compressed, but it can't be decompressed. Can't load.");
            }
        }

        // if first letter of string is { or [, data is json
        if(saveDataString.charAt(0) === '{' || saveDataString.charAt(0) === '[') {
            try {
                saveData = JSON.parse(saveDataString);
            } catch(err) {
                var errMessage = "Couldn't load save data. It didn't parse correctly.";
                if(importSaveData) {
                    errMessage += " Did you paste the entire string?";
                }
                throw new Error(errMessage);
            }
        }

        // if first letter of string was [, data was packed, unpack it
        if(saveDataString.charAt(0) === '[') {
            try {
                //check version
                var currentVersion = SharkGame.Save.saveUpdaters.length - 1;
                var saveVersion = saveData.shift()
                if(typeof saveVersion !== "number" || saveVersion % 1 !== 0 || saveVersion < 0 || saveVersion > currentVersion) {
                    throw new Error("Invalid save version!");
                }
                //create matching template
                var template = {};
                for(var i = 0; i <= saveVersion; i++) {
                    var updater = SharkGame.Save.saveUpdaters[i];
                    template = updater(template);
                }
                //unpack
                saveData = SharkGame.Save.expandData(template, saveData);
                saveData.saveVersion = saveVersion;
            } catch(err) {
                throw new Error("Couldn't unpack packed save data. Reason: " + err.message);
            }
        }

        if(saveData) {
            // go through it

            //check for updates
            var currentVersion = SharkGame.Save.saveUpdaters.length - 1;
            saveData.saveVersion = saveData.saveVersion || 0;
            if(saveData.saveVersion < currentVersion) {
                for(i = saveData.saveVersion + 1; i <= currentVersion; i++) {
                    var updater = SharkGame.Save.saveUpdaters[i];
                    saveData = updater(saveData);
                    saveData.saveVersion = i;
                }
                // let player know update went fine
                SharkGame.Log.addMessage("Updated save data from v " + saveData.version + " to " + SharkGame.VERSION + ".");
            }

            if(saveData.resources) {
                $.each(saveData.resources, function(k, v) {
                    // check that this isn't an old resource that's been removed from the game for whatever reason
                    if(SharkGame.ResourceTable[k]) {
                        SharkGame.ResourceTable[k].amount = isNaN(v.amount) ? 0 : v.amount;
                        SharkGame.ResourceTable[k].totalAmount = isNaN(v.totalAmount) ? 0 : v.amount;
                    }
                });
            }

            // hacky kludge: force table creation
            SharkGame.Resources.reconstructResourcesTable();

            if(saveData.upgrades) {
                $.each(saveData.upgrades, function(k, v) {
                    if(saveData.upgrades[k]) {
                        SharkGame.Lab.addUpgrade(k);
                    }
                });
            }

            if(saveData.tabs) {
                $.each(saveData.tabs, function(k, v) {
                    if(SharkGame.Tabs[k]) {
                        SharkGame.Tabs[k].discovered = v.discovered;
                    }
                });
                if(saveData.tabs.current) {
                    SharkGame.Tabs.current = saveData.tabs.current;
                }
            }

            if(saveData.gateCostsMet) {
                $.each(saveData.gateCostsMet, function(k, v) {
                    // only pay attention to valid costs
                    if(SharkGame.Gate.costs[k]) {
                        SharkGame.Gate.costsMet[k] = v;
                    }
                });
            }

            if(saveData.settings) {
                $.each(saveData.settings, function(k, v) {
                    if(SharkGame.Settings.current[k]) {
                        SharkGame.Settings.current[k] = v;
                        // update anything tied to this setting right off the bat
                        (SharkGame.Settings[k].onChange || $.noop)();
                    }
                });
            }

            var currTimestamp = (new Date()).getTime();
            // create surrogate timestamps if necessary
            if((typeof saveData.timestampLastSave !== "number")) {
                saveData.timestampLastSave = currTimestamp;
            }
            if((typeof saveData.timestampGameStart !== "number")) {
                saveData.timestampGameStart = currTimestamp;
            }
            if((typeof saveData.timestampRunStart !== "number")) {
                saveData.timestampRunStart = currTimestamp;
            }

            // if offline mode is enabled
            if(SharkGame.Settings.current.offlineModeActive) {
                // get times elapsed since last save game
                var now = (new Date()).getTime();
                var secondsElapsed = (now - saveData.timestampLastSave) / 1000;
                if(secondsElapsed < 0) {
                    // something went hideously wrong or someone abused a system clock somewhere
                    secondsElapsed = 0;
                }

                // process this
                SharkGame.Resources.recalculateIncomeTable();
                SharkGame.Main.processSimTime(secondsElapsed);

                // acknowledge long time gaps
                if(secondsElapsed > 3600) {
                    var notification = "Welcome back! It's been ";
                    var numHours = Math.floor(secondsElapsed / 3600);
                    if(numHours > 24) {
                        var numDays = Math.floor(numHours / 24);
                        if(numDays > 7) {
                            var numWeeks = Math.floor(numDays / 7);
                            if(numWeeks > 4) {
                                var numMonths = Math.floor(numWeeks / 4);
                                if(numMonths > 12) {
                                    var numYears = Math.floor(numMonths / 12);
                                    notification += "almost " + ( numYears === 1 ? "a" : numYears ) + " year" + SharkGame.plural(numYears) + ", thanks for remembering this exists!"
                                } else {
                                    notification += "like " + (numMonths === 1 ? "a" : numMonths ) + " month" + SharkGame.plural(numMonths) + ", it's getting kinda crowded.";
                                }
                            } else {
                                notification += "about " + (numWeeks === 1 ? "a" : numWeeks) + " week" + SharkGame.plural(numWeeks) + ", you were gone a while!";
                            }
                        } else {
                            notification += (numDays === 1 ? "a" : numDays ) + " day" + SharkGame.plural(numDays) + ", and look at all the stuff you have now!";
                        }
                    } else {
                        notification += (numHours === 1 ? "an" : numHours ) + " hour" + SharkGame.plural(numHours) + " since you were seen around here!";
                    }
                    SharkGame.Log.addMessage(notification);
                }
            }
        } else {
            throw new Error("Couldn't load saved game. I don't know how to break this to you, but I think your save is corrupted.");
        }
    },

    importData: function(data) {
        // decode from ascii85
        var saveData;
        try {
            saveData = ascii85.decode(data);
        } catch(err) {
            SharkGame.Log.addError("That's not encoded properly. Are you sure that's a real save export string?");
        }
        // load the game from this save data string
        try {
            SharkGame.Save.loadGame(saveData);
        } catch(err) {
            SharkGame.Log.addError(err.message);
        }
        // refresh current tab
        SharkGame.Main.setUpTab();
    },

    exportData: function() {
        // get save
        var saveData = localStorage.getItem(SharkGame.Save.saveFileName);
        if(saveData === null) {
            saveData = SharkGame.Save.saveGame(true);
        }
        // encode save
        return ascii85.encode(saveData);
    },

    savedGameExists: function() {
        return (localStorage.getItem(SharkGame.Save.saveFileName) !== null);
    },

    deleteSave: function() {
        localStorage.removeItem(SharkGame.Save.saveFileName);
    },

    // Thanks to Dylan for managing to crush saves down to a much smaller size!
    createBlueprint: function(template) {
        function createPart(t) {
            var bp = [];
            $.each(t, function(k, v) {
                if(typeof v === "object" && v !== null) {
                    bp.push([k, createPart(v)]);
                } else {
                    bp.push(k);
                }
            });
            bp.sort(function(a, b) {
                a = typeof a === "object" ? a[0] : a;
                b = typeof b === "object" ? b[0] : b;
                return a > b;
            });
            return bp;
        }

        return createPart(template);
    },

    flattenData: function(template, source) {
        var out = [];

        function flattenPart(bp, src) {
            $.each(bp, function(_, slot) {
                if(typeof slot === "object") {
                    flattenPart(slot[1], src[slot[0]]);
                } else {
                    var elem = src[slot];
                    if(typeof elem === "number" && slot!== "timestamp" && slot !== "timestampLastSave" && slot !== "timestampGameStart" && slot !== "timestampRunStart") {
                        elem = Number(elem.toPrecision(5));
                    }
                    out.push(elem);
                }
            });
        }

        flattenPart(SharkGame.Save.createBlueprint(template), source);
        return out;
    },

    expandData: function(template, data) {
        function expandPart(bp) {
            var out = {}; //todo: array support
            $.each(bp, function(_, slot) {
                if(typeof slot === "object") {
                    out[slot[0]] = expandPart(slot[1]);
                } else {
                    out[slot] = data.shift();
                }
            });
            return out;
        }

        return expandPart(SharkGame.Save.createBlueprint(template));
    },

    saveUpdaters: [ //used to update saves and to make templates
        function(save) {
            //no one is converting a real save to version 0, so it doesn't need real values
            save.version = null;
            save.timestamp = null;
            save.resources = {};
            $.each(["essence", "shark", "ray", "crab", "scientist", "nurse", "laser", "maker", "planter", "brood", "crystalMiner", "autoTransmuter", "fishMachine", "science", "fish", "sand", "crystal", "kelp", "seaApple", "sharkonium"], function(i, v) {
                save.resources[v] = {amount: null, totalAmount: null};
            });
            save.upgrades = {};
            $.each(["crystalBite", "crystalSpade", "crystalContainer", "underwaterChemistry", "seabedGeology", "thermalVents", "laserRays", "automation", "engineering", "kelpHorticulture", "xenobiology", "biology", "rayBiology", "crabBiology", "sunObservation", "transmutation", "exploration", "farExploration", "gateDiscovery"], function(i, v) {
                save.upgrades[v] = null;
            });
            save.tabs = {"current": null, "home": {"discovered": null}, "lab": {"discovered": null}, "gate": {"discovered": null}};
            save.settings = {"buyAmount": null, "offlineModeActive": null, "autosaveFrequency": null, "logMessageMax": null, "sidebarWidth": null, "showAnimations": null, "colorCosts": null};
            save.gateCostsMet = {"fish": null, "sand": null, "crystal": null, "kelp": null, "seaApple": null, "sharkonium": null};
            return save;
        },

        // future updaters for save versions beyond the base:
        // they get passed the result of the previous updater and it continues in a chain
        // and they start based on the version they were save

        function(save) {
            return $.extend(true, save, {
                "resources": {"sandDigger": {"amount": 0, "totalAmount": 0}},
                "settings": {"showTabHelp": false, "groupResources": false},
                "timestampLastSave": save.timestamp,
                "timestampGameStart": null,
                "timestampRunStart": null
            });
        }
    ]
};<commit_msg>changed one line to be less paranoid<commit_after>SharkGame.Save = {

    saveFileName: "sharkGameSave",

    saveGame: function(suppressSavingToStorage, dontFlat) {
        // populate save data object
        var saveData = {};
        saveData.version = SharkGame.VERSION;
        saveData.resources = {};
        saveData.tabs = {};
        saveData.settings = {};
        saveData.upgrades = {};
        saveData.gateCostsMet = {};

        $.each(SharkGame.ResourceTable, function(k, v) {
            saveData.resources[k] = {
                amount: v.amount,
                totalAmount: v.totalAmount
            };
        });

        $.each(SharkGame.Upgrades, function(k, v) {
            saveData.upgrades[k] = v.purchased;
        });

        $.each(SharkGame.Tabs, function(k, v) {
            saveData.tabs[k] = {
                discovered: v.discovered
            }
        });
        saveData.tabs.current = SharkGame.Tabs.current;

        $.each(SharkGame.Gate.costsMet, function(k, v) {
            saveData.gateCostsMet[k] = v;
        });

        $.each(SharkGame.Settings, function(k, v) {
            if(i !== "current") {
                saveData.settings[k] = SharkGame.Settings.current[k];
            }
        });

        // add timestamp
        //saveData.timestamp = (new Date()).getTime();
        saveData.timestampLastSave = (new Date()).getTime();
        saveData.timestampGameStart = SharkGame.timestampGameStart;
        saveData.timestampRunStart = SharkGame.timestampRunStart;

        if(dontFlat) {
            saveData.saveVersion = SharkGame.Save.saveUpdaters.length - 1;
        } else {
            //make a current-version template
            var saveVersion = SharkGame.Save.saveUpdaters.length - 1;
            var template = {};
            for(var i = 0; i <= saveVersion; i++) {
                var updater = SharkGame.Save.saveUpdaters[i];
                template = updater(template);
            }
            //flatten
            var flatData = SharkGame.Save.flattenData(template, saveData);
            flatData.unshift(saveVersion);
            var saveString = pako.deflate(JSON.stringify(flatData), {to: 'string'});
        }

        if(!suppressSavingToStorage) {
            try {
                localStorage.setItem(SharkGame.Save.saveFileName, saveString);
            } catch(err) {
                throw new Error("Couldn't save to local storage. Reason: " + err.message);
            }
        }
        return saveString;
    },

    loadGame: function(importSaveData) {
        var saveData;
        var saveDataString = importSaveData || localStorage.getItem(SharkGame.Save.saveFileName);

        if(!saveDataString) {
            throw new Error("Tried to load game, but no game to load.");
        }

        // if first letter of string is x, data is compressed and needs uncompressing.
        if(saveDataString.charAt(0) === 'x') {
            // decompress string
            try {
                saveDataString = pako.inflate(saveDataString, {to: 'string'});
            } catch(err) {
                throw new Error("Saved data is compressed, but it can't be decompressed. Can't load.");
            }
        }

        // if first letter of string is { or [, data is json
        if(saveDataString.charAt(0) === '{' || saveDataString.charAt(0) === '[') {
            try {
                saveData = JSON.parse(saveDataString);
            } catch(err) {
                var errMessage = "Couldn't load save data. It didn't parse correctly.";
                if(importSaveData) {
                    errMessage += " Did you paste the entire string?";
                }
                throw new Error(errMessage);
            }
        }

        // if first letter of string was [, data was packed, unpack it
        if(saveDataString.charAt(0) === '[') {
            try {
                //check version
                var currentVersion = SharkGame.Save.saveUpdaters.length - 1;
                var saveVersion = saveData.shift()
                if(typeof saveVersion !== "number" || saveVersion % 1 !== 0 || saveVersion < 0 || saveVersion > currentVersion) {
                    throw new Error("Invalid save version!");
                }
                //create matching template
                var template = {};
                for(var i = 0; i <= saveVersion; i++) {
                    var updater = SharkGame.Save.saveUpdaters[i];
                    template = updater(template);
                }
                //unpack
                saveData = SharkGame.Save.expandData(template, saveData);
                saveData.saveVersion = saveVersion;
            } catch(err) {
                throw new Error("Couldn't unpack packed save data. Reason: " + err.message);
            }
        }

        if(saveData) {
            // go through it

            //check for updates
            var currentVersion = SharkGame.Save.saveUpdaters.length - 1;
            saveData.saveVersion = saveData.saveVersion || 0;
            if(saveData.saveVersion < currentVersion) {
                for(i = saveData.saveVersion + 1; i <= currentVersion; i++) {
                    var updater = SharkGame.Save.saveUpdaters[i];
                    saveData = updater(saveData);
                    saveData.saveVersion = i;
                }
                // let player know update went fine
                SharkGame.Log.addMessage("Updated save data from v " + saveData.version + " to " + SharkGame.VERSION + ".");
            }

            if(saveData.resources) {
                $.each(saveData.resources, function(k, v) {
                    // check that this isn't an old resource that's been removed from the game for whatever reason
                    if(SharkGame.ResourceTable[k]) {
                        SharkGame.ResourceTable[k].amount = isNaN(v.amount) ? 0 : v.amount;
                        SharkGame.ResourceTable[k].totalAmount = isNaN(v.totalAmount) ? 0 : v.amount;
                    }
                });
            }

            // hacky kludge: force table creation
            SharkGame.Resources.reconstructResourcesTable();

            if(saveData.upgrades) {
                $.each(saveData.upgrades, function(k, v) {
                    if(saveData.upgrades[k]) {
                        SharkGame.Lab.addUpgrade(k);
                    }
                });
            }

            if(saveData.tabs) {
                $.each(saveData.tabs, function(k, v) {
                    if(SharkGame.Tabs[k]) {
                        SharkGame.Tabs[k].discovered = v.discovered;
                    }
                });
                if(saveData.tabs.current) {
                    SharkGame.Tabs.current = saveData.tabs.current;
                }
            }

            if(saveData.gateCostsMet) {
                $.each(saveData.gateCostsMet, function(k, v) {
                    // only pay attention to valid costs
                    if(SharkGame.Gate.costs[k]) {
                        SharkGame.Gate.costsMet[k] = v;
                    }
                });
            }

            if(saveData.settings) {
                $.each(saveData.settings, function(k, v) {
                    if(SharkGame.Settings.current[k]) {
                        SharkGame.Settings.current[k] = v;
                        // update anything tied to this setting right off the bat
                        (SharkGame.Settings[k].onChange || $.noop)();
                    }
                });
            }

            var currTimestamp = (new Date()).getTime();
            // create surrogate timestamps if necessary
            if((typeof saveData.timestampLastSave !== "number")) {
                saveData.timestampLastSave = currTimestamp;
            }
            if((typeof saveData.timestampGameStart !== "number")) {
                saveData.timestampGameStart = currTimestamp;
            }
            if((typeof saveData.timestampRunStart !== "number")) {
                saveData.timestampRunStart = currTimestamp;
            }

            // if offline mode is enabled
            if(SharkGame.Settings.current.offlineModeActive) {
                // get times elapsed since last save game
                var now = (new Date()).getTime();
                var secondsElapsed = (now - saveData.timestampLastSave) / 1000;
                if(secondsElapsed < 0) {
                    // something went hideously wrong or someone abused a system clock somewhere
                    secondsElapsed = 0;
                }

                // process this
                SharkGame.Resources.recalculateIncomeTable();
                SharkGame.Main.processSimTime(secondsElapsed);

                // acknowledge long time gaps
                if(secondsElapsed > 3600) {
                    var notification = "Welcome back! It's been ";
                    var numHours = Math.floor(secondsElapsed / 3600);
                    if(numHours > 24) {
                        var numDays = Math.floor(numHours / 24);
                        if(numDays > 7) {
                            var numWeeks = Math.floor(numDays / 7);
                            if(numWeeks > 4) {
                                var numMonths = Math.floor(numWeeks / 4);
                                if(numMonths > 12) {
                                    var numYears = Math.floor(numMonths / 12);
                                    notification += "almost " + ( numYears === 1 ? "a" : numYears ) + " year" + SharkGame.plural(numYears) + ", thanks for remembering this exists!"
                                } else {
                                    notification += "like " + (numMonths === 1 ? "a" : numMonths ) + " month" + SharkGame.plural(numMonths) + ", it's getting kinda crowded.";
                                }
                            } else {
                                notification += "about " + (numWeeks === 1 ? "a" : numWeeks) + " week" + SharkGame.plural(numWeeks) + ", you were gone a while!";
                            }
                        } else {
                            notification += (numDays === 1 ? "a" : numDays ) + " day" + SharkGame.plural(numDays) + ", and look at all the stuff you have now!";
                        }
                    } else {
                        notification += (numHours === 1 ? "an" : numHours ) + " hour" + SharkGame.plural(numHours) + " since you were seen around here!";
                    }
                    SharkGame.Log.addMessage(notification);
                }
            }
        } else {
            throw new Error("Couldn't load saved game. I don't know how to break this to you, but I think your save is corrupted.");
        }
    },

    importData: function(data) {
        // decode from ascii85
        var saveData;
        try {
            saveData = ascii85.decode(data);
        } catch(err) {
            SharkGame.Log.addError("That's not encoded properly. Are you sure that's a real save export string?");
        }
        // load the game from this save data string
        try {
            SharkGame.Save.loadGame(saveData);
        } catch(err) {
            SharkGame.Log.addError(err.message);
        }
        // refresh current tab
        SharkGame.Main.setUpTab();
    },

    exportData: function() {
        // get save
        var saveData = localStorage.getItem(SharkGame.Save.saveFileName);
        if(saveData === null) {
            saveData = SharkGame.Save.saveGame(true);
        }
        // encode save
        return ascii85.encode(saveData);
    },

    savedGameExists: function() {
        return (localStorage.getItem(SharkGame.Save.saveFileName) !== null);
    },

    deleteSave: function() {
        localStorage.removeItem(SharkGame.Save.saveFileName);
    },

    // Thanks to Dylan for managing to crush saves down to a much smaller size!
    createBlueprint: function(template) {
        function createPart(t) {
            var bp = [];
            $.each(t, function(k, v) {
                if(typeof v === "object" && v !== null) {
                    bp.push([k, createPart(v)]);
                } else {
                    bp.push(k);
                }
            });
            bp.sort(function(a, b) {
                a = typeof a === "object" ? a[0] : a;
                b = typeof b === "object" ? b[0] : b;
                return a > b;
            });
            return bp;
        }

        return createPart(template);
    },

    flattenData: function(template, source) {
        var out = [];

        function flattenPart(bp, src) {
            $.each(bp, function(_, slot) {
                if(typeof slot === "object") {
                    flattenPart(slot[1], src[slot[0]]);
                } else {
                    var elem = src[slot];
                    if(typeof elem === "number" && slot !== "timestampLastSave" && slot !== "timestampGameStart" && slot !== "timestampRunStart") {
                        elem = Number(elem.toPrecision(5));
                    }
                    out.push(elem);
                }
            });
        }

        flattenPart(SharkGame.Save.createBlueprint(template), source);
        return out;
    },

    expandData: function(template, data) {
        function expandPart(bp) {
            var out = {}; //todo: array support
            $.each(bp, function(_, slot) {
                if(typeof slot === "object") {
                    out[slot[0]] = expandPart(slot[1]);
                } else {
                    out[slot] = data.shift();
                }
            });
            return out;
        }

        return expandPart(SharkGame.Save.createBlueprint(template));
    },

    saveUpdaters: [ //used to update saves and to make templates
        function(save) {
            //no one is converting a real save to version 0, so it doesn't need real values
            save.version = null;
            save.timestamp = null;
            save.resources = {};
            $.each(["essence", "shark", "ray", "crab", "scientist", "nurse", "laser", "maker", "planter", "brood", "crystalMiner", "autoTransmuter", "fishMachine", "science", "fish", "sand", "crystal", "kelp", "seaApple", "sharkonium"], function(i, v) {
                save.resources[v] = {amount: null, totalAmount: null};
            });
            save.upgrades = {};
            $.each(["crystalBite", "crystalSpade", "crystalContainer", "underwaterChemistry", "seabedGeology", "thermalVents", "laserRays", "automation", "engineering", "kelpHorticulture", "xenobiology", "biology", "rayBiology", "crabBiology", "sunObservation", "transmutation", "exploration", "farExploration", "gateDiscovery"], function(i, v) {
                save.upgrades[v] = null;
            });
            save.tabs = {"current": null, "home": {"discovered": null}, "lab": {"discovered": null}, "gate": {"discovered": null}};
            save.settings = {"buyAmount": null, "offlineModeActive": null, "autosaveFrequency": null, "logMessageMax": null, "sidebarWidth": null, "showAnimations": null, "colorCosts": null};
            save.gateCostsMet = {"fish": null, "sand": null, "crystal": null, "kelp": null, "seaApple": null, "sharkonium": null};
            return save;
        },

        // future updaters for save versions beyond the base:
        // they get passed the result of the previous updater and it continues in a chain
        // and they start based on the version they were save

        function(save) {
            return $.extend(true, save, {
                "resources": {"sandDigger": {"amount": 0, "totalAmount": 0}},
                "settings": {"showTabHelp": false, "groupResources": false},
                "timestampLastSave": save.timestamp,
                "timestampGameStart": null,
                "timestampRunStart": null
            });
        }
    ]
};<|endoftext|>


<commit_before>/*jslint nomen: true */
/*globals define: true */

define([
  'jquery',
  'lib/lodash'
],

function($, _) {
  'use strict';

  return {
      "name": "Standard neighborhood survey",
      "type": "mobile",
      "questions": [
        {
          "name": "structure",
          "text": "Is there a structure on the site?",
          "answers": [
            {
              "value": "yes",
              "text": "Yes",
              "questions": [
                {
                  "name":"condition",
                  "text":"What is the condition of the structure?",
                  "answers":[
                    {
                      "value":"excellent",
                      "text":"Excellent"
                    },
                    {
                      "value":"good",
                      "text":"Good"
                    },
                    {
                      "value":"fair",
                      "text":"Fair"
                    },
                    {
                      "value":"poor",
                      "text":"Poor"
                    },
                    {
                      "value":"needs-demolition",
                      "text":"Needs demolition"
                    }
                  ]
                },
                {
                  "name": "structure-properties",
                  "text": "Do any of the following apply?",
                  "type": "checkbox",
                  "answers": [
                    {
                      "name": "vacanct",
                      "value": "yes",
                      "text": "Vacant"
                    },
                    {
                      "name": "vod",
                      "value": "yes",
                      "text": "Vacant, open, and dangerous"
                    },
                    {
                      "name": "fire-damage",
                      "value": "yes",
                      "text": "Fire damage"
                    }
                  ]
                }
              ]
            },
            {
              "value": "no",
              "text": "No",
              "questions": [
                {
                  "name": "improvements",
                  "text": "Is the site improved or unimproved?",
                  "answers": [
                    {
                      "value":"improved",
                      "text": "Improved"
                    },
                    {
                      "value":"unimproved",
                      "text": "Unimproved"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "name": "dumping",
          "text": "Is there illegal dumping on the site?",
          "answers": [
            {
              "value": "yes",
              "text": "Yes"
            },
            {
              "value": "no",
              "text": "No"
            }
          ]
        }
      ]
    };
}); // End Root module<commit_msg>Fix spelling of vacant<commit_after>/*jslint nomen: true */
/*globals define: true */

define([
  'jquery',
  'lib/lodash'
],

function($, _) {
  'use strict';

  return {
      "name": "Standard neighborhood survey",
      "type": "mobile",
      "questions": [
        {
          "name": "structure",
          "text": "Is there a structure on the site?",
          "answers": [
            {
              "value": "yes",
              "text": "Yes",
              "questions": [
                {
                  "name":"condition",
                  "text":"What is the condition of the structure?",
                  "answers":[
                    {
                      "value":"excellent",
                      "text":"Excellent"
                    },
                    {
                      "value":"good",
                      "text":"Good"
                    },
                    {
                      "value":"fair",
                      "text":"Fair"
                    },
                    {
                      "value":"poor",
                      "text":"Poor"
                    },
                    {
                      "value":"needs-demolition",
                      "text":"Needs demolition"
                    }
                  ]
                },
                {
                  "name": "structure-properties",
                  "text": "Do any of the following apply?",
                  "type": "checkbox",
                  "answers": [
                    {
                      "name": "vacant",
                      "value": "yes",
                      "text": "Vacant"
                    },
                    {
                      "name": "vod",
                      "value": "yes",
                      "text": "Vacant, open, and dangerous"
                    },
                    {
                      "name": "fire-damage",
                      "value": "yes",
                      "text": "Fire damage"
                    }
                  ]
                }
              ]
            },
            {
              "value": "no",
              "text": "No",
              "questions": [
                {
                  "name": "improvements",
                  "text": "Is the site improved or unimproved?",
                  "answers": [
                    {
                      "value":"improved",
                      "text": "Improved"
                    },
                    {
                      "value":"unimproved",
                      "text": "Unimproved"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "name": "dumping",
          "text": "Is there illegal dumping on the site?",
          "answers": [
            {
              "value": "yes",
              "text": "Yes"
            },
            {
              "value": "no",
              "text": "No"
            }
          ]
        }
      ]
    };
}); // End Root module<|endoftext|>


<commit_before>import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import signUp from './signUp';
import login from './login';
import book from './book';
import favorites from './favorites';
import profile from './profile';
import borrowRequests from './borrowrequests';
import returnRequests from './returnrequests';
import { books, mostUpvotedBooks } from './books';

const rootReducer = combineReducers({
  form: formReducer,
  signUp,
  login,
  books,
  book,
  mostUpvotedBooks,
  favorites,
  borrowRequests,
  returnRequests,
  profile,
  routing: routerReducer,
});

export default rootReducer;
<commit_msg>Update index.js<commit_after>import { combineReducers } from 'redux';
import { routerReducer } from 'react-router-redux';
import { reducer as formReducer } from 'redux-form';
import signUp from './signUp';
import login from './login';
import book from './book';
import favorites from './favorites';
import profile from './profile';
import borrowRequests from './borrowRequests';
import returnRequests from './returnRequests';
import { books, mostUpvotedBooks } from './books';

const rootReducer = combineReducers({
  form: formReducer,
  signUp,
  login,
  books,
  book,
  mostUpvotedBooks,
  favorites,
  borrowRequests,
  returnRequests,
  profile,
  routing: routerReducer,
});

export default rootReducer;
<|endoftext|>