<commit_before>Template.editUniverse.helpers({
	workshops() {
		return Workshops.find({
			'universesLinked.universeId': {
				$ne: this._id
			}
		});
	}
});

Template.editUniverse.events({
	'click #save': function(event) {
		event.preventDefault();
		var universeData = {
			universeId: Router.current().params._id,
			name: $('#universeName').val(),
			label: $('#universeLabel').val()
		};
		if (!universeData.name) {
			return throwError('Name must be filled');
		}
		if (!universeData.label) {
			return throwError('Label must be filled');
		}
		if (universeData.name && universeData.label) {
			Meteor.call('updateAUniverse', universeData, (error, result) => {
				if (error) {
					return throwError(error.message);
				} else {
					return throwError('Update succesful !');
				}
			});
		}
	}
});
<commit_msg>Change var into const<commit_after>Template.editUniverse.helpers({
	workshops() {
		return Workshops.find({
			'universesLinked.universeId': {
				$ne: this._id
			}
		});
	}
});

Template.editUniverse.events({
	'click #save': function(event) {
		event.preventDefault();
		const universeData = {
			universeId: Router.current().params._id,
			name: $('#universeName').val(),
			label: $('#universeLabel').val()
		};
		if (!universeData.name) {
			return throwError('Name must be filled');
		}
		if (!universeData.label) {
			return throwError('Label must be filled');
		}
		if (universeData.name && universeData.label) {
			Meteor.call('updateAUniverse', universeData, (error, result) => {
				if (error) {
					return throwError(error.message);
				} else {
					return throwError('Update succesful !');
				}
			});
		}
	}
});
<|endoftext|>


<commit_before>{
    "genres": [

               {
               "url": "https://image.tmdb.org/t/p/w500/atlgUPxj5PLdEUwtSm9O0Hd2Scr.jpg",
               "title":"The Most Anticipated Movies Of 2018",
               "id":"14762"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/50xTVRANi9p423luloU4SA6QgbM.jpg",
               "title":"The Best Of 2017",
               "id":"56d0942b9251413e680069fe"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/rqAHkvXldb9tHlnbQDwOzRi0yVD.jpg",
               "title":"The Marvel Universe Collection",
               "id":"50941077760ee35e1500000c"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/uyzUPJ2krcOOZERfV391lmtVQsu.jpg",
               "title":"The DC Universe Collection",
               "id":"5094147819c2955e4c00006a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/tZjVVIYXACV4IIIhXeIM59ytqwS.jpg",
               "title":"The Entire Star Wars Collection",
               "id":"56c09c7b925141398a003932"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/br4TdosVhbpFKEJrZ84OYI42JkN.jpg",
               "title":"Summer Time Is Here",
               "id":"57496ed092514134780015d4"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ld8CzJneOCVSCq4dm6zRmQzlIZl.jpg",
               "title":"Just Survival",
               "id":"56f99a869251412fdd006c0a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/g7y79gXw88PRl5MXD9BiJaWPA9Z.jpg",
               "title":"For A Romantic Night",
               "id":"56aa016ac3a36872e90053c7"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/9pdIEOHiAcOAHsVW0Ia7v4tOrot.jpg",
               "title":"Good Old Westerns",
               "id":"56aa0180c3a36872d800567c"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ohKncfyZi0NLR1oyyz7ETRnxxWd.jpg",
               "title":"The James Bond - Movie Collection",
               "id":"5308b87fc3a36842010027be"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/xDEOxA01480uLTWuvQCw61VmDBt.jpg",
               "title":"The Gangster Classics",
               "id":"56d0bf859251415ad90004bc"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/iqE52K9m00rq3wutzfvTcUlstaL.jpg",
               "title":"End Of The World Films",
               "id":"56f9aa5e9251414de600016e"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ymzaJLm2EIYbG9n7snVfCnHAPQk.jpg",
               "title":"Saddest Films Of All Time",
               "id":"56f9a5dc9251414deb0000fa"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/1LrtAhWPSEetJLjblXvnaYtl7eA.jpg",
               "title":"Experience The Truth Of World War II ",
               "id":"56d0b844925141799e0040c3"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/mZcqznpPHNPHONEbiT6IGH6j4d.jpg",
               "title":"Great Movies Instructed By Steven Spielberg",
               "id":"56d5f8e592514112cd000543"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/AoYGqtWxcNmQjQIpRCMtzpFfL1T.jpg",
               "title":"The 10 Funniest Movies Of 2015",
               "id":"56d05cf39251413e5f005ce2"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/cDctk61tUeQz4LX7tTFPknI28ea.jpg",
               "title":"15 Movies Your Have To See",
               "id":"56d0bafd9251413e59006b20"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/lXOqBOcx1t5A3YhJEIfJZOkigwH.jpg",
               "title":"The 10 Most Anticipated Titles Of 2016",
               "id":"56d047339251413e6a005fed"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/dYtAyg4vD88hIfrR1VKDnVGhnE6.jpg",
               "title":"The Very Best Of Leonardo DiCaprio",
               "id":"56d05e3e9251416e3a003383"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/jt2Y00h7cOWVAZLCGR444D1B6QV.jpg",
               "title":"The 88th Academy Awards (Oscars 2016)",
               "id":"569bc01dc3a36858c6000030"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/sSvgNBeBNzAuKl8U8sP50ETJPgx.jpg",
               "title":"The Top 25 Movies Of 2015",
               "id":"56d091769251413e6d0069d4"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/aY0kmLSNKFbHZyvmHqiuegVlDFW.jpg",
               "title":"The Entire DreamWorks Animation Collection",
               "id":"56d5fcf092514139320041ea"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/f2WtyrZeupq7hQ4SX0SXuMUbjOa.jpg",
               "title":"Movies Starring Amanda Seyfried",
               "id":"56d314eac3a3681e3c010f42"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/hQIPa1oKM6fSzyp1UBxBfVpSCj2.jpg",
               "title":"Late Valentine With Your Only One",
               "id":"56d2235e9251413e5f00afa1"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/s0ICtHdrTWYaBRJSbOgKEYrm3F4.jpg",
               "title":"For A Quiet Family Night",
               "id":"56deec15c3a36803bf002fd0"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/kFslzsvxOQgjbuDfta3kzbOWQY5.jpg",
               "title":"The Alien Story",
               "id":"56dee364c3a36803ce0031c9"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/oH39T1bwjztw6qh89pvVAJBsUUq.jpg",
               "title":"The Disney Classics",
               "id":"51224e42760ee3297424a1e0"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/lAdRfK0Clun517bq5V0meoIx7SG.jpg",
               "title":"Horror For A Spooky Night",
               "id":"54e4781b925141195300adba"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/sEgULSEnywgdSesVHFHpPAbOijl.jpg",
               "title":"The Finest Work Of Matt Damon",
               "id":"56d05f33c3a3681e3e006ae8"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/l7zANdjgTvYqwZUx76Vk0EKpCH5.jpg",
               "title":"The Pirates Of The Caribbean Collection",
               "id":"56d03aac9251416e3a002e5a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/xu9zaAevzQ5nnrsXN6JcahLnG4i.jpg",
               "title":"The Best Of Matthew McConaughey",
               "id":"56d0614a9251413e6a006438"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/6kVVfNT0auG6fU5SFQ1zbayNWUC.jpg",
               "title":"The Greatest From Paramount Pictures",
               "id":"56def1d8925141579d001f89"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/cHy7nSitAVgvZ7qfCK4JO47t3oZ.jpg",
               "title":"The Entire Transformer Collection",
               "id":"56def7349251412eee0000ca"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/kUYha9j3oFgRdSQlROfj61ISjeO.jpg",
               "title":"Good Science Fiction Flicks",
               "id":"54408e79929fb858d1000052"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/7MGIEdu0Mokqt8i7snflYRLbooI.jpg",
               "title":"Best Picture Winners - The Academy Awards",
               "id":"509ec17b19c2950a0600050d"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/iNGNoZbDKzawUulvL6x0drHOZNI.jpg",
               "title":"The Entire Lord Of The Rings Collection",
               "id":"56d046029251416e3a002fc8"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/bIlYH4l2AyYvEysmS2AOfjO7Dn8.jpg",
               "title":"The Terminator Collection",
               "id":"54906e07c3a3686d6e002470"
               }

               ]

}<commit_msg>updated lists<commit_after>{
    "genres": [

               {
               "url": "https://image.tmdb.org/t/p/w500/atlgUPxj5PLdEUwtSm9O0Hd2Scr.jpg",
               "title":"The Most Anticipated Movies Of 2018",
               "id":"14762"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/mwZk1PKhemcjak9rViVwrn3HLCR.jpg",
               "title":"The Best Of 2017",
               "id":"56d0942b9251413e680069fe"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/rqAHkvXldb9tHlnbQDwOzRi0yVD.jpg",
               "title":"The Marvel Universe Collection",
               "id":"50941077760ee35e1500000c"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/uyzUPJ2krcOOZERfV391lmtVQsu.jpg",
               "title":"The DC Universe Collection",
               "id":"5094147819c2955e4c00006a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/tZjVVIYXACV4IIIhXeIM59ytqwS.jpg",
               "title":"The Entire Star Wars Collection",
               "id":"56c09c7b925141398a003932"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/br4TdosVhbpFKEJrZ84OYI42JkN.jpg",
               "title":"Summer Time Is Here",
               "id":"57496ed092514134780015d4"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ld8CzJneOCVSCq4dm6zRmQzlIZl.jpg",
               "title":"Just Survival",
               "id":"56f99a869251412fdd006c0a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/g7y79gXw88PRl5MXD9BiJaWPA9Z.jpg",
               "title":"For A Romantic Night",
               "id":"56aa016ac3a36872e90053c7"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/9pdIEOHiAcOAHsVW0Ia7v4tOrot.jpg",
               "title":"Good Old Westerns",
               "id":"56aa0180c3a36872d800567c"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ohKncfyZi0NLR1oyyz7ETRnxxWd.jpg",
               "title":"The James Bond - Movie Collection",
               "id":"5308b87fc3a36842010027be"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/xDEOxA01480uLTWuvQCw61VmDBt.jpg",
               "title":"The Gangster Classics",
               "id":"56d0bf859251415ad90004bc"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/iqE52K9m00rq3wutzfvTcUlstaL.jpg",
               "title":"End Of The World Films",
               "id":"56f9aa5e9251414de600016e"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/ymzaJLm2EIYbG9n7snVfCnHAPQk.jpg",
               "title":"Saddest Films Of All Time",
               "id":"56f9a5dc9251414deb0000fa"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/1LrtAhWPSEetJLjblXvnaYtl7eA.jpg",
               "title":"Experience The Truth Of World War II ",
               "id":"56d0b844925141799e0040c3"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/mZcqznpPHNPHONEbiT6IGH6j4d.jpg",
               "title":"Great Movies Instructed By Steven Spielberg",
               "id":"56d5f8e592514112cd000543"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/AoYGqtWxcNmQjQIpRCMtzpFfL1T.jpg",
               "title":"The 10 Funniest Movies Of 2015",
               "id":"56d05cf39251413e5f005ce2"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/cDctk61tUeQz4LX7tTFPknI28ea.jpg",
               "title":"15 Movies Your Have To See",
               "id":"56d0bafd9251413e59006b20"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/lXOqBOcx1t5A3YhJEIfJZOkigwH.jpg",
               "title":"The 10 Most Anticipated Titles Of 2016",
               "id":"56d047339251413e6a005fed"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/dYtAyg4vD88hIfrR1VKDnVGhnE6.jpg",
               "title":"The Very Best Of Leonardo DiCaprio",
               "id":"56d05e3e9251416e3a003383"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/jt2Y00h7cOWVAZLCGR444D1B6QV.jpg",
               "title":"The 88th Academy Awards (Oscars 2016)",
               "id":"569bc01dc3a36858c6000030"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/sSvgNBeBNzAuKl8U8sP50ETJPgx.jpg",
               "title":"The Top 25 Movies Of 2015",
               "id":"56d091769251413e6d0069d4"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/aY0kmLSNKFbHZyvmHqiuegVlDFW.jpg",
               "title":"The Entire DreamWorks Animation Collection",
               "id":"56d5fcf092514139320041ea"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/f2WtyrZeupq7hQ4SX0SXuMUbjOa.jpg",
               "title":"Movies Starring Amanda Seyfried",
               "id":"56d314eac3a3681e3c010f42"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/hQIPa1oKM6fSzyp1UBxBfVpSCj2.jpg",
               "title":"Late Valentine With Your Only One",
               "id":"56d2235e9251413e5f00afa1"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/s0ICtHdrTWYaBRJSbOgKEYrm3F4.jpg",
               "title":"For A Quiet Family Night",
               "id":"56deec15c3a36803bf002fd0"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/kFslzsvxOQgjbuDfta3kzbOWQY5.jpg",
               "title":"The Alien Story",
               "id":"56dee364c3a36803ce0031c9"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/oH39T1bwjztw6qh89pvVAJBsUUq.jpg",
               "title":"The Disney Classics",
               "id":"51224e42760ee3297424a1e0"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/lAdRfK0Clun517bq5V0meoIx7SG.jpg",
               "title":"Horror For A Spooky Night",
               "id":"54e4781b925141195300adba"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/sEgULSEnywgdSesVHFHpPAbOijl.jpg",
               "title":"The Finest Work Of Matt Damon",
               "id":"56d05f33c3a3681e3e006ae8"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/l7zANdjgTvYqwZUx76Vk0EKpCH5.jpg",
               "title":"The Pirates Of The Caribbean Collection",
               "id":"56d03aac9251416e3a002e5a"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/xu9zaAevzQ5nnrsXN6JcahLnG4i.jpg",
               "title":"The Best Of Matthew McConaughey",
               "id":"56d0614a9251413e6a006438"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/6kVVfNT0auG6fU5SFQ1zbayNWUC.jpg",
               "title":"The Greatest From Paramount Pictures",
               "id":"56def1d8925141579d001f89"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/cHy7nSitAVgvZ7qfCK4JO47t3oZ.jpg",
               "title":"The Entire Transformer Collection",
               "id":"56def7349251412eee0000ca"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/kUYha9j3oFgRdSQlROfj61ISjeO.jpg",
               "title":"Good Science Fiction Flicks",
               "id":"54408e79929fb858d1000052"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/7MGIEdu0Mokqt8i7snflYRLbooI.jpg",
               "title":"Best Picture Winners - The Academy Awards",
               "id":"509ec17b19c2950a0600050d"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/iNGNoZbDKzawUulvL6x0drHOZNI.jpg",
               "title":"The Entire Lord Of The Rings Collection",
               "id":"56d046029251416e3a002fc8"
               },

               {
               "url": "https://image.tmdb.org/t/p/w500/bIlYH4l2AyYvEysmS2AOfjO7Dn8.jpg",
               "title":"The Terminator Collection",
               "id":"54906e07c3a3686d6e002470"
               }

               ]

}<|endoftext|>


<commit_before>"use strict";
const createClient = require('hafas-client');
const bvgProfile = require('hafas-client/p/bvg');
const vbbClient = createClient(bvgProfile, 'MagicMirror module MMM-PublicTransportBerlin v1.2.5 (https://github.com/deg0nz/MMM-PublicTransportBerlin)');

let VbbFetcher = function (config) {
  this.config = config;
};

VbbFetcher.prototype.getStationId = function () {
  return this.config.stationId;
};

VbbFetcher.prototype.getStationName = function () {
  return vbbClient.station(this.config.stationId).then((response) => {
    return response.name;
  });
};

VbbFetcher.prototype.fetchDepartures = function () {
  // when value for a request is calculated to be 5 minutes before travelTimeToStation time
  // so we can also show the non-reachable departures in the module
  let when;

  if (this.config.travelTimeToStation > 0) {
    when = new Date();
    when.setTime((Date.now() + this.config.travelTimeToStation * 60000) - (5 * 60000));
  } else {
    when = Date.now();
  }

  let opt;

  // Handle single direction case
  if(!this.config.directionStationId || this.config.directionStationId === "") {
    opt = {
      when: when,
      duration: this.config.departureMinutes
      };
  } else {
    opt = {
      direction: this.config.directionStationId,
      when: when,
      duration: this.config.departureMinutes
    };
  }

  return vbbClient.departures(this.config.stationId, opt)
    .then((response) => {
        return this.processData(response);
    }).catch((e) => {
        throw e;
    });
};

VbbFetcher.prototype.processData = function (data) {
    let departuresData = {
      stationId: this.config.stationId,
      departuresArray: []
    };

    data.forEach((row) => {
        // check for:
        // ignored stations
        // excluded transportation types
        // ignored lines

      // TODO: Make real stop/station handling here
      // Quick fix to work around missing station objects
      if(!row.station) {
          row.station = row.stop;
      }

      if (!this.config.ignoredStations.includes(row.station.id)
        && !this.config.excludedTransportationTypes.includes(row.line.product)
          && !this.config.ignoredLines.includes(row.line.name)
      ) {
        let current = {
            when: row.when || row.formerScheduledWhen,
            delay: row.delay || 0,
            cancelled: row.cancelled || false,
            name: row.line.name,
            nr: row.line.nr,
            type: row.line.product,
            direction: row.direction
        };

        departuresData.departuresArray.push(current);
      }
    });

    departuresData.departuresArray.sort(compareTimes);
    return departuresData;
};

function compareTimes(a, b) {
    if (a < b) {
        return -1;
    }
    if (a > b) {
        return 1
    }
    return 0
}

// helper function to print departure for debugging
function printDeparture(row) {
  let delayMinutes = Math.floor((((row.delay % 31536000) % 86400) % 3600) / 60);

  let time = row.when.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});

  console.log(time + " " + delayMinutes + " " + row.line.product + " " + row.direction + " | stationId: " + row.station.id);
}

module.exports = VbbFetcher;
<commit_msg>Fixed wrong sorting of departures<commit_after>"use strict";
const createClient = require('hafas-client');
const bvgProfile = require('hafas-client/p/bvg');
const vbbClient = createClient(bvgProfile, 'MagicMirror module MMM-PublicTransportBerlin v1.2.5 (https://github.com/deg0nz/MMM-PublicTransportBerlin)');

let VbbFetcher = function (config) {
  this.config = config;
};

VbbFetcher.prototype.getStationId = function () {
  return this.config.stationId;
};

VbbFetcher.prototype.getStationName = function () {
  return vbbClient.station(this.config.stationId).then((response) => {
    return response.name;
  });
};

VbbFetcher.prototype.fetchDepartures = function () {
  // when value for a request is calculated to be 5 minutes before travelTimeToStation time
  // so we can also show the non-reachable departures in the module
  let when;

  if (this.config.travelTimeToStation > 0) {
    when = new Date();
    when.setTime((Date.now() + this.config.travelTimeToStation * 60000) - (5 * 60000));
  } else {
    when = Date.now();
  }

  let opt;

  // Handle single direction case
  if(!this.config.directionStationId || this.config.directionStationId === "") {
    opt = {
      when: when,
      duration: this.config.departureMinutes
      };
  } else {
    opt = {
      direction: this.config.directionStationId,
      when: when,
      duration: this.config.departureMinutes
    };
  }

  return vbbClient.departures(this.config.stationId, opt)
    .then((response) => {
        return this.processData(response);
    }).catch((e) => {
        throw e;
    });
};

VbbFetcher.prototype.processData = function (data) {
    let departuresData = {
      stationId: this.config.stationId,
      departuresArray: []
    };

    data.forEach((row) => {
        // check for:
        // ignored stations
        // excluded transportation types
        // ignored lines

      // TODO: Make real stop/station handling here
      // Quick fix to work around missing station objects
      if(!row.station) {
          row.station = row.stop;
      }

      if (!this.config.ignoredStations.includes(row.station.id)
        && !this.config.excludedTransportationTypes.includes(row.line.product)
          && !this.config.ignoredLines.includes(row.line.name)
      ) {
        let current = {
            when: row.when || row.formerScheduledWhen,
            delay: row.delay || 0,
            cancelled: row.cancelled || false,
            name: row.line.name,
            nr: row.line.nr,
            type: row.line.product,
            direction: row.direction
        };

        departuresData.departuresArray.push(current);
      }
    });

    departuresData.departuresArray.sort(compareTimes);
    return departuresData;
};

function compareTimes(a, b) {
  if (a.when < b.when) {
    return -1;
  }
  if (a.when > b.when) {
    return 1
  }
  return 0
}

// helper function to print departure for debugging
function printDeparture(row) {
  let delayMinutes = Math.floor((((row.delay % 31536000) % 86400) % 3600) / 60);

  let time = row.when.toLocaleTimeString([], {hour: "2-digit", minute: "2-digit"});

  console.log(time + " " + delayMinutes + " " + row.line.product + " " + row.direction + " | stationId: " + row.station.id);
}

module.exports = VbbFetcher;
<|endoftext|>


<commit_before>import Cropper from 'cropperjs'

const previewPropType = typeof window === 'undefined'
  ? [String, Array]
  : [String, Array, Element, NodeList]

export default {
  render(h) {
    return h('div', { style: this.containerStyle }, [
      h('img', {
        ref: 'img',
        attrs: {
          src: this.src,
          alt: this.alt || 'image',
          style: 'max-width: 100%'
        },
        style: this.imgStyle
      })
    ])
  },
  props: {
    // Library props
    containerStyle: Object,
    src: {
      type: String,
      default: ''
    },
    alt: String,
    imgStyle: Object,

    // CropperJS props
    viewMode: Number,
    dragMode: String,
    aspectRatio: Number,
    data: Object,
    preview: previewPropType,
    responsive: {
      type: Boolean,
      default: true
    },
    restore: {
      type: Boolean,
      default: true
    },
    checkCrossOrigin: {
      type: Boolean,
      default: true
    },
    checkOrientation: {
      type: Boolean,
      default: true
    },
    modal: {
      type: Boolean,
      default: true
    },
    guides: {
      type: Boolean,
      default: true
    },
    center: {
      type: Boolean,
      default: true
    },
    highlight: {
      type: Boolean,
      default: true
    },
    background: {
      type: Boolean,
      default: true
    },
    autoCrop: {
      type: Boolean,
      default: true
    },
    autoCropArea: Number,
    movable: {
      type: Boolean,
      default: true
    },
    rotatable: {
      type: Boolean,
      default: true
    },
    scalable: {
      type: Boolean,
      default: true
    },
    zoomable: {
      type: Boolean,
      default: true
    },
    zoomOnTouch: {
      type: Boolean,
      default: true
    },
    zoomOnWheel: {
      type: Boolean,
      default: true
    },
    wheelZoomRatio: Number,
    cropBoxMovable: {
      type: Boolean,
      default: true
    },
    cropBoxResizable: {
      type: Boolean,
      default: true
    },
    toggleDragModeOnDblclick: {
      type: Boolean,
      default: true
    },

    // Size limitation
    minCanvasWidth: Number,
    minCanvasHeight: Number,
    minCropBoxWidth: Number,
    minCropBoxHeight: Number,
    minContainerWidth: Number,
    minContainerHeight: Number,

    // callbacks
    ready: Function,
    cropstart: Function,
    cropmove: Function,
    cropend: Function,
    crop: Function,
    zoom: Function
  },
  mounted() {
    const { containerStyle, src, alt, imgStyle, ...data } = this.$options.props
    const props = {}

    for (const key in data) {
      if (this[key] !== undefined) {
        props[key] = this[key]
      }
    }

    this.cropper = new Cropper(this.$refs.img, props)
  },
  methods: {
    // Reset the image and crop box to their initial states
    reset() {
      return this.cropper.reset()
    },

    // Clear the crop box
    clear() {
      return this.cropper.clear()
    },

    // Init crop box manually
    initCrop() {
      return this.cropper.crop()
    },

    /**
     * Replace the image's src and rebuild the cropper
     * @param {string} url - The new URL.
     * @param {boolean} [onlyColorChanged] - Indicate if the new image only changed color.
     * @returns {Object} this
     */
    replace(url, onlyColorChanged = false) {
      return this.cropper.replace(url, onlyColorChanged)
    },

    // Enable (unfreeze) the cropper
    enable() {
      return this.cropper.enable()
    },

    // Disable (freeze) the cropper
    disable() {
      return this.cropper.disable()
    },

    // Destroy the cropper and remove the instance from the image
    destroy() {
      return this.cropper.destroy()
    },

    /**
     * Move the canvas with relative offsets
     * @param {number} offsetX - The relative offset distance on the x-axis.
     * @param {number} offsetY - The relative offset distance on the y-axis.
     * @returns {Object} this
     */
    move(offsetX, offsetY) {
      return this.cropper.move(offsetX, offsetY)
    },

    /**
     * Move the canvas to an absolute point
     * @param {number} x - The x-axis coordinate.
     * @param {number} [y=x] - The y-axis coordinate.
     * @returns {Object} this
     */
    moveTo(x, y = x) {
      return this.cropper.moveTo(x, y)
    },

    /**
     * Zoom the canvas with a relative ratio
     * @param {number} ratio - The target ratio.
     * @param {Event} _originalEvent - The original event if any.
     * @returns {Object} this
     */
    relativeZoom(ratio, _originalEvent) {
      return this.cropper.zoom(ratio, _originalEvent)
    },

    /**
     * Zoom the canvas to an absolute ratio
     * @param {number} ratio - The target ratio.
     * @param {Event} _originalEvent - The original event if any.
     * @returns {Object} this
     */
    zoomTo(ratio, _originalEvent) {
      return this.cropper.zoomTo(ratio, _originalEvent)
    },

    /**
     * Rotate the canvas with a relative degree
     * @param {number} degree - The rotate degree.
     * @returns {Object} this
     */
    rotate(degree) {
      return this.cropper.rotate(degree)
    },

    /**
     * Rotate the canvas to an absolute degree
     * @param {number} degree - The rotate degree.
     * @returns {Object} this
     */
    rotateTo(degree) {
      return this.cropper.rotateTo(degree)
    },

    /**
     * Scale the image on the x-axis.
     * @param {number} scaleX - The scale ratio on the x-axis.
     * @returns {Object} this
     */
    scaleX(scaleX) {
      return this.cropper.scaleX(scaleX)
    },

    /**
     * Scale the image on the y-axis.
     * @param {number} scaleY - The scale ratio on the y-axis.
     * @returns {Object} this
     */
    scaleY(scaleY) {
      return this.cropper.scaleY(scaleY)
    },

    /**
     * Scale the image
     * @param {number} scaleX - The scale ratio on the x-axis.
     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
     * @returns {Object} this
     */
    scale(scaleX, scaleY = scaleX) {
      return this.cropper.scale(scaleX, scaleY)
    },

    /**
     * Get the cropped area position and size data (base on the original image)
     * @param {boolean} [rounded=false] - Indicate if round the data values or not.
     * @returns {Object} The result cropped data.
     */
    getData(rounded = false) {
      return this.cropper.getData(rounded)
    },

    /**
     * Set the cropped area position and size with new data
     * @param {Object} data - The new data.
     * @returns {Object} this
     */
    setData(data) {
      return this.cropper.setData(data)
    },

    /**
     * Get the container size data.
     * @returns {Object} The result container data.
     */
    getContainerData() {
      return this.cropper.getContainerData()
    },

    /**
     * Get the image position and size data.
     * @returns {Object} The result image data.
     */
    getImageData() {
      return this.cropper.getImageData()
    },

    /**
     * Get the canvas position and size data.
     * @returns {Object} The result canvas data.
     */
    getCanvasData() {
      return this.cropper.getCanvasData()
    },

    /**
     * Set the canvas position and size with new data.
     * @param {Object} data - The new canvas data.
     * @returns {Object} this
     */
    setCanvasData(data) {
      return this.cropper.setCanvasData(data)
    },

    /**
     * Get the crop box position and size data.
     * @returns {Object} The result crop box data.
     */
    getCropBoxData() {
      return this.cropper.getCropBoxData()
    },

    /**
     * Set the crop box position and size with new data.
     * @param {Object} data - The new crop box data.
     * @returns {Object} this
     */
    setCropBoxData(data) {
      return this.cropper.setCropBoxData(data)
    },

    /**
     * Get a canvas drawn the cropped image.
     * @param {Object} [options={}] - The config options.
     * @returns {HTMLCanvasElement} - The result canvas.
     */
    getCroppedCanvas(options = {}) {
      return this.cropper.getCroppedCanvas(options)
    },

    /**
     * Change the aspect ratio of the crop box.
     * @param {number} aspectRatio - The new aspect ratio.
     * @returns {Object} this
     */
    setAspectRatio(aspectRatio) {
      return this.cropper.setAspectRatio(aspectRatio)
    },

    /**
     * Change the drag mode.
     * @param {string} mode - The new drag mode.
     * @returns {Object} this
     */
    setDragMode(mode) {
      return this.cropper.setDragMode(mode)
    }
  }
}
<commit_msg>Added initialAspectRatio prop to VueCropper.js (#52)<commit_after>import Cropper from 'cropperjs'

const previewPropType = typeof window === 'undefined'
  ? [String, Array]
  : [String, Array, Element, NodeList]

export default {
  render(h) {
    return h('div', { style: this.containerStyle }, [
      h('img', {
        ref: 'img',
        attrs: {
          src: this.src,
          alt: this.alt || 'image',
          style: 'max-width: 100%'
        },
        style: this.imgStyle
      })
    ])
  },
  props: {
    // Library props
    containerStyle: Object,
    src: {
      type: String,
      default: ''
    },
    alt: String,
    imgStyle: Object,

    // CropperJS props
    viewMode: Number,
    dragMode: String,
    initialAspectRatio: Number,
    aspectRatio: Number,
    data: Object,
    preview: previewPropType,
    responsive: {
      type: Boolean,
      default: true
    },
    restore: {
      type: Boolean,
      default: true
    },
    checkCrossOrigin: {
      type: Boolean,
      default: true
    },
    checkOrientation: {
      type: Boolean,
      default: true
    },
    modal: {
      type: Boolean,
      default: true
    },
    guides: {
      type: Boolean,
      default: true
    },
    center: {
      type: Boolean,
      default: true
    },
    highlight: {
      type: Boolean,
      default: true
    },
    background: {
      type: Boolean,
      default: true
    },
    autoCrop: {
      type: Boolean,
      default: true
    },
    autoCropArea: Number,
    movable: {
      type: Boolean,
      default: true
    },
    rotatable: {
      type: Boolean,
      default: true
    },
    scalable: {
      type: Boolean,
      default: true
    },
    zoomable: {
      type: Boolean,
      default: true
    },
    zoomOnTouch: {
      type: Boolean,
      default: true
    },
    zoomOnWheel: {
      type: Boolean,
      default: true
    },
    wheelZoomRatio: Number,
    cropBoxMovable: {
      type: Boolean,
      default: true
    },
    cropBoxResizable: {
      type: Boolean,
      default: true
    },
    toggleDragModeOnDblclick: {
      type: Boolean,
      default: true
    },

    // Size limitation
    minCanvasWidth: Number,
    minCanvasHeight: Number,
    minCropBoxWidth: Number,
    minCropBoxHeight: Number,
    minContainerWidth: Number,
    minContainerHeight: Number,

    // callbacks
    ready: Function,
    cropstart: Function,
    cropmove: Function,
    cropend: Function,
    crop: Function,
    zoom: Function
  },
  mounted() {
    const { containerStyle, src, alt, imgStyle, ...data } = this.$options.props
    const props = {}

    for (const key in data) {
      if (this[key] !== undefined) {
        props[key] = this[key]
      }
    }

    this.cropper = new Cropper(this.$refs.img, props)
  },
  methods: {
    // Reset the image and crop box to their initial states
    reset() {
      return this.cropper.reset()
    },

    // Clear the crop box
    clear() {
      return this.cropper.clear()
    },

    // Init crop box manually
    initCrop() {
      return this.cropper.crop()
    },

    /**
     * Replace the image's src and rebuild the cropper
     * @param {string} url - The new URL.
     * @param {boolean} [onlyColorChanged] - Indicate if the new image only changed color.
     * @returns {Object} this
     */
    replace(url, onlyColorChanged = false) {
      return this.cropper.replace(url, onlyColorChanged)
    },

    // Enable (unfreeze) the cropper
    enable() {
      return this.cropper.enable()
    },

    // Disable (freeze) the cropper
    disable() {
      return this.cropper.disable()
    },

    // Destroy the cropper and remove the instance from the image
    destroy() {
      return this.cropper.destroy()
    },

    /**
     * Move the canvas with relative offsets
     * @param {number} offsetX - The relative offset distance on the x-axis.
     * @param {number} offsetY - The relative offset distance on the y-axis.
     * @returns {Object} this
     */
    move(offsetX, offsetY) {
      return this.cropper.move(offsetX, offsetY)
    },

    /**
     * Move the canvas to an absolute point
     * @param {number} x - The x-axis coordinate.
     * @param {number} [y=x] - The y-axis coordinate.
     * @returns {Object} this
     */
    moveTo(x, y = x) {
      return this.cropper.moveTo(x, y)
    },

    /**
     * Zoom the canvas with a relative ratio
     * @param {number} ratio - The target ratio.
     * @param {Event} _originalEvent - The original event if any.
     * @returns {Object} this
     */
    relativeZoom(ratio, _originalEvent) {
      return this.cropper.zoom(ratio, _originalEvent)
    },

    /**
     * Zoom the canvas to an absolute ratio
     * @param {number} ratio - The target ratio.
     * @param {Event} _originalEvent - The original event if any.
     * @returns {Object} this
     */
    zoomTo(ratio, _originalEvent) {
      return this.cropper.zoomTo(ratio, _originalEvent)
    },

    /**
     * Rotate the canvas with a relative degree
     * @param {number} degree - The rotate degree.
     * @returns {Object} this
     */
    rotate(degree) {
      return this.cropper.rotate(degree)
    },

    /**
     * Rotate the canvas to an absolute degree
     * @param {number} degree - The rotate degree.
     * @returns {Object} this
     */
    rotateTo(degree) {
      return this.cropper.rotateTo(degree)
    },

    /**
     * Scale the image on the x-axis.
     * @param {number} scaleX - The scale ratio on the x-axis.
     * @returns {Object} this
     */
    scaleX(scaleX) {
      return this.cropper.scaleX(scaleX)
    },

    /**
     * Scale the image on the y-axis.
     * @param {number} scaleY - The scale ratio on the y-axis.
     * @returns {Object} this
     */
    scaleY(scaleY) {
      return this.cropper.scaleY(scaleY)
    },

    /**
     * Scale the image
     * @param {number} scaleX - The scale ratio on the x-axis.
     * @param {number} [scaleY=scaleX] - The scale ratio on the y-axis.
     * @returns {Object} this
     */
    scale(scaleX, scaleY = scaleX) {
      return this.cropper.scale(scaleX, scaleY)
    },

    /**
     * Get the cropped area position and size data (base on the original image)
     * @param {boolean} [rounded=false] - Indicate if round the data values or not.
     * @returns {Object} The result cropped data.
     */
    getData(rounded = false) {
      return this.cropper.getData(rounded)
    },

    /**
     * Set the cropped area position and size with new data
     * @param {Object} data - The new data.
     * @returns {Object} this
     */
    setData(data) {
      return this.cropper.setData(data)
    },

    /**
     * Get the container size data.
     * @returns {Object} The result container data.
     */
    getContainerData() {
      return this.cropper.getContainerData()
    },

    /**
     * Get the image position and size data.
     * @returns {Object} The result image data.
     */
    getImageData() {
      return this.cropper.getImageData()
    },

    /**
     * Get the canvas position and size data.
     * @returns {Object} The result canvas data.
     */
    getCanvasData() {
      return this.cropper.getCanvasData()
    },

    /**
     * Set the canvas position and size with new data.
     * @param {Object} data - The new canvas data.
     * @returns {Object} this
     */
    setCanvasData(data) {
      return this.cropper.setCanvasData(data)
    },

    /**
     * Get the crop box position and size data.
     * @returns {Object} The result crop box data.
     */
    getCropBoxData() {
      return this.cropper.getCropBoxData()
    },

    /**
     * Set the crop box position and size with new data.
     * @param {Object} data - The new crop box data.
     * @returns {Object} this
     */
    setCropBoxData(data) {
      return this.cropper.setCropBoxData(data)
    },

    /**
     * Get a canvas drawn the cropped image.
     * @param {Object} [options={}] - The config options.
     * @returns {HTMLCanvasElement} - The result canvas.
     */
    getCroppedCanvas(options = {}) {
      return this.cropper.getCroppedCanvas(options)
    },

    /**
     * Change the aspect ratio of the crop box.
     * @param {number} aspectRatio - The new aspect ratio.
     * @returns {Object} this
     */
    setAspectRatio(aspectRatio) {
      return this.cropper.setAspectRatio(aspectRatio)
    },

    /**
     * Change the drag mode.
     * @param {string} mode - The new drag mode.
     * @returns {Object} this
     */
    setDragMode(mode) {
      return this.cropper.setDragMode(mode)
    }
  }
}
<|endoftext|>


<commit_before>this["ProjectList"] = this["ProjectList"] || {};
this["ProjectList"]["templates"] = this["ProjectList"]["templates"] || {};

this["ProjectList"]["templates"]["projects"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, helper, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}), alias4=helpers.helperMissing, alias5="function";

  return "\n
\n\n
\n\n
\n
"
    + alias2(alias1(((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.name : stack1), depth0))
    + "
\n
"
    + alias2(alias1(((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.description : stack1), depth0))
    + "
\n
\n                    Contact Project Lead\n
\n
\n

\n
\n
Project Needs & Interests
\n
\n
Skills Needed:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.skillsNeeded : stack1),{"name":"each","hash":{},"fn":container.program(2, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
Learning Opportunities:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.skillsOffered : stack1),{"name":"each","hash":{},"fn":container.program(4, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
Civic Interests:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.interests : stack1),{"name":"each","hash":{},"fn":container.program(6, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
\n\n    \n
\n\n
\n
\n                    Pending Tasks\n
\n"
    + ((stack1 = helpers["if"].call(alias3,((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.todoItems : stack1),{"name":"if","hash":{},"fn":container.program(8, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n

\n
\n                    Progress Made\n
\n"
    + ((stack1 = helpers["if"].call(alias3,((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.progressItems : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n

  \n
\n                    Additional Info\n
\n                        \n
\n
\n
\n\n
\n\n
\n            Show project details\n
\n\n
\n\n";
},"2":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
\n                            "
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "\n
\n";
},"4":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
"
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "
\n";
},"6":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
"
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "
\n";
},"8":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = blockParams[1][0]) != null ? stack1.attributes : stack1)) != null ? stack1.todoItems : stack1),{"name":"each","hash":{},"fn":container.program(9, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n";
},"9":function(container,depth0,helpers,partials,data) {
    return "
"
    + container.escapeExpression(container.lambda(depth0, depth0))
    + "
\n";
},"11":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = blockParams[1][0]) != null ? stack1.attributes : stack1)) != null ? stack1.progressItems : stack1),{"name":"each","hash":{},"fn":container.program(9, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n
Project List
\n\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.projects : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "    \n\n
";
},"useData":true,"useBlockParams":true});<commit_msg>removed contact project lead button<commit_after>this["ProjectList"] = this["ProjectList"] || {};
this["ProjectList"]["templates"] = this["ProjectList"]["templates"] || {};

this["ProjectList"]["templates"]["projects"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, helper, alias1=container.lambda, alias2=container.escapeExpression, alias3=depth0 != null ? depth0 : (container.nullContext || {}), alias4=helpers.helperMissing, alias5="function";

  return "\n
\n\n
\n\n
\n
"
    + alias2(alias1(((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.name : stack1), depth0))
    + "
\n
"
    + alias2(alias1(((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.description : stack1), depth0))
    + "
\n                \n
\n

\n
\n
Project Needs & Interests
\n
\n
Skills Needed:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.skillsNeeded : stack1),{"name":"each","hash":{},"fn":container.program(2, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
Learning Opportunities:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.skillsOffered : stack1),{"name":"each","hash":{},"fn":container.program(4, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
Civic Interests:
\n
\n"
    + ((stack1 = helpers.each.call(alias3,((stack1 = ((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.newMatchingConfig : stack1)) != null ? stack1.interests : stack1),{"name":"each","hash":{},"fn":container.program(6, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n
\n
\n\n\n
\n\n
\n
\n                    Pending Tasks\n
\n"
    + ((stack1 = helpers["if"].call(alias3,((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.todoItems : stack1),{"name":"if","hash":{},"fn":container.program(8, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n

\n
\n                    Progress Made\n
\n"
    + ((stack1 = helpers["if"].call(alias3,((stack1 = ((stack1 = blockParams[0][0]) != null ? stack1.attributes : stack1)) != null ? stack1.progressItems : stack1),{"name":"if","hash":{},"fn":container.program(11, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n
\n

\n
\n                    Additional Info\n
\n\n
\n
\n
\n\n
\n\n
\n            Show project details\n
\n\n
\n\n";
},"2":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
\n                            "
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "\n
\n";
},"4":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
"
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "
\n";
},"6":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1, alias1=container.lambda, alias2=container.escapeExpression;

  return "
"
    + alias2(alias1(((stack1 = blockParams[0][0]) != null ? stack1.title : stack1), depth0))
    + "
\n";
},"8":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = blockParams[1][0]) != null ? stack1.attributes : stack1)) != null ? stack1.todoItems : stack1),{"name":"each","hash":{},"fn":container.program(9, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n";
},"9":function(container,depth0,helpers,partials,data) {
    return "
"
    + container.escapeExpression(container.lambda(depth0, depth0))
    + "
\n";
},"11":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),((stack1 = ((stack1 = blockParams[1][0]) != null ? stack1.attributes : stack1)) != null ? stack1.progressItems : stack1),{"name":"each","hash":{},"fn":container.program(9, data, 0, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "
\n";
},"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams) {
    var stack1;

  return "
\n
Project List
\n\n"
    + ((stack1 = helpers.each.call(depth0 != null ? depth0 : (container.nullContext || {}),(depth0 != null ? depth0.projects : depth0),{"name":"each","hash":{},"fn":container.program(1, data, 1, blockParams),"inverse":container.noop,"data":data,"blockParams":blockParams})) != null ? stack1 : "")
    + "    \n\n
\n";
},"useData":true,"useBlockParams":true});<|endoftext|>


<commit_before>$(function () {

  // Allow user configuration?
  const allowRegex = true;

  const reNotCache = {};
  function escapeRegexp(s, not) {
    if (not) {
      let re = reNotCache[not];
      if (!re) {
        const characters = '-\/\\^$*+?.()|[\]{}'.replace(new RegExp('[' + escapeRegexp(not) + ']', 'g'), '');
        re = reNotCache[not] = new RegExp('[' + escapeRegexp(characters) + ']', 'g');
      }
      return s.replace(re, '\\$&');
    }
    return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  }

  function constructRegex(searchTerm, makeRe, allowRegex) {
    try {
      if (allowRegex) {
        return makeRe(searchTerm);
      }
    } catch (e) {
    }
    return makeRe(escapeRegexp(searchTerm, '.'));
  }

  var getSearchWeight = function (searchTerm, $matchedItem) {
    let weight = 0;
    // We could get smarter on the weight here
    if ($matchedItem.data('shortname') === searchTerm) {
      weight++;
    }
    return weight;
  };

  // sort function callback
  var weightSorter = function (a, b) {
    var aW = $(a).data('weight') || 0;
    var bW = $(b).data('weight') || 0;
    return bW - aW;
  };

  // Show an item related a current documentation automatically
  const longname = $('.page-title').data('filename')
    .replace(/\.[a-z]+$/, '')
    .replace('module-', 'module:')
    .replace(/_/g, '/')
    .replace(/-/g, '~');
  var $currentItem = $('.navigation .item[data-name*="' + longname.toLowerCase() + '"]:eq(0)');

  if ($currentItem.length) {
    $currentItem
      .prependTo('.navigation .list')
      .show()
      .find('.member-list')
      .show();
  }

  function doSearch(searchTerm) {
    var $el = $('.navigation');

    if (searchTerm.length > 1) {
      searchTerm = searchTerm.toLowerCase();
      const regexp = constructRegex(searchTerm, function (searchTerm) {
        return new RegExp(searchTerm);
      }, allowRegex);
      $el.find('li, .member-list').hide();

      $el.find('li').each(function (i, v) {
        const $item = $(v);
        const name = $item.data('name');

        if (regexp.test(name)) {
          const $classEntry = $item.closest('.item');
          const $members = $item.closest('.member-list');

          // Do the weight thing
          const weight = getSearchWeight(searchTerm, $classEntry);
          $classEntry.data('weight', weight);

          $item.show();
          $members.show();
          $classEntry.show();
        }
      });

      $(".navigation ul.list li.item:visible")
        .sort(weightSorter) // sort elements
        .appendTo(".navigation ul.list"); // append again to the list

    } else {
      $currentItem.prependTo('.navigation .list');
      $currentItem.find('.member-list, li').show();
      $el.find('.item').show();
    }

    $el.find('.list').scrollTop(0);
  }

  const searchInput = $('#search').get(0);
  // Skip searches when typing fast.
  let key;
  let start;
  const brandNode = document.querySelector('.brand');
  function queueSearch() {
    if (!key) {
      start = Date.now();
      key = setTimeout(function () {
        key = undefined;

        const searchTerm = searchInput.value;
        doSearch(searchTerm);

        const time = Date.now() - start
        brandNode.innerHTML = time + ' ms';
        console.log(searchTerm + ':', time, 'ms');
        start = undefined;
      }, 0);
    }
  }

  // Search Items
  searchInput.addEventListener('input', queueSearch);

  // Toggle when click an item element
  $('.navigation').on('click', '.toggle', function (e) {
    $(this).parent().parent().find('.member-list').toggle();
  });

  // Auto resizing on navigation
  var _onResize = function () {
    var height = $(window).height();
    var $el = $('.navigation');

    $el.height(height).find('.list').height(height - 133);
  };

  $(window).on('resize', _onResize);
  _onResize();

  var currentVersion = document.getElementById('package-version').innerHTML;

  // warn about outdated version
  var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
  fetch(packageUrl).then(function(response) {
    return response.json();
  }).then(function(json) {
    var latestVersion = json.version;
    document.getElementById('latest-version').innerHTML = latestVersion;
    var url = window.location.href;
    var branchSearch = url.match(/\/([^\/]*)\/apidoc\//);
    var cookieText = 'dismissed=-' + latestVersion + '-';
    var dismissed = document.cookie.indexOf(cookieText) != -1;
    if (branchSearch && !dismissed && /^v[0-9\.]*$/.test(branchSearch[1]) && currentVersion != latestVersion) {
      var link = url.replace(branchSearch[0], '/latest/apidoc/');
      fetch(link, {method: 'head'}).then(function(response) {
        var a = document.getElementById('latest-link');
        a.href = response.status == 200 ? link : '../../latest/apidoc/';
      });
      var latestCheck = document.getElementById('latest-check');
      latestCheck.style.display = '';
      document.getElementById('latest-dismiss').onclick = function() {
        latestCheck.style.display = 'none';
        document.cookie = cookieText;
      }
    }
  });

  // create source code links to github
  var srcLinks = $('div.tag-source');
  srcLinks.each(function(i, el) {
    var textParts = el.innerHTML.trim().split(', ');
    var link = 'https://github.com/openlayers/openlayers/blob/v' + currentVersion + '/src/ol/' +
      textParts[0];
    el.innerHTML = '' + textParts[0] + ', ' +
      '' +
      textParts[1] + '';
  });
});
<commit_msg>Search if the search input is not empty after page loads<commit_after>$(function () {

  // Allow user configuration?
  const allowRegex = true;

  const reNotCache = {};
  function escapeRegexp(s, not) {
    if (not) {
      let re = reNotCache[not];
      if (!re) {
        const characters = '-\/\\^$*+?.()|[\]{}'.replace(new RegExp('[' + escapeRegexp(not) + ']', 'g'), '');
        re = reNotCache[not] = new RegExp('[' + escapeRegexp(characters) + ']', 'g');
      }
      return s.replace(re, '\\$&');
    }
    return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  }

  function constructRegex(searchTerm, makeRe, allowRegex) {
    try {
      if (allowRegex) {
        return makeRe(searchTerm);
      }
    } catch (e) {
    }
    return makeRe(escapeRegexp(searchTerm, '.'));
  }

  var getSearchWeight = function (searchTerm, $matchedItem) {
    let weight = 0;
    // We could get smarter on the weight here
    if ($matchedItem.data('shortname') === searchTerm) {
      weight++;
    }
    return weight;
  };

  // sort function callback
  var weightSorter = function (a, b) {
    var aW = $(a).data('weight') || 0;
    var bW = $(b).data('weight') || 0;
    return bW - aW;
  };

  // Show an item related a current documentation automatically
  const longname = $('.page-title').data('filename')
    .replace(/\.[a-z]+$/, '')
    .replace('module-', 'module:')
    .replace(/_/g, '/')
    .replace(/-/g, '~');
  var $currentItem = $('.navigation .item[data-name*="' + longname.toLowerCase() + '"]:eq(0)');

  if ($currentItem.length) {
    $currentItem
      .prependTo('.navigation .list')
      .show()
      .find('.member-list')
      .show();
  }

  function doSearch(searchTerm) {
    var $el = $('.navigation');

    if (searchTerm.length > 1) {
      searchTerm = searchTerm.toLowerCase();
      const regexp = constructRegex(searchTerm, function (searchTerm) {
        return new RegExp(searchTerm);
      }, allowRegex);
      $el.find('li, .member-list').hide();

      $el.find('li').each(function (i, v) {
        const $item = $(v);
        const name = $item.data('name');

        if (regexp.test(name)) {
          const $classEntry = $item.closest('.item');
          const $members = $item.closest('.member-list');

          // Do the weight thing
          const weight = getSearchWeight(searchTerm, $classEntry);
          $classEntry.data('weight', weight);

          $item.show();
          $members.show();
          $classEntry.show();
        }
      });

      $(".navigation ul.list li.item:visible")
        .sort(weightSorter) // sort elements
        .appendTo(".navigation ul.list"); // append again to the list

    } else {
      $currentItem.prependTo('.navigation .list');
      $currentItem.find('.member-list, li').show();
      $el.find('.item').show();
    }

    $el.find('.list').scrollTop(0);
  }

  const searchInput = $('#search').get(0);
  // Skip searches when typing fast.
  let key;
  let start;
  const brandNode = document.querySelector('.brand');
  function queueSearch() {
    if (!key) {
      start = Date.now();
      key = setTimeout(function () {
        key = undefined;

        const searchTerm = searchInput.value;
        doSearch(searchTerm);

        const time = Date.now() - start
        brandNode.innerHTML = time + ' ms';
        console.log(searchTerm + ':', time, 'ms');
        start = undefined;
      }, 0);
    }
  }

  // Search Items
  searchInput.addEventListener('input', queueSearch);
  if (searchInput.value) {
    doSearch(searchInput.value);
  }

  // Toggle when click an item element
  $('.navigation').on('click', '.toggle', function (e) {
    $(this).parent().parent().find('.member-list').toggle();
  });

  // Auto resizing on navigation
  var _onResize = function () {
    var height = $(window).height();
    var $el = $('.navigation');

    $el.height(height).find('.list').height(height - 133);
  };

  $(window).on('resize', _onResize);
  _onResize();

  var currentVersion = document.getElementById('package-version').innerHTML;

  // warn about outdated version
  var packageUrl = 'https://raw.githubusercontent.com/openlayers/openlayers.github.io/build/package.json';
  fetch(packageUrl).then(function(response) {
    return response.json();
  }).then(function(json) {
    var latestVersion = json.version;
    document.getElementById('latest-version').innerHTML = latestVersion;
    var url = window.location.href;
    var branchSearch = url.match(/\/([^\/]*)\/apidoc\//);
    var cookieText = 'dismissed=-' + latestVersion + '-';
    var dismissed = document.cookie.indexOf(cookieText) != -1;
    if (branchSearch && !dismissed && /^v[0-9\.]*$/.test(branchSearch[1]) && currentVersion != latestVersion) {
      var link = url.replace(branchSearch[0], '/latest/apidoc/');
      fetch(link, {method: 'head'}).then(function(response) {
        var a = document.getElementById('latest-link');
        a.href = response.status == 200 ? link : '../../latest/apidoc/';
      });
      var latestCheck = document.getElementById('latest-check');
      latestCheck.style.display = '';
      document.getElementById('latest-dismiss').onclick = function() {
        latestCheck.style.display = 'none';
        document.cookie = cookieText;
      }
    }
  });

  // create source code links to github
  var srcLinks = $('div.tag-source');
  srcLinks.each(function(i, el) {
    var textParts = el.innerHTML.trim().split(', ');
    var link = 'https://github.com/openlayers/openlayers/blob/v' + currentVersion + '/src/ol/' +
      textParts[0];
    el.innerHTML = '' + textParts[0] + ', ' +
      '' +
      textParts[1] + '';
  });
});
<|endoftext|>


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

dojo.declare("dijit._Contained",
	null,
	{
		// summary
		//		Mixin for widgets that are children of a container widget

		getParent: function(){
			// summary:
			//		returns the parent widget of this widget, assuming the parent
			//		implements dijit._Container
			for(var p=this.domNode.parentNode; p; p=p.parentNode){
				var id = p.getAttribute && p.getAttribute("widgetId");
				if(id){
					var parent = dijit.byId(id);
					return parent.isContainer ? parent : null;
				}
			}
			return null;
		},

		_getSibling: function(which){
			var node = this.domNode;
			do{
				node = node[which+"Sibling"];
			}while(node && node.nodeType != 1);
			if(!node){ return null; } // null
			var id = node.getAttribute("widgetId");
			return dijit.byId(id);
		},

		getPreviousSibling: function(){
			// summary:
			//		returns null if this is the first child of the parent,
			//		otherwise returns the next element sibling to the "left".

			return this._getSibling("previous");
		},

		getNextSibling: function(){
			// summary:
			//		returns null if this is the last child of the parent,
			//		otherwise returns the next element sibling to the "right".

			return this._getSibling("next");
		}
	}
);

dojo.declare("dijit._Container",
	null,
	{
		// summary
		//		Mixin for widgets that contain a list of children like SplitContainer

		isContainer: true,

		addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
			// summary:
			//		Process the given child widget, inserting it's dom node as
			//		a child of our dom node

			if(insertIndex === undefined){
				insertIndex = "last";
			}
			var refNode = this.containerNode || this.domNode;
			if(insertIndex && typeof insertIndex == "number"){
				var children = dojo.query("> [widgetid]", refNode);
				if(children && children.length >= insertIndex){
					refNode = children[insertIndex-1]; insertIndex = "after";
				}
			}
			dojo.place(widget.domNode, refNode, insertIndex);

			// If I've been started but the child widget hasn't been started,
			// start it now.  Make sure to do this after widget has been
			// inserted into the DOM tree, so it can see that it's being controlled by me,
			// so it doesn't try to size itself.
			if(this._started && !widget._started){
				widget.startup();
			}
		},

		removeChild: function(/*Widget*/ widget){
			// summary:
			//		removes the passed widget instance from this widget but does
			//		not destroy it
			var node = widget.domNode;
			node.parentNode.removeChild(node);	// detach but don't destroy
		},

		_nextElement: function(node){
			do{
				node = node.nextSibling;
			}while(node && node.nodeType != 1);
			return node;
		},

		_firstElement: function(node){
			node = node.firstChild;
			if(node && node.nodeType != 1){
				node = this._nextElement(node);
			}
			return node;
		},

		getChildren: function(){
			// summary:
			//		Returns array of children widgets
			return dojo.query("> [widgetId]", this.containerNode || this.domNode).map(dijit.byNode); // Array
		},

		hasChildren: function(){
			// summary:
			//		Returns true if widget has children
			var cn = this.containerNode || this.domNode;
			return !!this._firstElement(cn); // Boolean
		},

		_getSiblingOfChild: function(/*Widget*/ child, /*int*/ dir){
			// summary:
			//		get the next or previous sibling of child
			// dir:
			//		if 1, get the next sibling
			//		if -1, get the previous sibling
			var node = child.domNode;
			var which = (dir == -1 ? "previousSibling" : "nextSibling");
			do{
				node = node[which];
			}while(node && node.nodeType != 1);
			return node ? dijit.byNode(node) : null;
		}
	}
);

dojo.declare("dijit._KeyNavContainer",
	[dijit._Container],
	{

		// summary:
		//		A _Container with keyboard navigation of its children.
		//		To use this mixin, call connectKeyNavHandlers() in
		//		postCreate() and call connectKeyNavChildren() in startup().

		focusedChild: null,

		_keyNavCodes: {},

		connectKeyNavHandlers: function(/*Array*/ prevKeyCodes, /*Array*/ nextKeyCodes){
			// summary:
			//		Call in postCreate() to attach the keyboard handlers
			//		to the container.
			// preKeyCodes: Array
			//		Key codes for navigating to the previous child.
			// nextKeyCodes: Array
			//		Key codes for navigating to the next child.

			var keyCodes = this._keyNavCodes = {};
			dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = -1 });
			dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = 1 });
			this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
			if(dojo.isIE){
				this.connect(this.domNode, "onactivate", "_onContainerFocus");
				this.connect(this.domNode, "ondeactivate", "_onContainerBlur");
			}else{
				this.connect(this.domNode, "onfocus", "_onContainerFocus");
				this.connect(this.domNode, "onblur", "_onContainerBlur");
			}
		},

		connectKeyNavChildren: function(){
			// summary:
			//		Call in setup() to attach focus handlers to the
			//		container's children.
			dojo.forEach(this.getChildren(), dojo.hitch(this, "_connectChild"));
		},

		addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
			// summary: Add a child to our _Container
			dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
			this._connectChild(widget);
		},

		focus: function(){
			// summary: Default focus() implementation: focus the first child.
			this.focusFirstChild();
		},

		focusFirstChild: function(){
			// summary: Focus the first focusable child in the container.
			this.focusChild(this._getFirstFocusableChild());
		},

		focusChild: function(/*Widget*/ widget){
			// summary: Focus widget.
			if(widget){
				if(this.focusedChild && widget !== this.focusedChild){
					this._onChildBlur(this.focusedChild);
				}
				this.focusedChild = widget;
				widget.focus();
			}
		},

		_connectChild: function(/*Widget*/ widget){
			(widget.focusNode || widget.domNode).setAttribute("tabIndex", -1);
			if(dojo.isIE){
				this.connect(widget.domNode, "onactivate", "_onChildFocus");
			}else{
				this.connect(widget.domNode, "onfocus", "_onChildFocus");
			}
		},

		_onContainerFocus: function(evt){
			this.domNode.setAttribute("tabIndex", -1);
			if(evt.target === this.domNode){
				this.focusFirstChild();
			}
		},

		_onContainerBlur: function(evt){
			if(this.tabIndex){
				this.domNode.setAttribute("tabIndex", this.tabIndex);
			}
		},

		_onContainerKeypress: function(evt){
			if(evt.ctrlKey || evt.altKey){ return; }
			var dir = this._keyNavCodes[evt.keyCode];
			if(dir){
				this.focusChild(this._getNextFocusableChild(this.focusedChild, dir));
				dojo.stopEvent(evt);
			}
		},

		_onChildFocus: function(evt){
			this.focusedChild = dijit.byNode(evt.currentTarget);
		},

		_onChildBlur: function(/*Widget*/ widget){
			// summary:
			//		Called when focus leaves a child widget to go
			//		to a sibling widget.
		},

		_getFirstFocusableChild: function(){
			return this._getNextFocusableChild(null, 1);
		},

		_getNextFocusableChild: function(child, dir){
			if(child){
				child = this._getSiblingOfChild(child, dir);
			}
			var children = this.getChildren();
			for(var i=0; i < children.length; i++){
				if(!child){
					child = children[(dir>0) ? 0 : (children.length-1)];
				}
				if(child.isFocusable()){
					return child;
				}
				child = this._getSiblingOfChild(child, dir);
			}
		}
	}
);
<commit_msg>Fixes #4464: Add Toolbar support for widgets, such as ComboButton, with multiple focus points.<commit_after>dojo.provide("dijit._Container");

dojo.declare("dijit._Contained",
	null,
	{
		// summary
		//		Mixin for widgets that are children of a container widget

		getParent: function(){
			// summary:
			//		returns the parent widget of this widget, assuming the parent
			//		implements dijit._Container
			for(var p=this.domNode.parentNode; p; p=p.parentNode){
				var id = p.getAttribute && p.getAttribute("widgetId");
				if(id){
					var parent = dijit.byId(id);
					return parent.isContainer ? parent : null;
				}
			}
			return null;
		},

		_getSibling: function(which){
			var node = this.domNode;
			do{
				node = node[which+"Sibling"];
			}while(node && node.nodeType != 1);
			if(!node){ return null; } // null
			var id = node.getAttribute("widgetId");
			return dijit.byId(id);
		},

		getPreviousSibling: function(){
			// summary:
			//		returns null if this is the first child of the parent,
			//		otherwise returns the next element sibling to the "left".

			return this._getSibling("previous");
		},

		getNextSibling: function(){
			// summary:
			//		returns null if this is the last child of the parent,
			//		otherwise returns the next element sibling to the "right".

			return this._getSibling("next");
		}
	}
);

dojo.declare("dijit._Container",
	null,
	{
		// summary
		//		Mixin for widgets that contain a list of children like SplitContainer

		isContainer: true,

		addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
			// summary:
			//		Process the given child widget, inserting it's dom node as
			//		a child of our dom node

			if(insertIndex === undefined){
				insertIndex = "last";
			}
			var refNode = this.containerNode || this.domNode;
			if(insertIndex && typeof insertIndex == "number"){
				var children = dojo.query("> [widgetid]", refNode);
				if(children && children.length >= insertIndex){
					refNode = children[insertIndex-1]; insertIndex = "after";
				}
			}
			dojo.place(widget.domNode, refNode, insertIndex);

			// If I've been started but the child widget hasn't been started,
			// start it now.  Make sure to do this after widget has been
			// inserted into the DOM tree, so it can see that it's being controlled by me,
			// so it doesn't try to size itself.
			if(this._started && !widget._started){
				widget.startup();
			}
		},

		removeChild: function(/*Widget*/ widget){
			// summary:
			//		removes the passed widget instance from this widget but does
			//		not destroy it
			var node = widget.domNode;
			node.parentNode.removeChild(node);	// detach but don't destroy
		},

		_nextElement: function(node){
			do{
				node = node.nextSibling;
			}while(node && node.nodeType != 1);
			return node;
		},

		_firstElement: function(node){
			node = node.firstChild;
			if(node && node.nodeType != 1){
				node = this._nextElement(node);
			}
			return node;
		},

		getChildren: function(){
			// summary:
			//		Returns array of children widgets
			return dojo.query("> [widgetId]", this.containerNode || this.domNode).map(dijit.byNode); // Array
		},

		hasChildren: function(){
			// summary:
			//		Returns true if widget has children
			var cn = this.containerNode || this.domNode;
			return !!this._firstElement(cn); // Boolean
		},

		_getSiblingOfChild: function(/*Widget*/ child, /*int*/ dir){
			// summary:
			//		get the next or previous sibling of child
			// dir:
			//		if 1, get the next sibling
			//		if -1, get the previous sibling
			var node = child.domNode;
			var which = (dir>0 ? "nextSibling" : "previousSibling");
			do{
				node = node[which];
			}while(node && node.nodeType != 1);
			return node ? dijit.byNode(node) : null;
		}
	}
);

dojo.declare("dijit._KeyNavContainer",
	[dijit._Container],
	{

		// summary:
		//		A _Container with keyboard navigation of its children.
		//		To use this mixin, call connectKeyNavHandlers() in
		//		postCreate() and call connectKeyNavChildren() in startup().

		focusedChild: null,

		_keyNavCodes: {},

		connectKeyNavHandlers: function(/*Array*/ prevKeyCodes, /*Array*/ nextKeyCodes){
			// summary:
			//		Call in postCreate() to attach the keyboard handlers
			//		to the container.
			// preKeyCodes: Array
			//		Key codes for navigating to the previous child.
			// nextKeyCodes: Array
			//		Key codes for navigating to the next child.

			var keyCodes = this._keyNavCodes = {};
			var prev = dojo.hitch(this, this.focusPrev);
			var next = dojo.hitch(this, this.focusNext);
			dojo.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev });
			dojo.forEach(nextKeyCodes, function(code){ keyCodes[code] = next });
			this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
			if(dojo.isIE){
				this.connect(this.domNode, "onactivate", "_onContainerFocus");
				this.connect(this.domNode, "ondeactivate", "_onContainerBlur");
			}else{
				this.connect(this.domNode, "onfocus", "_onContainerFocus");
				this.connect(this.domNode, "onblur", "_onContainerBlur");
			}
		},

		startupKeyNavChildren: function(){
			// summary:
			//		Call in startup() to set child tabindexes to -1
			dojo.forEach(this.getChildren(), dojo.hitch(this, "_setTabIndexMinusOne"));
		},

		addChild: function(/*Widget*/ widget, /*int?*/ insertIndex){
			// summary: Add a child to our _Container
			dijit._KeyNavContainer.superclass.addChild.apply(this, arguments);
			this._setTabIndexMinusOne(widget);
		},

		focus: function(){
			// summary: Default focus() implementation: focus the first child.
			this.focusFirstChild();
		},

		focusFirstChild: function(){
			// summary: Focus the first focusable child in the container.
			this.focusChild(this._getFirstFocusableChild());
		},

		focusNext: function(){
			// summary: Focus the next widget or focal node (for widgets
			//		with multiple focal nodes) within this container.
			if(this.focusedChild && this.focusedChild.hasNextFocalNode
					&& this.focusedChild.hasNextFocalNode()){
				this.focusedChild.focusNext();
				return;
			}
			var child = this._getNextFocusableChild(this.focusedChild, 1);
			if(child.getFocalNodes){
				this.focusChild(child, child.getFocalNodes()[0]);
			}else{
				this.focusChild(child);
			}
		},

		focusPrev: function(){
			// summary: Focus the previous widget or focal node (for widgets
			//		with multiple focal nodes) within this container.
			if(this.focusedChild && this.focusedChild.hasPrevFocalNode
					&& this.focusedChild.hasPrevFocalNode()){
				this.focusedChild.focusPrev();
				return;
			}
			var child = this._getNextFocusableChild(this.focusedChild, -1);
			if(child.getFocalNodes){
				var nodes = child.getFocalNodes();
				this.focusChild(child, nodes[nodes.length-1]);
			}else{
				this.focusChild(child);
			}
		},

		focusChild: function(/*Widget*/ widget, /*Node?*/ node){
			// summary: Focus widget. Optionally focus 'node' within widget.
			if(widget){
				if(this.focusedChild && widget !== this.focusedChild){
					this._onChildBlur(this.focusedChild);
				}
				this.focusedChild = widget;
				if(node && widget.focusFocalNode){
					widget.focusFocalNode(node);
				}else{
					widget.focus();
				}
			}
		},

		_setTabIndexMinusOne: function(/*Widget*/ widget){
			if(widget.getFocalNodes){
				dojo.forEach(widget.getFocalNodes(), function(node){
					node.setAttribute("tabIndex", -1);
				});
			}else{
				(widget.focusNode || widget.domNode).setAttribute("tabIndex", -1);
			}
		},

		_onContainerFocus: function(evt){
			this.domNode.setAttribute("tabIndex", -1);
			if(evt.target === this.domNode){
				this.focusFirstChild();
			}else{
				var widget = dijit.getEnclosingWidget(evt.target);
				if(widget){
					this.focusedChild = widget;
				}
			}
		},

		_onContainerBlur: function(evt){
			if(this.tabIndex){
				this.domNode.setAttribute("tabIndex", this.tabIndex);
			}
		},

		_onContainerKeypress: function(evt){
			if(evt.ctrlKey || evt.altKey){ return; }
			var func = this._keyNavCodes[evt.keyCode];
			if(func){
				func();
				dojo.stopEvent(evt);
			}
		},

		_onChildBlur: function(/*Widget*/ widget){
			// summary:
			//		Called when focus leaves a child widget to go
			//		to a sibling widget.
		},

		_getFirstFocusableChild: function(){
			return this._getNextFocusableChild(null, 1);
		},

		_getNextFocusableChild: function(child, dir){
			if(child){
				child = this._getSiblingOfChild(child, dir);
			}
			var children = this.getChildren();
			for(var i=0; i < children.length; i++){
				if(!child){
					child = children[(dir>0) ? 0 : (children.length-1)];
				}
				if(child.isFocusable()){
					return child;
				}
				child = this._getSiblingOfChild(child, dir);
			}
		}
	}
);
<|endoftext|>