<commit_before>var pg = require('pg').native,
Q= require("q"),
config= require("./config.js");
//_= require("lodash"),

var localTime= new Date();

Date.prototype.getWeek = function() {
  var fjan = new Date(this.getFullYear(), 0, 4);
  return Math.ceil((((this - fjan) / 86400000) + fjan.getDay()+1)/7);
};

Date.prototype.yyyymmdd = function() {
 var yyyy = this.getFullYear().toString();
 var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
 var dd  = this.getDate().toString();
 return yyyy +"-"+ (mm[1]?mm:"0"+mm[0]) +"-"+ (dd[1]?dd:"0"+dd[0]); // padding
};

var getDate = function(week, day, year) {
  year= year || new Date().getFullYear();
  var simple = new Date(year, 0, 1 + (week - 1) * 7);
  var date = simple;
  date.setDate(simple.getDate() - simple.getDay() + day +
              (simple.getDay() <= 4 ? 1 : 8));
  return date;
};

var toDay= function(s){
  return ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"].indexOf(s);
};

// convert calendar-week notation to dates
var matchDate = function(string, day, year){
  var weekMatch= string.match(/(\d{1}|\d{2})(\.|)/);
  if(weekMatch=== null){
    return new Error("Can't match week! str: " +string+
                     " matches: " +weekMatch);
  }
  return getDate(
    Number(weekMatch[1]),
    toDay(day),
    year)
    .yyyymmdd();
};

// converts different timeformats into a list of timespans
var processDate= function(dateString, dayString){
  var timespans= [],
  note= "";

  // try to match dd.mm.yyyy dates
  var dateMatch= dateString.match(/(\d{2})\.(\d{2})\.(\d{4})/);
  if(dateMatch){
    timespans.push(dateMatch[3]+"-"+dateMatch[2]+"-"+dateMatch[1]);

  // try to match complex timespans (xx. KW)
  }else if(dateString.indexOf(".") !== -1){

    // check for U or G weeks
    var match= dateString.match(/^(?:(U|G)).+/);
    var recurring= match ? 2: 1;
    if(match){
      dateString= dateString.slice(3, -1);
    }

    // split by ';' and ',' and then by '-'
    var commaSplit= dateString.split(/[,;] /);

    for(var i=0; i < commaSplit.length; i++){
      var hyphenSplit= commaSplit[i].split(/(?: |)- /);

      // extract week / years from parts
      var dates=[];

      // exactly 2 hyphens
      if(hyphenSplit.length <= 2){
        for(var j=0; j < hyphenSplit.length; j++){
          var yearMatch= hyphenSplit[j].match(/(\d{4})/);
          for(var x= i; yearMatch === null && x < commaSplit.length; x++){
            yearMatch= commaSplit[x].match(/(\d{4})/);
          }

          if(yearMatch === null){
            if(localTime.getMonth() > 2 && localTime.getMonth() < 9){ //sommersemester

              yearMatch= [, localTime.getFullYear()];
            }else{
              return new Error("Missing Year -> "+ dateString);
            }
          }

          var m= matchDate(hyphenSplit[j], dayString, yearMatch[1]);
          if(m instanceof Error){ return m; }
          dates.push(m);
        }
      }else if(hyphenSplit.length === 3){
        var m1= matchDate(hyphenSplit[0], dayString);
        if(m1 instanceof Error){ return m1; }

        var m2= matchDate(hyphenSplit[2], dayString);
        if(m2 instanceof Error){ return m2; }

        dates.push(m1, m2);

      }else{
        return new Error("Couldn't parse Timespan -> " + commaSplit[i]);
      }

      if(dates.length>2){
        console.log("-----------",dateString,"-------", split.length);
        console.log(dates);
      }

      if(dates.length>1){ dates.push(recurring); }
      timespans.push(dates);
    }
  }else{
    note= dateString;
  }
  if(typeof timespans === "string"){
    console.log(dateString, dayString, timespans);
  }
  return {
    timespans: timespans,
    note: note
  };
};

var processTimes= function(timeString){
  var split= timeString.split(" - ");
  if( split.length !== 2){
    return new Error("Wrong time format: "+timeString);
  }
  var times= [];
  for(var i=0; i<split.length; i++){
    var match= split[i].match(/(\d{2})\.(\d{2})/);
    if(match){ times.push(match[1]+":"+match[2]); }
  }
  return times;
};

// bring the event to a default format, return null for non-standard formats
var processEvent = function(event){
  var dates= processDate(event.date, event.day);
  if(dates instanceof Error){ return dates; }

  var times= processTimes(event.timespan);
  if(times instanceof Error){ return times; }

  var fs= event.fs.split(", ");
  return {
    courseName: event.name,
    courseLecturer: event.lecturer,

    data: {
      type: event.type,
      location: event.location,
      dates: dates.timespans,
      times: times,
      fs: fs,
      note: dates.note,
    }
  };
};

// run through list of events -> format before writing to database
exports.processEvents= function(events){
  var data= [],
  course= {},
  first= true;

  localTime= new Date();

  console.time("processEvents");

  for(var i=0; i<events.length; i++){
    var event= processEvent(events[i]);

    if(event instanceof Error){
      console.error(event);
      console.log(events[i]);
      continue;
    }

//    fsJoin.push.apply(fsJoin, event.data.fs)

    // accumulate events of the same events to one dataset
    // Assumption: sorted events
    if(event.courseName !== course.name){
      if(first){
        first= false;
      }else{
        data.push(course);
      }
      course= {
        name: event.courseName,
        lecturer: event.courseLecturer,
        events: [],
      };
    }
    course.events.push(event.data);
  }

  data.push(course);

  console.timeEnd("processEvents");
//  console.log("huhu",fsJoin.length);
  return data;
};

// promise based query
var query= function(client, prepared){
  var deferred= Q.defer(),
  query= client.query(prepared),
  reference= this;

  query.on("row", function(row, result){
    result.addRow(row);
  });
  query.on("error", function(error){
    deferred.reject(error);
  });
  query.on("end", function(result){
    deferred.resolve({
      ref: reference,
      value: result,
    });
  });

  return deferred.promise;
};

// send formatted events to postgres
exports.saveEvents= function(data){
  var deferred= Q.defer();
  var client = new pg.Client(config.pgString);

  // connect to postgres
  client.connect(function(err) {
    if(err) {
      deferred.reject("Could not connect to Database");
      return console.error('could not connect to postgres', err);
    }
    console.log("connected to db");

    var insertDates= function(eventResult){
      var eventId= eventResult.value.rows[0].id,
      event= eventResult.ref,
      results= [];

      event.dates.forEach(function(date){
        if(typeof date === "string"){
          date= [date];
        }
        var interval= date.length > 2 ? date[2] : 0;
        var endDate= date.length > 1 ? date[1] : date[0];
        var result= query.call(date, client, {
          name: "insertDate",
          text: "INSERT INTO\
                dates (startDate, endDate, startTime, endTime, interval, event)\
                VALUES($1, $2, $3, $4, $5, $6)",
          values: [date[0], endDate, event.times[0], event.times[1], interval+" W", eventId]
        });
        results.push(result);
      });
      return Q.all(results);
    };

    var insertEventMajors= function(majorResult){
      var majorId= majorResult.value.rows[0].id,
      event= majorResult.ref;

      var result= query.call(event, client, {
        name: "insertEventMajor",
        text: "INSERT INTO\
               eventmajors (eventid, majorid)\
               VALUES($1, $2)",
        values: [event.id, majorId],
      });

      return result;
    };

    var insertMajors= function(eventResult){
      var eventId= eventResult.value.rows[0].id,
      event= eventResult.ref,
      results= [];

      event.id= eventId;

      event.fs.forEach(function(major){
        var result= query.call(event, client, {
          name: "insertMajor",
          text: "WITH s AS ( \
                SELECT id, major\
                FROM majors\
                WHERE major= $1\
                )\
                ,i AS ( INSERT INTO majors(major)\
                SELECT $1\
                WHERE NOT EXISTS (SELECT 1 FROM s)\
                RETURNING id, major\
                )\
                SELECT id, major\
                FROM i\
                UNION ALL\
                SELECT id, major\
                FROM s",
          values: [major]
        })
        .then(insertEventMajors);
        results.push(result);
      });
      return Q.all(results);
    };

    var onEventInsert= function(eventResult){
      var results= [insertDates(eventResult), insertMajors(eventResult)];
      return Q.all(results);
    };

    var insertEvents= function(courseResult){
      var courseId= courseResult.value.rows[0].id,
      course= courseResult.ref,
      results= [];

      course.events.forEach(function(event){
        var result= query.call(event, client, {
          name: "insertEvent",
          text: "INSERT INTO\
                events (type, location, note, courseId)\
                VALUES($1, $2, $3, $4)\
                RETURNING id",
          values: [event.type, event.location, event.note, courseId]
        })
        .then(onEventInsert);
        results.push(result);
      });
      return Q.all(results);
    };

    var insertCourses= function(){
      var results= [];
      data.forEach(function(course){
        var result= query.call(course, client, {
          name: "insertCourse",
          text: "INSERT INTO\
                 courses (name, lecturer)\
                 VALUES($1, $2)\
                 RETURNING id",
          values: [course.name, course.lecturer]
        })
        .then(insertEvents);
        results.push(result);
      });
      return Q.all(results);
    };

    query(client, {
      name: "clearTables",
      text: "TRUNCATE courses, majors RESTART IDENTITY CASCADE"
    }).then(function(){
      console.log("cleared db");
      return insertCourses();
    })
    .then(function(){
      console.log("disconnecting from db");
      client.end();
      deferred.resolve("");
    })
    .fail(function(error){
      console.error(error.stack);
      client.end();
      deferred.reject("Failed running Query");
    });
  });

  return deferred.promise;
};

exports.queryDB= function(queryObject){
  var client = new pg.Client(config.pgString);
  var deferred= Q.defer();

  // connect to postgres
  client.connect(function(err) {
    if(err) { return console.error('could not connect to postgres', err); }

    query(client, queryObject)
    .then(function(result){
      client.end();
      deferred.resolve(result.value.rows);
    })
    .fail(function(err){
      client.end();
      deferred.reject(new Error('Failed running query - '+ err));
    });
  });

  return deferred.promise;
};
<commit_msg>fix weekmatch regexp<commit_after>var pg = require('pg').native,
Q= require("q"),
config= require("./config.js");
//_= require("lodash"),

var localTime= new Date();

Date.prototype.getWeek = function() {
  var fjan = new Date(this.getFullYear(), 0, 4);
  return Math.ceil((((this - fjan) / 86400000) + fjan.getDay()+1)/7);
};

Date.prototype.yyyymmdd = function() {
 var yyyy = this.getFullYear().toString();
 var mm = (this.getMonth()+1).toString(); // getMonth() is zero-based
 var dd  = this.getDate().toString();
 return yyyy +"-"+ (mm[1]?mm:"0"+mm[0]) +"-"+ (dd[1]?dd:"0"+dd[0]); // padding
};

var getDate = function(week, day, year) {
  year= year || new Date().getFullYear();
  var simple = new Date(year, 0, 1 + (week - 1) * 7);
  var date = simple;
  date.setDate(simple.getDate() - simple.getDay() + day +
              (simple.getDay() <= 4 ? 1 : 8));
  return date;
};

var toDay= function(s){
  return ["Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag", "Sonntag"].indexOf(s);
};

// convert calendar-week notation to dates
var matchDate = function(string, day, year){
  var weekMatch= string.match(/(\d{1,2})(\.|)/);
  if(weekMatch=== null){
    return new Error("Can't match week! str: " +string+
                     " matches: " +weekMatch);
  }
  return getDate(
    Number(weekMatch[1]),
    toDay(day),
    year)
    .yyyymmdd();
};

// converts different timeformats into a list of timespans
var processDate= function(dateString, dayString){
  var timespans= [],
  note= "";

  // try to match dd.mm.yyyy dates
  var dateMatch= dateString.match(/(\d{2})\.(\d{2})\.(\d{4})/);
  if(dateMatch){
    timespans.push(dateMatch[3]+"-"+dateMatch[2]+"-"+dateMatch[1]);

  // try to match complex timespans (xx. KW)
  }else if(dateString.indexOf(".") !== -1){

    // check for U or G weeks
    var match= dateString.match(/^(?:(U|G)).+/);
    var recurring= match ? 2: 1;
    if(match){
      dateString= dateString.slice(3, -1);
    }

    // split by ';' and ',' and then by '-'
    var commaSplit= dateString.split(/[,;] /);

    for(var i=0; i < commaSplit.length; i++){
      var hyphenSplit= commaSplit[i].split(/(?: |)- /);

      // extract week / years from parts
      var dates=[];

      // exactly 2 hyphens
      if(hyphenSplit.length <= 2){
        for(var j=0; j < hyphenSplit.length; j++){
          var yearMatch= hyphenSplit[j].match(/(\d{4})/);
          for(var x= i; yearMatch === null && x < commaSplit.length; x++){
            yearMatch= commaSplit[x].match(/(\d{4})/);
          }

          if(yearMatch === null){
            if(localTime.getMonth() > 2 && localTime.getMonth() < 9){ //sommersemester

              yearMatch= [, localTime.getFullYear()];
            }else{
              return new Error("Missing Year -> "+ dateString);
            }
          }

          var m= matchDate(hyphenSplit[j], dayString, yearMatch[1]);
          if(m instanceof Error){ return m; }
          dates.push(m);
        }
      }else if(hyphenSplit.length === 3){
        var m1= matchDate(hyphenSplit[0], dayString);
        if(m1 instanceof Error){ return m1; }

        var m2= matchDate(hyphenSplit[2], dayString);
        if(m2 instanceof Error){ return m2; }

        dates.push(m1, m2);

      }else{
        return new Error("Couldn't parse Timespan -> " + commaSplit[i]);
      }

      if(dates.length>2){
        console.log("-----------",dateString,"-------", split.length);
        console.log(dates);
      }

      if(dates.length>1){ dates.push(recurring); }
      timespans.push(dates);
    }
  }else{
    note= dateString;
  }
  if(typeof timespans === "string"){
    console.log(dateString, dayString, timespans);
  }
  return {
    timespans: timespans,
    note: note
  };
};

var processTimes= function(timeString){
  var split= timeString.split(" - ");
  if( split.length !== 2){
    return new Error("Wrong time format: "+timeString);
  }
  var times= [];
  for(var i=0; i<split.length; i++){
    var match= split[i].match(/(\d{2})\.(\d{2})/);
    if(match){ times.push(match[1]+":"+match[2]); }
  }
  return times;
};

// bring the event to a default format, return null for non-standard formats
var processEvent = function(event){
  var dates= processDate(event.date, event.day);
  if(dates instanceof Error){ return dates; }

  var times= processTimes(event.timespan);
  if(times instanceof Error){ return times; }

  var fs= event.fs.split(", ");
  return {
    courseName: event.name,
    courseLecturer: event.lecturer,

    data: {
      type: event.type,
      location: event.location,
      dates: dates.timespans,
      times: times,
      fs: fs,
      note: dates.note,
    }
  };
};

// run through list of events -> format before writing to database
exports.processEvents= function(events){
  var data= [],
  course= {},
  first= true;

  localTime= new Date();

  console.time("processEvents");

  for(var i=0; i<events.length; i++){
    var event= processEvent(events[i]);

    if(event instanceof Error){
      console.error(event);
      console.log(events[i]);
      continue;
    }

//    fsJoin.push.apply(fsJoin, event.data.fs)

    // accumulate events of the same events to one dataset
    // Assumption: sorted events
    if(event.courseName !== course.name){
      if(first){
        first= false;
      }else{
        data.push(course);
      }
      course= {
        name: event.courseName,
        lecturer: event.courseLecturer,
        events: [],
      };
    }
    course.events.push(event.data);
  }

  data.push(course);

  console.timeEnd("processEvents");
//  console.log("huhu",fsJoin.length);
  return data;
};

// promise based query
var query= function(client, prepared){
  var deferred= Q.defer(),
  query= client.query(prepared),
  reference= this;

  query.on("row", function(row, result){
    result.addRow(row);
  });
  query.on("error", function(error){
    deferred.reject(error);
  });
  query.on("end", function(result){
    deferred.resolve({
      ref: reference,
      value: result,
    });
  });

  return deferred.promise;
};

// send formatted events to postgres
exports.saveEvents= function(data){
  var deferred= Q.defer();
  var client = new pg.Client(config.pgString);

  // connect to postgres
  client.connect(function(err) {
    if(err) {
      deferred.reject("Could not connect to Database");
      return console.error('could not connect to postgres', err);
    }
    console.log("connected to db");

    var insertDates= function(eventResult){
      var eventId= eventResult.value.rows[0].id,
      event= eventResult.ref,
      results= [];

      event.dates.forEach(function(date){
        if(typeof date === "string"){
          date= [date];
        }
        var interval= date.length > 2 ? date[2] : 0;
        var endDate= date.length > 1 ? date[1] : date[0];
        var result= query.call(date, client, {
          name: "insertDate",
          text: "INSERT INTO\
                dates (startDate, endDate, startTime, endTime, interval, event)\
                VALUES($1, $2, $3, $4, $5, $6)",
          values: [date[0], endDate, event.times[0], event.times[1], interval+" W", eventId]
        });
        results.push(result);
      });
      return Q.all(results);
    };

    var insertEventMajors= function(majorResult){
      var majorId= majorResult.value.rows[0].id,
      event= majorResult.ref;

      var result= query.call(event, client, {
        name: "insertEventMajor",
        text: "INSERT INTO\
               eventmajors (eventid, majorid)\
               VALUES($1, $2)",
        values: [event.id, majorId],
      });

      return result;
    };

    var insertMajors= function(eventResult){
      var eventId= eventResult.value.rows[0].id,
      event= eventResult.ref,
      results= [];

      event.id= eventId;

      event.fs.forEach(function(major){
        var result= query.call(event, client, {
          name: "insertMajor",
          text: "WITH s AS ( \
                SELECT id, major\
                FROM majors\
                WHERE major= $1\
                )\
                ,i AS ( INSERT INTO majors(major)\
                SELECT $1\
                WHERE NOT EXISTS (SELECT 1 FROM s)\
                RETURNING id, major\
                )\
                SELECT id, major\
                FROM i\
                UNION ALL\
                SELECT id, major\
                FROM s",
          values: [major]
        })
        .then(insertEventMajors);
        results.push(result);
      });
      return Q.all(results);
    };

    var onEventInsert= function(eventResult){
      var results= [insertDates(eventResult), insertMajors(eventResult)];
      return Q.all(results);
    };

    var insertEvents= function(courseResult){
      var courseId= courseResult.value.rows[0].id,
      course= courseResult.ref,
      results= [];

      course.events.forEach(function(event){
        var result= query.call(event, client, {
          name: "insertEvent",
          text: "INSERT INTO\
                events (type, location, note, courseId)\
                VALUES($1, $2, $3, $4)\
                RETURNING id",
          values: [event.type, event.location, event.note, courseId]
        })
        .then(onEventInsert);
        results.push(result);
      });
      return Q.all(results);
    };

    var insertCourses= function(){
      var results= [];
      data.forEach(function(course){
        var result= query.call(course, client, {
          name: "insertCourse",
          text: "INSERT INTO\
                 courses (name, lecturer)\
                 VALUES($1, $2)\
                 RETURNING id",
          values: [course.name, course.lecturer]
        })
        .then(insertEvents);
        results.push(result);
      });
      return Q.all(results);
    };

    query(client, {
      name: "clearTables",
      text: "TRUNCATE courses, majors RESTART IDENTITY CASCADE"
    }).then(function(){
      console.log("cleared db");
      return insertCourses();
    })
    .then(function(){
      console.log("disconnecting from db");
      client.end();
      deferred.resolve("");
    })
    .fail(function(error){
      console.error(error.stack);
      client.end();
      deferred.reject("Failed running Query");
    });
  });

  return deferred.promise;
};

exports.queryDB= function(queryObject){
  var client = new pg.Client(config.pgString);
  var deferred= Q.defer();

  // connect to postgres
  client.connect(function(err) {
    if(err) { return console.error('could not connect to postgres', err); }

    query(client, queryObject)
    .then(function(result){
      client.end();
      deferred.resolve(result.value.rows);
    })
    .fail(function(err){
      client.end();
      deferred.reject(new Error('Failed running query - '+ err));
    });
  });

  return deferred.promise;
};
<|endoftext|>


<commit_before>import { msg } from '../app/messages'
import { URL_NO_USER, RESERVED_URLS, URL_RESERVED_PREFIX } from '../app/routing'
import {
  // createBuildings,
  updateBuildingPosition
} from '../segments/buildings'
import { DEFAULT_SEGMENTS } from '../segments/default'
import { getSegmentInfo } from '../segments/info'
import { normalizeAllSegmentWidths } from '../segments/resizing'
import { getVariantString, getVariantArray } from '../segments/variant_utils'
import {
  segmentsChanged,
  repositionSegments,
  createSegmentDom
} from '../segments/view'
import { getSignInData, isSignedIn } from '../users/authentication'
import { getUnits, getLeftHandTraffic, propagateUnits } from '../users/localization'
import { normalizeSlug } from '../util/helpers'
import { generateRandSeed } from '../util/random'
import { updateStreetName } from './name'
import {
  createNewUndoIfNecessary,
  unifyUndoStack
} from './undo_stack'
import {
  DEFAULT_STREET_WIDTH,
  normalizeStreetWidth,
  resizeStreetWidth
} from './width'
import { updateLastStreetInfo, scheduleSavingStreetToServer } from './xhr'
import {
  updateStreetWidth,
  updateSegments,
  setUpdateTime,
  saveCreatorId,
  updateStreetData
} from '../store/actions/street'
import { resetUndoStack } from '../store/actions/undo'
import store from '../store'

const DEFAULT_BUILDING_HEIGHT_LEFT = 4
const DEFAULT_BUILDING_HEIGHT_RIGHT = 3
const DEFAULT_BUILDING_VARIANT_LEFT = 'narrow'
const DEFAULT_BUILDING_VARIANT_RIGHT = 'wide'
const DEFAULT_BUILDING_HEIGHT_EMPTY = 1
const DEFAULT_BUILDING_VARIANT_EMPTY = 'grass'

let _lastStreet

export function getLastStreet () {
  return _lastStreet
}

export function setLastStreet (value) {
  _lastStreet = value
}

const LATEST_SCHEMA_VERSION = 18
// 1: starting point
// 2: adding leftBuildingHeight and rightBuildingHeight
// 3: adding leftBuildingVariant and rightBuildingVariant
// 4: adding transit shelter elevation
// 5: adding another lamp type (traditional)
// 6: colored streetcar lanes
// 7: colored bus and light rail lanes
// 8: colored bike lane
// 9: second car type: truck
// 10: sidewalk density
// 11: unify median and planting strip into divider
// 12: getting rid of small tree
// 13: bike rack elevation
// 14: wayfinding has three types
// 15: sidewalks have rand seed
// 16: stop saving undo stack
// 17: alternative colors for bike lanes
// 18: change lat/lng format from array to object

export const DEFAULT_NAME = msg('DEFAULT_STREET_NAME')

function incrementSchemaVersion (street) {
  let segment, variant
  if (!street.schemaVersion) {
    street.schemaVersion = 1
  }

  switch (street.schemaVersion) {
    case 1:
      street.leftBuildingHeight = DEFAULT_BUILDING_HEIGHT_LEFT
      street.rightBuildingHeight = DEFAULT_BUILDING_HEIGHT_RIGHT
      break
    case 2:
      street.leftBuildingVariant = DEFAULT_BUILDING_VARIANT_LEFT
      street.rightBuildingVariant = DEFAULT_BUILDING_VARIANT_RIGHT
      break
    case 3:
      for (var i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'transit-shelter') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['transit-shelter-elevation'] = 'street-level'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 4:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-lamp') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['lamp-type'] = 'modern'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 5:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'streetcar') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['public-transit-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 6:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bus-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bus-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        } else if (segment.type === 'light-rail') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['public-transit-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 7:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bike-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 8:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'drive-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['car-type'] = 'car'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 9:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['sidewalk-density'] = 'normal'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 10:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'planting-strip') {
          segment.type = 'divider'

          if (segment.variantString === '') {
            segment.variantString = 'planting-strip'
          }
        } else if (segment.type === 'small-median') {
          segment.type = 'divider'
          segment.variantString = 'median'
        }
      }
      break
    case 11:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'divider') {
          if (segment.variantString === 'small-tree') {
            segment.variantString = 'big-tree'
          }
        } else if (segment.type === 'sidewalk-tree') {
          if (segment.variantString === 'small') {
            segment.variantString = 'big'
          }
        }
      }
      break
    case 12:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-bike-rack') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bike-rack-elevation'] = 'sidewalk'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 13:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-wayfinding') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['wayfinding-type'] = 'large'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 14:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          segment.randSeed = 35
        }
      }
      break
    case 15:
      store.dispatch(resetUndoStack())
      break
    case 16:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          if (variant['bike-asphalt'] === 'colored') {
            variant['bike-asphalt'] = 'green'
          }
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 17:
      if (street.location && Array.isArray(street.location.latlng)) {
        street.location.latlng = {
          lat: street.location.latlng[0],
          lng: street.location.latlng[1]
        }
      }
  }

  street.schemaVersion++
}

export function updateToLatestSchemaVersion (street) {
  var updated = false
  while (!street.schemaVersion || (street.schemaVersion < LATEST_SCHEMA_VERSION)) {
    incrementSchemaVersion(street)
    updated = true
  }

  return updated
}

export function createDomFromData () {
  document.querySelector('#street-section-editable').innerHTML = ''
  const street = store.getState().street

  for (var i in street.segments) {
    var segment = street.segments[i]

    // Add some additional data structures
    // TODO: populate data structure elsewhere
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.warnings = []

    var el = createSegmentDom(segment)
    document.querySelector('#street-section-editable').appendChild(el)

    segment.el = el
    segment.el.dataNo = i
  }

  repositionSegments()
  updateBuildingPosition()
  // createBuildings()
}

export function setStreetCreatorId (newId) {
  store.dispatch(saveCreatorId(newId))

  unifyUndoStack()
  updateLastStreetInfo()
}

export function setUpdateTimeToNow () {
  const updateTime = new Date().getTime()
  store.dispatch(setUpdateTime(updateTime))
  unifyUndoStack()
}

let ignoreStreetChanges = false

export function setIgnoreStreetChanges (value) {
  ignoreStreetChanges = value
}

export function saveStreetToServerIfNecessary () {
  if (ignoreStreetChanges || store.getState().errors.abortEverything) {
    return
  }

  const street = store.getState().street
  var currentData = trimStreetData(street)

  if (JSON.stringify(currentData) !== JSON.stringify(_lastStreet)) {
    if (street.editCount !== null) {
      street.editCount++
    }
    setUpdateTimeToNow()

    // Some parts of the UI need to know this happened to respond to it
    // TODO: figure out appropriate event name
    window.dispatchEvent(new window.CustomEvent('stmx:save_street'))

    createNewUndoIfNecessary(_lastStreet, currentData)

    scheduleSavingStreetToServer()

    _lastStreet = currentData
  }
}

// Copies only the data necessary for save/undo.
export function trimStreetData (street) {
  var newData = {}

  newData.schemaVersion = street.schemaVersion

  newData.width = street.width
  newData.name = street.name

  newData.id = street.id
  newData.namespacedId = street.namespacedId
  newData.creatorId = street.creatorId
  newData.originalStreetId = street.originalStreetId
  newData.units = street.units

  newData.location = street.location
  newData.userUpdated = street.userUpdated

  if (street.editCount !== null) {
    // console.log('saving editCount', street.editCount)
    newData.editCount = street.editCount
  } else {
    // console.log('not saving editCount')
  }

  newData.leftBuildingHeight = street.leftBuildingHeight
  newData.rightBuildingHeight = street.rightBuildingHeight
  newData.leftBuildingVariant = street.leftBuildingVariant
  newData.rightBuildingVariant = street.rightBuildingVariant

  newData.segments = []

  for (var i in street.segments) {
    var segment = {}
    segment.type = street.segments[i].type
    segment.variantString = street.segments[i].variantString
    segment.width = street.segments[i].width
    if (street.segments[i].randSeed) {
      segment.randSeed = street.segments[i].randSeed
    }

    newData.segments.push(segment)
  }

  return newData
}

// TODO this function should not exist; all the data should be in street.
// object to begin with
export function createDataFromDom () {
  var els = document.querySelectorAll('#street-section-editable > .segment')
  const segments = []

  for (var i = 0, el; el = els[i]; i++) { // eslint-disable-line no-cond-assign
    var segment = {}
    segment.type = el.getAttribute('type')
    if (el.getAttribute('rand-seed')) {
      segment.randSeed = parseInt(el.getAttribute('rand-seed'))
    }
    segment.variantString = el.getAttribute('variant-string')
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.width = parseFloat(el.getAttribute('data-width'))
    segment.el = el
    segment.warnings = []
    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
}

function fillDefaultSegments () {
  const segments = []
  let leftHandTraffic = getLeftHandTraffic()

  for (var i in DEFAULT_SEGMENTS[leftHandTraffic]) {
    var segment = DEFAULT_SEGMENTS[leftHandTraffic][i]
    segment.warnings = []
    segment.variantString = getVariantString(segment.variant)

    if (getSegmentInfo(segment.type).needRandSeed) {
      segment.randSeed = generateRandSeed()
    }

    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
  normalizeAllSegmentWidths()
}

export function getStreetUrl (street) {
  var url = '/'
  if (street.creatorId) {
    if (RESERVED_URLS.indexOf(street.creatorId) !== -1) {
      url += URL_RESERVED_PREFIX
    }

    url += street.creatorId
  } else {
    url += URL_NO_USER
  }

  url += '/'

  url += street.namespacedId

  if (street.creatorId) {
    var slug = normalizeSlug(street.name)
    url += '/' + encodeURIComponent(slug)
  }

  return url
}

export function prepareDefaultStreet () {
  const defaultStreet = {
    units: getUnits(),
    location: null,
    name: DEFAULT_NAME,
    userUpdated: false,
    editCount: 0,
    leftBuildingHeight: DEFAULT_BUILDING_HEIGHT_LEFT,
    leftBuildingVariant: DEFAULT_BUILDING_VARIANT_LEFT,
    rightBuildingHeight: DEFAULT_BUILDING_HEIGHT_RIGHT,
    rightBuildingVariant: DEFAULT_BUILDING_VARIANT_RIGHT
  }

  store.dispatch(updateStreetData(defaultStreet))
  propagateUnits()
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))

  // console.log('editCount = 0 on default street')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  fillDefaultSegments()
  setUpdateTimeToNow()
}

export function prepareEmptyStreet () {
  const emptyStreet = {
    units: getUnits(),
    location: null,
    name: DEFAULT_NAME,
    userUpdated: false,
    editCount: 0,
    leftBuildingHeight: DEFAULT_BUILDING_HEIGHT_EMPTY,
    leftBuildingVariant: DEFAULT_BUILDING_VARIANT_EMPTY,
    rightBuildingHeight: DEFAULT_BUILDING_HEIGHT_EMPTY,
    rightBuildingVariant: DEFAULT_BUILDING_VARIANT_EMPTY,
    segments: []
  }

  store.dispatch(updateStreetData(emptyStreet))
  propagateUnits()
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))

  // console.log('editCount = 0 on empty street!')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  setUpdateTimeToNow()
}

/**
 * @todo: documentation
 *
 * @param {Boolean} dontScroll - document this
 * @param {Boolean} save - if set to `false`, calling this function will not
 *          cause a re-save of street to the server. (e.g. in the case of
 *          live update feature.) Default is `true`.
 */
export function updateEverything (dontScroll, save = true) {
  setIgnoreStreetChanges(true)
  propagateUnits()
  // TODO Verify that we don't need to dispatch an update width event here
  createDomFromData()
  segmentsChanged()
  resizeStreetWidth(dontScroll)
  updateStreetName(store.getState().street)
  setIgnoreStreetChanges(false)
  _lastStreet = trimStreetData(store.getState().street)

  if (save === true) {
    scheduleSavingStreetToServer()
  }
}
<commit_msg>removed updateBuildingPosition<commit_after>import { msg } from '../app/messages'
import { URL_NO_USER, RESERVED_URLS, URL_RESERVED_PREFIX } from '../app/routing'
// import {
//   createBuildings,
//   updateBuildingPosition
// } from '../segments/buildings'
import { DEFAULT_SEGMENTS } from '../segments/default'
import { getSegmentInfo } from '../segments/info'
import { normalizeAllSegmentWidths } from '../segments/resizing'
import { getVariantString, getVariantArray } from '../segments/variant_utils'
import {
  segmentsChanged,
  repositionSegments,
  createSegmentDom
} from '../segments/view'
import { getSignInData, isSignedIn } from '../users/authentication'
import { getUnits, getLeftHandTraffic, propagateUnits } from '../users/localization'
import { normalizeSlug } from '../util/helpers'
import { generateRandSeed } from '../util/random'
import { updateStreetName } from './name'
import {
  createNewUndoIfNecessary,
  unifyUndoStack
} from './undo_stack'
import {
  DEFAULT_STREET_WIDTH,
  normalizeStreetWidth,
  resizeStreetWidth
} from './width'
import { updateLastStreetInfo, scheduleSavingStreetToServer } from './xhr'
import {
  updateStreetWidth,
  updateSegments,
  setUpdateTime,
  saveCreatorId,
  updateStreetData
} from '../store/actions/street'
import { resetUndoStack } from '../store/actions/undo'
import store from '../store'

const DEFAULT_BUILDING_HEIGHT_LEFT = 4
const DEFAULT_BUILDING_HEIGHT_RIGHT = 3
const DEFAULT_BUILDING_VARIANT_LEFT = 'narrow'
const DEFAULT_BUILDING_VARIANT_RIGHT = 'wide'
const DEFAULT_BUILDING_HEIGHT_EMPTY = 1
const DEFAULT_BUILDING_VARIANT_EMPTY = 'grass'

let _lastStreet

export function getLastStreet () {
  return _lastStreet
}

export function setLastStreet (value) {
  _lastStreet = value
}

const LATEST_SCHEMA_VERSION = 18
// 1: starting point
// 2: adding leftBuildingHeight and rightBuildingHeight
// 3: adding leftBuildingVariant and rightBuildingVariant
// 4: adding transit shelter elevation
// 5: adding another lamp type (traditional)
// 6: colored streetcar lanes
// 7: colored bus and light rail lanes
// 8: colored bike lane
// 9: second car type: truck
// 10: sidewalk density
// 11: unify median and planting strip into divider
// 12: getting rid of small tree
// 13: bike rack elevation
// 14: wayfinding has three types
// 15: sidewalks have rand seed
// 16: stop saving undo stack
// 17: alternative colors for bike lanes
// 18: change lat/lng format from array to object

export const DEFAULT_NAME = msg('DEFAULT_STREET_NAME')

function incrementSchemaVersion (street) {
  let segment, variant
  if (!street.schemaVersion) {
    street.schemaVersion = 1
  }

  switch (street.schemaVersion) {
    case 1:
      street.leftBuildingHeight = DEFAULT_BUILDING_HEIGHT_LEFT
      street.rightBuildingHeight = DEFAULT_BUILDING_HEIGHT_RIGHT
      break
    case 2:
      street.leftBuildingVariant = DEFAULT_BUILDING_VARIANT_LEFT
      street.rightBuildingVariant = DEFAULT_BUILDING_VARIANT_RIGHT
      break
    case 3:
      for (var i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'transit-shelter') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['transit-shelter-elevation'] = 'street-level'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 4:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-lamp') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['lamp-type'] = 'modern'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 5:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'streetcar') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['public-transit-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 6:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bus-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bus-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        } else if (segment.type === 'light-rail') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['public-transit-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 7:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bike-asphalt'] = 'regular'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 8:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'drive-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['car-type'] = 'car'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 9:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['sidewalk-density'] = 'normal'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 10:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'planting-strip') {
          segment.type = 'divider'

          if (segment.variantString === '') {
            segment.variantString = 'planting-strip'
          }
        } else if (segment.type === 'small-median') {
          segment.type = 'divider'
          segment.variantString = 'median'
        }
      }
      break
    case 11:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'divider') {
          if (segment.variantString === 'small-tree') {
            segment.variantString = 'big-tree'
          }
        } else if (segment.type === 'sidewalk-tree') {
          if (segment.variantString === 'small') {
            segment.variantString = 'big'
          }
        }
      }
      break
    case 12:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-bike-rack') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['bike-rack-elevation'] = 'sidewalk'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 13:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-wayfinding') {
          variant = getVariantArray(segment.type, segment.variantString)
          variant['wayfinding-type'] = 'large'
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 14:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          segment.randSeed = 35
        }
      }
      break
    case 15:
      store.dispatch(resetUndoStack())
      break
    case 16:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          if (variant['bike-asphalt'] === 'colored') {
            variant['bike-asphalt'] = 'green'
          }
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 17:
      if (street.location && Array.isArray(street.location.latlng)) {
        street.location.latlng = {
          lat: street.location.latlng[0],
          lng: street.location.latlng[1]
        }
      }
  }

  street.schemaVersion++
}

export function updateToLatestSchemaVersion (street) {
  var updated = false
  while (!street.schemaVersion || (street.schemaVersion < LATEST_SCHEMA_VERSION)) {
    incrementSchemaVersion(street)
    updated = true
  }

  return updated
}

export function createDomFromData () {
  document.querySelector('#street-section-editable').innerHTML = ''
  const street = store.getState().street

  for (var i in street.segments) {
    var segment = street.segments[i]

    // Add some additional data structures
    // TODO: populate data structure elsewhere
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.warnings = []

    var el = createSegmentDom(segment)
    document.querySelector('#street-section-editable').appendChild(el)

    segment.el = el
    segment.el.dataNo = i
  }

  repositionSegments()
  // updateBuildingPosition()
  // createBuildings()
}

export function setStreetCreatorId (newId) {
  store.dispatch(saveCreatorId(newId))

  unifyUndoStack()
  updateLastStreetInfo()
}

export function setUpdateTimeToNow () {
  const updateTime = new Date().getTime()
  store.dispatch(setUpdateTime(updateTime))
  unifyUndoStack()
}

let ignoreStreetChanges = false

export function setIgnoreStreetChanges (value) {
  ignoreStreetChanges = value
}

export function saveStreetToServerIfNecessary () {
  if (ignoreStreetChanges || store.getState().errors.abortEverything) {
    return
  }

  const street = store.getState().street
  var currentData = trimStreetData(street)

  if (JSON.stringify(currentData) !== JSON.stringify(_lastStreet)) {
    if (street.editCount !== null) {
      street.editCount++
    }
    setUpdateTimeToNow()

    // Some parts of the UI need to know this happened to respond to it
    // TODO: figure out appropriate event name
    window.dispatchEvent(new window.CustomEvent('stmx:save_street'))

    createNewUndoIfNecessary(_lastStreet, currentData)

    scheduleSavingStreetToServer()

    _lastStreet = currentData
  }
}

// Copies only the data necessary for save/undo.
export function trimStreetData (street) {
  var newData = {}

  newData.schemaVersion = street.schemaVersion

  newData.width = street.width
  newData.name = street.name

  newData.id = street.id
  newData.namespacedId = street.namespacedId
  newData.creatorId = street.creatorId
  newData.originalStreetId = street.originalStreetId
  newData.units = street.units

  newData.location = street.location
  newData.userUpdated = street.userUpdated

  if (street.editCount !== null) {
    // console.log('saving editCount', street.editCount)
    newData.editCount = street.editCount
  } else {
    // console.log('not saving editCount')
  }

  newData.leftBuildingHeight = street.leftBuildingHeight
  newData.rightBuildingHeight = street.rightBuildingHeight
  newData.leftBuildingVariant = street.leftBuildingVariant
  newData.rightBuildingVariant = street.rightBuildingVariant

  newData.segments = []

  for (var i in street.segments) {
    var segment = {}
    segment.type = street.segments[i].type
    segment.variantString = street.segments[i].variantString
    segment.width = street.segments[i].width
    if (street.segments[i].randSeed) {
      segment.randSeed = street.segments[i].randSeed
    }

    newData.segments.push(segment)
  }

  return newData
}

// TODO this function should not exist; all the data should be in street.
// object to begin with
export function createDataFromDom () {
  var els = document.querySelectorAll('#street-section-editable > .segment')
  const segments = []

  for (var i = 0, el; el = els[i]; i++) { // eslint-disable-line no-cond-assign
    var segment = {}
    segment.type = el.getAttribute('type')
    if (el.getAttribute('rand-seed')) {
      segment.randSeed = parseInt(el.getAttribute('rand-seed'))
    }
    segment.variantString = el.getAttribute('variant-string')
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.width = parseFloat(el.getAttribute('data-width'))
    segment.el = el
    segment.warnings = []
    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
}

function fillDefaultSegments () {
  const segments = []
  let leftHandTraffic = getLeftHandTraffic()

  for (var i in DEFAULT_SEGMENTS[leftHandTraffic]) {
    var segment = DEFAULT_SEGMENTS[leftHandTraffic][i]
    segment.warnings = []
    segment.variantString = getVariantString(segment.variant)

    if (getSegmentInfo(segment.type).needRandSeed) {
      segment.randSeed = generateRandSeed()
    }

    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
  normalizeAllSegmentWidths()
}

export function getStreetUrl (street) {
  var url = '/'
  if (street.creatorId) {
    if (RESERVED_URLS.indexOf(street.creatorId) !== -1) {
      url += URL_RESERVED_PREFIX
    }

    url += street.creatorId
  } else {
    url += URL_NO_USER
  }

  url += '/'

  url += street.namespacedId

  if (street.creatorId) {
    var slug = normalizeSlug(street.name)
    url += '/' + encodeURIComponent(slug)
  }

  return url
}

export function prepareDefaultStreet () {
  const defaultStreet = {
    units: getUnits(),
    location: null,
    name: DEFAULT_NAME,
    userUpdated: false,
    editCount: 0,
    leftBuildingHeight: DEFAULT_BUILDING_HEIGHT_LEFT,
    leftBuildingVariant: DEFAULT_BUILDING_VARIANT_LEFT,
    rightBuildingHeight: DEFAULT_BUILDING_HEIGHT_RIGHT,
    rightBuildingVariant: DEFAULT_BUILDING_VARIANT_RIGHT
  }

  store.dispatch(updateStreetData(defaultStreet))
  propagateUnits()
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))

  // console.log('editCount = 0 on default street')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  fillDefaultSegments()
  setUpdateTimeToNow()
}

export function prepareEmptyStreet () {
  const emptyStreet = {
    units: getUnits(),
    location: null,
    name: DEFAULT_NAME,
    userUpdated: false,
    editCount: 0,
    leftBuildingHeight: DEFAULT_BUILDING_HEIGHT_EMPTY,
    leftBuildingVariant: DEFAULT_BUILDING_VARIANT_EMPTY,
    rightBuildingHeight: DEFAULT_BUILDING_HEIGHT_EMPTY,
    rightBuildingVariant: DEFAULT_BUILDING_VARIANT_EMPTY,
    segments: []
  }

  store.dispatch(updateStreetData(emptyStreet))
  propagateUnits()
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))

  // console.log('editCount = 0 on empty street!')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  setUpdateTimeToNow()
}

/**
 * @todo: documentation
 *
 * @param {Boolean} dontScroll - document this
 * @param {Boolean} save - if set to `false`, calling this function will not
 *          cause a re-save of street to the server. (e.g. in the case of
 *          live update feature.) Default is `true`.
 */
export function updateEverything (dontScroll, save = true) {
  setIgnoreStreetChanges(true)
  propagateUnits()
  // TODO Verify that we don't need to dispatch an update width event here
  createDomFromData()
  segmentsChanged()
  resizeStreetWidth(dontScroll)
  updateStreetName(store.getState().street)
  setIgnoreStreetChanges(false)
  _lastStreet = trimStreetData(store.getState().street)

  if (save === true) {
    scheduleSavingStreetToServer()
  }
}
<|endoftext|>


<commit_before>import { msg } from '../app/messages'
import { URL_NO_USER, RESERVED_URLS, URL_RESERVED_PREFIX } from '../app/routing'
import {
  createBuildings,
  updateBuildingPosition
} from '../segments/buildings'
import { DEFAULT_SEGMENTS } from '../segments/default'
import { getSegmentInfo } from '../segments/info'
import { normalizeAllSegmentWidths } from '../segments/resizing'
import { getVariantString, getVariantArray } from '../segments/variant_utils'
import {
  segmentsChanged,
  repositionSegments,
  createSegmentDom
} from '../segments/view'
import { getSignInData, isSignedIn } from '../users/authentication'
import { getUnits, getLeftHandTraffic, propagateUnits } from '../users/localization'
import { normalizeSlug } from '../util/helpers'
import { generateRandSeed } from '../util/random'
import { updateStreetName } from './name'
import {
  createNewUndoIfNecessary,
  unifyUndoStack
} from './undo_stack'
import {
  DEFAULT_STREET_WIDTH,
  normalizeStreetWidth,
  resizeStreetWidth
} from './width'
import { updateLastStreetInfo, scheduleSavingStreetToServer } from './xhr'
import {
  setBuildingVariant,
  setBuildingFloorValue,
  addLocation,
  updateSchemaVersion,
  clearSegments,
  saveCreatorId,
  updateEditCount,
  updateStreetWidth,
  saveStreetName,
  setUnits,
  updateSegments,
  changeSegmentVariant,
  saveStreetId,
  setUpdateTime,
  saveOriginalStreetId
} from '../store/actions/street'
import { resetUndoStack } from '../store/actions/undo'
import store from '../store'

const DEFAULT_BUILDING_HEIGHT_LEFT = 4
const DEFAULT_BUILDING_HEIGHT_RIGHT = 3
const DEFAULT_BUILDING_VARIANT_LEFT = 'narrow'
const DEFAULT_BUILDING_VARIANT_RIGHT = 'wide'
const DEFAULT_BUILDING_HEIGHT_EMPTY = 1
const DEFAULT_BUILDING_VARIANT_EMPTY = 'grass'

let _lastStreet

export function getLastStreet () {
  return _lastStreet
}

export function setLastStreet (value) {
  _lastStreet = value
}

const LATEST_SCHEMA_VERSION = 18
// 1: starting point
// 2: adding leftBuildingHeight and rightBuildingHeight
// 3: adding leftBuildingVariant and rightBuildingVariant
// 4: adding transit shelter elevation
// 5: adding another lamp type (traditional)
// 6: colored streetcar lanes
// 7: colored bus and light rail lanes
// 8: colored bike lane
// 9: second car type: truck
// 10: sidewalk density
// 11: unify median and planting strip into divider
// 12: getting rid of small tree
// 13: bike rack elevation
// 14: wayfinding has three types
// 15: sidewalks have rand seed
// 16: stop saving undo stack
// 17: alternative colors for bike lanes
// 18: change lat/lng format from array to object

export const DEFAULT_NAME = msg('DEFAULT_STREET_NAME')

function incrementSchemaVersion (street) {
  let segment, variant, schemaVersion

  if (!street.schemaVersion) {
    schemaVersion = 1
  }

  switch (street.schemaVersion) {
    case 1:
      store.dispatch(setBuildingFloorValue('left', DEFAULT_BUILDING_HEIGHT_LEFT))
      store.dispatch(setBuildingFloorValue('right', DEFAULT_BUILDING_HEIGHT_RIGHT))
      break
    case 2:
      store.dispatch(setBuildingVariant('left', DEFAULT_BUILDING_VARIANT_LEFT))
      store.dispatch(setBuildingVariant('right', DEFAULT_BUILDING_VARIANT_RIGHT))
      break
    case 3:
      for (var i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'transit-shelter') {
          store.dispatch(changeSegmentVariant(i, 'transit-shelter-elevation', 'street-level'))
        }
      }
      break
    case 4:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-lamp') {
          store.dispatch(changeSegmentVariant(i, 'lamp-type', 'modern'))
        }
      }
      break
    case 5:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'streetcar') {
          store.dispatch(changeSegmentVariant(i, 'public-transit-asphalt', 'regular'))
        }
      }
      break
    case 6:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bus-lane') {
          store.dispatch(changeSegmentVariant(i, 'bus-asphalt', 'regular'))
        } else if (segment.type === 'light-rail') {
          store.dispatch(changeSegmentVariant(i, 'public-transit-asphalt', 'regular'))
        }
      }
      break
    case 7:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          store.dispatch(changeSegmentVariant(i, 'bike-asphalt', 'regular'))
        }
      }
      break
    case 8:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'drive-lane') {
          store.dispatch(changeSegmentVariant(i, 'car-type', 'car'))
        }
      }
      break
    case 9:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          store.dispatch(changeSegmentVariant(i, 'sidewalk-density', 'normal'))
        }
      }
      break
    case 10:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'planting-strip') {
          segment.type = 'divider'

          if (segment.variantString === '') {
            segment.variantString = 'planting-strip'
          }
        } else if (segment.type === 'small-median') {
          segment.type = 'divider'
          segment.variantString = 'median'
        }
      }
      break
    case 11:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'divider') {
          if (segment.variantString === 'small-tree') {
            segment.variantString = 'big-tree'
          }
        } else if (segment.type === 'sidewalk-tree') {
          if (segment.variantString === 'small') {
            segment.variantString = 'big'
          }
        }
      }
      break
    case 12:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-bike-rack') {
          store.dispatch(changeSegmentVariant(i, 'bike-rack-elevation', 'sidewalk'))
        }
      }
      break
    case 13:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-wayfinding') {
          store.dispatch(changeSegmentVariant(i, 'wayfinding-type', 'large'))
        }
      }
      break
    case 14:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          segment.randSeed = 35
        }
      }
      break
    case 15:
      store.dispatch(resetUndoStack())
      break
    case 16:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          if (variant['bike-asphalt'] === 'colored') {
            variant['bike-asphalt'] = 'green'
          }
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 17:
      if (street.location && street.location.latlng) {
        const location = {
          ...street.location,
          latlng: {
            lat: street.location.latlng[0],
            lng: street.location.latlng[1]
          }
        }
        store.dispatch(addLocation(location))
      }
  }

  schemaVersion++
  store.dispatch(updateSchemaVersion(schemaVersion))
}

export function updateStreetData (street) {
  store.dispatch(saveCreatorId(street.creatorId))
  store.dispatch(updateEditCount(street.editCount))
  store.dispatch(saveStreetId(street.id, street.namespacedId))
  setBuilding('left', street.leftBuildingVariant, street.leftBuildingHeight)
  setBuilding('right', street.rightBuildingVariant, street.rightBuildingHeight)
  store.dispatch(addLocation(street.location))
  store.dispatch(saveStreetName(street.name, street.userUpdated))
  store.dispatch(saveOriginalStreetId(street.originalStreetId))
  store.dispatch(updateSchemaVersion(street.schemaVersion))
  store.dispatch(updateSegments(street.segments))
  store.dispatch(setUnits(street.units))
  store.dispatch(setUpdateTime(street.updatedAt))
  store.dispatch(updateStreetWidth(street.width))
}

function setBuilding (position, variant, height) {
  store.dispatch(setBuildingVariant(position, variant))
  store.dispatch(setBuildingFloorValue(position, height))
}

export function updateToLatestSchemaVersion (street) {
  var updated = false
  while (!street.schemaVersion || (street.schemaVersion < LATEST_SCHEMA_VERSION)) {
    incrementSchemaVersion(street)
    updated = true
  }

  return updated
}

export function createDomFromData () {
  document.querySelector('#street-section-editable').innerHTML = ''
  const street = store.getState().street

  for (var i in street.segments) {
    var segment = street.segments[i]

    // Add some additional data structures
    // TODO: populate data structure elsewhere
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.warnings = []

    var el = createSegmentDom(segment)
    document.querySelector('#street-section-editable').appendChild(el)

    segment.el = el
    segment.el.dataNo = i
  }

  repositionSegments()
  updateBuildingPosition()
  createBuildings()
}

export function setStreetCreatorId (newId) {
  store.dispatch(saveCreatorId(newId))

  unifyUndoStack()
  updateLastStreetInfo()
}

export function setUpdateTimeToNow () {
  store.dispatch(setUpdateTime(new Date().getTime()))
  unifyUndoStack()
}

let ignoreStreetChanges = false

export function setIgnoreStreetChanges (value) {
  ignoreStreetChanges = value
}

export function saveStreetToServerIfNecessary () {
  if (ignoreStreetChanges || store.getState().errors.abortEverything) {
    return
  }

  const street = store.getState().street
  var currentData = trimStreetData(street)

  if (JSON.stringify(currentData) !== JSON.stringify(_lastStreet)) {
    if (street.editCount !== null) {
      street.editCount++
    }
    setUpdateTimeToNow()

    // Some parts of the UI need to know this happened to respond to it
    // TODO: figure out appropriate event name
    window.dispatchEvent(new window.CustomEvent('stmx:save_street'))

    createNewUndoIfNecessary(_lastStreet, currentData)

    scheduleSavingStreetToServer()

    _lastStreet = currentData
  }
}

// Copies only the data necessary for save/undo.
export function trimStreetData (street) {
  var newData = {}

  newData.schemaVersion = street.schemaVersion

  newData.width = street.width
  newData.name = street.name

  newData.id = street.id
  newData.namespacedId = street.namespacedId
  newData.creatorId = street.creatorId
  newData.originalStreetId = street.originalStreetId
  newData.units = street.units

  newData.location = street.location
  newData.userUpdated = street.userUpdated

  if (street.editCount !== null) {
    // console.log('saving editCount', street.editCount)
    newData.editCount = street.editCount
  } else {
    // console.log('not saving editCount')
  }

  newData.leftBuildingHeight = street.leftBuildingHeight
  newData.rightBuildingHeight = street.rightBuildingHeight
  newData.leftBuildingVariant = street.leftBuildingVariant
  newData.rightBuildingVariant = street.rightBuildingVariant

  newData.segments = []

  for (var i in street.segments) {
    var segment = {}
    segment.type = street.segments[i].type
    segment.variantString = street.segments[i].variantString
    segment.width = street.segments[i].width
    if (street.segments[i].randSeed) {
      segment.randSeed = street.segments[i].randSeed
    }

    newData.segments.push(segment)
  }

  return newData
}

// TODO this function should not exist; all the data should be in street.
// object to begin with
export function createDataFromDom () {
  var els = document.querySelectorAll('#street-section-editable > .segment')
  const segments = []

  for (var i = 0, el; el = els[i]; i++) { // eslint-disable-line no-cond-assign
    var segment = {}
    segment.type = el.getAttribute('type')
    if (el.getAttribute('rand-seed')) {
      segment.randSeed = parseInt(el.getAttribute('rand-seed'))
    }
    segment.variantString = el.getAttribute('variant-string')
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.width = parseFloat(el.getAttribute('data-width'))
    segment.el = el
    segment.warnings = []
    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
}

function fillDefaultSegments () {
  const segments = []
  let leftHandTraffic = getLeftHandTraffic()

  for (var i in DEFAULT_SEGMENTS[leftHandTraffic]) {
    var segment = DEFAULT_SEGMENTS[leftHandTraffic][i]
    segment.warnings = []
    segment.variantString = getVariantString(segment.variant)

    if (getSegmentInfo(segment.type).needRandSeed) {
      segment.randSeed = generateRandSeed()
    }

    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
  normalizeAllSegmentWidths()
}

export function getStreetUrl (street) {
  var url = '/'
  if (street.creatorId) {
    if (RESERVED_URLS.indexOf(street.creatorId) !== -1) {
      url += URL_RESERVED_PREFIX
    }

    url += street.creatorId
  } else {
    url += URL_NO_USER
  }

  url += '/'

  url += street.namespacedId

  if (street.creatorId) {
    var slug = normalizeSlug(street.name)
    url += '/' + encodeURIComponent(slug)
  }

  return url
}

export function prepareDefaultStreet () {
  store.dispatch(setUnits(getUnits()))
  propagateUnits()

  store.dispatch(saveStreetName(DEFAULT_NAME, false))
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))
  store.dispatch(setBuildingFloorValue('left', DEFAULT_BUILDING_HEIGHT_LEFT))
  store.dispatch(setBuildingFloorValue('right', DEFAULT_BUILDING_HEIGHT_RIGHT))
  store.dispatch(setBuildingVariant('left', DEFAULT_BUILDING_VARIANT_LEFT))
  store.dispatch(setBuildingVariant('right', DEFAULT_BUILDING_VARIANT_RIGHT))
  store.dispatch(updateEditCount(0))

  // console.log('editCount = 0 on default street')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  fillDefaultSegments()
  setUpdateTimeToNow()
}

export function prepareEmptyStreet () {
  store.dispatch(setUnits(getUnits()))
  propagateUnits()

  store.dispatch(saveStreetName(DEFAULT_NAME, false))
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))
  store.dispatch(setBuildingFloorValue('left', DEFAULT_BUILDING_HEIGHT_EMPTY))
  store.dispatch(setBuildingFloorValue('right', DEFAULT_BUILDING_HEIGHT_EMPTY))
  store.dispatch(setBuildingVariant('left', DEFAULT_BUILDING_VARIANT_EMPTY))
  store.dispatch(setBuildingVariant('right', DEFAULT_BUILDING_VARIANT_EMPTY))
  store.dispatch(updateEditCount(0))
  store.dispatch(clearSegments())

  // console.log('editCount = 0 on empty street!')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  setUpdateTimeToNow()
}

/**
 * @todo: documentation
 *
 * @param {Boolean} dontScroll - document this
 * @param {Boolean} save - if set to `false`, calling this function will not
 *          cause a re-save of street to the server. (e.g. in the case of
 *          live update feature.) Default is `true`.
 */
export function updateEverything (dontScroll, save = true) {
  setIgnoreStreetChanges(true)
  propagateUnits()
  // TODO Verify that we don't need to dispatch an update width event here
  createDomFromData()
  segmentsChanged()
  resizeStreetWidth(dontScroll)
  updateStreetName()
  setIgnoreStreetChanges(false)
  _lastStreet = trimStreetData(store.getState().street)

  if (save === true) {
    scheduleSavingStreetToServer()
  }
}
<commit_msg>use setBuilding to update building variants and floor height<commit_after>import { msg } from '../app/messages'
import { URL_NO_USER, RESERVED_URLS, URL_RESERVED_PREFIX } from '../app/routing'
import {
  createBuildings,
  updateBuildingPosition
} from '../segments/buildings'
import { DEFAULT_SEGMENTS } from '../segments/default'
import { getSegmentInfo } from '../segments/info'
import { normalizeAllSegmentWidths } from '../segments/resizing'
import { getVariantString, getVariantArray } from '../segments/variant_utils'
import {
  segmentsChanged,
  repositionSegments,
  createSegmentDom
} from '../segments/view'
import { getSignInData, isSignedIn } from '../users/authentication'
import { getUnits, getLeftHandTraffic, propagateUnits } from '../users/localization'
import { normalizeSlug } from '../util/helpers'
import { generateRandSeed } from '../util/random'
import { updateStreetName } from './name'
import {
  createNewUndoIfNecessary,
  unifyUndoStack
} from './undo_stack'
import {
  DEFAULT_STREET_WIDTH,
  normalizeStreetWidth,
  resizeStreetWidth
} from './width'
import { updateLastStreetInfo, scheduleSavingStreetToServer } from './xhr'
import {
  setBuildingVariant,
  setBuildingFloorValue,
  addLocation,
  updateSchemaVersion,
  clearSegments,
  saveCreatorId,
  updateEditCount,
  updateStreetWidth,
  saveStreetName,
  setUnits,
  updateSegments,
  changeSegmentVariant,
  saveStreetId,
  setUpdateTime,
  saveOriginalStreetId
} from '../store/actions/street'
import { resetUndoStack } from '../store/actions/undo'
import store from '../store'

const DEFAULT_BUILDING_HEIGHT_LEFT = 4
const DEFAULT_BUILDING_HEIGHT_RIGHT = 3
const DEFAULT_BUILDING_VARIANT_LEFT = 'narrow'
const DEFAULT_BUILDING_VARIANT_RIGHT = 'wide'
const DEFAULT_BUILDING_HEIGHT_EMPTY = 1
const DEFAULT_BUILDING_VARIANT_EMPTY = 'grass'

let _lastStreet

export function getLastStreet () {
  return _lastStreet
}

export function setLastStreet (value) {
  _lastStreet = value
}

const LATEST_SCHEMA_VERSION = 18
// 1: starting point
// 2: adding leftBuildingHeight and rightBuildingHeight
// 3: adding leftBuildingVariant and rightBuildingVariant
// 4: adding transit shelter elevation
// 5: adding another lamp type (traditional)
// 6: colored streetcar lanes
// 7: colored bus and light rail lanes
// 8: colored bike lane
// 9: second car type: truck
// 10: sidewalk density
// 11: unify median and planting strip into divider
// 12: getting rid of small tree
// 13: bike rack elevation
// 14: wayfinding has three types
// 15: sidewalks have rand seed
// 16: stop saving undo stack
// 17: alternative colors for bike lanes
// 18: change lat/lng format from array to object

export const DEFAULT_NAME = msg('DEFAULT_STREET_NAME')

function incrementSchemaVersion (street) {
  let segment, variant, schemaVersion

  if (!street.schemaVersion) {
    schemaVersion = 1
  }

  switch (street.schemaVersion) {
    case 1:
      store.dispatch(setBuildingFloorValue('left', DEFAULT_BUILDING_HEIGHT_LEFT))
      store.dispatch(setBuildingFloorValue('right', DEFAULT_BUILDING_HEIGHT_RIGHT))
      break
    case 2:
      store.dispatch(setBuildingVariant('left', DEFAULT_BUILDING_VARIANT_LEFT))
      store.dispatch(setBuildingVariant('right', DEFAULT_BUILDING_VARIANT_RIGHT))
      break
    case 3:
      for (var i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'transit-shelter') {
          store.dispatch(changeSegmentVariant(i, 'transit-shelter-elevation', 'street-level'))
        }
      }
      break
    case 4:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-lamp') {
          store.dispatch(changeSegmentVariant(i, 'lamp-type', 'modern'))
        }
      }
      break
    case 5:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'streetcar') {
          store.dispatch(changeSegmentVariant(i, 'public-transit-asphalt', 'regular'))
        }
      }
      break
    case 6:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bus-lane') {
          store.dispatch(changeSegmentVariant(i, 'bus-asphalt', 'regular'))
        } else if (segment.type === 'light-rail') {
          store.dispatch(changeSegmentVariant(i, 'public-transit-asphalt', 'regular'))
        }
      }
      break
    case 7:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          store.dispatch(changeSegmentVariant(i, 'bike-asphalt', 'regular'))
        }
      }
      break
    case 8:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'drive-lane') {
          store.dispatch(changeSegmentVariant(i, 'car-type', 'car'))
        }
      }
      break
    case 9:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          store.dispatch(changeSegmentVariant(i, 'sidewalk-density', 'normal'))
        }
      }
      break
    case 10:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'planting-strip') {
          segment.type = 'divider'

          if (segment.variantString === '') {
            segment.variantString = 'planting-strip'
          }
        } else if (segment.type === 'small-median') {
          segment.type = 'divider'
          segment.variantString = 'median'
        }
      }
      break
    case 11:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'divider') {
          if (segment.variantString === 'small-tree') {
            segment.variantString = 'big-tree'
          }
        } else if (segment.type === 'sidewalk-tree') {
          if (segment.variantString === 'small') {
            segment.variantString = 'big'
          }
        }
      }
      break
    case 12:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-bike-rack') {
          store.dispatch(changeSegmentVariant(i, 'bike-rack-elevation', 'sidewalk'))
        }
      }
      break
    case 13:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk-wayfinding') {
          store.dispatch(changeSegmentVariant(i, 'wayfinding-type', 'large'))
        }
      }
      break
    case 14:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'sidewalk') {
          segment.randSeed = 35
        }
      }
      break
    case 15:
      store.dispatch(resetUndoStack())
      break
    case 16:
      for (let i in street.segments) {
        segment = street.segments[i]
        if (segment.type === 'bike-lane') {
          variant = getVariantArray(segment.type, segment.variantString)
          if (variant['bike-asphalt'] === 'colored') {
            variant['bike-asphalt'] = 'green'
          }
          segment.variantString = getVariantString(variant)
        }
      }
      break
    case 17:
      if (street.location && street.location.latlng) {
        const location = {
          ...street.location,
          latlng: {
            lat: street.location.latlng[0],
            lng: street.location.latlng[1]
          }
        }
        store.dispatch(addLocation(location))
      }
  }

  schemaVersion++
  store.dispatch(updateSchemaVersion(schemaVersion))
}

export function updateStreetData (street) {
  store.dispatch(saveCreatorId(street.creatorId))
  store.dispatch(updateEditCount(street.editCount))
  store.dispatch(saveStreetId(street.id, street.namespacedId))
  setBuilding('left', street.leftBuildingVariant, street.leftBuildingHeight)
  setBuilding('right', street.rightBuildingVariant, street.rightBuildingHeight)
  store.dispatch(addLocation(street.location))
  store.dispatch(saveStreetName(street.name, street.userUpdated))
  store.dispatch(saveOriginalStreetId(street.originalStreetId))
  store.dispatch(updateSchemaVersion(street.schemaVersion))
  store.dispatch(updateSegments(street.segments))
  store.dispatch(setUnits(street.units))
  store.dispatch(setUpdateTime(street.updatedAt))
  store.dispatch(updateStreetWidth(street.width))
}

function setBuilding (position, variant, height) {
  store.dispatch(setBuildingVariant(position, variant))
  store.dispatch(setBuildingFloorValue(position, height))
}

export function updateToLatestSchemaVersion (street) {
  var updated = false
  while (!street.schemaVersion || (street.schemaVersion < LATEST_SCHEMA_VERSION)) {
    incrementSchemaVersion(street)
    updated = true
  }

  return updated
}

export function createDomFromData () {
  document.querySelector('#street-section-editable').innerHTML = ''
  const street = store.getState().street

  for (var i in street.segments) {
    var segment = street.segments[i]

    // Add some additional data structures
    // TODO: populate data structure elsewhere
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.warnings = []

    var el = createSegmentDom(segment)
    document.querySelector('#street-section-editable').appendChild(el)

    segment.el = el
    segment.el.dataNo = i
  }

  repositionSegments()
  updateBuildingPosition()
  createBuildings()
}

export function setStreetCreatorId (newId) {
  store.dispatch(saveCreatorId(newId))

  unifyUndoStack()
  updateLastStreetInfo()
}

export function setUpdateTimeToNow () {
  store.dispatch(setUpdateTime(new Date().getTime()))
  unifyUndoStack()
}

let ignoreStreetChanges = false

export function setIgnoreStreetChanges (value) {
  ignoreStreetChanges = value
}

export function saveStreetToServerIfNecessary () {
  if (ignoreStreetChanges || store.getState().errors.abortEverything) {
    return
  }

  const street = store.getState().street
  var currentData = trimStreetData(street)

  if (JSON.stringify(currentData) !== JSON.stringify(_lastStreet)) {
    if (street.editCount !== null) {
      street.editCount++
    }
    setUpdateTimeToNow()

    // Some parts of the UI need to know this happened to respond to it
    // TODO: figure out appropriate event name
    window.dispatchEvent(new window.CustomEvent('stmx:save_street'))

    createNewUndoIfNecessary(_lastStreet, currentData)

    scheduleSavingStreetToServer()

    _lastStreet = currentData
  }
}

// Copies only the data necessary for save/undo.
export function trimStreetData (street) {
  var newData = {}

  newData.schemaVersion = street.schemaVersion

  newData.width = street.width
  newData.name = street.name

  newData.id = street.id
  newData.namespacedId = street.namespacedId
  newData.creatorId = street.creatorId
  newData.originalStreetId = street.originalStreetId
  newData.units = street.units

  newData.location = street.location
  newData.userUpdated = street.userUpdated

  if (street.editCount !== null) {
    // console.log('saving editCount', street.editCount)
    newData.editCount = street.editCount
  } else {
    // console.log('not saving editCount')
  }

  newData.leftBuildingHeight = street.leftBuildingHeight
  newData.rightBuildingHeight = street.rightBuildingHeight
  newData.leftBuildingVariant = street.leftBuildingVariant
  newData.rightBuildingVariant = street.rightBuildingVariant

  newData.segments = []

  for (var i in street.segments) {
    var segment = {}
    segment.type = street.segments[i].type
    segment.variantString = street.segments[i].variantString
    segment.width = street.segments[i].width
    if (street.segments[i].randSeed) {
      segment.randSeed = street.segments[i].randSeed
    }

    newData.segments.push(segment)
  }

  return newData
}

// TODO this function should not exist; all the data should be in street.
// object to begin with
export function createDataFromDom () {
  var els = document.querySelectorAll('#street-section-editable > .segment')
  const segments = []

  for (var i = 0, el; el = els[i]; i++) { // eslint-disable-line no-cond-assign
    var segment = {}
    segment.type = el.getAttribute('type')
    if (el.getAttribute('rand-seed')) {
      segment.randSeed = parseInt(el.getAttribute('rand-seed'))
    }
    segment.variantString = el.getAttribute('variant-string')
    segment.variant = getVariantArray(segment.type, segment.variantString)
    segment.width = parseFloat(el.getAttribute('data-width'))
    segment.el = el
    segment.warnings = []
    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
}

function fillDefaultSegments () {
  const segments = []
  let leftHandTraffic = getLeftHandTraffic()

  for (var i in DEFAULT_SEGMENTS[leftHandTraffic]) {
    var segment = DEFAULT_SEGMENTS[leftHandTraffic][i]
    segment.warnings = []
    segment.variantString = getVariantString(segment.variant)

    if (getSegmentInfo(segment.type).needRandSeed) {
      segment.randSeed = generateRandSeed()
    }

    segments.push(segment)
  }
  store.dispatch(updateSegments(segments))
  normalizeAllSegmentWidths()
}

export function getStreetUrl (street) {
  var url = '/'
  if (street.creatorId) {
    if (RESERVED_URLS.indexOf(street.creatorId) !== -1) {
      url += URL_RESERVED_PREFIX
    }

    url += street.creatorId
  } else {
    url += URL_NO_USER
  }

  url += '/'

  url += street.namespacedId

  if (street.creatorId) {
    var slug = normalizeSlug(street.name)
    url += '/' + encodeURIComponent(slug)
  }

  return url
}

export function prepareDefaultStreet () {
  store.dispatch(setUnits(getUnits()))
  propagateUnits()

  store.dispatch(saveStreetName(DEFAULT_NAME, false))
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))
  setBuilding('left', DEFAULT_BUILDING_VARIANT_LEFT, DEFAULT_BUILDING_HEIGHT_LEFT)
  setBuilding('right', DEFAULT_BUILDING_VARIANT_RIGHT, DEFAULT_BUILDING_HEIGHT_RIGHT)
  store.dispatch(updateEditCount(0))

  // console.log('editCount = 0 on default street')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  fillDefaultSegments()
  setUpdateTimeToNow()
}

export function prepareEmptyStreet () {
  store.dispatch(setUnits(getUnits()))
  propagateUnits()

  store.dispatch(saveStreetName(DEFAULT_NAME, false))
  store.dispatch(updateStreetWidth(normalizeStreetWidth(DEFAULT_STREET_WIDTH)))
  setBuilding('left', DEFAULT_BUILDING_VARIANT_EMPTY, DEFAULT_BUILDING_HEIGHT_EMPTY)
  setBuilding('right', DEFAULT_BUILDING_VARIANT_EMPTY, DEFAULT_BUILDING_HEIGHT_EMPTY)
  store.dispatch(updateEditCount(0))
  store.dispatch(clearSegments())

  // console.log('editCount = 0 on empty street!')
  if (isSignedIn()) {
    setStreetCreatorId(getSignInData().userId)
  }
  setUpdateTimeToNow()
}

/**
 * @todo: documentation
 *
 * @param {Boolean} dontScroll - document this
 * @param {Boolean} save - if set to `false`, calling this function will not
 *          cause a re-save of street to the server. (e.g. in the case of
 *          live update feature.) Default is `true`.
 */
export function updateEverything (dontScroll, save = true) {
  setIgnoreStreetChanges(true)
  propagateUnits()
  // TODO Verify that we don't need to dispatch an update width event here
  createDomFromData()
  segmentsChanged()
  resizeStreetWidth(dontScroll)
  updateStreetName()
  setIgnoreStreetChanges(false)
  _lastStreet = trimStreetData(store.getState().street)

  if (save === true) {
    scheduleSavingStreetToServer()
  }
}
<|endoftext|>


<commit_before>import { mainLizmap, mainEventDispatcher } from '../modules/Globals.js';
import { html, render } from 'lit-html';

export default class Geolocation extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        // Display
        // Render positionTemplate and accuracyTemplate apart because their values might change a lot
        const positionTemplate = () => html`
            X : ${mainLizmap.geolocation.position ? mainLizmap.geolocation.position[0].toString() : ''}

            Y : ${mainLizmap.geolocation.position ? mainLizmap.geolocation.position[1].toString() : ''}`;

        const accuracyTemplate = () => html`
            Accuracy (m) : ${mainLizmap.geolocation.accuracy}`;

        const mainTemplate = () => html`

                <button class="btn btn-small btn-primary" @click=${ () => mainLizmap.geolocation.toggleTracking()}>${mainLizmap.geolocation.isTracking ? 'Stop' : 'Start'}</button>
                <button class="btn btn-small btn-primary" @click=${ () => mainLizmap.geolocation.center()} ?disabled=${!mainLizmap.geolocation.isTracking | mainLizmap.geolocation.isBind}>Center</button>
                <button class="btn btn-small btn-primary ${mainLizmap.geolocation.isBind ? 'active' : ''}" @click=${ () => mainLizmap.geolocation.toggleBind()} ?disabled=${!mainLizmap.geolocation.isTracking}>Stay centered</button>

${positionTemplate()}

${accuracyTemplate()}

`;

        render(mainTemplate(), this);

        mainEventDispatcher.addListener(
            () => {
                render(mainTemplate(), this);
            },
            'geolocation.isTracking'
        );

        mainEventDispatcher.addListener(
            () => {
                render(mainTemplate(), this);
            },
            'geolocation.isBind'
        );

        mainEventDispatcher.addListener(
            () => {
                render(positionTemplate(), this.querySelector('.geolocation-coords'));
            },
            'geolocation.position'
        );

        mainEventDispatcher.addListener(
            () => {
                render(accuracyTemplate(), this.querySelector('.geolocation-accuracy'));
            },
            'geolocation.accuracy'
        );
    }

    disconnectedCallback() {
    }
}
<commit_msg>Add HTML element to avoid error : Uncaught TypeError: Cannot read property 'nodeType' of null<commit_after>import { mainLizmap, mainEventDispatcher } from '../modules/Globals.js';
import { html, render } from 'lit-html';

export default class Geolocation extends HTMLElement {
    constructor() {
        super();
    }

    connectedCallback() {
        // Display
        // Render positionTemplate and accuracyTemplate apart because their values might change a lot
        const positionTemplate = () => html`

X : ${mainLizmap.geolocation.position ? mainLizmap.geolocation.position[0].toString() : ''}

Y : ${mainLizmap.geolocation.position ? mainLizmap.geolocation.position[1].toString() : ''}
`;

        const accuracyTemplate = () => html`

Accuracy (m) : ${mainLizmap.geolocation.accuracy}
`;

        const mainTemplate = () => html`

                <button class="btn btn-small btn-primary" @click=${ () => mainLizmap.geolocation.toggleTracking()}>${mainLizmap.geolocation.isTracking ? 'Stop' : 'Start'}</button>
                <button class="btn btn-small btn-primary" @click=${ () => mainLizmap.geolocation.center()} ?disabled=${!mainLizmap.geolocation.isTracking | mainLizmap.geolocation.isBind}>Center</button>
                <button class="btn btn-small btn-primary ${mainLizmap.geolocation.isBind ? 'active' : ''}" @click=${ () => mainLizmap.geolocation.toggleBind()} ?disabled=${!mainLizmap.geolocation.isTracking}>Stay centered</button>

${positionTemplate()}

${accuracyTemplate()}

`;

        render(mainTemplate(), this);

        mainEventDispatcher.addListener(
            () => {
                render(mainTemplate(), this);
            },
            'geolocation.isTracking'
        );

        mainEventDispatcher.addListener(
            () => {
                render(mainTemplate(), this);
            },
            'geolocation.isBind'
        );

        mainEventDispatcher.addListener(
            () => {
                render(positionTemplate(), this.querySelector('.geolocation-coords'));
            },
            'geolocation.position'
        );

        mainEventDispatcher.addListener(
            () => {
                render(accuracyTemplate(), this.querySelector('.geolocation-accuracy'));
            },
            'geolocation.accuracy'
        );
    }

    disconnectedCallback() {
    }
}
<|endoftext|>