<commit_before>// @flow
/* global SETTINGS: false */
import React from 'react';
import Icon from 'react-mdl/lib/Icon';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';

import {
  TOAST_SUCCESS,
  TOAST_FAILURE,
} from '../constants';
import ErrorMessage from '../components/ErrorMessage';
import Navbar from '../components/Navbar';
import Toast from '../components/Toast';
import {
  FETCH_SUCCESS,
  FETCH_FAILURE,
} from '../actions';
import {
  fetchUserProfile,
  clearProfile,
  startProfileEdit,
  updateProfileValidation,
  updateValidationVisibility,
} from '../actions/profile';
import {
  addProgramEnrollment,
  clearEnrollments,
  fetchProgramEnrollments,
  setCurrentProgramEnrollment,
} from '../actions/programs';
import {
  setEnrollDialogError,
  setEnrollDialogVisibility,
  setToastMessage,
  setEnrollSelectedProgram,
  setNavDrawerOpen,
  clearUI,
  setPhotoDialogVisibility,
  setProgram,
} from '../actions/ui';
import { validateProfileComplete } from '../lib/validation/profile';
import { currentOrFirstIncompleteStep } from '../util/util';
import type {
  AvailableProgram,
  AvailableProgramsState,
} from '../flow/enrollmentTypes';
import type { ProfileGetResult } from '../flow/profileTypes';
import type { UIState } from '../reducers/ui';
import { ALL_ERRORS_VISIBLE } from '../constants';

const PROFILE_REGEX = /^\/profile\/?[a-z]?/;
const LEARNER_REGEX = /^\/learner\/?[a-z]?/;

class App extends React.Component {
  props: {
    children:                 React$Element<*>[],
    userProfile:              ProfileGetResult,
    location:                 Object,
    currentProgramEnrollment: AvailableProgram,
    dispatch:                 Dispatch,
    programs:                 AvailableProgramsState,
    history:                  Object,
    ui:                       UIState,
    signupDialog:             Object,
  };

  static contextTypes = {
    router:   React.PropTypes.object.isRequired
  };

  updateRequirements() {
    if (SETTINGS.user) {
      this.fetchUserProfile(SETTINGS.user.username);
    }
    this.fetchEnrollments();
    this.requireCompleteProfile();
  }

  componentDidMount() {
    this.updateRequirements();
  }

  componentDidUpdate() {
    this.updateRequirements();
  }

  componentWillUnmount() {
    const { dispatch } = this.props;
    const username = SETTINGS.user ? SETTINGS.user.username : null;
    dispatch(clearProfile(username));
    dispatch(clearUI());
    dispatch(clearEnrollments());
  }

  fetchUserProfile(username) {
    const { userProfile, dispatch, location: { pathname } } = this.props;
    if (userProfile.getStatus === undefined) {
      dispatch(fetchUserProfile(username)).then(() => {
        if ( PROFILE_REGEX.test(pathname) ) {
          dispatch(startProfileEdit(SETTINGS.user.username));
        }
      });
    }
  }

  fetchEnrollments() {
    const {
      programs,
      dispatch,
      location: { pathname },
      currentProgramEnrollment,
      ui: { selectedProgram },
    } = this.props;
    if (programs.getStatus === undefined && SETTINGS.user) {
      dispatch(fetchProgramEnrollments()).then(({payload}) => {
        if ( PROFILE_REGEX.test(pathname) && currentProgramEnrollment && !selectedProgram ) {
          let selected = payload.find(program => program.id === currentProgramEnrollment.id);
          dispatch(setProgram(selected));
        }
      });
    }
  }

  requireCompleteProfile() {
    const {
      userProfile,
      userProfile: { profile },
      location: { pathname },
      dispatch,
      ui: { profileStep }
    } = this.props;
    const [ complete, step, errors] = validateProfileComplete(profile);
    const username = SETTINGS.user ? SETTINGS.user.username : null;
    const idealStep = currentOrFirstIncompleteStep(profileStep, step);

    if (
      userProfile.getStatus === FETCH_SUCCESS &&
      !PROFILE_REGEX.test(pathname) &&
      (!complete || !profile.filled_out)
    ) {
      dispatch(startProfileEdit(SETTINGS.user.username));
      dispatch(updateValidationVisibility(username, ALL_ERRORS_VISIBLE));
      dispatch(updateProfileValidation(username, errors));
      this.context.router.push(`/profile/${idealStep}`);
    }
  }

  addProgramEnrollment = (programId: number): Promise<*> => {
    const { dispatch } = this.props;
    return dispatch(addProgramEnrollment(programId));
  };

  setEnrollDialogError = (error: ?string): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollDialogError(error));
  };

  setEnrollDialogVisibility = (visibility: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollDialogVisibility(visibility));
  };

  setEnrollSelectedProgram = (programId: ?number): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollSelectedProgram(programId));
  };

  setCurrentProgramEnrollment = (enrollment: AvailableProgram): void => {
    const { dispatch } = this.props;
    dispatch(setCurrentProgramEnrollment(enrollment));
  };

  clearMessage = (): void => {
    const { dispatch } = this.props;
    dispatch(setToastMessage(null));
  };

  setNavDrawerOpen = (bool: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setNavDrawerOpen(bool));
  }

  setPhotoDialogVisibility = (bool: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setPhotoDialogVisibility(bool));
  };

  render() {
    const {
      currentProgramEnrollment,
      programs,
      ui: {
        enrollDialogError,
        enrollDialogVisibility,
        toastMessage,
        enrollSelectedProgram,
        navDrawerOpen,
      },
      location: { pathname },
      userProfile: { profile },
    } = this.props;
    let { children } = this.props;
    let empty = false;
    if (PROFILE_REGEX.test(pathname)) {
      empty = true;
    }

    if (programs.getStatus === FETCH_FAILURE && !LEARNER_REGEX.test(pathname)) {
      children = <ErrorMessage errorInfo={programs.getErrorInfo} />;
      empty = true;
    }

    let open = false;
    let message, title, icon;
    if (toastMessage) {
      open = true;

      if (toastMessage.icon === TOAST_FAILURE) {
        icon = <Icon name="error" key="icon "/>;
      } else if (toastMessage.icon === TOAST_SUCCESS) {
        icon = <Icon name="done" key="icon" />;
      }
      title = toastMessage.title;
      message = toastMessage.message;
    }

    return

      <Navbar
        addProgramEnrollment={this.addProgramEnrollment}
        currentProgramEnrollment={currentProgramEnrollment}
        empty={empty}
        enrollDialogError={enrollDialogError}
        enrollDialogVisibility={enrollDialogVisibility}
        enrollSelectedProgram={enrollSelectedProgram}
        pathname={pathname}
        programs={programs.availablePrograms}
        fetchAddStatus={programs.postStatus}
        setCurrentProgramEnrollment={this.setCurrentProgramEnrollment}
        setEnrollDialogError={this.setEnrollDialogError}
        setEnrollDialogVisibility={this.setEnrollDialogVisibility}
        setEnrollSelectedProgram={this.setEnrollSelectedProgram}
        setNavDrawerOpen={this.setNavDrawerOpen}
        navDrawerOpen={navDrawerOpen}
        profile={profile}
        setPhotoDialogVisibility={this.setPhotoDialogVisibility}
      />
      <Toast onTimeout={this.clearMessage} open={open}>
        {icon}

          {title}
          {message}

      </Toast>

        { children }

;
  }
}

const mapStateToProps = (state) => {
  const user = SETTINGS.user;
  let profile = {
    profile: {}
  };
  if (user && state.profiles[user.username] !== undefined) {
    profile = state.profiles[user.username];
  }
  return {
    userProfile:              profile,
    ui:                       state.ui,
    currentProgramEnrollment: state.currentProgramEnrollment,
    programs:                 state.programs,
    courseEnrollments:        state.courseEnrollments,
    signupDialog:             state.signupDialog,
  };
};

export default connect(mapStateToProps)(App);
<commit_msg>Fixed infinite loop in profile validation (#2344)<commit_after>// @flow
/* global SETTINGS: false */
import React from 'react';
import Icon from 'react-mdl/lib/Icon';
import { connect } from 'react-redux';
import type { Dispatch } from 'redux';

import {
  TOAST_SUCCESS,
  TOAST_FAILURE,
} from '../constants';
import ErrorMessage from '../components/ErrorMessage';
import Navbar from '../components/Navbar';
import Toast from '../components/Toast';
import {
  FETCH_SUCCESS,
  FETCH_FAILURE,
} from '../actions';
import {
  fetchUserProfile,
  clearProfile,
  startProfileEdit,
  updateProfileValidation,
  updateValidationVisibility,
} from '../actions/profile';
import {
  addProgramEnrollment,
  clearEnrollments,
  fetchProgramEnrollments,
  setCurrentProgramEnrollment,
} from '../actions/programs';
import {
  setEnrollDialogError,
  setEnrollDialogVisibility,
  setToastMessage,
  setEnrollSelectedProgram,
  setNavDrawerOpen,
  clearUI,
  setPhotoDialogVisibility,
  setProgram,
} from '../actions/ui';
import { validateProfileComplete } from '../lib/validation/profile';
import { currentOrFirstIncompleteStep } from '../util/util';
import type {
  AvailableProgram,
  AvailableProgramsState,
} from '../flow/enrollmentTypes';
import type { ProfileGetResult } from '../flow/profileTypes';
import type { UIState } from '../reducers/ui';
import { ALL_ERRORS_VISIBLE } from '../constants';

const PROFILE_REGEX = /^\/profile\/?[a-z]?/;
const LEARNER_REGEX = /^\/learner\/?[a-z]?/;

class App extends React.Component {
  props: {
    children:                 React$Element<*>[],
    userProfile:              ProfileGetResult,
    location:                 Object,
    currentProgramEnrollment: AvailableProgram,
    dispatch:                 Dispatch,
    programs:                 AvailableProgramsState,
    history:                  Object,
    ui:                       UIState,
    signupDialog:             Object,
  };

  static contextTypes = {
    router:   React.PropTypes.object.isRequired
  };

  updateRequirements() {
    if (SETTINGS.user) {
      this.fetchUserProfile(SETTINGS.user.username);
    }
    this.fetchEnrollments();
  }

  componentDidMount() {
    this.updateRequirements();
  }

  componentDidUpdate() {
    this.updateRequirements();
  }

  componentWillUnmount() {
    const { dispatch } = this.props;
    const username = SETTINGS.user ? SETTINGS.user.username : null;
    dispatch(clearProfile(username));
    dispatch(clearUI());
    dispatch(clearEnrollments());
  }

  fetchUserProfile(username) {
    const { userProfile, dispatch, location: { pathname } } = this.props;
    if (userProfile.getStatus === undefined) {
      dispatch(fetchUserProfile(username)).then(() => {
        this.requireCompleteProfile();
        if ( PROFILE_REGEX.test(pathname) ) {
          dispatch(startProfileEdit(SETTINGS.user.username));
        }
      });
    }
  }

  fetchEnrollments() {
    const {
      programs,
      dispatch,
      location: { pathname },
      currentProgramEnrollment,
      ui: { selectedProgram },
    } = this.props;
    if (programs.getStatus === undefined && SETTINGS.user) {
      dispatch(fetchProgramEnrollments()).then(({payload}) => {
        if ( PROFILE_REGEX.test(pathname) && currentProgramEnrollment && !selectedProgram ) {
          let selected = payload.find(program => program.id === currentProgramEnrollment.id);
          dispatch(setProgram(selected));
        }
      });
    }
  }

  requireCompleteProfile() {
    const {
      userProfile,
      userProfile: { profile },
      location: { pathname },
      dispatch,
      ui: { profileStep }
    } = this.props;
    const [ complete, step, errors] = validateProfileComplete(profile);
    const username = SETTINGS.user ? SETTINGS.user.username : null;
    const idealStep = currentOrFirstIncompleteStep(profileStep, step);

    if (
      userProfile.getStatus === FETCH_SUCCESS &&
      !PROFILE_REGEX.test(pathname) &&
      (!complete || !profile.filled_out)
    ) {
      dispatch(startProfileEdit(SETTINGS.user.username));
      dispatch(updateValidationVisibility(username, ALL_ERRORS_VISIBLE));
      dispatch(updateProfileValidation(username, errors));
      this.context.router.push(`/profile/${idealStep}`);
    }
  }

  addProgramEnrollment = (programId: number): Promise<*> => {
    const { dispatch } = this.props;
    return dispatch(addProgramEnrollment(programId));
  };

  setEnrollDialogError = (error: ?string): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollDialogError(error));
  };

  setEnrollDialogVisibility = (visibility: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollDialogVisibility(visibility));
  };

  setEnrollSelectedProgram = (programId: ?number): void => {
    const { dispatch } = this.props;
    dispatch(setEnrollSelectedProgram(programId));
  };

  setCurrentProgramEnrollment = (enrollment: AvailableProgram): void => {
    const { dispatch } = this.props;
    dispatch(setCurrentProgramEnrollment(enrollment));
  };

  clearMessage = (): void => {
    const { dispatch } = this.props;
    dispatch(setToastMessage(null));
  };

  setNavDrawerOpen = (bool: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setNavDrawerOpen(bool));
  }

  setPhotoDialogVisibility = (bool: boolean): void => {
    const { dispatch } = this.props;
    dispatch(setPhotoDialogVisibility(bool));
  };

  render() {
    const {
      currentProgramEnrollment,
      programs,
      ui: {
        enrollDialogError,
        enrollDialogVisibility,
        toastMessage,
        enrollSelectedProgram,
        navDrawerOpen,
      },
      location: { pathname },
      userProfile: { profile },
    } = this.props;
    let { children } = this.props;
    let empty = false;
    if (PROFILE_REGEX.test(pathname)) {
      empty = true;
    }

    if (programs.getStatus === FETCH_FAILURE && !LEARNER_REGEX.test(pathname)) {
      children = <ErrorMessage errorInfo={programs.getErrorInfo} />;
      empty = true;
    }

    let open = false;
    let message, title, icon;
    if (toastMessage) {
      open = true;

      if (toastMessage.icon === TOAST_FAILURE) {
        icon = <Icon name="error" key="icon "/>;
      } else if (toastMessage.icon === TOAST_SUCCESS) {
        icon = <Icon name="done" key="icon" />;
      }
      title = toastMessage.title;
      message = toastMessage.message;
    }

    return

      <Navbar
        addProgramEnrollment={this.addProgramEnrollment}
        currentProgramEnrollment={currentProgramEnrollment}
        empty={empty}
        enrollDialogError={enrollDialogError}
        enrollDialogVisibility={enrollDialogVisibility}
        enrollSelectedProgram={enrollSelectedProgram}
        pathname={pathname}
        programs={programs.availablePrograms}
        fetchAddStatus={programs.postStatus}
        setCurrentProgramEnrollment={this.setCurrentProgramEnrollment}
        setEnrollDialogError={this.setEnrollDialogError}
        setEnrollDialogVisibility={this.setEnrollDialogVisibility}
        setEnrollSelectedProgram={this.setEnrollSelectedProgram}
        setNavDrawerOpen={this.setNavDrawerOpen}
        navDrawerOpen={navDrawerOpen}
        profile={profile}
        setPhotoDialogVisibility={this.setPhotoDialogVisibility}
      />
      <Toast onTimeout={this.clearMessage} open={open}>
        {icon}

          {title}
          {message}

      </Toast>

        { children }

;
  }
}

const mapStateToProps = (state) => {
  const user = SETTINGS.user;
  let profile = {
    profile: {}
  };
  if (user && state.profiles[user.username] !== undefined) {
    profile = state.profiles[user.username];
  }
  return {
    userProfile:              profile,
    ui:                       state.ui,
    currentProgramEnrollment: state.currentProgramEnrollment,
    programs:                 state.programs,
    courseEnrollments:        state.courseEnrollments,
    signupDialog:             state.signupDialog,
  };
};

export default connect(mapStateToProps)(App);
<|endoftext|>


<commit_before>var express		= require('express');
var fs			= require('fs');
var io			= require('socket.io');
var crypto		= require('crypto');

var app			= express.createServer();
var staticDir	= express.static;

io				= io.listen(app);

var opts = {
	port: process.env.PORT || 1948,
	baseDir : __dirname + '/../../'
};

io.sockets.on('connection', function(socket) {
	socket.on('slidechanged', function(slideData) {
		if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
		if (createHash(slideData.secret) === slideData.socketId) {
			slideData.secret = null;
			socket.broadcast.emit(slideData.socketId, slideData);
		};
	});
});

app.configure(function() {
	[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
		app.use('/' + dir, staticDir(opts.baseDir + dir));
	});
});

app.get("/", function(req, res) {
	res.writeHead(200, {'Content-Type': 'text/html'});
	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
});

app.get("/token", function(req,res) {
	var ts = new Date().getTime();
	var rand = Math.floor(Math.random()*9999999);
	var secret = ts.toString() + rand.toString();
	res.send({secret: secret, socketId: createHash(secret)});
});

var createHash = function(secret) {
	var cipher = crypto.createCipher('blowfish', secret);
	return(cipher.final('hex'));
};

// Actually listen
app.listen(opts.port || null);

var brown = '\033[33m',
	green = '\033[32m',
	reset = '\033[0m';

console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );<commit_msg>correct baseDir<commit_after>var express		= require('express');
var fs			= require('fs');
var io			= require('socket.io');
var crypto		= require('crypto');

var app			= express.createServer();
var staticDir	= express.static;

io				= io.listen(app);

var opts = {
	port: process.env.PORT || 1948,
	baseDir : __dirname + '/../../../'
};

io.sockets.on('connection', function(socket) {
	socket.on('slidechanged', function(slideData) {
		if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
		if (createHash(slideData.secret) === slideData.socketId) {
			slideData.secret = null;
			socket.broadcast.emit(slideData.socketId, slideData);
		};
	});
});

app.configure(function() {
	[ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
		app.use('/' + dir, staticDir(opts.baseDir + dir));
	});
});

app.get("/", function(req, res) {
	res.writeHead(200, {'Content-Type': 'text/html'});
	fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
});

app.get("/token", function(req,res) {
	var ts = new Date().getTime();
	var rand = Math.floor(Math.random()*9999999);
	var secret = ts.toString() + rand.toString();
	res.send({secret: secret, socketId: createHash(secret)});
});

var createHash = function(secret) {
	var cipher = crypto.createCipher('blowfish', secret);
	return(cipher.final('hex'));
};

// Actually listen
app.listen(opts.port || null);

var brown = '\033[33m',
	green = '\033[32m',
	reset = '\033[0m';

console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );<|endoftext|>


<commit_before>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require","../deep", "../deep-unit"], function (require, deep, Unit) {

    //_______________________________________________________________ GENERIC STORE TEST CASES

    var unit = {
        title:"deep/units/ocm",
        stopOnError:false,
        tests : {
			base:function(){
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						test:2,
						title:"hello world"
					},
					mode3:{
						backgrounds:["this::../mode2"],
						description:"mode 3 description"
					}
				});
				myManager.flatten(); // seek and apply backgrounds
				return deep.when([myManager("mode1"),myManager("mode2"), myManager("mode3")]).equal(
					[
						{ test:1 },
						{ test:2, title:"hello world"},
						{ test:2, title:"hello world", description:"mode 3 description"}
					]
				);
           },
           currentMode:function(){
				var myManager = deep.ocm({
					mode1:{
						title:"should not see this"
					},
					mode2:{
						title:"hello world"
					}
				});
				myManager.mode("mode2");
				return deep.when(myManager())
				.equal({ title:"hello world" });
			},
			modeCollection:function(){
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						title:"hello world"
					}
				});
				return deep.when([myManager("mode1", "mode2"), myManager("mode2", "mode1")])
				.equal([ { test:1, title:"hello world"}, { title:"hello world", test:1 }]);
			},
			setGroup:function()
			{
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						title:"hello world"
					}
				});

				myManager.group("myGroup");

				deep.setModes({ "myGroup":["mode2", "mode1"]}); // set modes in current deep.context

				return deep.modes({ "myGroup":"mode1" }) // start a chain with provided modes
				.delay(5)
				.done(function(success){
					return myManager();
				})
				.equal({test:1})
				.modes({ "myGroup":"mode2"} )
				.delay(5)
				.done(function(success){
					return myManager();
				})
				.equal({ title:"hello world"});
			},
			groupCollection:function(){
				return deep.modes({
					group1:"mode1"
				})
				.done(function(success){
					return deep.modes({ group2:"mode2" })
					.delay(5)
					.done(function(success){
						return deep.context.modes;
					})
					.equal({ group2:"mode2", group1:"mode1" });
				})
				.delay(5)
				.done(function(success){
					return deep.context.modes;
				})
				.equal({ group1:"mode1" });
			},
			shared:function(){
				var obj = deep.ocm({
					mode1:{
						myShared:deep.Shared([1,2,3]),
						myShared2:deep.Shared({ a:1 })
					},
					mode2:{
						backgrounds:["this::../mode1"],
						myShared:[4,5],
						myShared2:{ b:2 }
					}
				});

				return obj.flatten().done(function(obj){
					obj("mode1").myShared.push(6);
					obj("mode1").myShared2.c = 3;;
					return [obj("mode1"), obj("mode2")];
				})
				.equal([
					{ myShared:[1,2,3,4,5,6], myShared2:{ a:1, _deep_shared_:true, b:2,  c:3}},
					{ myShared:[1,2,3,4,5,6], myShared2:{ a:1,  _deep_shared_:true, b:2, c:3}}
				]);
			}
        }
    };

    return unit;
});
<commit_msg>change from up/bottom refactor<commit_after>if (typeof define !== 'function') {
    var define = require('amdefine')(module);
}

define(["require","../deep", "../deep-unit"], function (require, deep, Unit) {

    //_______________________________________________________________ GENERIC STORE TEST CASES

    var unit = {
        title:"deep/units/ocm",
        stopOnError:false,
        tests : {
			base:function(){
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						test:2,
						title:"hello world"
					},
					mode3:{
						backgrounds:["this::../mode2"],
						description:"mode 3 description"
					}
				});
				myManager.flatten(); // seek and apply backgrounds
				return deep.when([myManager("mode1"),myManager("mode2"), myManager("mode3")]).equal(
					[
						{ test:1 },
						{ test:2, title:"hello world"},
						{ test:2, title:"hello world", description:"mode 3 description"}
					]
				);
           },
           currentMode:function(){
				var myManager = deep.ocm({
					mode1:{
						title:"should not see this"
					},
					mode2:{
						title:"hello world"
					}
				});
				myManager.mode("mode2");
				return deep.when(myManager())
				.equal({ title:"hello world" });
			},
			modeCollection:function(){
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						title:"hello world"
					}
				});
				return deep.when([myManager("mode1", "mode2"), myManager("mode2", "mode1")])
				.equal([ { test:1, title:"hello world"}, { title:"hello world", test:1 }]);
			},
			setGroup:function()
			{
				var myManager = deep.ocm({
					mode1:{
						test:1
					},
					mode2:{
						title:"hello world"
					}
				});

				myManager.group("myGroup");

				deep.setModes({ "myGroup":["mode2", "mode1"]}); // set modes in current deep.context

				return deep.modes({ "myGroup":"mode1" }) // start a chain with provided modes
				.delay(5)
				.done(function(success){
					return myManager();
				})
				.equal({test:1})
				.modes({ "myGroup":"mode2"} )
				.delay(5)
				.done(function(success){
					return myManager();
				})
				.equal({ title:"hello world"});
			},
			groupCollection:function(){
				return deep.modes({
					group1:"mode1"
				})
				.done(function(success){
					return deep.modes({ group2:"mode2" })
					.delay(5)
					.done(function(success){
						return deep.context.modes;
					})
					.equal({ group1:"mode1", group2:"mode2" });
				})
				.delay(5)
				.done(function(success){
					return deep.context.modes;
				})
				.equal({ group1:"mode1" });
			},
			shared:function(){
				var obj = deep.ocm({
					mode1:{
						myShared:deep.Shared([1,2,3]),
						myShared2:deep.Shared({ a:1 })
					},
					mode2:{
						backgrounds:["this::../mode1"],
						myShared:[4,5],
						myShared2:{ b:2 }
					}
				});

				return obj.flatten().done(function(obj){
					obj("mode1").myShared.push(6);
					obj("mode1").myShared2.c = 3;;
					return [obj("mode1"), obj("mode2")];
				})
				.equal([
					{ myShared:[1,2,3,4,5,6], myShared2:{ a:1, _deep_shared_:true, b:2,  c:3}},
					{ myShared:[1,2,3,4,5,6], myShared2:{ a:1,  _deep_shared_:true, b:2, c:3}}
				]);
			}
        }
    };

    return unit;
});
<|endoftext|>


<commit_before>'use strict';

var isPlainObject = require('./is-plain-object')
  , forEach       = require('./for-each')

  , process;

process = function self(value, key) {
	if (isPlainObject(value)) {
		forEach(value, self, this);
	} else {
		this[key] = value;
	}
};

module.exports = function (obj) {
	var flattened = {};
	forEach(obj, process, flattened);
	return flattened;
};
<commit_msg>-- whitespace<commit_after>'use strict';

var isPlainObject = require('./is-plain-object')
  , forEach       = require('./for-each')

  , process;

process = function self(value, key) {
	if (isPlainObject(value)) forEach(value, self, this);
	else this[key] = value;
};

module.exports = function (obj) {
	var flattened = {};
	forEach(obj, process, flattened);
	return flattened;
};
<|endoftext|>


<commit_before>var orm = require('../../../dbbs');
var Subscriber = orm.model("subscriber");
var request = require('request');
var assert = require('assert');

orm.setup()

var testEmail = 'ash.1382@gmail.com';

describe('Testing registration requests:',function() {
  it('User registered successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert(JSON.parse(body)['value'],'Unable to register user');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Email Already in Use',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'emailInUse');
      console.log('\tResponse received : ', body);
      done();
    });
  });/*
  it('Bad Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"Something@gmailler.com\", \"password\": \"my\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"asdf.com\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });*/
  it('Missing Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/Bad-Token',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});
/*
describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json',
        'X-Access-Token': token
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json',
        'X-Access-Token': token
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();

*/
/*
describe('Testing fetching of subscribers:',function() {
  it('Subscriber found',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/fetch',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Subscriber not found',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/fetch',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});*/
<commit_msg>Update restTest.js<commit_after>var orm = require('../../../dbbs');
var Subscriber = orm.model("subscriber");
var request = require('request');
var assert = require('assert');

orm.setup()

var testEmail = 'ash.1382@gmail.com';

describe('Testing registration requests:',function() {
  it('User registered successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert(JSON.parse(body)['value'],'Unable to register user');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Email Already in Use',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\", \"ip\": \"192.162.0.8\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'emailInUse');
      console.log('\tResponse received : ', body);
      done();
    });
  });/*
  it('Bad Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"Something@gmailler.com\", \"password\": \"my\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"asdf.com\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'badEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });*/
  it('Missing Password',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingPwd');
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing Email',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      assert.equal(JSON.parse(body)['error']['type'],'missingEmail');
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/'+token,
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Bad verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify/Bad-Token',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});

/*
describe('Testing verification requests:',function() {
  it('User verified successfully',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json',
        'X-Access-Token': token
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('User already verified',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/verify',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json',
        'X-Access-Token': token
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Missing verification token',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/register',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();

*/
/*
describe('Testing fetching of subscribers:',function() {
  it('Subscriber found',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/fetch',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
  it('Subscriber not found',function(done) {
    request( {
      url: 'http://localhost:3000/api/subscriber/fetch',
      body: '{ \"email\": \"'+testEmail+'\", \"password\": \"my password\"}',
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    }, function (error, response, body) {
      console.log('\tResponse received : ', body);
      done();
    });
  });
});*/
<|endoftext|>


<commit_before>//** Property.System.Object **//
var ff = {
    behaviours: {},
    transforms: {}
};
$(document).ready(function () {
    $(".ff-behaviour").live("focus", function () {
        var behaviour = $(this).data("ff-behaviour");
        if (ff.behaviours[behaviour]) {
            ff.behaviours[behaviour](this);
        }
    });
});
$(document).ready(function () {

    $(".ff-choices input.ff-choice-selector:!checked").live("change", function () { //unchecked choice radios
        var choiceArea = $(this).closest(".ff-choice");
        var choices = choiceArea.closest(".ff-choices");
        choices
            .find("> .ff-choice")
            .find(":input")
            .not(choices.find(".ff-choice-selector")
                    .not(choices.find(".ff-choices .ff-choice-selector")))
            .attr("disabled", "disabled").each(function () {
                $("span.[data-valmsg-for='" + $(this).attr("name") + "']").css("display", "none");
                if ($.validator) {
                    $.validator.defaults.unhighlight(this);
                }
            });
        var myInputs = choiceArea.find(":input").not(choiceArea.find(".ff-choice input"));
        myInputs.attr("disabled", null).each(function () {
            if ($("span[data-valmsg-for='" + $(this).attr("name") + "']").css("display", "").hasClass("field-validation-error")) {
                if ($.validator) {
                    $.validator.defaults.highlight(this);
                }
            }

        });

        var childChoices = choiceArea.find(".ff-choice").not(choiceArea.find(".ff-choice .ff-choice"));
        childChoices.find(".ff-choice-selector").not(childChoices.find(".ff-choices .ff-choice-selector"))
            .attr("disabled", null).not("[checked!='checked']").trigger("change");
    });

    $(".ff-choices .ff-choice").live("click", function (e) {
        if ($(e.target).parents().index($(this)) >= 0) {
            var option = $(this).find("> * > .ff-choice-selector[disabled!='disabled'][checked!='checked']");
            if (option.length) {
                var choicesArea = $(this).closest(".ff-choices-area");
                var picker = choicesArea.find(".ff-choice-picker").not(choicesArea.find(".ff-choices-area .ff-choice-picker"));
                if (picker.length) {
                    picker.find("option:eq(" + $(this).index() + ")").attr("selected", "selected").trigger("change");
                } else {
                    option.attr("checked", "checked").trigger("change");
                }
                e.stopPropagation();
                $(e.target).click();
            }
        }
    });

    $(".ff-choice-picker").live("change", function () {
        var choices = $(this).closest(".ff-choices-area").find("> .ff-choices");
        var radios = choices.find(".ff-choice-selector")
            .not(choices.find(".ff-choices .ff-choice-selector"));
        radios.closest(".ff-choice").hide();
        $(radios[$(this).val()]).attr("checked", "checked").trigger("change").closest(".ff-choice").show();

    });
});

//** Property.System.DateTime ** //

$.extend(ff.behaviours, {},
    {
        datepicker: function (t) {
            if (!$(t).hasClass("hasDatepicker") && $.datepicker) {
                // TODO: a more comprehensive switch of .NET to jQuery formats. REF: http://docs.jquery.com/UI/Datepicker/formatDate
                var format = $(t).data("ff-format");
                switch (format) {
                    case "dd MMM yyyy":
                        format = "dd M yy";
                        break;
                    case "dd/MM/yyyy":
                        format = "dd/mm/yy";
                        break;
                }
                $(t).datepicker({ showOn: "focus", dateFormat: format }).focus();
                return true;
            }
            return false;
        },
        datetimepicker: function (t) {
            if (!$(t).hasClass("hasDatepicker") && $.datetimepicker) {
                $(t).datetimepicker({ showOn: "focus" }).focus();
                return true;
            }
            return false;
        }
    }
);

//Collections

$.extend(ff.transforms,
    { remove: function ($el) { $el.remove(); } },
    { swap: function ($el1, $el2) {
        $el1.before($el2);
    }
    });

$(document).ready(function () {
    function newId() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
    $(".ff-add-item").live("click", function (e) {
        var $form = $($('<div/>').html($(this).closest("li").find("script[type='text/html']").html()).text());
        var modelName = $(this).data("modelname");
        var renumber = function (index, attr) {
            if (!attr) return attr;
            return modelName + "[" + newIndex + "]." + attr;
        };

        var newObject = $('<li>').append($form.children().clone());

        var newIndex = newId(); // $(this).closest("ul").children().length - 1;

        $(newObject).insertBefore($(this).closest(".ff-collection").find("> ul").children().last());

        $(":input", newObject).attr("name", renumber).attr("id", renumber);
        $(newObject).find("[data-valmsg-for]").attr("data-valmsg-for", renumber);

        $form.find(":input").val(null);
        if ($.validator) {
            if ($.validator.unobtrusive.parseDynamicContent) {
                $.validator.unobtrusive.parseDynamicContent(newObject);
            }
        }
        return false;
    }); // end on click
    $(".ff-remove-parent").live("click", function () {
        ff.transforms.remove($(this).closest("li"));
        return false;
    }); // live click
    $(".ff-move-up").live("click", function () {
        ff.transforms.swap($(this).closest("li").prev(), $(this).closest("li"));

        return false;
    }); // live click
    $(".ff-move-down").live("click", function () {
        ff.transforms.swap($(this).closest("li"), $(this).closest("li").next(":not(.ff-not-collection-item)"));
        return false;
    }); // live click
});
if ($.validator) {
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest(".control-group").addClass("error");
        },
        unhighlight: function (element) {
            $(element).closest(".control-group").removeClass("error");
        }
    });
}
 <commit_msg>Fix for jquery check<commit_after>//** Property.System.Object **//
var ff = {
    behaviours: {},
    transforms: {}
};
$(document).ready(function () {
    $(".ff-behaviour").live("focus", function () {
        var behaviour = $(this).data("ff-behaviour");
        if (ff.behaviours[behaviour]) {
            ff.behaviours[behaviour](this);
        }
    });
});
$(document).ready(function () {

    $(".ff-choices input.ff-choice-selector:not(:checked)").live("change", function () { //unchecked choice radios
        var choiceArea = $(this).closest(".ff-choice");
        var choices = choiceArea.closest(".ff-choices");
        choices
            .find("> .ff-choice")
            .find(":input")
            .not(choices.find(".ff-choice-selector")
                    .not(choices.find(".ff-choices .ff-choice-selector")))
            .attr("disabled", "disabled").each(function () {
                $("span.[data-valmsg-for='" + $(this).attr("name") + "']").css("display", "none");
                if ($.validator) {
                    $.validator.defaults.unhighlight(this);
                }
            });
        var myInputs = choiceArea.find(":input").not(choiceArea.find(".ff-choice input"));
        myInputs.attr("disabled", null).each(function () {
            if ($("span[data-valmsg-for='" + $(this).attr("name") + "']").css("display", "").hasClass("field-validation-error")) {
                if ($.validator) {
                    $.validator.defaults.highlight(this);
                }
            }

        });

        var childChoices = choiceArea.find(".ff-choice").not(choiceArea.find(".ff-choice .ff-choice"));
        childChoices.find(".ff-choice-selector").not(childChoices.find(".ff-choices .ff-choice-selector"))
            .attr("disabled", null).not("[checked!='checked']").trigger("change");
    });

    $(".ff-choices .ff-choice").live("click", function (e) {
        if ($(e.target).parents().index($(this)) >= 0) {
            var option = $(this).find("> * > .ff-choice-selector[disabled!='disabled'][checked!='checked']");
            if (option.length) {
                var choicesArea = $(this).closest(".ff-choices-area");
                var picker = choicesArea.find(".ff-choice-picker").not(choicesArea.find(".ff-choices-area .ff-choice-picker"));
                if (picker.length) {
                    picker.find("option:eq(" + $(this).index() + ")").attr("selected", "selected").trigger("change");
                } else {
                    option.attr("checked", "checked").trigger("change");
                }
                e.stopPropagation();
                $(e.target).click();
            }
        }
    });

    $(".ff-choice-picker").live("change", function () {
        var choices = $(this).closest(".ff-choices-area").find("> .ff-choices");
        var radios = choices.find(".ff-choice-selector")
            .not(choices.find(".ff-choices .ff-choice-selector"));
        radios.closest(".ff-choice").hide();
        $(radios[$(this).val()]).attr("checked", "checked").trigger("change").closest(".ff-choice").show();

    });
});

//** Property.System.DateTime ** //

$.extend(ff.behaviours, {},
    {
        datepicker: function (t) {
            if (!$(t).hasClass("hasDatepicker") && $.datepicker) {
                // TODO: a more comprehensive switch of .NET to jQuery formats. REF: http://docs.jquery.com/UI/Datepicker/formatDate
                var format = $(t).data("ff-format");
                switch (format) {
                    case "dd MMM yyyy":
                        format = "dd M yy";
                        break;
                    case "dd/MM/yyyy":
                        format = "dd/mm/yy";
                        break;
                }
                $(t).datepicker({ showOn: "focus", dateFormat: format }).focus();
                return true;
            }
            return false;
        },
        datetimepicker: function (t) {
            if (!$(t).hasClass("hasDatepicker") && $.datetimepicker) {
                $(t).datetimepicker({ showOn: "focus" }).focus();
                return true;
            }
            return false;
        }
    }
);

//Collections

$.extend(ff.transforms,
    { remove: function ($el) { $el.remove(); } },
    { swap: function ($el1, $el2) {
        $el1.before($el2);
    }
    });

$(document).ready(function () {
    function newId() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
    $(".ff-add-item").live("click", function (e) {
        var $form = $($('<div/>').html($(this).closest("li").find("script[type='text/html']").html()).text());
        var modelName = $(this).data("modelname");
        var renumber = function (index, attr) {
            if (!attr) return attr;
            return modelName + "[" + newIndex + "]." + attr;
        };

        var newObject = $('<li>').append($form.children().clone());

        var newIndex = newId(); // $(this).closest("ul").children().length - 1;

        $(newObject).insertBefore($(this).closest(".ff-collection").find("> ul").children().last());

        $(":input", newObject).attr("name", renumber).attr("id", renumber);
        $(newObject).find("[data-valmsg-for]").attr("data-valmsg-for", renumber);

        $form.find(":input").val(null);
        if ($.validator) {
            if ($.validator.unobtrusive.parseDynamicContent) {
                $.validator.unobtrusive.parseDynamicContent(newObject);
            }
        }
        return false;
    }); // end on click
    $(".ff-remove-parent").live("click", function () {
        ff.transforms.remove($(this).closest("li"));
        return false;
    }); // live click
    $(".ff-move-up").live("click", function () {
        ff.transforms.swap($(this).closest("li").prev(), $(this).closest("li"));

        return false;
    }); // live click
    $(".ff-move-down").live("click", function () {
        ff.transforms.swap($(this).closest("li"), $(this).closest("li").next(":not(.ff-not-collection-item)"));
        return false;
    }); // live click
});
if ($.validator) {
    $.validator.setDefaults({
        highlight: function (element) {
            $(element).closest(".control-group").addClass("error");
        },
        unhighlight: function (element) {
            $(element).closest(".control-group").removeClass("error");
        }
    });
}
 <|endoftext|>


<commit_before>const util = require("./util");
function maybe_add_narrowed_messages(messages, msg_list) {
    const ids = [];

    for (const elem of messages) {
        ids.push(elem.id);
    }

    channel.get({
        url: '/json/messages/matches_narrow',
        data: {msg_ids: JSON.stringify(ids),
               narrow: JSON.stringify(narrow_state.public_operators())},
        timeout: 5000,
        success: function (data) {
            if (msg_list !== current_msg_list) {
                // We unnarrowed in the mean time
                return;
            }

            let new_messages = [];
            const elsewhere_messages = [];

            for (const elem of messages) {
                if (data.messages.hasOwnProperty(elem.id)) {
                    util.set_match_data(elem, data.messages[elem.id]);
                    new_messages.push(elem);
                } else {
                    elsewhere_messages.push(elem);
                }
            }

            // This second call to add_message_metadata in the
            // insert_new_messages code path helps in very rare race
            // conditions, where e.g. the current user's name was
            // edited in between when they sent the message and when
            // we hear back from the server and can echo the new
            // message.  Arguably, it's counterproductive complexity.
            new_messages = new_messages.map(message_store.add_message_metadata);

            message_util.add_new_messages(new_messages, msg_list);
            unread_ops.process_visible();
            notifications.notify_messages_outside_current_search(elsewhere_messages);
        },
        error: function () {
            // We might want to be more clever here
            setTimeout(function () {
                if (msg_list === current_msg_list) {
                    // Don't actually try again if we unnarrowed
                    // while waiting
                    maybe_add_narrowed_messages(messages, msg_list);
                }
            }, 5000);
        }});
}

exports.insert_new_messages = function insert_new_messages(messages, sent_by_this_client) {
    messages = messages.map(message_store.add_message_metadata);

    unread.process_loaded_messages(messages);

    // message_list.all is a data-only list that we use to populate
    // other lists, so we always update this
    message_util.add_new_messages(messages, message_list.all);

    let render_info;

    if (narrow_state.active()) {
        // We do this NOW even though the home view is not active,
        // because we want the home view to load fast later.
        message_util.add_new_messages(messages, home_msg_list);

        if (narrow_state.filter().can_apply_locally()) {
            render_info = message_util.add_new_messages(messages, message_list.narrowed);
        } else {
            // if we cannot apply locally, we have to wait for this callback to happen to notify
            maybe_add_narrowed_messages(messages, message_list.narrowed);
        }
    } else {
        // we're in the home view, so update its list
        render_info = message_util.add_new_messages(messages, home_msg_list);
    }

    if (sent_by_this_client) {
        const need_user_to_scroll = render_info && render_info.need_user_to_scroll;
        // sent_by_this_client will be true if ANY of the messages
        // were sent by this client; notifications.notify_local_mixes
        // will filter out any not sent by us.
        notifications.notify_local_mixes(messages, need_user_to_scroll);
    }

    activity.process_loaded_messages(messages);

    unread_ui.update_unread_counts();
    resize.resize_page_components();

    unread_ops.process_visible();
    notifications.received_messages(messages);
    stream_list.update_streams_sidebar();
    pm_list.update_private_messages();
};

exports.update_messages = function update_messages(events) {
    const msgs_to_rerender = [];
    let topic_edited = false;
    let changed_narrow = false;
    let changed_compose = false;
    let message_content_edited = false;
    let stream_changed = false;

    for (const event of events) {
        const msg = message_store.get(event.message_id);
        if (msg === undefined) {
            continue;
        }

        delete msg.local_edit_timestamp;

        msgs_to_rerender.push(msg);

        message_store.update_booleans(msg, event.flags);

        unread.update_message_for_mention(msg);

        condense.un_cache_message_content_height(msg.id);

        if (event.rendered_content !== undefined) {
            msg.content = event.rendered_content;
        }

        if (event.is_me_message !== undefined) {
            msg.is_me_message = event.is_me_message;
        }

        const row = current_msg_list.get_row(event.message_id);
        if (row.length > 0) {
            message_edit.end_message_row_edit(row);
        }

        const new_topic = util.get_edit_event_topic(event);

        const {new_stream_id} = event;

        // A topic edit may affect multiple messages, listed in
        // event.message_ids. event.message_id is still the first message
        // where the user initiated the edit.
        topic_edited = new_topic !== undefined;
        stream_changed = new_stream_id !== undefined;
        if (topic_edited || stream_changed) {
            const going_forward_change = ['change_later', 'change_all'].includes(event.propagate_mode);

            const stream_name = stream_data.get_sub_by_id(event.stream_id).name;
            const compose_stream_name = compose_state.stream_name();
            const orig_topic = util.get_edit_event_orig_topic(event);

            if (going_forward_change && stream_name && compose_stream_name) {
                if (stream_name.toLowerCase() === compose_stream_name.toLowerCase()) {
                    if (orig_topic === compose_state.topic()) {
                        changed_compose = true;
                        compose_state.topic(new_topic);
                        compose_fade.set_focused_recipient("stream");
                    }
                }
            }

            const current_filter = narrow_state.filter();
            if (going_forward_change) {
                // This logic is a bit awkward.  What we're trying to
                // accomplish is two things:
                //
                // * If we're currently narrowed to a topic that was just moved,
                //   renarrow to the new location.
                // * We determine whether enough of the topic was moved to justify
                //   renarrowing by checking if the currently selected message is moved.
                //
                // Corner cases around only moving some messages in a topic
                // need to be thought about carefully when making changes.
                //
                // Code further down takes care of the actual rerendering of
                // messages within a narrow.
                const current_id = current_msg_list.selected_id();
                const selection_changed_topic = event.message_ids.includes(current_id);
                if (selection_changed_topic) {
                    if (current_filter && current_filter.has_topic(stream_name, orig_topic)) {
                        let new_filter = current_filter;
                        if (new_filter && stream_changed) {
                            // TODO: This logic doesn't handle the
                            // case where we're a guest user and the
                            // message moves to a stream we cannot
                            // access, which would cause the
                            // stream_data lookup here to fail.
                            //
                            // The fix is likely somewhat involved, so punting for now.
                            const new_stream_name = stream_data.get_sub_by_id(new_stream_id).name;
                            new_filter = new_filter.filter_with_new_params({
                                operator: 'stream',
                                operand: new_stream_name,
                            });
                            changed_narrow = true;
                        }

                        if (new_filter && topic_edited) {
                            new_filter = new_filter.filter_with_new_params({
                                operator: 'topic',
                                operand: new_topic,
                            });
                            changed_narrow = true;
                        }

                        if (changed_narrow) {
                            const operators = new_filter.operators();
                            const opts = {
                                trigger: 'stream/topic change',
                                then_select_id: current_id,
                            };
                            narrow.activate(operators, opts);
                            changed_narrow = true;
                        }
                    }
                }
            }

            for (const id of event.message_ids) {
                const msg = message_store.get(id);
                if (msg === undefined) {
                    continue;
                }

                // Remove the recent topics entry for the old topics;
                // must be called before we call set_message_topic.
                stream_topic_history.remove_message({
                    stream_id: msg.stream_id,
                    topic_name: msg.topic,
                });

                // Update the unread counts; again, this must be called
                // before we modify the topic field on the message.
                unread.update_unread_topics(msg, event);

                // Now edit the attributes of our message object.
                if (topic_edited) {
                    msg.topic = new_topic;
                    msg.topic_links = event.topic_links;
                }
                if (stream_changed) {
                    const new_stream_name = stream_data.get_sub_by_id(new_stream_id).name;
                    msg.stream_id = event.new_stream_id;
                    msg.stream = new_stream_name;
                    msg.display_recipient = new_stream_name;
                }

                // Add the recent topics entry for the new stream/topics.
                stream_topic_history.add_message({
                    stream_id: msg.stream_id,
                    topic_name: msg.topic,
                    message_id: msg.id,
                });

                if (!changed_narrow && current_filter && current_filter.can_apply_locally() &&
                    !current_filter.predicate()(msg)) {
                    // This topic edit makes this message leave the
                    // current narrow, which is not being changed as
                    // part of processing this event.  So we should
                    // remove the message from the current/narrowed message list.
                    const cur_row = current_msg_list.get_row(id);
                    if (cur_row !== undefined) {
                        current_msg_list.remove_and_rerender([{id: id}]);
                    }
                }
            }
        }

        if (event.orig_content !== undefined) {
            if (page_params.realm_allow_edit_history) {
                // Most correctly, we should do this for topic edits as
                // well; but we don't use the data except for content
                // edits anyway.
                const edit_history_entry = {
                    edited_by: event.edited_by,
                    prev_content: event.orig_content,
                    prev_rendered_content: event.orig_rendered_content,
                    prev_rendered_content_version: event.prev_rendered_content_version,
                    timestamp: event.edit_timestamp,
                };
                // Add message's edit_history in message dict
                // For messages that are edited, edit_history needs to
                // be added to message in frontend.
                if (msg.edit_history === undefined) {
                    msg.edit_history = [];
                }
                msg.edit_history = [edit_history_entry].concat(msg.edit_history);
            }
            message_content_edited = true;

            // Update raw_content, so that editing a few times in a row is fast.
            msg.raw_content = event.content;
        }

        msg.last_edit_timestamp = event.edit_timestamp;
        delete msg.last_edit_timestr;

        notifications.received_messages([msg]);
        alert_words.process_message(msg);

        if (topic_edited || stream_changed) {
            // if topic is changed
            let pre_edit_topic = util.get_edit_event_orig_topic(event);
            let post_edit_topic = new_topic;

            if (!topic_edited) {
                pre_edit_topic = msg.topic;
                post_edit_topic = pre_edit_topic;
            }
            recent_senders.process_topic_edit(
                event.stream_id, pre_edit_topic, post_edit_topic, new_stream_id);
        }
    }

    // If a topic was edited, we re-render the whole view to get any
    // propagated edits to be updated (since the topic edits can have
    // changed the correct grouping of messages).
    if (topic_edited || stream_changed) {
        home_msg_list.update_muting_and_rerender();
        // However, we don't need to rerender message_list.narrowed if
        // we just changed the narrow earlier in this function.
        if (!changed_narrow && current_msg_list === message_list.narrowed) {
            message_list.narrowed.update_muting_and_rerender();
        }
    } else {
        // If the content of the message was edited, we do a special animation.
        current_msg_list.view.rerender_messages(msgs_to_rerender, message_content_edited);
        if (current_msg_list === message_list.narrowed) {
            home_msg_list.view.rerender_messages(msgs_to_rerender);
        }
    }

    if (changed_compose) {
        // We need to do this after we rerender the message list, to
        // produce correct results.
        compose_fade.update_message_list();
    }

    unread_ui.update_unread_counts();
    stream_list.update_streams_sidebar();
    pm_list.update_private_messages();
};

window.message_events = exports;
<commit_msg>message_events: Update edited msgs to be rerendered together.<commit_after>const util = require("./util");
function maybe_add_narrowed_messages(messages, msg_list) {
    const ids = [];

    for (const elem of messages) {
        ids.push(elem.id);
    }

    channel.get({
        url: '/json/messages/matches_narrow',
        data: {msg_ids: JSON.stringify(ids),
               narrow: JSON.stringify(narrow_state.public_operators())},
        timeout: 5000,
        success: function (data) {
            if (msg_list !== current_msg_list) {
                // We unnarrowed in the mean time
                return;
            }

            let new_messages = [];
            const elsewhere_messages = [];

            for (const elem of messages) {
                if (data.messages.hasOwnProperty(elem.id)) {
                    util.set_match_data(elem, data.messages[elem.id]);
                    new_messages.push(elem);
                } else {
                    elsewhere_messages.push(elem);
                }
            }

            // This second call to add_message_metadata in the
            // insert_new_messages code path helps in very rare race
            // conditions, where e.g. the current user's name was
            // edited in between when they sent the message and when
            // we hear back from the server and can echo the new
            // message.  Arguably, it's counterproductive complexity.
            new_messages = new_messages.map(message_store.add_message_metadata);

            message_util.add_new_messages(new_messages, msg_list);
            unread_ops.process_visible();
            notifications.notify_messages_outside_current_search(elsewhere_messages);
        },
        error: function () {
            // We might want to be more clever here
            setTimeout(function () {
                if (msg_list === current_msg_list) {
                    // Don't actually try again if we unnarrowed
                    // while waiting
                    maybe_add_narrowed_messages(messages, msg_list);
                }
            }, 5000);
        }});
}

exports.insert_new_messages = function insert_new_messages(messages, sent_by_this_client) {
    messages = messages.map(message_store.add_message_metadata);

    unread.process_loaded_messages(messages);

    // message_list.all is a data-only list that we use to populate
    // other lists, so we always update this
    message_util.add_new_messages(messages, message_list.all);

    let render_info;

    if (narrow_state.active()) {
        // We do this NOW even though the home view is not active,
        // because we want the home view to load fast later.
        message_util.add_new_messages(messages, home_msg_list);

        if (narrow_state.filter().can_apply_locally()) {
            render_info = message_util.add_new_messages(messages, message_list.narrowed);
        } else {
            // if we cannot apply locally, we have to wait for this callback to happen to notify
            maybe_add_narrowed_messages(messages, message_list.narrowed);
        }
    } else {
        // we're in the home view, so update its list
        render_info = message_util.add_new_messages(messages, home_msg_list);
    }

    if (sent_by_this_client) {
        const need_user_to_scroll = render_info && render_info.need_user_to_scroll;
        // sent_by_this_client will be true if ANY of the messages
        // were sent by this client; notifications.notify_local_mixes
        // will filter out any not sent by us.
        notifications.notify_local_mixes(messages, need_user_to_scroll);
    }

    activity.process_loaded_messages(messages);

    unread_ui.update_unread_counts();
    resize.resize_page_components();

    unread_ops.process_visible();
    notifications.received_messages(messages);
    stream_list.update_streams_sidebar();
    pm_list.update_private_messages();
};

exports.update_messages = function update_messages(events) {
    const msgs_to_rerender = [];
    let topic_edited = false;
    let changed_narrow = false;
    let changed_compose = false;
    let message_content_edited = false;
    let stream_changed = false;

    for (const event of events) {
        const msg = message_store.get(event.message_id);
        if (msg === undefined) {
            continue;
        }

        delete msg.local_edit_timestamp;

        msgs_to_rerender.push(msg);

        message_store.update_booleans(msg, event.flags);

        unread.update_message_for_mention(msg);

        condense.un_cache_message_content_height(msg.id);

        if (event.rendered_content !== undefined) {
            msg.content = event.rendered_content;
        }

        if (event.is_me_message !== undefined) {
            msg.is_me_message = event.is_me_message;
        }

        const row = current_msg_list.get_row(event.message_id);
        if (row.length > 0) {
            message_edit.end_message_row_edit(row);
        }

        const new_topic = util.get_edit_event_topic(event);

        const {new_stream_id} = event;

        // A topic edit may affect multiple messages, listed in
        // event.message_ids. event.message_id is still the first message
        // where the user initiated the edit.
        topic_edited = new_topic !== undefined;
        stream_changed = new_stream_id !== undefined;
        if (topic_edited || stream_changed) {
            const going_forward_change = ['change_later', 'change_all'].includes(event.propagate_mode);

            const stream_name = stream_data.get_sub_by_id(event.stream_id).name;
            const compose_stream_name = compose_state.stream_name();
            const orig_topic = util.get_edit_event_orig_topic(event);

            if (going_forward_change && stream_name && compose_stream_name) {
                if (stream_name.toLowerCase() === compose_stream_name.toLowerCase()) {
                    if (orig_topic === compose_state.topic()) {
                        changed_compose = true;
                        compose_state.topic(new_topic);
                        compose_fade.set_focused_recipient("stream");
                    }
                }
            }

            const current_filter = narrow_state.filter();
            if (going_forward_change) {
                // This logic is a bit awkward.  What we're trying to
                // accomplish is two things:
                //
                // * If we're currently narrowed to a topic that was just moved,
                //   renarrow to the new location.
                // * We determine whether enough of the topic was moved to justify
                //   renarrowing by checking if the currently selected message is moved.
                //
                // Corner cases around only moving some messages in a topic
                // need to be thought about carefully when making changes.
                //
                // Code further down takes care of the actual rerendering of
                // messages within a narrow.
                const current_id = current_msg_list.selected_id();
                const selection_changed_topic = event.message_ids.includes(current_id);
                if (selection_changed_topic) {
                    if (current_filter && current_filter.has_topic(stream_name, orig_topic)) {
                        let new_filter = current_filter;
                        if (new_filter && stream_changed) {
                            // TODO: This logic doesn't handle the
                            // case where we're a guest user and the
                            // message moves to a stream we cannot
                            // access, which would cause the
                            // stream_data lookup here to fail.
                            //
                            // The fix is likely somewhat involved, so punting for now.
                            const new_stream_name = stream_data.get_sub_by_id(new_stream_id).name;
                            new_filter = new_filter.filter_with_new_params({
                                operator: 'stream',
                                operand: new_stream_name,
                            });
                            changed_narrow = true;
                        }

                        if (new_filter && topic_edited) {
                            new_filter = new_filter.filter_with_new_params({
                                operator: 'topic',
                                operand: new_topic,
                            });
                            changed_narrow = true;
                        }

                        if (changed_narrow) {
                            const operators = new_filter.operators();
                            const opts = {
                                trigger: 'stream/topic change',
                                then_select_id: current_id,
                            };
                            narrow.activate(operators, opts);
                            changed_narrow = true;
                        }
                    }
                }
            }

            const messages_to_rerender = [];
            for (const id of event.message_ids) {
                const msg = message_store.get(id);
                if (msg === undefined) {
                    continue;
                }

                // Remove the recent topics entry for the old topics;
                // must be called before we call set_message_topic.
                stream_topic_history.remove_message({
                    stream_id: msg.stream_id,
                    topic_name: msg.topic,
                });

                // Update the unread counts; again, this must be called
                // before we modify the topic field on the message.
                unread.update_unread_topics(msg, event);

                // Now edit the attributes of our message object.
                if (topic_edited) {
                    msg.topic = new_topic;
                    msg.topic_links = event.topic_links;
                }
                if (stream_changed) {
                    const new_stream_name = stream_data.get_sub_by_id(new_stream_id).name;
                    msg.stream_id = event.new_stream_id;
                    msg.stream = new_stream_name;
                    msg.display_recipient = new_stream_name;
                }

                // Add the recent topics entry for the new stream/topics.
                stream_topic_history.add_message({
                    stream_id: msg.stream_id,
                    topic_name: msg.topic,
                    message_id: msg.id,
                });

                if (!changed_narrow && current_filter && current_filter.can_apply_locally() &&
                    !current_filter.predicate()(msg)) {
                    // This topic edit makes this message leave the
                    // current narrow, which is not being changed as
                    // part of processing this event.  So we should
                    // remove the message from the current/narrowed message list.
                    const cur_row = current_msg_list.get_row(id);
                    if (cur_row !== undefined) {
                        messages_to_rerender.push({id: id});
                    }
                }
            }
            current_msg_list.remove_and_rerender(messages_to_rerender);
        }

        if (event.orig_content !== undefined) {
            if (page_params.realm_allow_edit_history) {
                // Most correctly, we should do this for topic edits as
                // well; but we don't use the data except for content
                // edits anyway.
                const edit_history_entry = {
                    edited_by: event.edited_by,
                    prev_content: event.orig_content,
                    prev_rendered_content: event.orig_rendered_content,
                    prev_rendered_content_version: event.prev_rendered_content_version,
                    timestamp: event.edit_timestamp,
                };
                // Add message's edit_history in message dict
                // For messages that are edited, edit_history needs to
                // be added to message in frontend.
                if (msg.edit_history === undefined) {
                    msg.edit_history = [];
                }
                msg.edit_history = [edit_history_entry].concat(msg.edit_history);
            }
            message_content_edited = true;

            // Update raw_content, so that editing a few times in a row is fast.
            msg.raw_content = event.content;
        }

        msg.last_edit_timestamp = event.edit_timestamp;
        delete msg.last_edit_timestr;

        notifications.received_messages([msg]);
        alert_words.process_message(msg);

        if (topic_edited || stream_changed) {
            // if topic is changed
            let pre_edit_topic = util.get_edit_event_orig_topic(event);
            let post_edit_topic = new_topic;

            if (!topic_edited) {
                pre_edit_topic = msg.topic;
                post_edit_topic = pre_edit_topic;
            }
            recent_senders.process_topic_edit(
                event.stream_id, pre_edit_topic, post_edit_topic, new_stream_id);
        }
    }

    // If a topic was edited, we re-render the whole view to get any
    // propagated edits to be updated (since the topic edits can have
    // changed the correct grouping of messages).
    if (topic_edited || stream_changed) {
        home_msg_list.update_muting_and_rerender();
        // However, we don't need to rerender message_list.narrowed if
        // we just changed the narrow earlier in this function.
        if (!changed_narrow && current_msg_list === message_list.narrowed) {
            message_list.narrowed.update_muting_and_rerender();
        }
    } else {
        // If the content of the message was edited, we do a special animation.
        current_msg_list.view.rerender_messages(msgs_to_rerender, message_content_edited);
        if (current_msg_list === message_list.narrowed) {
            home_msg_list.view.rerender_messages(msgs_to_rerender);
        }
    }

    if (changed_compose) {
        // We need to do this after we rerender the message list, to
        // produce correct results.
        compose_fade.update_message_list();
    }

    unread_ui.update_unread_counts();
    stream_list.update_streams_sidebar();
    pm_list.update_private_messages();
};

window.message_events = exports;
<|endoftext|>


<commit_before>import http2 from 'http2';
import { Inbound, Outbound } from './defs';
import { logger } from '../utils';

import {
  CONNECT_TO_REMOTE,
  CONNECTED_TO_REMOTE,
  PIPE_DECODE,
  PIPE_ENCODE,
} from '../constants';

const { HTTP2_HEADER_PATH, HTTP2_HEADER_METHOD } = http2.constants;

export class Http2Inbound extends Inbound {

  _session = null;

  _stream = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onClose = this.onClose.bind(this);
    if (this.ctx.socket) {
      this._stream = this.ctx.socket;
      this._session = this._stream.session;
      this._stream.on('data', this.onReceive);
      this._session.on('error', this.onError);
      this._session.on('timeout', this.onTimeout);
      this._session.on('close', this.onClose);
      this._session.setTimeout(this._config.timeout);
    }
  }

  get name() {
    return 'h2:inbound';
  }

  get writable() {
    return this._stream && this._stream.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._stream.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    const direction = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
    this.ctx.pipe.feed(direction, buffer);
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onClose() {
    this.close();
    if (this._outbound) {
      this._outbound.close();
      this._outbound = null;
    }
  }

  close() {
    if (this._session) {
      this._session.destroy();
      this._session = null;
    }
    if (!this._destroyed) {
      this._destroyed = true;
      this.emit('close');
    }
  }

}

export class Http2Outbound extends Outbound {

  _session = null;

  _stream = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onClose = this.onClose.bind(this);
  }

  get name() {
    return 'h2:outbound';
  }

  get writable() {
    return this._stream && this._stream.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._stream.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    const direction = this._config.is_client ? PIPE_DECODE : PIPE_ENCODE;
    this.ctx.pipe.feed(direction, buffer);
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onClose() {
    this.close();
    if (this._inbound) {
      this._inbound.close();
      this._inbound = null;
    }
  }

  close() {
    if (this._session) {
      this._session.destroy();
      this._session = null;
    }
    if (!this._destroyed) {
      this._destroyed = true;
      this.emit('close');
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case CONNECT_TO_REMOTE:
        this.onConnectToRemote(action);
        break;
      default:
        break;
    }
  }

  async onConnectToRemote(action) {
    const { host, port, keepAlive, onConnected } = action.payload;
    if (!keepAlive || !this._session) {
      const { server_host, server_port, server_pathname } = this._config;
      try {
        await this.connect({
          host: server_host,
          port: server_port,
          pathname: server_pathname,
        });

        // session
        this._session.on('connect', () => {
          if (typeof onConnected === 'function') {
            try {
              onConnected((buffer) => {
                if (buffer) {
                  const type = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
                  this.ctx.pipe.feed(type, buffer, { cid: this.ctx.proxyRequest.cid, host, port });
                }
              });
            } catch (err) {
              logger.error(`[${this.name}] [${this.remote}] onConnected callback error: ${err.message}`);
              this.emit('_error', err);
            }
          }
          this.broadcast({ type: CONNECTED_TO_REMOTE, payload: { host, port } });
        });

        // stream
        this._stream = this._session.request({
          [HTTP2_HEADER_METHOD]: ':post',
          [HTTP2_HEADER_PATH]: server_pathname || '/',
        }, {
          endStream: false,
        });
        this._stream.on('error', this.onError);
        this._stream.on('data', this.onReceive);

      } catch (err) {
        logger.warn(`[${this.name}] [${this.remote}] cannot connect to ${server_host}:${server_port}, ${err.message}`);
        this.emit('_error', err);
        this.onClose();
      }
    } else {
      this.broadcast({ type: CONNECTED_TO_REMOTE, payload: { host, port } });
    }
  }

  async connect({ host, port, pathname }) {
    // close alive connection before create a new one
    if (this._session && !this._session.closed) {
      this._session.destroy();
    }

    const address = `h2://${host}:${port}` + (pathname ? pathname : '');
    logger.info(`[${this.name}] [${this.remote}] connecting to ${address}`);

    const options = {};
    if (this._config.tls_cert_self_signed) {
      options.ca = this._config.tls_cert;
    }
    this._session = http2.connect(`https://${host}:${port}`, options);
    this._session.on('close', this.onClose);
    this._session.on('timeout', this.onTimeout);
    this._session.on('error', this.onError);
    this._session.setTimeout(this._config.timeout);
  }

}
<commit_msg>transports/h2: fix value of HTTP2_HEADER_METHOD<commit_after>import http2 from 'http2';
import { Inbound, Outbound } from './defs';
import { logger } from '../utils';

import {
  CONNECT_TO_REMOTE,
  CONNECTED_TO_REMOTE,
  PIPE_DECODE,
  PIPE_ENCODE,
} from '../constants';

const { HTTP2_HEADER_PATH, HTTP2_HEADER_METHOD } = http2.constants;

export class Http2Inbound extends Inbound {

  _session = null;

  _stream = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onClose = this.onClose.bind(this);
    if (this.ctx.socket) {
      this._stream = this.ctx.socket;
      this._session = this._stream.session;
      this._stream.on('data', this.onReceive);
      this._session.on('error', this.onError);
      this._session.on('timeout', this.onTimeout);
      this._session.on('close', this.onClose);
      this._session.setTimeout(this._config.timeout);
    }
  }

  get name() {
    return 'h2:inbound';
  }

  get writable() {
    return this._stream && this._stream.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._stream.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    const direction = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
    this.ctx.pipe.feed(direction, buffer);
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onClose() {
    this.close();
    if (this._outbound) {
      this._outbound.close();
      this._outbound = null;
    }
  }

  close() {
    if (this._session) {
      this._session.destroy();
      this._session = null;
    }
    if (!this._destroyed) {
      this._destroyed = true;
      this.emit('close');
    }
  }

}

export class Http2Outbound extends Outbound {

  _session = null;

  _stream = null;

  _destroyed = false;

  constructor(props) {
    super(props);
    this.onError = this.onError.bind(this);
    this.onReceive = this.onReceive.bind(this);
    this.onTimeout = this.onTimeout.bind(this);
    this.onClose = this.onClose.bind(this);
  }

  get name() {
    return 'h2:outbound';
  }

  get writable() {
    return this._stream && this._stream.writable;
  }

  write(buffer) {
    if (this.writable) {
      this._stream.write(buffer);
    }
  }

  onError(err) {
    logger.warn(`[${this.name}] [${this.remote}] ${err.message}`);
    this.emit('_error', err);
  }

  onReceive(buffer) {
    const direction = this._config.is_client ? PIPE_DECODE : PIPE_ENCODE;
    this.ctx.pipe.feed(direction, buffer);
  }

  onTimeout() {
    logger.warn(`[${this.name}] [${this.remote}] timeout: no I/O on the connection for ${this._config.timeout / 1e3}s`);
    this.onClose();
  }

  onClose() {
    this.close();
    if (this._inbound) {
      this._inbound.close();
      this._inbound = null;
    }
  }

  close() {
    if (this._session) {
      this._session.destroy();
      this._session = null;
    }
    if (!this._destroyed) {
      this._destroyed = true;
      this.emit('close');
    }
  }

  onBroadcast(action) {
    switch (action.type) {
      case CONNECT_TO_REMOTE:
        this.onConnectToRemote(action);
        break;
      default:
        break;
    }
  }

  async onConnectToRemote(action) {
    const { host, port, keepAlive, onConnected } = action.payload;
    if (!keepAlive || !this._session) {
      const { server_host, server_port, server_pathname } = this._config;
      try {
        await this.connect({
          host: server_host,
          port: server_port,
          pathname: server_pathname,
        });

        // session
        this._session.on('connect', () => {
          if (typeof onConnected === 'function') {
            try {
              onConnected((buffer) => {
                if (buffer) {
                  const type = this._config.is_client ? PIPE_ENCODE : PIPE_DECODE;
                  this.ctx.pipe.feed(type, buffer, { cid: this.ctx.proxyRequest.cid, host, port });
                }
              });
            } catch (err) {
              logger.error(`[${this.name}] [${this.remote}] onConnected callback error: ${err.message}`);
              this.emit('_error', err);
            }
          }
          this.broadcast({ type: CONNECTED_TO_REMOTE, payload: { host, port } });
        });

        // stream
        this._stream = this._session.request({
          [HTTP2_HEADER_METHOD]: 'POST',
          [HTTP2_HEADER_PATH]: server_pathname || '/',
        }, {
          endStream: false,
        });
        this._stream.on('error', this.onError);
        this._stream.on('data', this.onReceive);

      } catch (err) {
        logger.warn(`[${this.name}] [${this.remote}] cannot connect to ${server_host}:${server_port}, ${err.message}`);
        this.emit('_error', err);
        this.onClose();
      }
    } else {
      this.broadcast({ type: CONNECTED_TO_REMOTE, payload: { host, port } });
    }
  }

  async connect({ host, port, pathname }) {
    // close alive connection before create a new one
    if (this._session && !this._session.closed) {
      this._session.destroy();
    }

    const address = `h2://${host}:${port}` + (pathname ? pathname : '');
    logger.info(`[${this.name}] [${this.remote}] connecting to ${address}`);

    const options = {};
    if (this._config.tls_cert_self_signed) {
      options.ca = this._config.tls_cert;
    }
    this._session = http2.connect(`https://${host}:${port}`, options);
    this._session.on('close', this.onClose);
    this._session.on('timeout', this.onTimeout);
    this._session.on('error', this.onError);
    this._session.setTimeout(this._config.timeout);
  }

}
<|endoftext|>