<commit_before>import 'babel/polyfill';

import page      from 'page';
import React     from 'react';
import fastclick from 'fastclick';

import socket    from './utils/socket';
import UserStore from './stores/user';
import User      from './models/user';

import BoardView      from './views/board';
import WorkspaceView  from './views/workspace';

import LoginView      from './views/form';
import RegisterView   from './views/form';
import GuestLoginView from './views/form';
import UserAccessView from './views/form';
import UserAction     from './actions/user';

import qs          from 'query-string';
import FormView    from './views/form/';
import ProfileView from './views/profile';

// This should fix some of the issues with clicking and touch enabled devices.
fastclick(document.body);

// Define middleware to be used with 'page' routes (or pages, whatever).
const middleware = {
	user: {
		is: (...types) => {
			return function(ctx, next) {
				if((ctx.user = UserStore.getUser())) {
					let userHasType = types.reduce((has, type) => {
						return has || ctx.user.type === type;
					}, false);

					// If the user is a regular 'user', we can access anything!
					if(userHasType && ctx.user.type === User.Type.User) {
						return next();
					}

					// If the user is a guest, we need to make sure he or she
					// has access to the resource being accessed. Guests only
					// have access to a specific board.
					if(userHasType && ctx.user.type === User.Type.Guest) {
						if(ctx.params.hasOwnProperty('id')) {
							if(ctx.params.id === ctx.user.access) {
								return next();
							}
						}
						return page.redirect(`/boards/${ctx.user.access}`);
					}
				}
				return page.redirect('/login');
			}
		},

		notGuest: (ctx, next) => {
			if(ctx.user = UserStore.getUser()) {
				if(ctx.user.type === User.Type.Guest) {
					if(ctx.params.hasOwnProperty('id')) {
						if(ctx.params.id === ctx.user.access) {
							return page.redirect(`/boards/${ctx.user.access}`);
						}
					}
				} else {
					UserAction.giveBoardAccess(ctx.params.id, ctx.params.code).then(() => {
						return page.show(`/boards/${ctx.params.id}`);
					});
				}
			}
			return next();
		},

		loggedOut: (ctx, next) => {
			if(ctx.user = UserStore.getUser()) {
				if(ctx.user.type === User.Type.Guest) {
					// If the logged in user a 'guest', he or she is redirected
					// to the board the guest has access to.
					return page.redirect(`/boards/${ctx.user.access}`);
				} else {
					return page.redirect('/boards');
				}
			}
			return next();
		}
	},
	socket: {
		connect: (ctx, next) => {
			socket.connect({ token: UserStore.getToken() }).then(next);
		},
		disconnect: (ctx, next) => {
			socket.disconnect().then(next);
		}
	}
}

// The router, in a sense, is also a view. Thus it should be able to listen to
// stores the same as other views.
// We listen to changes to the UserStore, so that when the user is logged out
// for various reasons, we can redirect the user to the 'login' page. This, in
// a sense, works very similarly to the AngularJS '$http.interceptor'.
UserStore.addChangeListener(() => {
	if(!UserStore.getUser() || !UserStore.getToken()) {
		return page.redirect('/login');
	}
});

page('/login',
	middleware.user.loggedOut,
	middleware.socket.disconnect,
	() => {
		return React.render(
			<FormView formProfile="loginForm" />,
			document.getElementById('application')
		);
});

page('/login/callback',
	(ctx, next) => {
	let accessToken = qs.parse(ctx.querystring).access_token;
	if(ctx.querystring.length > 0) {
			if(accessToken && accessToken.length > 0) {
				localStorage.setItem('token', accessToken);
			}
			if(localStorage.getItem('share_board') && localStorage.getItem('share_accessCode')){
				let boardID = localStorage.getItem('share_board');
				let accessCode = localStorage.getItem('share_accessCode');
				UserAction.load().then(() => {
					UserAction.giveBoardAccess(boardID, accessCode).then(() => {
						localStorage.removeItem('share_board');
						localStorage.removeItem('share_accessCode');
						return page.show(`/boards/${boardID}`);
					});
				});
			} else {
				UserAction.load().then(() => { page.redirect('/boards'); });
			}
		}
	return next();
});

page('/register',
	middleware.user.loggedOut,
	middleware.socket.disconnect,
	() => {
		return React.render(
			<FormView formProfile="registerForm" />,
			document.getElementById('application')
		);
	});

page('/profile',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	() => {
		return React.render(
			<ProfileView formProfile="profileSettings" />,
			document.getElementById('application')
		);
	});

page('/profile/login',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	() => {
		return React.render(
			<ProfileView formProfile="loginSettings" />,
			document.getElementById('application')
		);
	});

page('/boards/:id/access/:code',
	middleware.user.notGuest,
	middleware.socket.disconnect,
	(ctx) => {
		return React.render(
			<FormView formProfile="guestLoginForm" boardID={ctx.params.id}
				accessCode={ctx.params.code} />,
			document.getElementById('application')
		);
	});

page('/userlogin/boards/:id/access/:code',
	middleware.socket.disconnect,
	(ctx) => {
		localStorage.setItem('share_accessCode', ctx.params.code);
		localStorage.setItem('share_board', ctx.params.id);
		return React.render(
			<UserAccessView formProfile="userAccessForm" boardID={ctx.params.id}
							accessCode={ctx.params.code} />,
			document.getElementById('application')
		);
	});

page('/boards',
	middleware.user.is(User.Type.User),
	middleware.socket.connect,
	(ctx) => {
		return React.render(
			<WorkspaceView user={ctx.user} />,
			document.getElementById('application')
		);
	});

page('/boards/:id',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	(ctx) => {
		return React.render(
			<BoardView id={ctx.params.id} user={ctx.user} />,
			document.getElementById('application')
		);
	});

page('*', () => page.redirect('/boards'));
page.start();
<commit_msg>Fixed a bug where users would end in endless redirect loop if users account type was invalid.<commit_after>import 'babel/polyfill';

import page      from 'page';
import React     from 'react';
import fastclick from 'fastclick';

import socket    from './utils/socket';
import UserStore from './stores/user';
import User      from './models/user';

import BoardView      from './views/board';
import WorkspaceView  from './views/workspace';

import LoginView      from './views/form';
import RegisterView   from './views/form';
import GuestLoginView from './views/form';
import UserAccessView from './views/form';
import UserAction     from './actions/user';

import qs          from 'query-string';
import FormView    from './views/form/';
import ProfileView from './views/profile';

// This should fix some of the issues with clicking and touch enabled devices.
fastclick(document.body);

// Define middleware to be used with 'page' routes (or pages, whatever).
const middleware = {
	user: {
		is: (...types) => {
			return function(ctx, next) {
				if((ctx.user = UserStore.getUser())) {
					let userHasType = types.reduce((has, type) => {
						return has || ctx.user.type === type;
					}, false);

					// If the user is a regular 'user', we can access anything!
					if(userHasType && ctx.user.type === User.Type.User) {
						return next();
					}

					// If the user is a guest, we need to make sure he or she
					// has access to the resource being accessed. Guests only
					// have access to a specific board.
					if(userHasType && ctx.user.type === User.Type.Guest) {
						if(ctx.params.hasOwnProperty('id')) {
							if(ctx.params.id === ctx.user.access) {
								return next();
							}
						}
						return page.redirect(`/boards/${ctx.user.access}`);
					}

					if(!userHasType){
						UserAction.logout()
						.then(() => {
							return page.redirect('/login');
						});
					}
				}
				return page.redirect('/login');
			}
		},

		notGuest: (ctx, next) => {
			if(ctx.user = UserStore.getUser()) {
				if(ctx.user.type === User.Type.Guest) {
					if(ctx.params.hasOwnProperty('id')) {
						if(ctx.params.id === ctx.user.access) {
							return page.redirect(`/boards/${ctx.user.access}`);
						}
					}
				} else {
					UserAction.giveBoardAccess(ctx.params.id, ctx.params.code).then(() => {
						return page.show(`/boards/${ctx.params.id}`);
					});
				}
			}
			return next();
		},

		loggedOut: (ctx, next) => {
			if(ctx.user = UserStore.getUser()) {
				if(ctx.user.type === User.Type.Guest) {
					// If the logged in user a 'guest', he or she is redirected
					// to the board the guest has access to.
					return page.redirect(`/boards/${ctx.user.access}`);
				} else {
					return page.redirect('/boards');
				}
			}
			return next();
		}
	},
	socket: {
		connect: (ctx, next) => {
			socket.connect({ token: UserStore.getToken() }).then(next);
		},
		disconnect: (ctx, next) => {
			socket.disconnect().then(next);
		}
	}
}

// The router, in a sense, is also a view. Thus it should be able to listen to
// stores the same as other views.
// We listen to changes to the UserStore, so that when the user is logged out
// for various reasons, we can redirect the user to the 'login' page. This, in
// a sense, works very similarly to the AngularJS '$http.interceptor'.
UserStore.addChangeListener(() => {
	if(!UserStore.getUser() || !UserStore.getToken()) {
		return page.redirect('/login');
	}
});

page('/login',
	middleware.user.loggedOut,
	middleware.socket.disconnect,
	() => {
		return React.render(
			<FormView formProfile="loginForm" />,
			document.getElementById('application')
		);
});

page('/login/callback',
	(ctx, next) => {
	let accessToken = qs.parse(ctx.querystring).access_token;
	if(ctx.querystring.length > 0) {
			if(accessToken && accessToken.length > 0) {
				localStorage.setItem('token', accessToken);
			}
			if(localStorage.getItem('share_board') && localStorage.getItem('share_accessCode')){
				let boardID = localStorage.getItem('share_board');
				let accessCode = localStorage.getItem('share_accessCode');
				UserAction.load().then(() => {
					UserAction.giveBoardAccess(boardID, accessCode).then(() => {
						localStorage.removeItem('share_board');
						localStorage.removeItem('share_accessCode');
						return page.show(`/boards/${boardID}`);
					});
				});
			} else {
				UserAction.load().then(() => { page.redirect('/boards'); });
			}
		}
	return next();
});

page('/register',
	middleware.user.loggedOut,
	middleware.socket.disconnect,
	() => {
		return React.render(
			<FormView formProfile="registerForm" />,
			document.getElementById('application')
		);
	});

page('/profile',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	() => {
		return React.render(
			<ProfileView formProfile="profileSettings" />,
			document.getElementById('application')
		);
	});

page('/profile/login',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	() => {
		return React.render(
			<ProfileView formProfile="loginSettings" />,
			document.getElementById('application')
		);
	});

page('/boards/:id/access/:code',
	middleware.user.notGuest,
	middleware.socket.disconnect,
	(ctx) => {
		return React.render(
			<FormView formProfile="guestLoginForm" boardID={ctx.params.id}
				accessCode={ctx.params.code} />,
			document.getElementById('application')
		);
	});

page('/userlogin/boards/:id/access/:code',
	middleware.socket.disconnect,
	(ctx) => {
		localStorage.setItem('share_accessCode', ctx.params.code);
		localStorage.setItem('share_board', ctx.params.id);
		return React.render(
			<UserAccessView formProfile="userAccessForm" boardID={ctx.params.id}
							accessCode={ctx.params.code} />,
			document.getElementById('application')
		);
	});

page('/boards',
	middleware.user.is(User.Type.User),
	middleware.socket.connect,
	(ctx) => {
		return React.render(
			<WorkspaceView user={ctx.user} />,
			document.getElementById('application')
		);
	});

page('/boards/:id',
	middleware.user.is(User.Type.User, User.Type.Guest),
	middleware.socket.connect,
	(ctx) => {
		return React.render(
			<BoardView id={ctx.params.id} user={ctx.user} />,
			document.getElementById('application')
		);
	});

page('*', () => page.redirect('/boards'));
page.start();
<|endoftext|>


<commit_before>import {HttpClient} from 'aurelia-fetch-client';

const tables = ['systems', 'roomtypes', 'cantons', 'technologies'];

export class Database {

  constructor() {
    this.data = {};
    this.http = new HttpClient();
    this.http.configure(config => {
      config
        .useStandardConfiguration()
        .withBaseUrl('//www.zeta.hoeranlagenverzeichnis.ch/query.php?')
        .withDefaults({
          headers: {
            // 'X-Requested-With': 'Fetch'
          }
        })
        .withInterceptor({
          request(request) {
            console.log(`Requesting ${request.method} ${request.url}`);
            return request;
          },
          response(response) {
            console.log(`Received ${response.status} ${response.url}`);
            return response;
          }
        });
    });
  }

  load() {
    return Promise.all(
      tables.map(table => this.http
        .fetch(`table=${table}`)
        .then(response => response.json())
        .catch(error => {
          console.error(error);
          return {
            results: []
          };
        }))
    ).then(data => {
      tables.forEach((table, index) => {
        this.data[table] = data[index].results;
      });
      let locations = {};
      let buildingCounter = 0;
      let buildings = this.data.systems.reduce((data, system) => {
        let buildingId = `${system.plz}-${system.strasse_nr}-${system.gebaeude}`;
        let orgId = `${system.organisation}`;
        if (!data[buildingId]) {
          buildingCounter++;
          data[buildingId] = {
            id: buildingCounter,
            name: system.gebaeude,
            lat: system.lat,
            lng: system.lng,
            strasse_nr: system.strasse_nr,
            plz: system.plz,
            ort: system.ort,
            kanton: this.data.cantons.find(item => item.kantonID === system.kantonID),
            rooms: []
          }
        }
        system.gebaeudeID = data[buildingId].id;
        data[buildingId].rooms.push(system);
        let locationId = system.ort.trim();
        if (!locations[locationId]) {
          locations[locationId] = {
            name: locationId,
            plz: system.plz,
            bounds: L.latLngBounds()
          }
        }
        locations[locationId].bounds.extend(L.latLng(system.lat, system.lng));
        return data;
      }, {});
      this.data.buildings = Object.keys(buildings).map(key => {
        return buildings[key];
      }).map((building, index, all) => {
        let org = {};
        building.rooms.forEach((room) => {
          let orgId = room.organisation;
          if (!org[orgId]) {
            org[orgId] = {
              name: room.organisation,
              rooms: []
            }
          }
          org[orgId].rooms.push(room);
        });
        building.organisations = Object.keys(org).map(key => org[key]);
        return building;
      });
      this.data.locations = Object.keys(locations).map(key => locations[key]);
      return this.data;
    });
  }

  queryBuildingsByRoomType(roomType) {
    return this.data.buildings.slice().reduce((acc, building) => {
      let rooms = building.rooms.slice().filter(room => room.typID === roomType.typID);
      if (rooms.length > 0) {
        let org = {};
        building.rooms = rooms;
        building.rooms.forEach((room) => {
          let orgId = room.organisation;
          if (!org[orgId]) {
            org[orgId] = {
              name: room.organisation,
              rooms: []
            }
          }
          org[orgId].rooms.push(room);
        });
        building.organisations = Object.keys(org).map(key => org[key]);
        acc.push(building);
      }
      return acc;
    }, []);
  }

  search(text, count=10) {
    let results = {
      locations: [],
      buildings: []
    }
    if (text.length < 3) {
      return results;
    }
    text = text.toLowerCase();
    text = text.split(' ');
    text = text.filter(word => word !== '');

    for (let i = 0; i < this.data.locations.length; i++) {
      let location = this.data.locations[i];
      let searchString = `${location.name} ${location.plz}`.toLowerCase();
      let matches = 0;
      for (let i = 0; i < text.length; i++) {
        let word = text[i];
        if (searchString.indexOf(word) > -1) {
          matches++;
        }
      }
      if (matches === text.length) {
        results.locations.push(location);
      }
      if (results.locations.length === 3) {
        break;
      }
    }

    for (let i = 0; i < this.data.buildings.length; i++) {
      let building = this.data.buildings[i];
      let searchString = `${building.name} ${building.strasse_nr} ${building.ort} ${building.plz} ${building.kanton.kantonkuerzel}`;
      building.organisations.forEach(org => {
        searchString = `${searchString} ${org.name}`;
      });
      building.rooms.forEach(room => {
        let roomType = this.data.roomtypes.find(item => item.typID === room.typID);
        searchString = `${searchString} ${roomType.typ}`;
      });
      searchString = searchString.toLowerCase();
      let matches = 0;
      for (let i = 0; i < text.length; i++) {
        let word = text[i];
        if (searchString.indexOf(word) > -1) {
          matches++;
        }
      }
      if (matches === text.length) {
        results.buildings.push(building);
      }
      if (results.buildings.length === count) {
        break;
      }
    }

    return results;
  }
}
<commit_msg>trim search text and concat functions<commit_after>import {HttpClient} from 'aurelia-fetch-client';

const tables = ['systems', 'roomtypes', 'cantons', 'technologies'];

export class Database {

  constructor() {
    this.data = {};
    this.http = new HttpClient();
    this.http.configure(config => {
      config
        .useStandardConfiguration()
        .withBaseUrl('//www.zeta.hoeranlagenverzeichnis.ch/query.php?')
        .withDefaults({
          headers: {
            // 'X-Requested-With': 'Fetch'
          }
        })
        .withInterceptor({
          request(request) {
            console.log(`Requesting ${request.method} ${request.url}`);
            return request;
          },
          response(response) {
            console.log(`Received ${response.status} ${response.url}`);
            return response;
          }
        });
    });
  }

  load() {
    return Promise.all(
      tables.map(table => this.http
        .fetch(`table=${table}`)
        .then(response => response.json())
        .catch(error => {
          console.error(error);
          return {
            results: []
          };
        }))
    ).then(data => {
      tables.forEach((table, index) => {
        this.data[table] = data[index].results;
      });
      let locations = {};
      let buildingCounter = 0;
      let buildings = this.data.systems.reduce((data, system) => {
        let buildingId = `${system.plz}-${system.strasse_nr}-${system.gebaeude}`;
        let orgId = `${system.organisation}`;
        if (!data[buildingId]) {
          buildingCounter++;
          data[buildingId] = {
            id: buildingCounter,
            name: system.gebaeude,
            lat: system.lat,
            lng: system.lng,
            strasse_nr: system.strasse_nr,
            plz: system.plz,
            ort: system.ort,
            kanton: this.data.cantons.find(item => item.kantonID === system.kantonID),
            rooms: []
          }
        }
        system.gebaeudeID = data[buildingId].id;
        data[buildingId].rooms.push(system);
        let locationId = system.ort.trim();
        if (!locations[locationId]) {
          locations[locationId] = {
            name: locationId,
            plz: system.plz,
            bounds: L.latLngBounds()
          }
        }
        locations[locationId].bounds.extend(L.latLng(system.lat, system.lng));
        return data;
      }, {});
      this.data.buildings = Object.keys(buildings).map(key => {
        return buildings[key];
      }).map((building, index, all) => {
        let org = {};
        building.rooms.forEach((room) => {
          let orgId = room.organisation;
          if (!org[orgId]) {
            org[orgId] = {
              name: room.organisation,
              rooms: []
            }
          }
          org[orgId].rooms.push(room);
        });
        building.organisations = Object.keys(org).map(key => org[key]);
        return building;
      });
      this.data.locations = Object.keys(locations).map(key => locations[key]);
      return this.data;
    });
  }

  queryBuildingsByRoomType(roomType) {
    return this.data.buildings.slice().reduce((acc, building) => {
      let rooms = building.rooms.slice().filter(room => room.typID === roomType.typID);
      if (rooms.length > 0) {
        let org = {};
        building.rooms = rooms;
        building.rooms.forEach((room) => {
          let orgId = room.organisation;
          if (!org[orgId]) {
            org[orgId] = {
              name: room.organisation,
              rooms: []
            }
          }
          org[orgId].rooms.push(room);
        });
        building.organisations = Object.keys(org).map(key => org[key]);
        acc.push(building);
      }
      return acc;
    }, []);
  }

  search(text, count=10) {
    let results = {
      locations: [],
      buildings: []
    }

    text = text.trim();

    if (text.length < 3) {
      return results;
    }

    text = text.toLowerCase().split(' ').filter(word => word !== '');

    for (let i = 0; i < this.data.locations.length; i++) {
      let location = this.data.locations[i];
      let searchString = `${location.name} ${location.plz}`.toLowerCase();
      let matches = 0;
      for (let i = 0; i < text.length; i++) {
        let word = text[i];
        if (searchString.indexOf(word) > -1) {
          matches++;
        }
      }
      if (matches === text.length) {
        results.locations.push(location);
      }
      if (results.locations.length === 3) {
        break;
      }
    }

    for (let i = 0; i < this.data.buildings.length; i++) {
      let building = this.data.buildings[i];
      let searchString = `${building.name} ${building.strasse_nr} ${building.ort} ${building.plz} ${building.kanton.kantonkuerzel}`;
      building.organisations.forEach(org => {
        searchString = `${searchString} ${org.name}`;
      });
      building.rooms.forEach(room => {
        let roomType = this.data.roomtypes.find(item => item.typID === room.typID);
        searchString = `${searchString} ${roomType.typ}`;
      });
      searchString = searchString.toLowerCase();
      let matches = 0;
      for (let i = 0; i < text.length; i++) {
        let word = text[i];
        if (searchString.indexOf(word) > -1) {
          matches++;
        }
      }
      if (matches === text.length) {
        results.buildings.push(building);
      }
      if (results.buildings.length === count) {
        break;
      }
    }

    return results;
  }
}
<|endoftext|>


<commit_before>var _ = require("lodash");
var path = require("path");
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var compiler = require("krl-compiler");
var version = require("../package.json").version;

var httpGetKRL = function(url, callback){
    request(url, function(err, resp, body){
        if(err)
            return callback(err);
        if(resp.statusCode !== 200)
            return callback(new Error("Got a statusCode=" + resp.statusCode + " for: " + url));

        callback(null, body);
    });
};

var mergeGetPost = function(req){
    //give preference to post body params
    return _.assign({}, req.query, req.body);
};

module.exports = function(pe){
    var logs = {};
    var logRID = "io.picolabs.logging";
    var logEntry = function(context,message){
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            episode.logs.push(timestamp+" "+message);
        } else {
            console.log("[ERROR]","no episode found for",eci);
        }
    };
    var logEpisode = function(pico_id,context,callback){
        var eci = context.eci;
        var episode = logs[eci];
        if (!episode) {
            console.log("[ERROR]","no episode found for",eci);
            return;
        }
        pe.db.getEntVar(pico_id,logRID,"status",function(e,status){
            if (status) {
                pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){
                    data[episode.key] = episode.logs;
                    pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){
                        callback(delete logs[eci]);
                    });
                });
            } else {
                callback(delete logs[eci]);
            }
        });
    };
    pe.emitter.on("episode_start", function(context){
        console.log("EPISODE_START",context);
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            console.log("[ERROR]","episode already exists for",eci);
        } else {
            episode = {};
            episode.key = (
                    timestamp + " - " + eci
                    + " - " + ((context.event) ? context.event.eid : "query")
                    ).replace(/[.]/g, "-");
            episode.logs = [];
            logs[eci] = episode;
        }
    });
    pe.emitter.on("klog", function(context, val, message){
        console.log("[KLOG]", message, val);
        logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val));
    });
    pe.emitter.on("log-error", function(context_info, expression){
        console.log("[LOG-ERROR]",context_info,expression);
        logEntry(context_info,"[LOG-ERROR] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-warn", function(context_info, expression){
        console.log("[LOG-WARN]",context_info,expression);
        logEntry(context_info,"[LOG-WARN] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-info", function(context_info, expression){
        console.log("[LOG-INFO]",context_info,expression);
        logEntry(context_info,"[LOG-INFO] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-debug", function(context_info, expression){
        console.log("[LOG-DEBUG]",context_info,expression);
        logEntry(context_info,"[LOG-DEBUG] "+JSON.stringify(expression));
    });
    pe.emitter.on("debug", function(context, message){
        console.log("[DEBUG]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("error", function(context, message){
        console.error("[ERROR]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("episode_stop", function(context){
        console.log("EPISODE_STOP",context);
        var callback = function(outcome){
            console.log("[EPISODE_REMOVED]",outcome);
        };
        logEpisode(context.pico_id,context,callback);
    });

    var app = express();
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });
    app.use(express.static(path.resolve(__dirname, "..", "public")));
    app.use(bodyParser.json({type: "application/json"}));
    app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false}));

    var errResp = function(res, err){
        res.status(err.statusCode || 500).json({error: err.message});
    };

    app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){
        var event = {
            eci: req.params.eci,
            eid: req.params.eid,
            domain: req.params.domain,
            type: req.params.type,
            attrs: mergeGetPost(req)
        };
        pe.signalEvent(event, function(err, response){
            if(err) return errResp(res, err);
            res.json(response);
        });
    });

    app.all("/sky/cloud/:eci/:rid/:function", function(req, res){
        var query = {
            eci: req.params.eci,
            rid: req.params.rid,
            name: req.params["function"],
            args: mergeGetPost(req)
        };
        pe.runQuery(query, function(err, data){
            if(err) return errResp(res, err);
            if(_.isFunction(data)){
                data(res);
            }else{
                res.json(data);
            }
        });
    });

    app.all("/api/engine-version", function(req, res){
        res.json({"version": version});
    });

    app.all("/api/db-dump", function(req, res){
        pe.db.toObj(function(err, db_data){
            if(err) return errResp(res, err);
            res.json(db_data);
        });
    });

    app.all("/api/owner-eci", function(req, res){
        pe.db.getOwnerECI(function(err, eci){
            if(err) return errResp(res, err);
            res.json({ok: true, eci: eci});
        });
    });

    app.all("/api/pico/:id/new-channel", function(req, res){
        var args = mergeGetPost(req);

        pe.db.newChannel({
            pico_id: req.params.id,
            name: args.name,
            type: args.type
        }, function(err, new_channel){
            if(err) return errResp(res, err);
            res.json(new_channel);
        });
    });

    app.all("/api/pico/:id/rm-channel/:eci", function(req, res){
        pe.db.removeChannel(req.params.id, req.params.eci, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){
        pe.db.removeRuleset(req.params.id, req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ent-var/:rid/:var_name", function(req, res){
        pe.db.removeEntVar(req.params.id, req.params.rid, req.params.var_name, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/ruleset/compile", function(req, res){
        var args = mergeGetPost(req);

        try{
            res.json({ok: true, code: compiler(args.src).code});
        }catch(err){
            res.status(400).json({ error: err.toString() });
        }
    });

    app.all("/api/ruleset/register", function(req, res){
        var args = mergeGetPost(req);

        var register = function(src, meta){
            pe.registerRuleset(src, meta || {}, function(err, data){
                if(err) return errResp(res, err);
                res.json({ok: true, rid: data.rid, hash: data.hash});
            });
        };
        if(_.isString(args.src)){
            register(args.src);
        }else if(_.isString(args.url)){
            httpGetKRL(args.url, function(err, src){
                if(err) return errResp(res, err);
                register(src, {url: args.url});
            });
        }else{
            errResp(res, new Error("expected `src` or `url`"));
        }
    });

    app.all("/api/ruleset/flush/:rid", function(req, res){
        var rid = req.params.rid;
        pe.db.getEnabledRuleset(rid, function(err, rs_data){
            if(err) return errResp(res, err);

            var url = rs_data.url;
            if(_.isString(url)){
                httpGetKRL(url, function(err, src){
                    if(err) return errResp(res, err);

                    pe.registerRuleset(src, {url: url}, function(err, data){
                        if(err) return errResp(res, err);

                        res.json({ok: true, rid: data.rid, hash: data.hash});
                    });
                });
            }else{
                errResp(res, new Error("cannot flush a locally registered ruleset"));
            }
        });
    });

    return app;
};
<commit_msg>ability to register rulesets using file:// urls<commit_after>var _ = require("lodash");
var fs = require("fs");
var path = require("path");
var urllib = require("url");
var express = require("express");
var request = require("request");
var bodyParser = require("body-parser");
var compiler = require("krl-compiler");
var version = require("../package.json").version;

var httpGetKRL = function(url, callback){
    request(url, function(err, resp, body){
        if(err)
            return callback(err);
        if(resp.statusCode !== 200)
            return callback(new Error("Got a statusCode=" + resp.statusCode + " for: " + url));

        callback(null, body);
    });
};

var getKRLByURL = function(url, callback){
    var url_parsed = urllib.parse(url);
    if(url_parsed.protocol === "file:"){
        fs.readFile(url_parsed.path, function(err, data){
            if(err) return callback(err);
            callback(null, data.toString());
        });
        return;
    }
    httpGetKRL(url, callback);
};

var mergeGetPost = function(req){
    //give preference to post body params
    return _.assign({}, req.query, req.body);
};

module.exports = function(pe){
    var logs = {};
    var logRID = "io.picolabs.logging";
    var logEntry = function(context,message){
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            episode.logs.push(timestamp+" "+message);
        } else {
            console.log("[ERROR]","no episode found for",eci);
        }
    };
    var logEpisode = function(pico_id,context,callback){
        var eci = context.eci;
        var episode = logs[eci];
        if (!episode) {
            console.log("[ERROR]","no episode found for",eci);
            return;
        }
        pe.db.getEntVar(pico_id,logRID,"status",function(e,status){
            if (status) {
                pe.db.getEntVar(pico_id,logRID,"logs",function(e,data){
                    data[episode.key] = episode.logs;
                    pe.db.putEntVar(pico_id,logRID,"logs",data,function(e){
                        callback(delete logs[eci]);
                    });
                });
            } else {
                callback(delete logs[eci]);
            }
        });
    };
    pe.emitter.on("episode_start", function(context){
        console.log("EPISODE_START",context);
        var eci = context.eci;
        var timestamp = (new Date()).toISOString();
        var episode = logs[eci];
        if (episode) {
            console.log("[ERROR]","episode already exists for",eci);
        } else {
            episode = {};
            episode.key = (
                    timestamp + " - " + eci
                    + " - " + ((context.event) ? context.event.eid : "query")
                    ).replace(/[.]/g, "-");
            episode.logs = [];
            logs[eci] = episode;
        }
    });
    pe.emitter.on("klog", function(context, val, message){
        console.log("[KLOG]", message, val);
        logEntry(context,"[KLOG] "+message+" "+JSON.stringify(val));
    });
    pe.emitter.on("log-error", function(context_info, expression){
        console.log("[LOG-ERROR]",context_info,expression);
        logEntry(context_info,"[LOG-ERROR] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-warn", function(context_info, expression){
        console.log("[LOG-WARN]",context_info,expression);
        logEntry(context_info,"[LOG-WARN] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-info", function(context_info, expression){
        console.log("[LOG-INFO]",context_info,expression);
        logEntry(context_info,"[LOG-INFO] "+JSON.stringify(expression));
    });
    pe.emitter.on("log-debug", function(context_info, expression){
        console.log("[LOG-DEBUG]",context_info,expression);
        logEntry(context_info,"[LOG-DEBUG] "+JSON.stringify(expression));
    });
    pe.emitter.on("debug", function(context, message){
        console.log("[DEBUG]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("error", function(context, message){
        console.error("[ERROR]", context, message);
        logEntry(context,message);
    });
    pe.emitter.on("episode_stop", function(context){
        console.log("EPISODE_STOP",context);
        var callback = function(outcome){
            console.log("[EPISODE_REMOVED]",outcome);
        };
        logEpisode(context.pico_id,context,callback);
    });

    var app = express();
    app.use(function(req, res, next) {
        res.header("Access-Control-Allow-Origin", "*");
        res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
        next();
    });
    app.use(express.static(path.resolve(__dirname, "..", "public")));
    app.use(bodyParser.json({type: "application/json"}));
    app.use(bodyParser.urlencoded({type: "application/x-www-form-urlencoded", extended: false}));

    var errResp = function(res, err){
        res.status(err.statusCode || 500).json({error: err.message});
    };

    app.all("/sky/event/:eci/:eid/:domain/:type", function(req, res){
        var event = {
            eci: req.params.eci,
            eid: req.params.eid,
            domain: req.params.domain,
            type: req.params.type,
            attrs: mergeGetPost(req)
        };
        pe.signalEvent(event, function(err, response){
            if(err) return errResp(res, err);
            res.json(response);
        });
    });

    app.all("/sky/cloud/:eci/:rid/:function", function(req, res){
        var query = {
            eci: req.params.eci,
            rid: req.params.rid,
            name: req.params["function"],
            args: mergeGetPost(req)
        };
        pe.runQuery(query, function(err, data){
            if(err) return errResp(res, err);
            if(_.isFunction(data)){
                data(res);
            }else{
                res.json(data);
            }
        });
    });

    app.all("/api/engine-version", function(req, res){
        res.json({"version": version});
    });

    app.all("/api/db-dump", function(req, res){
        pe.db.toObj(function(err, db_data){
            if(err) return errResp(res, err);
            res.json(db_data);
        });
    });

    app.all("/api/owner-eci", function(req, res){
        pe.db.getOwnerECI(function(err, eci){
            if(err) return errResp(res, err);
            res.json({ok: true, eci: eci});
        });
    });

    app.all("/api/pico/:id/new-channel", function(req, res){
        var args = mergeGetPost(req);

        pe.db.newChannel({
            pico_id: req.params.id,
            name: args.name,
            type: args.type
        }, function(err, new_channel){
            if(err) return errResp(res, err);
            res.json(new_channel);
        });
    });

    app.all("/api/pico/:id/rm-channel/:eci", function(req, res){
        pe.db.removeChannel(req.params.id, req.params.eci, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ruleset/:rid", function(req, res){
        pe.db.removeRuleset(req.params.id, req.params.rid, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/pico/:id/rm-ent-var/:rid/:var_name", function(req, res){
        pe.db.removeEntVar(req.params.id, req.params.rid, req.params.var_name, function(err){
            if(err) return errResp(res, err);
            res.json({ok: true});
        });
    });

    app.all("/api/ruleset/compile", function(req, res){
        var args = mergeGetPost(req);

        try{
            res.json({ok: true, code: compiler(args.src).code});
        }catch(err){
            res.status(400).json({ error: err.toString() });
        }
    });

    app.all("/api/ruleset/register", function(req, res){
        var args = mergeGetPost(req);

        var register = function(src, meta){
            pe.registerRuleset(src, meta || {}, function(err, data){
                if(err) return errResp(res, err);
                res.json({ok: true, rid: data.rid, hash: data.hash});
            });
        };
        if(_.isString(args.src)){
            register(args.src);
        }else if(_.isString(args.url)){
            getKRLByURL(args.url, function(err, src){
                if(err) return errResp(res, err);
                register(src, {url: args.url});
            });
        }else{
            errResp(res, new Error("expected `src` or `url`"));
        }
    });

    app.all("/api/ruleset/flush/:rid", function(req, res){
        var rid = req.params.rid;
        pe.db.getEnabledRuleset(rid, function(err, rs_data){
            if(err) return errResp(res, err);

            var url = rs_data.url;
            if(_.isString(url)){
                getKRLByURL(url, function(err, src){
                    if(err) return errResp(res, err);

                    pe.registerRuleset(src, {url: url}, function(err, data){
                        if(err) return errResp(res, err);

                        res.json({ok: true, rid: data.rid, hash: data.hash});
                    });
                });
            }else{
                errResp(res, new Error("cannot flush a locally registered ruleset"));
            }
        });
    });

    return app;
};
<|endoftext|>


<commit_before>pc.extend(pc, function () {
    var rayDepth = 100000;
    var tmpVecA = new pc.Vec3();

    /**
     * @private
     * @name pc.Plane
     * @class An infinite plane
     * @description Create an infinite plane
     */
    var Plane = function Plane (point, normal) {
        this.normal = normal || new pc.Vec3(0, 0, 1);
        this.point  = point || new pc.Vec3(0, 0, 0);
        this.d = -this.normal.dot(this.point);
    };

    Plane.prototype = {
        distance: function (pos) {
            return this.normal.dot(pos) + this.d;
        },

        intersect: function (p0, p1) {
            var d0 = this.distance(p0);
            var d1 = this.distance(p1);

            return d0 / (d0 - d1);
        },

        intersectPosition: function (p0, p1) {
            var t = this.intersect(p0, p1);
            var r = tmpVecA.lerp(p0, p1, t);
            return r;
        },

        /**
         * @function
         * @name pc.Plane#intersectsRay
         * @description Test if a ray intersects with the infinite plane
         * @param {pc.Ray} ray Ray to test against (direction must be normalized)
         * @param {pc.Vec3} [point] If there is an intersection, the intersection point will be copied into here
         * @returns {Boolean} True if there is an intersection
         */
        intersectsRay: function (ray, point) {
            var p0 = ray.origin;
            var p1 = tmpVecA.copy(ray.direction).scale(rayDepth).add(p0);

            var t = this.intersect(p0, p1);

            var intersects = t >= 0;
            if (intersects && point)
                point.copy(ray.direction).scale(t * rayDepth).add(ray.origin);

            return intersects;
        },
    };

    return {
        Plane: Plane
    };
}());
<commit_msg>Simplified pc.Plane.intersectsRay function<commit_after>pc.extend(pc, function () {
    var rayDepth = 100000;
    var tmpVecA = new pc.Vec3();

    /**
     * @private
     * @name pc.Plane
     * @class An infinite plane
     * @description Create an infinite plane
     */
    var Plane = function Plane (point, normal) {
        this.normal = normal || new pc.Vec3(0, 0, 1);
        this.point  = point || new pc.Vec3(0, 0, 0);
        this.d = -this.normal.dot(this.point);
    };

    Plane.prototype = {
        distance: function (pos) {
            return this.normal.dot(pos) + this.d;
        },

        intersect: function (p0, p1) {
            var d0 = this.distance(p0);
            var d1 = this.distance(p1);

            return d0 / (d0 - d1);
        },

        intersectPosition: function (p0, p1) {
            var t = this.intersect(p0, p1);
            var r = tmpVecA.lerp(p0, p1, t);
            return r;
        },

        /**
         * @function
         * @name pc.Plane#intersectsRay
         * @description Test if a ray intersects with the infinite plane
         * @param {pc.Ray} ray Ray to test against (direction must be normalized)
         * @param {pc.Vec3} [point] If there is an intersection, the intersection point will be copied into here
         * @returns {Boolean} True if there is an intersection
         */
        intersectsRay: function (ray, point) {
            var pointToOrigin = tmpVecA.sub2(this.point, ray.origin);
            var t = this.normal.dot(pointToOrigin) / this.normal.dot(ray.direction);
            var intersects = t >= 0;

            if (intersects && point)
                point.copy(ray.direction).scale(t).add(ray.origin);

            return intersects;
        },
    };

    return {
        Plane: Plane
    };
}());
<|endoftext|>


<commit_before>var button;
var half_button_width = 124;
var half_button_height = 32;
var actionOnClick = function () {
	console.log('click');
	game.state.start('level-1');
};
var pad_clicked = false;
var over = function (levelText) {
    levelText.tint = 0xF9FE99;
    levelText.x = levelText.x+2;
    levelText.y = levelText.y+2;
};
var out = function (levelText) {
                levelText.tint = 0xFFFFFF;
            levelText.x = levelText.x-2;
            levelText.y = levelText.y-2;
}

var addLevelButton = function (text,x,y) {
    var lb = game.add.bitmapText(x, y, 'bits-0',text,32);
    lb.inputEnabled = true;

    lb.events.onInputOver.add(over, this);
    lb.events.onInputOut.add(out, this);
    lb.events.onInputDown.add(actionOnClick, this);
}
module.exports = {
    create: function(){
    //This is just like any other Phaser create function
    //game.state.start('play');

    	//button = game.add.button(half_button_width, (game.world.centerY - half_button_height), 'button', actionOnClick, this, 1, 0, 2);
        addLevelButton('Level 1',32,16);
        addLevelButton('Level 2',32*6,16);
        addLevelButton('Level 3',32*12,16);
        addLevelButton('Level 4',32,16*5);
        addLevelButton('Level 5',32*6,16*5);
        addLevelButton('Level 6',32*12,16*5);
		game.input.gamepad.start();

		pad = game.input.gamepad.pad1;

    },
    update: function(){
    //Game logic goes here
        if (pad.isDown(Phaser.Gamepad.XBOX360_A)) {
    		pad_clicked = true;

    	}

    	if (pad.isUp(Phaser.Gamepad.XBOX360_A) && pad_clicked) {
    		actionOnClick();
    	}
    },
};
<commit_msg>Added level text and seletions<commit_after>var button;
var half_button_width = 124;
var half_button_height = 32;
var actionOnClick = function (levelButton) {
    console.log(levelButton.levelSelect);
    var levelNumber = levelButton.text.split(' ')[1];
    var level = levels.filter(function(item) {
        console.log(item);
        return  levelNumber == item.level;
    });
    if(level.length === 0)return;
	console.log(level);
    console.log(levelButton);
    console.log(levelButton.text);
    console.log('level-' + level[0].level);
    game.globals.LEVELNUMBER =level[0].level;
    game.globals.LEVEL = 'level-' + level[0].level;
	game.state.start('level-' + level[0].level);
};
var pad_clicked = false;
var levels;
var over = function (levelText) {

    levelText.tint = 0xF9FE99;
    levelText.x = levelText.x+2;
    levelText.y = levelText.y+2;
};
var out = function (levelText) {
    levelText.tint = 0xFFFFFF;
    levelText.x = levelText.x-2;
    levelText.y = levelText.y-2;
}

var addLevelButton = function (text, x, y) {
    var lb = game.add.bitmapText(x, y, 'bits-0','Level ' + text,32);
    lb.inputEnabled = true;
    lb.levelSelect = text
    lb.events.onInputOver.add(over, this);
    lb.events.onInputOut.add(out, this);
    lb.events.onInputDown.add(actionOnClick, this);
}
module.exports = {
    create: function(){
    //This is just like any other Phaser create function
    //game.state.start('play');

    	//button = game.add.button(half_button_width, (game.world.centerY - half_button_height), 'button', actionOnClick, this, 1, 0, 2);
        var DataBase = require('../utils/storage');
        var db = new DataBase();

        console.log(db);
        db.getAllLevelsCleared().fetch(function (clearedLevels) {
            console.log(clearedLevels)

            levels = clearedLevels;
        });
        console.log(levels)
        addLevelButton('1',32,16);
        addLevelButton('2',32*6,16);
        addLevelButton('3',32*12,16);
        addLevelButton('4',32,16*5);
        addLevelButton('5',32*6,16*5);
        addLevelButton('6',32*12,16*5);
		game.input.gamepad.start();

		pad = game.input.gamepad.pad1;

    },
    update: function(){
    //Game logic goes here
        if (pad.isDown(Phaser.Gamepad.XBOX360_A)) {
    		pad_clicked = true;

    	}

    	if (pad.isUp(Phaser.Gamepad.XBOX360_A) && pad_clicked) {
    		actionOnClick();
    	}
    },
};
<|endoftext|>


<commit_before>import FormData from '../utils/io/form-data'
import runtime from '../core/runtime.js'
import callServices from '../utils/services/call.js'
import getShortId from '../utils/short-id.js'
import auth from '../auth.js'
import Promise from 'bluebird'
import fetch from '../utils/io/fetch.js'
import getMimeTypeFromFileName from '../utils/get-mime-type-from-filename.js'

// configs

var ANONYMOUS_USER_ID = 'anonymous-uploads'
var KEY_USER_ID_PLACEHOLDER = '{{userId}}'

// main

export default function putToStore (files, options) {

  options = options || {}

  if (!Array.isArray(files)) {

    // upload single file

    return putSingleFileToStore(files, options)

  } else {

    // upload multiple files and bundle progress events
    // TODO: add dir option

    var totalSize_ = 0
    var progress_ = []
    var onProgress_ = options.onProgress

    return Promise.map(files, function(file, i){
      totalSize_ += file.size
      return putSingleFileToStore(file, {
        dir: options.dir,
        onProgress: function(progress, total){
          progress_[i] = progress
          onProgress_(progress_.reduce(function(a, b) { return a+b; }, 0), totalSize_)
        }
      })
    })

  }

}

// private

function putSingleFileToStore (file, options) {

  // API
  var key = options.key
  var dir = options.dir
  var fileName = options.filename || options.fileName || file.name || 'unnamed.txt'
  var onProgress = options.onProgress

  return resolveKey(key, dir, fileName)
    .then(validateKey)
    .then(function (key) {
      return getCredentials(file, key)
    })
    .then(function (credentials) {
      return uploadFile(file, credentials, onProgress)
    })

}

function resolveKey (key, dir, fileName) {
  // prefer key. fallback to dir + fileName
  key = key ? key : (dir ? (dir[dir.length - 1] === '/' ? dir : dir + '/') + fileName : null)
  var isTemplateKey = key && key.indexOf(KEY_USER_ID_PLACEHOLDER) > -1

  // full key including userId provided
  if (key && !isTemplateKey) return Promise.resolve(key)

  // get user id
  return auth.getSession().then(function(session){
    if (isTemplateKey) {
      if (session.isAuthenticated) {
        // replace user id in template key
        return key.replace( '{{userId}}', session.user.id )
      } else {
        console.error('Using key parameter with template syntax requires authentication.')
        // reject with user friendly error message
        return Promise.reject('Please log in to upload file.')
      }
    } else {
      // key not provided
      var uploadFolder = getFormattedDate() + '_' + getShortId()
      if (session.isAuthenticated) {
        // construct new user specific key
        return '/' + session.user.id + '/' + uploadFolder + '/' + fileName
      } else {
        // construct anonymous key
        return '/' + ANONYMOUS_USER_ID + '/' + uploadFolder + '/' + fileName
      }
    }
  })
}

var keyValidationRegex = /^\/([a-zA-Z0-9\.\-\_]+\/)+([a-zA-Z0-9\.\-\_]+)$/
function validateKey (key) {
  if (keyValidationRegex.test(key)) {
    return Promise.resolve(key)
  } else {
    return Promise.reject(
      'Key format validation failed.\n'
      + key + '\n'
      + 'Key must match the following pattern\n'
      + '- must start with a slash\n'
      + '- must not end with a slash\n'
      + '- must have one or more directories\n'
      + '- must not include double slashes like: "//"\n'
      + '- allowed characters are: a-z A-Z 0-9 _ - . /'
    )
  }
}

function getCredentials (file, key) {
  // strip leading slash
  if (key[0] === '/') key = key.substring(1)
  // get credentials for upload
  return callServices('S3.getCredentials', {
    contentLength: file.size || file.length,
    contentType: getMimeTypeFromFileName(key),
    key: key
  })
}

function uploadFile (file, credentials, onProgress) {
  // upload directly to S3 using credentials
  var fd = new FormData()
  fd.append('key', credentials.key)
  fd.append('AWSAccessKeyId', credentials.AWSAccessKeyId)
  fd.append('acl', credentials.acl)
  fd.append('Content-Type', credentials.contentType)
  fd.append('policy', credentials.policy)
  fd.append('signature', credentials.signature)
  fd.append('success_action_status', '201')
  if (credentials.contentEncoding) {
    fd.append('Content-Encoding', credentials.contentEncoding)
  }
  fd.append('file', file)

  if (runtime.isBrowser) {

    // upload using XHR (in order to provide progress info)
    return new Promise(function(resolve, reject){
      var xhr = new XMLHttpRequest()
      xhr.crossOrigin = 'Anonymous'
      xhr.onload = function (event) {
        if (xhr.status >= 200 && xhr.status < 300) {
          var key = getKeyFromS3Response(xhr.responseText)
          key ? resolve(key) : reject ('Error Uploading File: '+xhr.responseText)
        } else {
          reject ('Error Uploading File: '+xhr.responseText)
        }
      }
      xhr.onerror = function (event) {
        reject(event)
      }
      if (onProgress) {
        xhr.upload.addEventListener('progress', function(e){
          onProgress(e.loaded, e.total)
        }, false)
      }
      xhr.open('POST', credentials.url, true)
      xhr.send(fd)
    })

  } else {

    // node environment: upload using fetch
    return fetch(credentials.url, {method: 'POST', body: fd}).then(function (res) {
      return res.text()
    }).then(function(str){
      return getKeyFromS3Response(str) || Promise.reject('Error Uploading File: '+str)
    })

  }
}

function getKeyFromS3Response (str) {
  // get file key from response
  var s = /<Key>(.*)<\/Key>/gi.exec(str)
  return s ? '/'+s[1] : false
}

function getFormattedDate() {
  var d = new Date()
  return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate()
    + '_' + d.getHours() + '-' + d.getMinutes() // + '-' + d.getSeconds()
}<commit_msg>Fixed key mismatch<commit_after>import FormData from '../utils/io/form-data'
import runtime from '../core/runtime.js'
import callServices from '../utils/services/call.js'
import getShortId from '../utils/short-id.js'
import auth from '../auth.js'
import Promise from 'bluebird'
import fetch from '../utils/io/fetch.js'
import getMimeTypeFromFileName from '../utils/get-mime-type-from-filename.js'

// configs

var ANONYMOUS_USER_ID = 'anonymous-uploads'
var KEY_USER_ID_PLACEHOLDER = '{{userId}}'

// main

export default function putToStore (files, options) {

  options = options || {}

  if (!Array.isArray(files)) {

    // upload single file

    return putSingleFileToStore(files, options)

  } else {

    // upload multiple files and bundle progress events
    // TODO: add dir option

    var totalSize_ = 0
    var progress_ = []
    var onProgress_ = options.onProgress

    return Promise.map(files, function(file, i){
      totalSize_ += file.size
      return putSingleFileToStore(file, {
        dir: options.dir,
        onProgress: function(progress, total){
          progress_[i] = progress
          if (onProgress_) onProgress_(progress_.reduce(function(a, b) { return a+b; }, 0), totalSize_)
        }
      })
    })

  }

}

// private

function putSingleFileToStore (file, options) {

  // API
  var key = options.key
  var dir = options.dir
  var fileName = options.filename || options.fileName || file.name || 'unnamed.txt'
  var onProgress = options.onProgress

  return resolveKey(key, dir, fileName)
    .then(validateKey)
    .then(function (key) {
      return getCredentials(file, key, fileName)
    })
    .then(function (credentials) {
      return uploadFile(file, credentials, onProgress)
    })

}

function resolveKey (key, dir, fileName) {
  // prefer key. fallback to dir + fileName
  key = key ? key : (dir ? (dir[dir.length - 1] === '/' ? dir : dir + '/') + fileName : null)
  var isTemplateKey = !!(key && key.indexOf(KEY_USER_ID_PLACEHOLDER) > -1)

  // full key including userId provided
  if (key && !isTemplateKey) return Promise.resolve(key)

  // get user id
  return auth.getSession().then(function(session){
    if (isTemplateKey) {
      if (session.isAuthenticated) {
        // replace user id in template key
        return key.replace( '{{userId}}', session.user.id )
      } else {
        console.error('Using key parameter with template syntax requires authentication.')
        // reject with user friendly error message
        return Promise.reject('Please log in to upload file.')
      }
    } else {
      // key not provided
      var uploadFolder = getFormattedDate() + '_' + getShortId()
      if (session.isAuthenticated) {
        // construct new user specific key
        return '/' + session.user.id + '/' + uploadFolder + '/' + fileName
      } else {
        // construct anonymous key
        var k = '/' + ANONYMOUS_USER_ID + '/' + uploadFolder + '/' + fileName
        return null
      }
    }
  })
}

var keyValidationRegex = /^\/([a-zA-Z0-9\.\-\_]+\/)+([a-zA-Z0-9\.\-\_]+)$/
function validateKey (key) {
  if (!key) {
    return Promise.resolve(null)
  } else if (keyValidationRegex.test(key)) {
    return Promise.resolve(key)
  } else {
    return Promise.reject(
      'Key format validation failed.\n'
      + key + '\n'
      + 'Key must match the following pattern\n'
      + '- must start with a slash\n'
      + '- must not end with a slash\n'
      + '- must have one or more directories\n'
      + '- must not include double slashes like: "//"\n'
      + '- allowed characters are: a-z A-Z 0-9 _ - . /'
    )
  }
}

function getCredentials (file, key, fileName) {
  // strip leading slash
  if (key && key[0] === '/') key = key.substring(1)
  // get credentials for upload
  var params = {
    contentLength: file.size || file.length
  }
  if (key) {
    params.contentType = getMimeTypeFromFileName(key)
    params.key = key
  } else if (fileName) {
    params.contentType = getMimeTypeFromFileName(fileName)
    params.fileName = fileName
  } else {
    return Promise.reject('Key or fileName param must be provided.')
  }
  return callServices('S3.getCredentials', params)
}

function uploadFile (file, credentials, onProgress) {
  // upload directly to S3 using credentials
  var fd = new FormData()
  fd.append('key', credentials.key)
  fd.append('AWSAccessKeyId', credentials.AWSAccessKeyId)
  fd.append('acl', credentials.acl)
  fd.append('Content-Type', credentials.contentType)
  fd.append('policy', credentials.policy)
  fd.append('signature', credentials.signature)
  fd.append('success_action_status', '201')
  if (credentials.contentEncoding) {
    fd.append('Content-Encoding', credentials.contentEncoding)
  }
  fd.append('file', file)

  if (runtime.isBrowser) {

    // upload using XHR (in order to provide progress info)
    return new Promise(function(resolve, reject){
      var xhr = new XMLHttpRequest()
      xhr.crossOrigin = 'Anonymous'
      xhr.onload = function (event) {
        if (xhr.status >= 200 && xhr.status < 300) {
          var key = getKeyFromS3Response(xhr.responseText)
          key ? resolve(key) : reject ('Error Uploading File: '+xhr.responseText)
        } else {
          reject ('Error Uploading File: '+xhr.responseText)
        }
      }
      xhr.onerror = function (event) {
        reject(event)
      }
      if (onProgress) {
        xhr.upload.addEventListener('progress', function(e){
          onProgress(e.loaded, e.total)
        }, false)
      }
      xhr.open('POST', credentials.url, true)
      xhr.send(fd)
    })

  } else {

    // node environment: upload using fetch
    return fetch(credentials.url, {method: 'POST', body: fd}).then(function (res) {
      return res.text()
    }).then(function(str){
      return getKeyFromS3Response(str) || Promise.reject('Error Uploading File: '+str)
    })

  }
}

function getKeyFromS3Response (str) {
  // get file key from response
  var s = /<Key>(.*)<\/Key>/gi.exec(str)
  return s ? '/'+s[1] : false
}

function getFormattedDate() {
  var d = new Date()
  return d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate()
    + '_' + d.getHours() + '-' + d.getMinutes() // + '-' + d.getSeconds()
}<|endoftext|>


<commit_before>import assignIn from 'lodash/assignIn';

import {
  DISPATCH_TYPE,
  STATE_TYPE
} from '../constants';

class Store {
  /**
   * Creates a new Proxy store
   * @param  {object} options An object of form {portName, state}, where `portName` is a required string and defines the name of the port for state transition changes and `state` is the initial state of this store (default `{}`)
   */
  constructor(extensionId, {portName, state = {}}) {
    if (!portName) {
      throw new Error('portName is required in options');
    }
    this.extensionId = extensionId; //keep the extensionId as an instance variable
    this.port = chrome.runtime.connect(this.extensionId, {name: portName});
    this.listeners = [];
    this.state = state;

    this.port.onMessage.addListener((message) => {
      if (message.type === STATE_TYPE) {
        this.replaceState(message.payload);
      }
    });

    this.dispatch = this.dispatch.bind(this); //add this context to dispatch
  }

  /**
   * Subscribes a listener function for all state changes
   * @param  {function} listener A listener function to be called when store state changes
   * @return {function}          An unsubscribe function which can be called to remove the listener from state updates
   */
  subscribe(listener) {
    this.listeners.push(listener);

    return () => {
      this.listeners = this.listeners.filter((l) => l !== listener);
    };
  }

  /**
   * Replace the current state with a new state. Notifies all listeners of state change.
   * @param  {object} state The new state for the store
   */
  replaceState(state) {
    this.state = state;

    this.listeners.forEach((l) => l());
  }

  /**
   * Get the current state of the store
   * @return {object} the current store state
   */
  getState() {
    return this.state;
  }

  /**
   * Dispatch an action to the background using messaging passing
   * @param  {object} data The action data to dispatch
   * @return {Promise}     Promise that will resolve/reject based on the action response from the background
   */
  dispatch(data) {
    return new Promise((resolve, reject) => {
      chrome.runtime.sendMessage(
        this.extensionId,
      {
        type: DISPATCH_TYPE,
        payload: data
      }, ({error, value}) => {
        //console.log('Error: ', error);
        if (error) {
          reject(assignIn((new Error()), error));
        } else {
          resolve(value && value.payload);
        }
      });
    });
  }
}

export default Store;
<commit_msg>optional extensionId param<commit_after>import assignIn from 'lodash/assignIn';

import {
  DISPATCH_TYPE,
  STATE_TYPE
} from '../constants';

class Store {
  /**
   * Creates a new Proxy store
   * @param  {object} options An object of form {portName, state}, where `portName` is a required string and defines the name of the port for state transition changes and `state` is the initial state of this store (default `{}`)
   */
  constructor({portName, state = {}, extensionId = ''}) {
    if (!portName) {
      throw new Error('portName is required in options');
    }
    this.extensionId = extensionId; //keep the extensionId as an instance variable
    this.port = chrome.runtime.connect(this.extensionId, {name: portName});
    this.listeners = [];
    this.state = state;

    this.port.onMessage.addListener((message) => {
      if (message.type === STATE_TYPE) {
        this.replaceState(message.payload);
      }
    });

    this.dispatch = this.dispatch.bind(this); //add this context to dispatch
  }

  /**
   * Subscribes a listener function for all state changes
   * @param  {function} listener A listener function to be called when store state changes
   * @return {function}          An unsubscribe function which can be called to remove the listener from state updates
   */
  subscribe(listener) {
    this.listeners.push(listener);

    return () => {
      this.listeners = this.listeners.filter((l) => l !== listener);
    };
  }

  /**
   * Replace the current state with a new state. Notifies all listeners of state change.
   * @param  {object} state The new state for the store
   */
  replaceState(state) {
    this.state = state;

    this.listeners.forEach((l) => l());
  }

  /**
   * Get the current state of the store
   * @return {object} the current store state
   */
  getState() {
    return this.state;
  }

  /**
   * Dispatch an action to the background using messaging passing
   * @param  {object} data The action data to dispatch
   * @return {Promise}     Promise that will resolve/reject based on the action response from the background
   */
  dispatch(data) {
    return new Promise((resolve, reject) => {
      chrome.runtime.sendMessage(
        this.extensionId,
      {
        type: DISPATCH_TYPE,
        payload: data
      }, ({error, value}) => {
        //console.log('Error: ', error);
        if (error) {
          reject(assignIn((new Error()), error));
        } else {
          resolve(value && value.payload);
        }
      });
    });
  }
}

export default Store;
<|endoftext|>


<commit_before>import axios from 'axios'
import { api } from '@/config'
import Moment from 'moment'

const state = {
	posts: []
}

const getters = {
	list: state => state.posts,
	count: state => state.posts.length,
	getPost: state => slug => state.posts.find(post => post.slug === slug)
}

const actions = {
	async init ({ commit }) {
		const { data } = await axios.get(api.posts)
		commit('SET_POSTS', data)
	}
}

const mutations = {
	SET_POSTS (state, posts) {
		const filteredPosts = []

		posts.forEach(item => {
			if (item.publish !== false && Moment().diff(item.date, 'days') > 1) {
				filteredPosts.push(item)
			}
		})
		state.posts = filteredPosts
	}
}

export default {
	namespaced: true,
	state,
	getters,
	actions,
	mutations
}
<commit_msg>posts update<commit_after>import axios from 'axios'
import { api } from '@/config'

const state = {
	posts: []
}

const getters = {
	list: state => state.posts,
	count: state => state.posts.length,
	getPost: state => slug => state.posts.find(post => post.slug === slug)
}

const actions = {
	async init ({ commit }) {
		const { data } = await axios.get(api.posts)
		commit('SET_POSTS', data)
	}
}

const mutations = {
	SET_POSTS (state, posts) {
		state.posts = posts.filter(item.publish !== false)
	}
}

export default {
	namespaced: true,
	state,
	getters,
	actions,
	mutations
}
<|endoftext|>


<commit_before>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2016  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import i from 'immutable';
import _ from 'lodash';

import * as a from '../actions';

const initialState = i.Map({});

const cleanUser = user => {
  const users = {};

  if (!user) {
    return users;
  }

  if (user.following) {
    for (const followed_user of user.following) {
      users[followed_user.id] = followed_user;
    }

    user = _.cloneDeep(user);
    delete user.following;
  }

  if (user.followers) {
    for (const follower of user.followers) {
      users[follower.id] = follower;
    }

    user = _.cloneDeep(user);
    delete user.following;
  }

  users[user.id] = user;

  return users;
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case a.users.ADD_USER:
    case a.users.SET_CURRENT_USER: {
      state = state.mergeDeep(i.fromJS(cleanUser(action.user)));

      break;
    }

    case a.posts.ADD_POST:
    case a.river.ADD_POST_TO_RIVER: {
      const user = action.post.user;
      const comment_authors = action.post.post_comments.map(comment => comment.user);
      const users = _.keyBy(_.uniq([user, ...comment_authors], 'id'), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.river.SET_POSTS_TO_RIVER:
    case a.river.SET_POSTS_TO_LIKES_RIVER:
    case a.river.SET_POSTS_TO_FAVOURITES_RIVER:
    case a.schools.SET_SCHOOL_POSTS:
    case a.hashtags.SET_HASHTAG_POSTS:
    case a.geotags.SET_GEOTAG_POSTS: {
      const authors = action.posts.map(post => post.user);
      const comment_authors = _.flatten(action.posts.map(post => post.post_comments.map(comment => comment.user)));
      const users = _.keyBy(_.uniq([...authors, ...comment_authors], 'id'), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.comments.SET_POST_COMMENTS: {
      const users = _.keyBy(_.map(action.comments, comment => comment.user), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.posts.SET_RELATED_POSTS: {
      const users = _.keyBy(action.posts.map(post => post.user), 'id');

      state = state.mergeDeep(i.fromJS(users));
    }
  }

  return state;
}
<commit_msg>Fixed displaying removed roles until full refresh the page<commit_after>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2016  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import i from 'immutable';
import _ from 'lodash';

import * as a from '../actions';

const initialState = i.Map({});

const cleanUser = user => {
  const users = {};

  if (!user) {
    return users;
  }

  if (user.following) {
    for (const followed_user of user.following) {
      users[followed_user.id] = followed_user;
    }

    user = _.cloneDeep(user);
    delete user.following;
  }

  if (user.followers) {
    for (const follower of user.followers) {
      users[follower.id] = follower;
    }

    user = _.cloneDeep(user);
    delete user.following;
  }

  users[user.id] = user;

  return users;
};

export default function reducer(state = initialState, action) {
  switch (action.type) {
    case a.users.ADD_USER:
    case a.users.SET_CURRENT_USER: {
      state = state.mergeDeep(i.fromJS(cleanUser(action.user)));

      if (action.user) {
        if (action.user.more && action.user.more.roles) {
          state = state.setIn([action.user.id, 'more', 'roles'], action.user.more.roles);
        }
      }

      break;
    }

    case a.posts.ADD_POST:
    case a.river.ADD_POST_TO_RIVER: {
      const user = action.post.user;
      const comment_authors = action.post.post_comments.map(comment => comment.user);
      const users = _.keyBy(_.uniq([user, ...comment_authors], 'id'), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.river.SET_POSTS_TO_RIVER:
    case a.river.SET_POSTS_TO_LIKES_RIVER:
    case a.river.SET_POSTS_TO_FAVOURITES_RIVER:
    case a.schools.SET_SCHOOL_POSTS:
    case a.hashtags.SET_HASHTAG_POSTS:
    case a.geotags.SET_GEOTAG_POSTS: {
      const authors = action.posts.map(post => post.user);
      const comment_authors = _.flatten(action.posts.map(post => post.post_comments.map(comment => comment.user)));
      const users = _.keyBy(_.uniq([...authors, ...comment_authors], 'id'), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.comments.SET_POST_COMMENTS: {
      const users = _.keyBy(_.map(action.comments, comment => comment.user), 'id');
      state = state.mergeDeep(i.fromJS(users));

      break;
    }

    case a.posts.SET_RELATED_POSTS: {
      const users = _.keyBy(action.posts.map(post => post.user), 'id');

      state = state.mergeDeep(i.fromJS(users));
    }
  }

  return state;
}
<|endoftext|>


<commit_before>;(function($$){ 'use strict';

  // (potentially expensive calculation)
  // apply the style to the element based on
  // - its bypass
  // - what selectors match it
  $$.styfn.apply = function( eles ){
    var self = this;

    for( var ie = 0; ie < eles.length; ie++ ){
      var ele = eles[ie];
      var changedCxts = false;

      if( self._private.newStyle ){
        ele._private.styleCxts = [];
        ele._private.style = {};
      }

      // console.log('APPLYING STYLESHEET\n--\n');

      // apply the styles
      for( var i = 0; i < this.length; i++ ){
        var context = this[i];
        var contextSelectorMatches = context.selector && context.selector.filter( ele ).length > 0; // NB: context.selector may be null for 'core'
        var props = context.properties;
        var newCxt = !ele._private.styleCxts[i];

        // console.log(i + ' : looking at selector: ' + context.selector);

        if( contextSelectorMatches ){ // then apply its properties

          // apply the properties in the context

          for( var j = 0; j < props.length; j++ ){ // for each prop
            var prop = props[j];
            var currentEleProp = ele._private.style[prop.name];
            var propIsFirstInEle = currentEleProp && currentEleProp.context === context;
            var needToUpdateCxtMapping = prop.mapped && propIsFirstInEle;

            //if(prop.mapped) debugger;

            if( newCxt || needToUpdateCxtMapping ){
              // console.log(i + ' + MATCH: applying property: ' + prop.name);
              this.applyParsedProperty( ele, prop, context );
            }
          }

          // keep a note that this context matches
          ele._private.styleCxts[i] = context;

          if( self._private.newStyle === false && newCxt ){
            changedCxts = true;
          }

        } else {

          // roll back style cxts that don't match now
          if( ele._private.styleCxts[i] ){
            // console.log(i + ' x MISS: rolling back context');
            this.rollBackContext( ele, context );
            changedCxts = true;
          }

          delete ele._private.styleCxts[i];
        }
      } // for context

      if( changedCxts ){
        this.updateTransitions( ele );
      }

    } // for elements

    self._private.newStyle = false;
  };

  // when a context's selector no longer matches the ele, roll back the context on the ele
  // (cheaper than having to recalc from the beginning)
  $$.styfn.rollBackContext = function( ele, context ){
    for( var j = 0; j < context.properties.length; j++ ){ // for each prop
      var prop = context.properties[j];
      var eleProp = ele._private.style[ prop.name ];

      // because bypasses do not store prevs, look at the bypassed property
      if( eleProp.bypassed ){
        eleProp = eleProp.bypassed;
      }

      var first = true;
      var lastEleProp;
      var l = 0;
      while( eleProp.prev ){
        var prev = eleProp.prev;

        if( eleProp.context === context ){

          if( first ){
            ele._private.style[ prop.name ] = prev;
          } else if( lastEleProp ){
            lastEleProp.prev = prev;
          }

        }

        lastEleProp = eleProp;
        eleProp = prev;
        first = false;
        l++;

        // in case we have a problematic prev list
        // if( l >= 100 ){
        //   debugger;
        // }
      }
    }
  };

  // apply a property to the style (for internal use)
  // returns whether application was successful
  //
  // now, this function flattens the property, and here's how:
  //
  // for parsedProp:{ bypass: true, deleteBypass: true }
  // no property is generated, instead the bypass property in the
  // element's style is replaced by what's pointed to by the `bypassed`
  // field in the bypass property (i.e. restoring the property the
  // bypass was overriding)
  //
  // for parsedProp:{ mapped: truthy }
  // the generated flattenedProp:{ mapping: prop }
  //
  // for parsedProp:{ bypass: true }
  // the generated flattenedProp:{ bypassed: parsedProp }
  $$.styfn.applyParsedProperty = function( ele, parsedProp, context ){
    parsedProp = $$.util.clone( parsedProp ); // copy b/c the same parsedProp may be applied to many elements, BUT
    // the instances put in each element should be unique to avoid overwriting other the lists of other elements

    var prop = parsedProp;
    var style = ele._private.style;
    var fieldVal, flatProp;
    var type = $$.style.properties[ prop.name ].type;
    var propIsBypass = prop.bypass;
    var origProp = style[ prop.name ];
    var origPropIsBypass = origProp && origProp.bypass;

    // can't apply auto to width or height unless it's a parent node
    if( (parsedProp.name === 'height' || parsedProp.name === 'width') && parsedProp.value === 'auto' && ele.isNode() && !ele.isParent() ){
      return false;
    }

    // check if we need to delete the current bypass
    if( propIsBypass && prop.deleteBypass ){ // then this property is just here to indicate we need to delete
      var currentProp = style[ prop.name ];

      // can only delete if the current prop is a bypass and it points to the property it was overriding
      if( !currentProp ){
        return true; // property is already not defined
      } else if( currentProp.bypass && currentProp.bypassed ){ // then replace the bypass property with the original

        // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
        style[ prop.name ] = currentProp.bypassed;
        return true;

      } else {
        return false; // we're unsuccessful deleting the bypass
      }
    }

    // put the property in the style objects
    switch( prop.mapped ){ // flatten the property if mapped
    case $$.style.types.mapData:
    case $$.style.types.mapLayoutData:

      var isLayout = prop.mapped === $$.style.types.mapLayoutData;

      // flatten the field (e.g. data.foo.bar)
      var fields = prop.field.split(".");
      var fieldVal = isLayout ? ele._private.layoutData : ele._private.data;
      for( var i = 0; i < fields.length && fieldVal; i++ ){
        var field = fields[i];
        fieldVal = fieldVal[ field ];
      }

      var percent;
      if( !$$.is.number(fieldVal) ){ // then keep the mapping but assume 0% for now
        percent = 0;
      } else {
        percent = (fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin);
      }

      if( type.color ){
        var r1 = prop.valueMin[0];
        var r2 = prop.valueMax[0];
        var g1 = prop.valueMin[1];
        var g2 = prop.valueMax[1];
        var b1 = prop.valueMin[2];
        var b2 = prop.valueMax[2];
        var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
        var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];

        var clr = [
          Math.round( r1 + (r2 - r1)*percent ),
          Math.round( g1 + (g2 - g1)*percent ),
          Math.round( b1 + (b2 - b1)*percent ),
          Math.round( a1 + (a2 - a1)*percent )
        ];

        flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing
          bypass: prop.bypass, // we're a bypass if the mapping property is a bypass
          name: prop.name,
          value: clr,
          strValue: [ "rgba(", clr[0], ", ", clr[1], ", ", clr[2], ", ", clr[3] , ")" ].join('') // fake it til you make it
        };

      } else if( type.number ){
        var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
        flatProp = this.parse( prop.name, calcValue, prop.bypass );

      } else {
        return false; // can only map to colours and numbers
      }

      if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
        flatProp = this.parse( prop.name, origProp.strValue, prop.bypass);
      }

      flatProp.mapping = prop; // keep a reference to the mapping
      prop = flatProp; // the flattened (mapped) property is the one we want

      break;

    // direct mapping
    case $$.style.types.data:
    case $$.style.types.layoutData:

      var isLayout = prop.mapped === $$.style.types.layoutData;

      // flatten the field (e.g. data.foo.bar)
      var fields = prop.field.split(".");
      var fieldVal = isLayout ? ele._private.layoutData : ele._private.data;
      for( var i = 0; i < fields.length && fieldVal; i++ ){
        var field = fields[i];
        fieldVal = fieldVal[ field ];
      }

      flatProp = this.parse( prop.name, fieldVal, prop.bypass );
      if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
        flatProp = this.parse( prop.name, origProp.strValue, prop.bypass);
      }

      flatProp.mapping = prop; // keep a reference to the mapping
      prop = flatProp; // the flattened (mapped) property is the one we want
      break;

    case undefined:
      break; // just set the property

    default:
      return false; // not a valid mapping
    }

    // if the property is a bypass property, then link the resultant property to the original one
    if( propIsBypass ){
      if( origPropIsBypass ){ // then this bypass overrides the existing one
        prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
      } else { // then link the orig prop to the new bypass
        prop.bypassed = origProp;
      }

      style[ prop.name ] = prop; // and set

    } else { // prop is not bypass
      var prevProp;

      if( origPropIsBypass ){ // then keep the orig prop (since it's a bypass) and link to the new prop
        prevProp = origProp.bypassed;

        origProp.bypassed = prop;
      } else { // then just replace the old prop with the new one
        prevProp = style[ prop.name ];

        style[ prop.name ] = prop;
      }

      if( prevProp && prevProp.mapping && prop.mapping && prevProp.context === context ){
        prevProp = prevProp.prev;
      }

      if( prevProp && prevProp !== prop ){
        prop.prev = prevProp;
      }
    }

    prop.context = context;

    return true;
  };

  // updates the visual style for all elements (useful for manual style modification after init)
  $$.styfn.update = function(){
    var cy = this._private.cy;
    var eles = cy.elements();

    eles.updateStyle();
  };

  // just update the functional properties (i.e. mappings) in the elements'
  // styles (less expensive than recalculation)
  $$.styfn.updateMappers = function( eles ){
    for( var i = 0; i < eles.length; i++ ){ // for each ele
      var ele = eles[i];
      var style = ele._private.style;

      for( var j = 0; j < $$.style.properties.length; j++ ){ // for each prop
        var prop = $$.style.properties[j];
        var propInStyle = style[ prop.name ];

        if( propInStyle && propInStyle.mapping ){
          var mapping = propInStyle.mapping;
          this.applyParsedProperty( ele, mapping ); // reapply the mapping property
        }
      }
    }
  };

  $$.styfn.updateTransitions = function( ele ){
    var self = this;
    var style = ele._private.style;

    var props = style['transition-property'].value;
    var duration = style['transition-duration'].value * 1000;
    var delay = style['transition-delay'].value * 1000;
    var css = {};

    if( props.length > 0 && duration > 0 ){

      // if already transitioning, then this is a duplicate
      if( ele._private.transitioning ){ return; }

      // build up the style to animate towards
      var anyPrev = false;
      for( var i = 0; i < props.length; i++ ){
        var prop = props[i];
        var styProp = style[ prop ];

        // if a previous value is defined, then we can animate from that value to the specified value
        if( styProp.prev ){
          css[ prop ] = style[ prop ].strValue; // to val
          this.applyBypass(ele, prop, styProp.prev.strValue); // from val
          anyPrev = true;
        }
      }

      // can't transition if there's nothing previous to transition from
      if( !anyPrev ){ return; }

console.log('transition ', ele.id());

      ele._private.transitioning = true;

      ele.stop();

      if( delay > 0 ){
        ele.delay( delay );
      }

      ele.animate({
        css: css
      }, {
        duration: duration,
        queue: false,
        complete: function(){
          self.removeAllBypasses( ele );
        }
      });

    } else if( ele._private.transitioning ){
console.log('cancel', ele.id())
      ele.stop();

      this.removeAllBypasses( ele );

      ele._private.transitioning = false;
    }
  };

})( cytoscape );<commit_msg>constant transitions working but initial state transition not right direction<commit_after>;(function($$){ 'use strict';

  // (potentially expensive calculation)
  // apply the style to the element based on
  // - its bypass
  // - what selectors match it
  $$.styfn.apply = function( eles ){
    var self = this;

    for( var ie = 0; ie < eles.length; ie++ ){
      var ele = eles[ie];
      var changedCxts = false;

      if( self._private.newStyle ){
        ele._private.styleCxts = [];
        ele._private.style = {};
      }

      // console.log('APPLYING STYLESHEET\n--\n');

      // apply the styles
      for( var i = 0; i < this.length; i++ ){
        var context = this[i];
        var contextSelectorMatches = context.selector && context.selector.filter( ele ).length > 0; // NB: context.selector may be null for 'core'
        var props = context.properties;
        var newCxt = !ele._private.styleCxts[i];

        // console.log(i + ' : looking at selector: ' + context.selector);

        if( contextSelectorMatches ){ // then apply its properties

          // apply the properties in the context

          for( var j = 0; j < props.length; j++ ){ // for each prop
            var prop = props[j];
            var currentEleProp = ele._private.style[prop.name];
            var propIsFirstInEle = currentEleProp && currentEleProp.context === context;
            var needToUpdateCxtMapping = prop.mapped && propIsFirstInEle;

            //if(prop.mapped) debugger;

            if( newCxt || needToUpdateCxtMapping ){
              // console.log(i + ' + MATCH: applying property: ' + prop.name);
              this.applyParsedProperty( ele, prop, context );
            }
          }

          // keep a note that this context matches
          ele._private.styleCxts[i] = context;

          if( self._private.newStyle === false && newCxt ){
            changedCxts = true;
          }

        } else {

          // roll back style cxts that don't match now
          if( ele._private.styleCxts[i] ){
            // console.log(i + ' x MISS: rolling back context');
            this.rollBackContext( ele, context );
            changedCxts = true;
          }

          delete ele._private.styleCxts[i];
        }
      } // for context

      if( changedCxts ){
        this.updateTransitions( ele );
      }

    } // for elements

    self._private.newStyle = false;
  };

  // when a context's selector no longer matches the ele, roll back the context on the ele
  // (cheaper than having to recalc from the beginning)
  $$.styfn.rollBackContext = function( ele, context ){
    for( var j = 0; j < context.properties.length; j++ ){ // for each prop
      var prop = context.properties[j];
      var eleProp = ele._private.style[ prop.name ];

      // because bypasses do not store prevs, look at the bypassed property
      if( eleProp.bypassed ){
        eleProp = eleProp.bypassed;
      }

      var first = true;
      var lastEleProp;
      var l = 0;
      while( eleProp.prev ){
        var prev = eleProp.prev;

        if( eleProp.context === context ){

          if( first ){
            ele._private.style[ prop.name ] = prev;
          } else if( lastEleProp ){
            lastEleProp.prev = prev;
          }

        }

        lastEleProp = eleProp;
        eleProp = prev;
        first = false;
        l++;

        // in case we have a problematic prev list
        // if( l >= 100 ){
        //   debugger;
        // }
      }
    }
  };

  // apply a property to the style (for internal use)
  // returns whether application was successful
  //
  // now, this function flattens the property, and here's how:
  //
  // for parsedProp:{ bypass: true, deleteBypass: true }
  // no property is generated, instead the bypass property in the
  // element's style is replaced by what's pointed to by the `bypassed`
  // field in the bypass property (i.e. restoring the property the
  // bypass was overriding)
  //
  // for parsedProp:{ mapped: truthy }
  // the generated flattenedProp:{ mapping: prop }
  //
  // for parsedProp:{ bypass: true }
  // the generated flattenedProp:{ bypassed: parsedProp }
  $$.styfn.applyParsedProperty = function( ele, parsedProp, context ){
    parsedProp = $$.util.clone( parsedProp ); // copy b/c the same parsedProp may be applied to many elements, BUT
    // the instances put in each element should be unique to avoid overwriting other the lists of other elements

    var prop = parsedProp;
    var style = ele._private.style;
    var fieldVal, flatProp;
    var type = $$.style.properties[ prop.name ].type;
    var propIsBypass = prop.bypass;
    var origProp = style[ prop.name ];
    var origPropIsBypass = origProp && origProp.bypass;

    // can't apply auto to width or height unless it's a parent node
    if( (parsedProp.name === 'height' || parsedProp.name === 'width') && parsedProp.value === 'auto' && ele.isNode() && !ele.isParent() ){
      return false;
    }

    // check if we need to delete the current bypass
    if( propIsBypass && prop.deleteBypass ){ // then this property is just here to indicate we need to delete
      var currentProp = style[ prop.name ];

      // can only delete if the current prop is a bypass and it points to the property it was overriding
      if( !currentProp ){
        return true; // property is already not defined
      } else if( currentProp.bypass && currentProp.bypassed ){ // then replace the bypass property with the original

        // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
        style[ prop.name ] = currentProp.bypassed;
        return true;

      } else {
        return false; // we're unsuccessful deleting the bypass
      }
    }

    // put the property in the style objects
    switch( prop.mapped ){ // flatten the property if mapped
    case $$.style.types.mapData:
    case $$.style.types.mapLayoutData:

      var isLayout = prop.mapped === $$.style.types.mapLayoutData;

      // flatten the field (e.g. data.foo.bar)
      var fields = prop.field.split(".");
      var fieldVal = isLayout ? ele._private.layoutData : ele._private.data;
      for( var i = 0; i < fields.length && fieldVal; i++ ){
        var field = fields[i];
        fieldVal = fieldVal[ field ];
      }

      var percent;
      if( !$$.is.number(fieldVal) ){ // then keep the mapping but assume 0% for now
        percent = 0;
      } else {
        percent = (fieldVal - prop.fieldMin) / (prop.fieldMax - prop.fieldMin);
      }

      if( type.color ){
        var r1 = prop.valueMin[0];
        var r2 = prop.valueMax[0];
        var g1 = prop.valueMin[1];
        var g2 = prop.valueMax[1];
        var b1 = prop.valueMin[2];
        var b2 = prop.valueMax[2];
        var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
        var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];

        var clr = [
          Math.round( r1 + (r2 - r1)*percent ),
          Math.round( g1 + (g2 - g1)*percent ),
          Math.round( b1 + (b2 - b1)*percent ),
          Math.round( a1 + (a2 - a1)*percent )
        ];

        flatProp = { // colours are simple, so just create the flat property instead of expensive string parsing
          bypass: prop.bypass, // we're a bypass if the mapping property is a bypass
          name: prop.name,
          value: clr,
          strValue: [ "rgba(", clr[0], ", ", clr[1], ", ", clr[2], ", ", clr[3] , ")" ].join('') // fake it til you make it
        };

      } else if( type.number ){
        var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
        flatProp = this.parse( prop.name, calcValue, prop.bypass );

      } else {
        return false; // can only map to colours and numbers
      }

      if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
        flatProp = this.parse( prop.name, origProp.strValue, prop.bypass);
      }

      flatProp.mapping = prop; // keep a reference to the mapping
      prop = flatProp; // the flattened (mapped) property is the one we want

      break;

    // direct mapping
    case $$.style.types.data:
    case $$.style.types.layoutData:

      var isLayout = prop.mapped === $$.style.types.layoutData;

      // flatten the field (e.g. data.foo.bar)
      var fields = prop.field.split(".");
      var fieldVal = isLayout ? ele._private.layoutData : ele._private.data;
      for( var i = 0; i < fields.length && fieldVal; i++ ){
        var field = fields[i];
        fieldVal = fieldVal[ field ];
      }

      flatProp = this.parse( prop.name, fieldVal, prop.bypass );
      if( !flatProp ){ // if we can't flatten the property, then use the origProp so we still keep the mapping itself
        flatProp = this.parse( prop.name, origProp.strValue, prop.bypass);
      }

      flatProp.mapping = prop; // keep a reference to the mapping
      prop = flatProp; // the flattened (mapped) property is the one we want
      break;

    case undefined:
      break; // just set the property

    default:
      return false; // not a valid mapping
    }

    // if the property is a bypass property, then link the resultant property to the original one
    if( propIsBypass ){
      if( origPropIsBypass ){ // then this bypass overrides the existing one
        prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
      } else { // then link the orig prop to the new bypass
        prop.bypassed = origProp;
      }

      style[ prop.name ] = prop; // and set

    } else { // prop is not bypass
      var prevProp;

      if( origPropIsBypass ){ // then keep the orig prop (since it's a bypass) and link to the new prop
        prevProp = origProp.bypassed;

        origProp.bypassed = prop;
      } else { // then just replace the old prop with the new one
        prevProp = style[ prop.name ];

        style[ prop.name ] = prop;
      }

      if( prevProp && prevProp.mapping && prop.mapping && prevProp.context === context ){
        prevProp = prevProp.prev;
      }

      if( prevProp && prevProp !== prop ){
        prop.prev = prevProp;
      }
    }

    prop.context = context;

    return true;
  };

  // updates the visual style for all elements (useful for manual style modification after init)
  $$.styfn.update = function(){
    var cy = this._private.cy;
    var eles = cy.elements();

    eles.updateStyle();
  };

  // just update the functional properties (i.e. mappings) in the elements'
  // styles (less expensive than recalculation)
  $$.styfn.updateMappers = function( eles ){
    for( var i = 0; i < eles.length; i++ ){ // for each ele
      var ele = eles[i];
      var style = ele._private.style;

      for( var j = 0; j < $$.style.properties.length; j++ ){ // for each prop
        var prop = $$.style.properties[j];
        var propInStyle = style[ prop.name ];

        if( propInStyle && propInStyle.mapping ){
          var mapping = propInStyle.mapping;
          this.applyParsedProperty( ele, mapping ); // reapply the mapping property
        }
      }
    }
  };

  $$.styfn.updateTransitions = function( ele ){
    var self = this;
    var style = ele._private.style;

    var props = style['transition-property'].value;
    var duration = style['transition-duration'].value * 1000;
    var delay = style['transition-delay'].value * 1000;
    var css = {};

    if( props.length > 0 && duration > 0 ){

      // if already transitioning, then this is a duplicate
      if( ele._private.transitioning ){ return; }

      // build up the style to animate towards
      var anyPrev = false;
      for( var i = 0; i < props.length; i++ ){
        var prop = props[i];
        var styProp = style[ prop ];

        // if a previous value is defined, then we can animate from that value to the specified value
        if( styProp.prev ){
          css[ prop ] = style[ prop ].strValue; // to val
          this.applyBypass(ele, prop, styProp.prev.strValue); // from val
          anyPrev = true;
        }
      }

      // can't transition if there's nothing previous to transition from
      if( !anyPrev ){ return; }

console.log('transition ', ele.id());

      ele._private.transitioning = true;

      ele.stop();

      if( delay > 0 ){
        ele.delay( delay );
      }

      ele.animate({
        css: css
      }, {
        duration: duration,
        queue: false,
        complete: function(){
          self.removeAllBypasses( ele );

          ele._private.transitioning = false;
        }
      });

    } else if( ele._private.transitioning ){
console.log('cancel', ele.id())
      ele.stop();

      this.removeAllBypasses( ele );

      ele._private.transitioning = false;
    }
  };

})( cytoscape );<|endoftext|>