<commit_before>#!/usr/bin/env node
"use strict";

// increase the libuv threadpool size to 1.5x the number of logical CPUs.
process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE ||
  Math.ceil(Math.max(4, require('os').cpus().length * 1.5));

var path = require("path"),
  debug = require("debug"),
  rabbit = require('rabbit.js'),
  Q = require("q");

debug = debug("tile-squirrel-cacher");

module.exports = function(opts) {
  var tilelive = require("tilelive");

  // load and register tilelive modules
  require("tilelive-modules/loader")(tilelive, opts);

  if (opts.uri) {
    throw "For now you must use a config file in queue mode";
  }

  if (opts.config) {
    var config = require(path.resolve(opts.config));
    debug("Loading " + Object.keys(config).length + " sources");

    //Load all sources
    var sources = {};
    var sinks = {};

    var sourceLoadingPromises = [];
    Object.keys(config).forEach(function(prefix) {
      (function(){
        var key = prefix;
        var deferred = Q.defer();
        sourceLoadingPromises.push(deferred.promise);
        tilelive.load(config[prefix]['source'], function(err, src){
          if(err) {
            console.log("Error loading source " + config[prefix]['source']);
            deferred.reject(err);
          }
          sources[key] = src;
          tilelive.load(config[prefix]['destination'], function(err, dest){
            if(err) {
              console.log("Error loading destination " + config[prefix]['destination']);
              deferred.reject(err);
            } else {
              sinks[key] = dest;
              src.getInfo(function(err, data){
                if(err){
                  deferred.reject(err);
                } else {
                  if(data) {
                    dest.startWriting(function(err){
                      if(err) {
                        deferred.reject(err);
                      } else {
                        dest.putInfo(data, function(err){
                          if(err) {
                            deferred.reject(err);
                          } else {
                            dest.stopWriting(function(err){
                              if(err) {
                                deferred.reject(err);
                              } else {
                                deferred.resolve();
                              }
                            });
                          }
                        });
                      }
                    });
                  }
                }
              });
            }
          });
        });
      }());
    });

    Q.all(sourceLoadingPromises).then(function() {
      debug("Finished loading sources");
    }, function(err) {
      debug("Error loading sources");
      console.log(err);
      process.exit(-1);
    });
    sourceLoadingPromises = null;

    var queueContext = rabbit.createContext('amqp://localhost');

    queueContext.on('ready', function() {
      var sub = queueContext.socket('WORKER', {prefetch: 2});
      sub.connect('tiles', function() {
        debug("Connected to queue");
        sub.on('data', function(note) {
          var subThis = this;
          debug("Received request " + note.toString());
          var messageComponents = note.toString().split("+");
          var sourceName = messageComponents[0];

          var tileComponents = messageComponents[1].split("/");
          var z = parseInt(tileComponents[0]);
          var startX, endX, startY, endY;
          var xComponents = tileComponents[1].split("-");
          if(xComponents.length == 1) {
            startX = endX = parseInt(xComponents[0]);
          } else {
            startX = parseInt(xComponents[0]);
            endX = parseInt(xComponents[1]);
          }

          var yComponents = tileComponents[2].split("-");
          if(yComponents.length == 1) {
            startY = endY = parseInt(yComponents[0]);
          } else {
            startY = parseInt(yComponents[0]);
            endY = parseInt(yComponents[1]);
          }

          var source = sources[sourceName];
          var dest = sinks[sourceName];
          debug("opening source " + config[sourceName]['destination'] + " for writing");
          dest.startWriting(function(err) {
            if(err) {
              console.log(err);
              throw err;
            }
            var tilePromises = [];

            for(var x = startX; x <= endX; x++) {
              for(var y = startY; y <= endY; y++) {
                (function(){
                  var tileX = x,
                    tileY = y,
                    tileZ = z,
                    deferred = Q.defer();

                  source.getTile(tileZ, tileX, tileY, function(err, tile, options) {
                    if(err) {
                      console.log(err);
                    } else {
                      debug("Rendered tile " + sourceName + "+" + tileZ + "/" + tileX + "/" + tileY);
                      dest.putTile(tileZ, tileX, tileY, tile, function(err){
                        if(err) {
                          console.log(err);
                          deferred.reject(err);
                        } else {
                          deferred.resolve();
                        }
                      });
                    }
                  });
                  tilePromises.push(deferred.promise);
                }());
              }
            }

            debug("Waiting for " + tilePromises.length + " tiles to render");
            Q.all(tilePromises).then(function() {
              debug("All tile promises resolved, closing source for writing");
              dest.stopWriting(function(err) {
                if(err) {
                  console.log(err);
                }
                subThis.ack();
              });
            }, function(err) {
              console.log("error resolving tile promises", err);
              subThis.ack();
            });
          });
        });
      });
    });
  }
};
<commit_msg>Wait for sources to load before connecting to queue<commit_after>#!/usr/bin/env node
"use strict";

// increase the libuv threadpool size to 1.5x the number of logical CPUs.
process.env.UV_THREADPOOL_SIZE = process.env.UV_THREADPOOL_SIZE ||
  Math.ceil(Math.max(4, require('os').cpus().length * 1.5));

var path = require("path"),
  debug = require("debug"),
  rabbit = require('rabbit.js'),
  Q = require("q");

debug = debug("tile-squirrel-cacher");

module.exports = function(opts) {
  var tilelive = require("tilelive");

  // load and register tilelive modules
  require("tilelive-modules/loader")(tilelive, opts);

  if (opts.uri) {
    throw "For now you must use a config file in queue mode";
  }

  //Load all sources
  var sources = {};
  var sinks = {};

  if (opts.config) {
    var config = require(path.resolve(opts.config));
    debug("Loading " + Object.keys(config).length + " sources");

    var sourceLoadingPromises = [];
    Object.keys(config).forEach(function(prefix) {
      (function(){
        var key = prefix;
        var deferred = Q.defer();
        sourceLoadingPromises.push(deferred.promise);
        tilelive.load(config[prefix]['source'], function(err, src){
          if(err) {
            console.log("Error loading source " + config[prefix]['source']);
            deferred.reject(err);
          }
          sources[key] = src;
          tilelive.load(config[prefix]['destination'], function(err, dest){
            if(err) {
              console.log("Error loading destination " + config[prefix]['destination']);
              deferred.reject(err);
            } else {
              sinks[key] = dest;
              src.getInfo(function(err, data){
                if(err){
                  deferred.reject(err);
                } else {
                  if(data) {
                    dest.startWriting(function(err){
                      if(err) {
                        deferred.reject(err);
                      } else {
                        dest.putInfo(data, function(err){
                          if(err) {
                            deferred.reject(err);
                          } else {
                            dest.stopWriting(function(err){
                              if(err) {
                                deferred.reject(err);
                              } else {
                                deferred.resolve();
                              }
                            });
                          }
                        });
                      }
                    });
                  }
                }
              });
            }
          });
        });
      }());
    });

    Q.all(sourceLoadingPromises).then(function() {
      debug("Finished loading sources");
      listen();
    }, function(err) {
      debug("Error loading sources");
      console.log(err);
      process.exit(-1);
    });
    sourceLoadingPromises = null;
  }

  function listen() {
    debug("Starting queue listener");
    var queueContext = rabbit.createContext('amqp://localhost');
    queueContext.on('ready', function() {
      debug("Connected to context");
      var sub = queueContext.socket('WORKER', {prefetch: 2});
      sub.connect('tiles', function() {
        debug("Connected to socket");
        sub.on('data', function(note) {
          var subThis = this;
          debug("Received request " + note.toString());
          var messageComponents = note.toString().split("+");
          var sourceName = messageComponents[0];

          var tileComponents = messageComponents[1].split("/");
          var z = parseInt(tileComponents[0]);
          var startX, endX, startY, endY;
          var xComponents = tileComponents[1].split("-");
          if(xComponents.length == 1) {
            startX = endX = parseInt(xComponents[0]);
          } else {
            startX = parseInt(xComponents[0]);
            endX = parseInt(xComponents[1]);
          }

          var yComponents = tileComponents[2].split("-");
          if(yComponents.length == 1) {
            startY = endY = parseInt(yComponents[0]);
          } else {
            startY = parseInt(yComponents[0]);
            endY = parseInt(yComponents[1]);
          }

          var source = sources[sourceName];
          var dest = sinks[sourceName];
          debug("opening source " + config[sourceName]['destination'] + " for writing");
          dest.startWriting(function(err) {
            if(err) {
              console.log(err);
              throw err;
            }
            var tilePromises = [];

            for(var x = startX; x <= endX; x++) {
              for(var y = startY; y <= endY; y++) {
                (function(){
                  var tileX = x,
                    tileY = y,
                    tileZ = z,
                    deferred = Q.defer();

                  source.getTile(tileZ, tileX, tileY, function(err, tile, options) {
                    if(err) {
                      console.log(err);
                    } else {
                      debug("Rendered tile " + sourceName + "+" + tileZ + "/" + tileX + "/" + tileY);
                      dest.putTile(tileZ, tileX, tileY, tile, function(err){
                        if(err) {
                          console.log(err);
                          deferred.reject(err);
                        } else {
                          deferred.resolve();
                        }
                      });
                    }
                  });
                  tilePromises.push(deferred.promise);
                }());
              }
            }

            debug("Waiting for " + tilePromises.length + " tiles to render");
            Q.all(tilePromises).then(function() {
              debug("All tile promises resolved, closing source for writing");
              dest.stopWriting(function(err) {
                if(err) {
                  console.log(err);
                }
                subThis.ack();
              });
            }, function(err) {
              console.log("error resolving tile promises", err);
              subThis.ack();
            });
          });
        });
      });
    });
  }
};
<|endoftext|>


<commit_before>/**
 *	Selects element in an array by index or by object property.
 */
Selector = function(array, journal)
{
	this.array = array;
	this.journal = journal || new Journal(2, false);

	this.invalidate = function()
	{
		this.journal.indexLast = this.journal.index;
		index = -1;
	}

	this.start = function()
	{
		return this.selectByIndex(0);
	}

	this.end = function()
	{
		return this.selectByIndex(this.array.length - 1);
	}

	this.last = function()
	{
		return this.selectByIndex(this.journal.last());
	}

	this.current = function()
	{
		return this.selectByIndex(this.journal.current());
	}

	this.setArray = function(value)
	{
		array = this.array = value;
		this.invalidate();
	}

	this.select = function(index)
	{
		this.journal.update(index);
		return this.current();
	}

	this.onLast = function()
	{
		return this.journal.current() == this.array.length - 1;
	}

	this.isIndexValid = function(index)
	{
		return 0 <= index && index <= this.array.length - 1;
	}

	this.selectByKey = function(key, value)
	{
		this.journal.update(this.array.map(function(e) { return e[key]; }).indexOf(value));
		return this.current();
	}
}<commit_msg>Outdated references to selectByIndex() renamed to select().<commit_after>/**
 *	Selects element in an array by index or by object property.
 */
Selector = function(array, journal)
{
	this.array = array;
	this.journal = journal || new Journal(2, false);

	this.invalidate = function()
	{
		this.journal.indexLast = this.journal.index;
		index = -1;
	}

	this.start = function()
	{
		return this.select(0);
	}

	this.end = function()
	{
		return this.select(this.array.length - 1);
	}

	this.last = function()
	{
		return this.select(this.journal.last());
	}

	this.current = function()
	{
		return this.select(this.journal.current());
	}

	this.setArray = function(value)
	{
		array = this.array = value;
		this.invalidate();
	}

	this.select = function(index)
	{
		this.journal.update(index);
		return this.current();
	}

	this.onLast = function()
	{
		return this.journal.current() == this.array.length - 1;
	}

	this.isIndexValid = function(index)
	{
		return 0 <= index && index <= this.array.length - 1;
	}

	this.selectByKey = function(key, value)
	{
		this.journal.update(this.array.map(function(e) { return e[key]; }).indexOf(value));
		return this.current();
	}
}<|endoftext|>


<commit_before>'use strict';

const boom = require('boom'), //Boom gives us some predefined http codes and proper responses
  co = require('../common'),
  userCtrl = require('../database/user'),
  userltiCtrl = require('../database/userlti'),
  //config = require('../configuration'),
  jwt = require('./jwt'),
  util = require('./util'),
  lti = require('ims-lti'),
  LTI_ID = require('../configuration.js').LTI_ID;

const PLATFORM_LTI_URL = require('../configs/microservices').platform.uri+ '/ltiLogin';

module.exports = {

  handleLTI: (req, res) => {

    let resource_id = req.params.resource_id;
    let isValid = false;

    return userltiCtrl.readAllLTIs()
      .then((ltiArray) => {
      //console.log("array.length"+ltiArray.length);
        let found = false;
        for(let i=0; i<ltiArray.length; i++){
          if(!found){
            let ltiObj = ltiArray[i];
            //console.log("lti.secret="+ltiObj.secret+ ", lti.key="+ltiObj.key);
            var ltiProvider = new lti.Provider(ltiObj.key, ltiObj.secret);
            ltiProvider.valid_request(req, function(err, isValid){
              if(err){
                console.log('There was an error in the LTI request', err);
              }
              else {
                console.log('There is the valid LTI request. i='+i);
                isValid = true;
                found = true;
                //console.log('isValid='+isValid);
                console.log("lti.id="+ltiObj._id+ ", lti.members.length="+ltiObj.members.length);
                proceedLTI(req, res, ltiObj);
              }
            });
          }//end if(!found)
        } //end for
      }).catch((error) => {
        console.log('userltiCtrl.readAllLTIs failed:', error);
        res(boom.badImplementation('Error', error));
      });
  },

};

function proceedLTI(req, res, ltiObj){
  //console.log('proceedLTI');
  console.log("proceedLTI.lti.id="+ltiObj._id+ ", lti.members.length="+ltiObj.members.length);

  var user = getUser(req);

  return util.isLTIIdentityAssigned(user.username)
    .then((result) => {
      if (result.assigned === false) {
        // If the user doesn't exist, create a new user
        return userCtrl.create(user)
          .then((result) => {
            //console.log('result='+simpleStringify(result));
            if (result[0] !== undefined && result[0] !== null) {
              //Error
              console.log('ajv error', result, co.parseAjvValidationErrors(result));
              return res(boom.badData('registration failed because data is wrong: ', co.parseAjvValidationErrors(result)));
            }
            if (result.insertedCount === 1) {
              //success
              console.log('new user created. successful, result.insertedId='+result.insertedId);
              let newUserId = result.insertedId;
              let data = {
                userid: result.insertedId,
                username: user.username,
                jwt: jwt.createLTIToken({
                  userid: result.insertedId,
                  username: user.username
                })
              };

              //Add new user to the LTI group
              console.log('ltiObj.members.length before='+ltiObj.members.length);
              let member = {
                    userid: newUserId,
                    joined: (new Date()).toISOString()
                };
              ltiObj.members.push(member);
              console.log('ltiObj.members.length after='+ltiObj.members.length);

              return userltiCtrl.update(ltiObj)
                .then((result) => {

                  //success
                  return res()
                    .redirect(PLATFORM_LTI_URL + '?data=' + encodeURIComponent(JSON.stringify(data)))
                    .temporary(true);

                }).catch((error) => {
                  console.log('userltiCtrl.update(lti) failed:', error);
                  res(boom.badImplementation('Error', error));
                });

            }
            res(boom.badImplementation());
          }).catch((error) => {
            console.log('userCtrl.create(user) failed:', error);
            res(boom.badImplementation('Error', error));
          });

      } else {
        // If the user is already registered, sign in
        console.log('already registered. signed in. result='+simpleStringify(result));
        console.log('PLATFORM_LTI_URL ='+PLATFORM_LTI_URL);
        console.log('result.userid ='+result.userid);
        let id = result.userid;
        let data = {
          userid: id,
          username: user.username,
          jwt: jwt.createLTIToken({
            userid: id,
            username: user.username
          })
        };

        //success
        return res()
          .redirect(PLATFORM_LTI_URL + '?data=' + encodeURIComponent(JSON.stringify(data)))
          .temporary(true);
        //  .redirect(PLATFORM_LTI_URL + '?resource_id='+resource_id+'&data=' + encodeURIComponent(JSON.stringify(data)))
        //  .temporary(true);
      }

    }).catch((error) => {
      //console.log('Error - util.isIdentityAssigned('+user.email+', '+user.username+') failed:', error);
      console.log('Error - util.isLTIIdentityAssigned( '+user.username+') failed:', error);
      res(boom.badImplementation('Error', error));
    });

}

function simpleStringify (object){
  let simpleObject = {};
  for (let prop in object ){
    if (!object.hasOwnProperty(prop)){
      continue;
    }
    if (typeof(object[prop]) == 'object'){
      continue;
    }
    if (typeof(object[prop]) == 'function'){
      continue;
    }
    simpleObject[prop] = object[prop];
  }
  return JSON.stringify(simpleObject); // returns cleaned up JSON
}

function getUser(req){
  //let username = util.parseAPIParameter(req.payload.ext_user_username).replace(/\s/g,'') || document.username.replace(/\s/g,'');
  console.log('req.payload.ext_user_username='+req.payload.ext_user_username);
  let username = util.parseAPIParameter(req.payload.ext_user_username).replace(/\s/g,'')+LTI_ID;
  var email = 'temp@temp.com';

  return {
    username: username,
    email: email,
    frontendLanguage: 'en',
    // spokenLanguages: [util.parseAPIParameter(req.payload.language)],
    //country:          util.parseAPIParameter(req.payload.launch_presentation_locale) || document.location || '',
    country:          util.parseAPIParameter(req.payload.launch_presentation_locale),
    picture:          '',
    description:      '',
    organization:     '',
    registered:       (new Date()).toISOString(),
    forename:         util.parseAPIParameter(req.payload.lis_person_name_given) || '',
    surname:          util.parseAPIParameter(req.payload.lis_person_name_family) || '',
    authorised: true
  };
}
<commit_msg>handler_lti updated<commit_after>'use strict';

const boom = require('boom'), //Boom gives us some predefined http codes and proper responses
  co = require('../common'),
  userCtrl = require('../database/user'),
  userltiCtrl = require('../database/userlti'),
  //config = require('../configuration'),
  jwt = require('./jwt'),
  util = require('./util'),
  lti = require('ims-lti'),
  LTI_ID = require('../configuration.js').LTI_ID;

const PLATFORM_LTI_URL = require('../configs/microservices').platform.uri+ '/ltiLogin';

module.exports = {

  handleLTI: (req, res) => {

    let resource_id = req.params.resource_id;
    let isValid = false;

    return userltiCtrl.readAllLTIs()
      .then((ltiArray) => {
      //console.log("array.length"+ltiArray.length);
        let found = false;
        for(let i=0; i<ltiArray.length; i++){
          if(!found){
            let ltiObj = ltiArray[i];
            //console.log("lti.secret="+ltiObj.secret+ ", lti.key="+ltiObj.key);
            var ltiProvider = new lti.Provider(ltiObj.key, ltiObj.secret);
            ltiProvider.valid_request(req, function(err, isValid){
              if(err){
                console.log('There was an error in the LTI request', err);
              }
              else {
                console.log('There is the valid LTI request. i='+i);
                isValid = true;
                found = true;
                //console.log('isValid='+isValid);
                //console.log('lti.id='+ltiObj._id+', lti.members.length='+ltiObj.members.length);
                proceedLTI(req, res, ltiObj);
              }
            });
          }//end if(!found)
        } //end for
      }).catch((error) => {
        console.log('userltiCtrl.readAllLTIs failed:', error);
        res(boom.badImplementation('Error', error));
      });
  },

};

function proceedLTI(req, res, ltiObj){
  //console.log('proceedLTI');
  //console.log('proceedLTI.lti.id='+ltiObj._id+ ', lti.members.length='+ltiObj.members.length);

  var user = getUser(req);

  return util.isLTIIdentityAssigned(user.username)
    .then((result) => {
      if (result.assigned === false) {
        // If the user doesn't exist, create a new user
        return userCtrl.create(user)
          .then((result) => {
            //console.log('result='+simpleStringify(result));
            if (result[0] !== undefined && result[0] !== null) {
              //Error
              console.log('ajv error', result, co.parseAjvValidationErrors(result));
              return res(boom.badData('registration failed because data is wrong: ', co.parseAjvValidationErrors(result)));
            }
            if (result.insertedCount === 1) {
              //success
              console.log('new user created. successful, result.insertedId='+result.insertedId);
              let newUserId = result.insertedId;
              let data = {
                userid: result.insertedId,
                username: user.username,
                jwt: jwt.createLTIToken({
                  userid: result.insertedId,
                  username: user.username
                })
              };

              //Add new user to the LTI group
              console.log('ltiObj.members.length before='+ltiObj.members.length);
              let member = {
                userid: newUserId,
                joined: (new Date()).toISOString()
              };
              ltiObj.members.push(member);
              console.log('ltiObj.members.length after='+ltiObj.members.length);

              return userltiCtrl.update(ltiObj)
                .then((result) => {

                  //success
                  return res()
                    .redirect(PLATFORM_LTI_URL + '?data=' + encodeURIComponent(JSON.stringify(data)))
                    .temporary(true);

                }).catch((error) => {
                  console.log('userltiCtrl.update(lti) failed:', error);
                  res(boom.badImplementation('Error', error));
                });

            }
            res(boom.badImplementation());
          }).catch((error) => {
            console.log('userCtrl.create(user) failed:', error);
            res(boom.badImplementation('Error', error));
          });

      } else {
        // If the user is already registered, sign in
        console.log('already registered. signed in. result='+simpleStringify(result));
        console.log('PLATFORM_LTI_URL ='+PLATFORM_LTI_URL);
        console.log('result.userid ='+result.userid);
        let id = result.userid;
        let data = {
          userid: id,
          username: user.username,
          jwt: jwt.createLTIToken({
            userid: id,
            username: user.username
          })
        };

        //success
        return res()
          .redirect(PLATFORM_LTI_URL + '?data=' + encodeURIComponent(JSON.stringify(data)))
          .temporary(true);
        //  .redirect(PLATFORM_LTI_URL + '?resource_id='+resource_id+'&data=' + encodeURIComponent(JSON.stringify(data)))
        //  .temporary(true);
      }

    }).catch((error) => {
      //console.log('Error - util.isIdentityAssigned('+user.email+', '+user.username+') failed:', error);
      console.log('Error - util.isLTIIdentityAssigned( '+user.username+') failed:', error);
      res(boom.badImplementation('Error', error));
    });

}

function simpleStringify (object){
  let simpleObject = {};
  for (let prop in object ){
    if (!object.hasOwnProperty(prop)){
      continue;
    }
    if (typeof(object[prop]) == 'object'){
      continue;
    }
    if (typeof(object[prop]) == 'function'){
      continue;
    }
    simpleObject[prop] = object[prop];
  }
  return JSON.stringify(simpleObject); // returns cleaned up JSON
}

function getUser(req){
  //let username = util.parseAPIParameter(req.payload.ext_user_username).replace(/\s/g,'') || document.username.replace(/\s/g,'');
  console.log('req.payload.ext_user_username='+req.payload.ext_user_username);
  let username = util.parseAPIParameter(req.payload.ext_user_username).replace(/\s/g,'')+LTI_ID;
  var email = 'temp@temp.com';

  return {
    username: username,
    email: email,
    frontendLanguage: 'en',
    // spokenLanguages: [util.parseAPIParameter(req.payload.language)],
    //country:          util.parseAPIParameter(req.payload.launch_presentation_locale) || document.location || '',
    country:          util.parseAPIParameter(req.payload.launch_presentation_locale),
    picture:          '',
    description:      '',
    organization:     '',
    registered:       (new Date()).toISOString(),
    forename:         util.parseAPIParameter(req.payload.lis_person_name_given) || '',
    surname:          util.parseAPIParameter(req.payload.lis_person_name_family) || '',
    authorised: true
  };
}
<|endoftext|>


<commit_before>var glm = require('gl-matrix'),
    vec3 = glm.vec3,
    vec4 = glm.vec4,
    mat4 = glm.mat4,
    quat = glm.quat;

var inherits = require('inherits');

var Movable = require('./movable');
var scratch = require('./scratch');
var Frustum = require('./frustum');

function Camera(canvas, follow) {
    Movable.call(this);

    this.canvas = canvas;
    this.matrix = mat4.create();
    this.inverse = mat4.create();

    this.verticalFieldOfView = Math.PI / 4;
    this.ratio;
    this.projection = mat4.create();

    this.follow = follow;
    this.view = 0;
    this.shoulderOffset = [ 0.4, 2, 2 ];
    this.thirdPersonOffset = [ 0, 2, 4 ];

    this.canvasResized();
}

inherits(Camera, Movable);

Camera.prototype.canvasResized = function() {
    var farDistance = 256; // 32 * 8 = 256. 500
    this.ratio = this.canvas.clientWidth / this.canvas.clientHeight;

    // Adjusts coordinates for the screen's aspect ration
    // Not sure to set near and far to ... seems arbitrary. Surely those values should match the frustum
    mat4.perspective(this.projection, this.verticalFieldOfView, this.ratio, 0.1, farDistance);

    this.frustum = new Frustum(this.verticalFieldOfView, this.ratio, 0.1, farDistance);
};

Camera.prototype.updateProjection = function() {
    var offset;
    switch (this.view) {
        // Over shoulder
        case 1:
            offset = this.shoulderOffset;
            //vec3.transformQuat(this.tempVector, offset, this.follow.getRotationQuat());
            // transform this offset, according to yaw
            //vec3.add(this.position, this.follow.getPosition(), this.tempVector);
            break;

        // Birds-eye
        case 2:
            offset = this.thirdPersonOffset;
            //vec3.transformQuat(this.tempVector, offset, this.follow.getRotationQuat());
            // transform this offset, according to yaw
            //vec3.add(this.position, this.follow.getPosition(), this.tempVector);
            break;

        // First-person
        default:
            offset = this.follow.getEyeOffset();
            break;
    }

    // Rotate eye offset into tempVector, which we'll then add to player position
    quat.rotateY(scratch.quat, scratch.identityQuat, this.follow.getYaw());
    vec3.transformQuat(scratch.vec3, offset, scratch.quat);
    vec3.add(this.position, this.follow.getPosition(), scratch.vec3);

    mat4.fromRotationTranslation(this.matrix, this.follow.getRotationQuat(), this.position);
    mat4.invert(this.inverse, this.matrix);
    mat4.multiply(this.inverse, this.projection, this.inverse);

    this.frustum.update(this.position, this.follow.getRotationQuat());

    return this.inverse;
};

Camera.prototype.nextView = function() {
    this.view++;
    if (this.view > 2) {
        this.view = 0;
    }
};

module.exports = Camera;<commit_msg>expand webgl distance beyond 8 chunks<commit_after>var glm = require('gl-matrix'),
    vec3 = glm.vec3,
    vec4 = glm.vec4,
    mat4 = glm.mat4,
    quat = glm.quat;

var inherits = require('inherits');

var Movable = require('./movable');
var scratch = require('./scratch');
var Frustum = require('./frustum');

function Camera(canvas, follow) {
    Movable.call(this);

    this.canvas = canvas;
    this.matrix = mat4.create();
    this.inverse = mat4.create();

    this.verticalFieldOfView = Math.PI / 4;
    this.ratio;
    this.projection = mat4.create();

    this.follow = follow;
    this.view = 0;
    this.shoulderOffset = [ 0.4, 2, 2 ];
    this.thirdPersonOffset = [ 0, 2, 4 ];

    this.canvasResized();
}

inherits(Camera, Movable);

Camera.prototype.canvasResized = function() {
    var farDistance = 384; // 32 * 12 = 384
    this.ratio = this.canvas.clientWidth / this.canvas.clientHeight;

    // Adjusts coordinates for the screen's aspect ration
    // Not sure to set near and far to ... seems arbitrary. Surely those values should match the frustum
    mat4.perspective(this.projection, this.verticalFieldOfView, this.ratio, 0.1, farDistance);

    this.frustum = new Frustum(this.verticalFieldOfView, this.ratio, 0.1, farDistance);
};

Camera.prototype.updateProjection = function() {
    var offset;
    switch (this.view) {
        // Over shoulder
        case 1:
            offset = this.shoulderOffset;
            //vec3.transformQuat(this.tempVector, offset, this.follow.getRotationQuat());
            // transform this offset, according to yaw
            //vec3.add(this.position, this.follow.getPosition(), this.tempVector);
            break;

        // Birds-eye
        case 2:
            offset = this.thirdPersonOffset;
            //vec3.transformQuat(this.tempVector, offset, this.follow.getRotationQuat());
            // transform this offset, according to yaw
            //vec3.add(this.position, this.follow.getPosition(), this.tempVector);
            break;

        // First-person
        default:
            offset = this.follow.getEyeOffset();
            break;
    }

    // Rotate eye offset into tempVector, which we'll then add to player position
    quat.rotateY(scratch.quat, scratch.identityQuat, this.follow.getYaw());
    vec3.transformQuat(scratch.vec3, offset, scratch.quat);
    vec3.add(this.position, this.follow.getPosition(), scratch.vec3);

    mat4.fromRotationTranslation(this.matrix, this.follow.getRotationQuat(), this.position);
    mat4.invert(this.inverse, this.matrix);
    mat4.multiply(this.inverse, this.projection, this.inverse);

    this.frustum.update(this.position, this.follow.getRotationQuat());

    return this.inverse;
};

Camera.prototype.nextView = function() {
    this.view++;
    if (this.view > 2) {
        this.view = 0;
    }
};

module.exports = Camera;
<|endoftext|>


<commit_before>/**
 * This file is responsible for the interactive fractal input
 */

var activeLine = undefined;
var side = 0;

var lines = {};
var lastLineId = 0;

/**
 * Recalculates the line specifications and calls redraw
 * Fixes fractal's baseline to the interactive baseline
 */
function guiRedraw() {
    var baseLine = lines["base-line"];
    spec = [];
    for (var visualLine in lines) {
        if (visualLine == "base-line") continue;
        lineSpec = getLineSpecRelativeToBaseLine(visualLine, baseLine);
        spec.push(lineSpec);
    }
    $('textarea#textarea-share').val(JSON.stringify(spec));
    redrawWithBaseLine(baseLine);
}

/**
 * Called directly from Add Line button
 * Adds a line
 */
function addLine() {
    lastLineId++;
    var lineSpec = lines["base-line"];
    var parent = document.getElementById('main-draw-table');
    addInteractiveLine(parent,
        lineSpec[0],
        lineSpec[1],
        lineSpec[2],
        lineSpec[3],
        "interactive-line-" + lastLineId);
    guiRedraw();
}

/**
 * Called directly from Remove Line button
 * Removes the line that was added last
 */
function removeLine() {
    if (lastLineId == 0) return;
    var id = 'interactive-line-' + lastLineId;
    delete lines[id];
    var toRemove = document.getElementById(id);
    toRemove.parentNode.removeChild(toRemove);
    lastLineId--;
    guiRedraw();
}

$(document).ready(function () {
    var parent = document.getElementById('main-draw-table');

    addInteractiveLine(parent, 50, 50, 800, 50, "base-line");

    parent.addEventListener("mouseup", function(event) {
        if (activeLine) {
            activeLine.style.stroke = '#000';
        }
        activeLine = undefined;
    }, true);
    parent.addEventListener("mousemove", function(event) {
        if (!activeLine) return;
        var x0 = event.offsetX;
        var y0 = event.offsetY;
        var line = lines[activeLine.id];
        if (side) {
          line[0] = x0;
          line[1] = y0;
        } else {
          line[2] = x0;
          line[3] = y0;
        }
        if (activeLine.id == "base-line") {
          line[1] = line[3] = y0;
        }
        activeLine.setAttribute("d", "M" + line[0] + " " + line[1] + " L" + line[2] + " " + line[3]);

        guiRedraw();
    }, false);

});

function addInteractiveLine(parent, x, y, x2, y2, id) {
    var interactiveLine = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    interactiveLine.setAttribute("d", "M" + x + " " + y + " L" + x2 + " " + y2);
    interactiveLine.classList.add("line-interactive");
    interactiveLine.id = id;
    lines[id] = [x,y,x2,y2];

    function generateMouseDownHandler(line) {
        return function(event) {
            mouse_moved = 0;
            activeLine = line;
            activeLine.style.stroke = '#0F0';
            var x0 = event.offsetX;
            var y0 = event.offsetY;
            var lineSpec = lines[activeLine.id];
            var dx1 = lineSpec[0] - x0;
            var dy1 = lineSpec[1] - y0;
            var dx2 = lineSpec[2] - x0;
            var dy2 = lineSpec[3] - y0;
            var l1 = dx1*dx1 + dy1*dy1;
            var l2 = dx2*dx2 + dy2*dy2;
            side = +(l1 < l2);
        }
    }
    interactiveLine.addEventListener("mousedown",
        generateMouseDownHandler(interactiveLine), true);

    interactiveLine.addEventListener("mousemove", function(){
        mouse_moved = 1;
    }, false);

//    parent.appendChild(visualLine);
    parent.appendChild(interactiveLine);
}
<commit_msg>Remove mouse_moved (unneccessary)<commit_after>/**
 * This file is responsible for the interactive fractal input
 */

var activeLine = undefined;
var side = 0;

var lines = {};
var lastLineId = 0;

/**
 * Recalculates the line specifications and calls redraw
 * Fixes fractal's baseline to the interactive baseline
 */
function guiRedraw() {
    var baseLine = lines["base-line"];
    spec = [];
    for (var visualLine in lines) {
        if (visualLine == "base-line") continue;
        lineSpec = getLineSpecRelativeToBaseLine(visualLine, baseLine);
        spec.push(lineSpec);
    }
    $('textarea#textarea-share').val(JSON.stringify(spec));
    redrawWithBaseLine(baseLine);
}

/**
 * Called directly from Add Line button
 * Adds a line
 */
function addLine() {
    lastLineId++;
    var lineSpec = lines["base-line"];
    var parent = document.getElementById('main-draw-table');
    addInteractiveLine(parent,
        lineSpec[0],
        lineSpec[1],
        lineSpec[2],
        lineSpec[3],
        "interactive-line-" + lastLineId);
    guiRedraw();
}

/**
 * Called directly from Remove Line button
 * Removes the line that was added last
 */
function removeLine() {
    if (lastLineId == 0) return;
    var id = 'interactive-line-' + lastLineId;
    delete lines[id];
    var toRemove = document.getElementById(id);
    toRemove.parentNode.removeChild(toRemove);
    lastLineId--;
    guiRedraw();
}

$(document).ready(function () {
    var parent = document.getElementById('main-draw-table');

    addInteractiveLine(parent, 50, 50, 800, 50, "base-line");

    parent.addEventListener("mouseup", function(event) {
        if (activeLine) {
            activeLine.style.stroke = '#000';
        }
        activeLine = undefined;
    }, true);
    parent.addEventListener("mousemove", function(event) {
        if (!activeLine) return;
        var x0 = event.offsetX;
        var y0 = event.offsetY;
        var line = lines[activeLine.id];
        if (side) {
          line[0] = x0;
          line[1] = y0;
        } else {
          line[2] = x0;
          line[3] = y0;
        }
        if (activeLine.id == "base-line") {
          line[1] = line[3] = y0;
        }
        activeLine.setAttribute("d", "M" + line[0] + " " + line[1] + " L" + line[2] + " " + line[3]);

        guiRedraw();
    }, false);

});

function addInteractiveLine(parent, x, y, x2, y2, id) {
    var interactiveLine = document.createElementNS('http://www.w3.org/2000/svg', 'path');
    interactiveLine.setAttribute("d", "M" + x + " " + y + " L" + x2 + " " + y2);
    interactiveLine.classList.add("line-interactive");
    interactiveLine.id = id;
    lines[id] = [x,y,x2,y2];

    function generateMouseDownHandler(line) {
        return function(event) {
            activeLine = line;
            activeLine.style.stroke = '#0F0';
            var x0 = event.offsetX;
            var y0 = event.offsetY;
            var lineSpec = lines[activeLine.id];
            var dx1 = lineSpec[0] - x0;
            var dy1 = lineSpec[1] - y0;
            var dx2 = lineSpec[2] - x0;
            var dy2 = lineSpec[3] - y0;
            var l1 = dx1*dx1 + dy1*dy1;
            var l2 = dx2*dx2 + dy2*dy2;
            side = +(l1 < l2);
        }
    }
    interactiveLine.addEventListener("mousedown",
        generateMouseDownHandler(interactiveLine), true);

//    parent.appendChild(visualLine);
    parent.appendChild(interactiveLine);
}
<|endoftext|>


<commit_before>var Q = require("q");
var _ = require("lodash");
var path = require("path");
var tmp = require('tmp');

var swig = require('./template');
var fs = require("./fs");
var parse = require("../parse");
var Plugin = require("./plugin");
var defaultConfig = require("./config");

var generators = {
    "site": require("./site"),
    "page": require("./page"),
    "ebook": require("./ebook"),
    "json": require("./json")
};

var defaultDescription = "Book generated using GitBook";

var containsFiles = function(dir, files) {
    return Q.all(_.map(files, function(file) {
        return fs.exists(path.join(dir, file));
    }))
    .then(_.all);
};

// Test if generator exists
var checkGenerator = function(options) {
    if (!generators[options.generator]) {
        return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")"));
    }
    return Q();
};

// Create the generator and load plugins
var loadGenerator = function(options) {
    return checkGenerator(options)
    .then(function() {
        var generator = new generators[options.generator](options);

        return generator.load()
        .then(_.constant(generator));
    });
};

var generate = function(options) {
    // Set defaults to options
    options = defaultConfig.defaults(options);

    // Validate options
    if (!options.input) {
        return Q.reject(new Error("Need option input (book input directory)"));
    }

    // Check files to get folder type (book, multilanguage book or neither)
    return checkGenerator(options)

    // Read config file
    .then(function() {
        try {
            var _config = require(path.resolve(options.input, options.configFile));

            options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
        }
        catch(err) {
            // No config file: not a big deal
            return Q();
        }
    })

    // Read readme
    .then(function() {
        return fs.readFile(path.join(options.input, "README.md"), "utf-8")
        .then(function(_readme) {
            _readme = parse.readme(_readme);

            options.title = options.title || _readme.title;
            options.description = options.description || _readme.description || defaultDescription;
        });
    })

    // Detect multi-languages book
    .then(function() {
        return containsFiles(options.input, ['LANGS.md'])
    })

    .then(function(isMultiLang) {
        // Multi language book
        if(isMultiLang) {
            return generateMultiLang(options);
        }

        // Book
        return generateBook(options);
    });
};

var generateMultiLang = function(options) {
    var langsSummary;
    options.output = options.output || path.join(options.input, "_book");

    return checkGenerator(options)

    // Multi-languages book
    .then(function() {
        return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
    })

    // Clean output folder
    .then(function(_langsSummary) {
        langsSummary = _langsSummary;
        return fs.remove(options.output);
    })
    .then(function() {
        return fs.mkdirp(options.output);
    })

    // Generate sub-books
    .then(function() {
        options.langsSummary = parse.langs(langsSummary);

        // Generated a book for each valid entry
        return _.reduce(options.langsSummary.list, function(prev, entry) {
            return prev.then(function() {
                return generate(_.extend({}, options, {
                    input: path.join(options.input, entry.path),
                    output: path.join(options.output, entry.path),
                    originalInput: options.input,
                    originalOutput: options.output
                }));
            })
        }, Q());
    })

    .then(function() {
        return loadGenerator(options);
    })

    // Generate languages index
    .then(function(generator) {
        return generator.langsIndex(options.langsSummary);
    })

    // Copy cover file
    .then(function() {
        return Q.all([
            fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")),
            fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg"))
        ])
        .fail(function() {
            return Q();
        })
    })

    // Return options to caller
    .then(_.constant(options));
};

/*
 *  Use a specific generator to convert a gitbook to a site/pdf/ebook/
 *  output is always a folder
 */
var generateBook = function(options) {
    var files;

    options.output = options.output || path.join(options.input, "_book");

    // Check if it's a book
    return containsFiles(options.input, ['SUMMARY.md', 'README.md'])

    // Fail if not a book
    .then(function(isBook) {
        if(!isBook) {
            return Q.reject(new Error("Invalid gitbook repository, need SUMMARY.md and README.md"));
        }
    })

    // Clean output folder
    .then(function() {
        return fs.remove(options.output);
    })

    .then(function() {
        return fs.mkdirp(options.output);
    })

    // List all files in the repository
    .then(function() {
        return fs.list(options.input)
        .then(function(_files) {
            files = _files;
        });
    })

    .then(function() {
        return loadGenerator(options);
    })

    // Convert files
    .then(function(generator) {
        // Generate the book
        return Q()

        // Get summary
        .then(function() {
            var summary = {
                path: path.join(options.input, "SUMMARY.md")
            };

            var _callHook = function(name) {
                return generator.callHook(name, summary)
                .then(function(_summary) {
                    summary = _summary;
                    return summary;
                });
            };

            return fs.readFile(summary.path, "utf-8")
                .then(function(_content) {
                    summary.content = _content;
                    return _callHook("summary:before");
                })
                .then(function() {
                    summary.content = parse.summary(summary.content);
                    return _callHook("summary:after");
                })
                .then(function() {
                    options.summary = summary.content;
                    options.navigation = parse.navigation(options.summary);
                });
        })

        // Read glossary
        .then(function() {
            var glossary = {};

            var _callHook = function(name) {
                return generator.callHook(name, glossary)
                .then(function(_glossary) {
                    glossary = _glossary;
                    return glossary;
                });
            };

            return fs.readFile(path.join(options.input, "GLOSSARY.md"), "utf-8")
                .fail(function() {
                    return "";
                })
                .then(function(_content) {
                    glossary.content = _content;
                    return _callHook("glossary:before");
                })
                .then(function() {
                    glossary.content = parse.glossary(glossary.content);
                    return _callHook("glossary:after");
                })
                .then(function() {
                    options.glossary = glossary.content;
                });
        })

        // Skip processing some files
        .then(function() {
            files = _.filter(files, function (file) {
                return !(
                    file === 'SUMMARY.md' ||
                    file === 'GLOSSARY.md'
                );
            });
        })

        // Copy file and replace markdown file
        .then(function() {
            return Q.all(
                _.chain(files)
                .map(function(file) {
                    if (!file) return;

                    if (file[file.length -1] == "/") {
                        return Q(generator.transferFolder(file));
                    } else if (path.extname(file) == ".md" && options.navigation[file] != null) {
                        return fs.readFile(path.join(options.input, file), "utf-8")
                        .then(function(content) {
                            return Q(generator.convertFile(content, file));
                        });
                    } else {
                        return Q(generator.transferFile(file));
                    }
                })
                .value()
            );
        })

        // Finish generation
        .then(function() {
            return generator.callHook("finish:before");
        });
        .then(function() {
            return generator.finish();
        })
        .then(function() {
            return generator.callHook("finish");
        });
    })

    // Return all options
    .then(function() {
        return options;
    });
};

/*
 *  Extract files from generate output in a temporary folder
 */
var generateFile = function(options) {
    options = _.defaults(options || {}, {
        input: null,
        output: null,
        extension: null
    });

    return Q.nfcall(tmp.dir)
    .then(function(tmpDir) {
        return generate(
            _.extend({},
                options,
                {
                    output: tmpDir
                })
        )
        .then(function(_options) {
            var ext = options.extension;
            var outputFile = options.output || path.resolve(options.input, "book."+ext);

            var copyFile = function(lang) {
                var _outputFile = outputFile;
                var _tmpDir = tmpDir;

                if (lang) {
                    _outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
                    _tmpDir = path.join(_tmpDir, lang);
                }

                return fs.copy(
                    path.join(_tmpDir, "index."+ext),
                    _outputFile
                );
            };

            // Multi-langs book
            return Q()
            .then(function() {
                if (_options.langsSummary) {
                    return Q.all(
                        _.map(_options.langsSummary.list, function(lang) {
                            return copyFile(lang.lang);
                        })
                    );
                } else {
                    return copyFile();
                }
            })
            .then(function() {
                return fs.remove(tmpDir);
            });
        });
    });
};

module.exports = {
    generators: generators,
    folder: generate,
    file: generateFile,
    book: generateBook,
    Plugin: Plugin,
};
<commit_msg>Fx typo<commit_after>var Q = require("q");
var _ = require("lodash");
var path = require("path");
var tmp = require('tmp');

var swig = require('./template');
var fs = require("./fs");
var parse = require("../parse");
var Plugin = require("./plugin");
var defaultConfig = require("./config");

var generators = {
    "site": require("./site"),
    "page": require("./page"),
    "ebook": require("./ebook"),
    "json": require("./json")
};

var defaultDescription = "Book generated using GitBook";

var containsFiles = function(dir, files) {
    return Q.all(_.map(files, function(file) {
        return fs.exists(path.join(dir, file));
    }))
    .then(_.all);
};

// Test if generator exists
var checkGenerator = function(options) {
    if (!generators[options.generator]) {
        return Q.reject(new Error("Invalid generator (availables are: "+_.keys(generators).join(", ")+")"));
    }
    return Q();
};

// Create the generator and load plugins
var loadGenerator = function(options) {
    return checkGenerator(options)
    .then(function() {
        var generator = new generators[options.generator](options);

        return generator.load()
        .then(_.constant(generator));
    });
};

var generate = function(options) {
    // Set defaults to options
    options = defaultConfig.defaults(options);

    // Validate options
    if (!options.input) {
        return Q.reject(new Error("Need option input (book input directory)"));
    }

    // Check files to get folder type (book, multilanguage book or neither)
    return checkGenerator(options)

    // Read config file
    .then(function() {
        try {
            var _config = require(path.resolve(options.input, options.configFile));

            options = _.merge(options, _.omit(_config, 'input', 'configFile', 'defaultsPlugins'));
        }
        catch(err) {
            // No config file: not a big deal
            return Q();
        }
    })

    // Read readme
    .then(function() {
        return fs.readFile(path.join(options.input, "README.md"), "utf-8")
        .then(function(_readme) {
            _readme = parse.readme(_readme);

            options.title = options.title || _readme.title;
            options.description = options.description || _readme.description || defaultDescription;
        });
    })

    // Detect multi-languages book
    .then(function() {
        return containsFiles(options.input, ['LANGS.md'])
    })

    .then(function(isMultiLang) {
        // Multi language book
        if(isMultiLang) {
            return generateMultiLang(options);
        }

        // Book
        return generateBook(options);
    });
};

var generateMultiLang = function(options) {
    var langsSummary;
    options.output = options.output || path.join(options.input, "_book");

    return checkGenerator(options)

    // Multi-languages book
    .then(function() {
        return fs.readFile(path.join(options.input, "LANGS.md"), "utf-8")
    })

    // Clean output folder
    .then(function(_langsSummary) {
        langsSummary = _langsSummary;
        return fs.remove(options.output);
    })
    .then(function() {
        return fs.mkdirp(options.output);
    })

    // Generate sub-books
    .then(function() {
        options.langsSummary = parse.langs(langsSummary);

        // Generated a book for each valid entry
        return _.reduce(options.langsSummary.list, function(prev, entry) {
            return prev.then(function() {
                return generate(_.extend({}, options, {
                    input: path.join(options.input, entry.path),
                    output: path.join(options.output, entry.path),
                    originalInput: options.input,
                    originalOutput: options.output
                }));
            })
        }, Q());
    })

    .then(function() {
        return loadGenerator(options);
    })

    // Generate languages index
    .then(function(generator) {
        return generator.langsIndex(options.langsSummary);
    })

    // Copy cover file
    .then(function() {
        return Q.all([
            fs.copy(path.join(options.input, "cover.jpg"), path.join(options.output, "cover.jpg")),
            fs.copy(path.join(options.input, "cover_small.jpg"), path.join(options.output, "cover_small.jpg"))
        ])
        .fail(function() {
            return Q();
        })
    })

    // Return options to caller
    .then(_.constant(options));
};

/*
 *  Use a specific generator to convert a gitbook to a site/pdf/ebook/
 *  output is always a folder
 */
var generateBook = function(options) {
    var files;

    options.output = options.output || path.join(options.input, "_book");

    // Check if it's a book
    return containsFiles(options.input, ['SUMMARY.md', 'README.md'])

    // Fail if not a book
    .then(function(isBook) {
        if(!isBook) {
            return Q.reject(new Error("Invalid gitbook repository, need SUMMARY.md and README.md"));
        }
    })

    // Clean output folder
    .then(function() {
        return fs.remove(options.output);
    })

    .then(function() {
        return fs.mkdirp(options.output);
    })

    // List all files in the repository
    .then(function() {
        return fs.list(options.input)
        .then(function(_files) {
            files = _files;
        });
    })

    .then(function() {
        return loadGenerator(options);
    })

    // Convert files
    .then(function(generator) {
        // Generate the book
        return Q()

        // Get summary
        .then(function() {
            var summary = {
                path: path.join(options.input, "SUMMARY.md")
            };

            var _callHook = function(name) {
                return generator.callHook(name, summary)
                .then(function(_summary) {
                    summary = _summary;
                    return summary;
                });
            };

            return fs.readFile(summary.path, "utf-8")
                .then(function(_content) {
                    summary.content = _content;
                    return _callHook("summary:before");
                })
                .then(function() {
                    summary.content = parse.summary(summary.content);
                    return _callHook("summary:after");
                })
                .then(function() {
                    options.summary = summary.content;
                    options.navigation = parse.navigation(options.summary);
                });
        })

        // Read glossary
        .then(function() {
            var glossary = {};

            var _callHook = function(name) {
                return generator.callHook(name, glossary)
                .then(function(_glossary) {
                    glossary = _glossary;
                    return glossary;
                });
            };

            return fs.readFile(path.join(options.input, "GLOSSARY.md"), "utf-8")
                .fail(function() {
                    return "";
                })
                .then(function(_content) {
                    glossary.content = _content;
                    return _callHook("glossary:before");
                })
                .then(function() {
                    glossary.content = parse.glossary(glossary.content);
                    return _callHook("glossary:after");
                })
                .then(function() {
                    options.glossary = glossary.content;
                });
        })

        // Skip processing some files
        .then(function() {
            files = _.filter(files, function (file) {
                return !(
                    file === 'SUMMARY.md' ||
                    file === 'GLOSSARY.md'
                );
            });
        })

        // Copy file and replace markdown file
        .then(function() {
            return Q.all(
                _.chain(files)
                .map(function(file) {
                    if (!file) return;

                    if (file[file.length -1] == "/") {
                        return Q(generator.transferFolder(file));
                    } else if (path.extname(file) == ".md" && options.navigation[file] != null) {
                        return fs.readFile(path.join(options.input, file), "utf-8")
                        .then(function(content) {
                            return Q(generator.convertFile(content, file));
                        });
                    } else {
                        return Q(generator.transferFile(file));
                    }
                })
                .value()
            );
        })

        // Finish generation
        .then(function() {
            return generator.callHook("finish:before");
        })
        .then(function() {
            return generator.finish();
        })
        .then(function() {
            return generator.callHook("finish");
        });
    })

    // Return all options
    .then(function() {
        return options;
    });
};

/*
 *  Extract files from generate output in a temporary folder
 */
var generateFile = function(options) {
    options = _.defaults(options || {}, {
        input: null,
        output: null,
        extension: null
    });

    return Q.nfcall(tmp.dir)
    .then(function(tmpDir) {
        return generate(
            _.extend({},
                options,
                {
                    output: tmpDir
                })
        )
        .then(function(_options) {
            var ext = options.extension;
            var outputFile = options.output || path.resolve(options.input, "book."+ext);

            var copyFile = function(lang) {
                var _outputFile = outputFile;
                var _tmpDir = tmpDir;

                if (lang) {
                    _outputFile = _outputFile.slice(0, -path.extname(_outputFile).length)+"_"+lang+path.extname(_outputFile);
                    _tmpDir = path.join(_tmpDir, lang);
                }

                return fs.copy(
                    path.join(_tmpDir, "index."+ext),
                    _outputFile
                );
            };

            // Multi-langs book
            return Q()
            .then(function() {
                if (_options.langsSummary) {
                    return Q.all(
                        _.map(_options.langsSummary.list, function(lang) {
                            return copyFile(lang.lang);
                        })
                    );
                } else {
                    return copyFile();
                }
            })
            .then(function() {
                return fs.remove(tmpDir);
            });
        });
    });
};

module.exports = {
    generators: generators,
    folder: generate,
    file: generateFile,
    book: generateBook,
    Plugin: Plugin,
};
<|endoftext|>


<commit_before>/*
* MultiSelect v0.9
* Copyright (c) 2012 Louis Cuny
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/

(function($){
  var msMethods = {
    'init' : function(options){
      this.settings = {
        disabledClass : 'disabled',
        dblClick : false
      };
      if(options) {
        this.settings = $.extend(this.settings, options);
      }
      var multiSelects = this;
      multiSelects.css('position', 'absolute').css('left', '-9999px');
      return multiSelects.each(function(){
        var ms = $(this),
            container,
            selectableContainer,
            selectionContainer,
            selectableUl,
            selectionUl;

        if (ms.next('.ms-container').length == 0){
          ms.attr('id', ms.attr('id') ? ms.attr('id') : 'ms-'+Math.ceil(Math.random()*1000));
          container = $('

'),
          selectableContainer = $('

'),
          selectionContainer = $('

'),
          selectableUl = $('

'),
          selectionUl = $('

');

          ms.data('settings', multiSelects.settings);

          var optgroupLabel = null,
              optgroupId = null,
              optgroupCpt = 0,
              scroll = 0;

          ms.find('optgroup, option').each(function(){
            if ($(this).is('optgroup')){
              optgroupLabel = $(this).attr('label');
              optgroupId = 'ms-'+ms.attr('id')+'-optgroup-'+optgroupCpt;

              selectableUl.append(
                '
\

\

'+optgroupLabel+'
\

\

');
              selectionUl.append(
                '
\

\

'+optgroupLabel+'
\

\

');
              optgroupCpt++;
            } else {

              var attributes = "";

              for (var cpt = 0; cpt < this.attributes.length; cpt++){
                var attr = this.attributes[cpt],
                    name = (attr.name == 'value') ? 'ms-value' : attr.name;

                attributes += name+'="'+attr.value+'" ';
              }
              var selectableLi = $('

'+$(this).text()+'

'),
                  selectedLi = selectableLi.clone();

              selectableLi.addClass('ms-elem-selectable');
              selectedLi.addClass('ms-elem-selection').hide();
              selectionUl.find('.ms-optgroup-label').hide();

              if ($(this).prop('disabled') || ms.prop('disabled')){
                selectableLi.prop('disabled', true);
                selectableLi.addClass(multiSelects.settings.disabledClass);
              }

              if (optgroupId){
                selectableUl.children('#'+optgroupId+'-selectable').find('ul').first().append(selectableLi);
                selectionUl.children('#'+optgroupId+'-selection').find('ul').first().append(selectedLi);
              } else {
                selectableUl.append(selectableLi);
                selectionUl.append(selectedLi);
              }
            }
          });

          if (multiSelects.settings.selectableHeader){
            selectableContainer.append(multiSelects.settings.selectableHeader);
          }
          selectableContainer.append(selectableUl);

          if (multiSelects.settings.selectionHeader){
            selectionContainer.append(multiSelects.settings.selectionHeader);
          }
          selectionContainer.append(selectionUl);

          container.append(selectableContainer);
          container.append(selectionContainer);

          ms.after(container);

          ms.find('option:selected').each(function(){
            ms.multiSelect('select', $(this).val(), 'init');
          });

          $('.ms-elem-selectable', selectableUl).on('mouseenter', function(){
            $('li', container).removeClass('ms-hover');
            $(this).addClass('ms-hover');
          }).on('mouseout', function(){
            $('li', container).removeClass('ms-hover');
          });

          if(multiSelects.settings.dblClick) {
            $('.ms-elem-selectable', selectableUl).on('dblclick', function(){
              ms.multiSelect('select', $(this).attr('ms-value'));
            });
            $('.ms-elem-selection', selectionUl).on('dblclick', function(){
              ms.multiSelect('deselect', $(this).attr('ms-value'));
            });
          } else {
            $('.ms-elem-selectable', selectableUl).on('click', function(){
              ms.multiSelect('select', $(this).attr('ms-value'));
            });
            $('.ms-elem-selection', selectionUl).on('click', function(){
              ms.multiSelect('deselect', $(this).attr('ms-value'));
            });
          }

          $('.ms-elem-selection', selectionUl).on('mouseenter', function(){
            $('li', selectionUl).removeClass('ms-hover');
            $(this).addClass('ms-hover');
          }).on('mouseout', function(){
            $('li', selectionUl).removeClass('ms-hover');
          });

          selectableUl.on('focusin', function(){
            $(this).addClass('ms-focus');
            selectionUl.focusout();
          }).on('focusout', function(){
            $(this).removeClass('ms-focus');
            $('li', container).removeClass('ms-hover');
          });

          selectionUl.on('focusin', function(){
            $(this).addClass('ms-focus');
          }).on('focusout', function(){
            $(this).removeClass('ms-focus');
            $('li', container).removeClass('ms-hover');
          });

          ms.on('focusin', function(){
            selectableUl.focus();
          }).on('focusout', function(){
            selectableUl.removeClass('ms-focus');
            selectionUl.removeClass('ms-focus');
          });

          ms.onKeyDown = function(e, keyContainer){
            var selectables = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', container),
                selectablesLength = selectables.length,
                selectableFocused = $('.'+keyContainer+' li.ms-hover', container),
                selectableFocusedIndex = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', container).index(selectableFocused),
                liHeight = selectables.first().outerHeight(),
                numberOfItemsDisplayed = Math.ceil(container.outerHeight()/liHeight),
                scrollStart = Math.ceil(numberOfItemsDisplayed/4);

            selectables.removeClass('ms-hover');
            if (e.keyCode == 32){ // space
              var method = keyContainer == 'ms-selectable' ? 'select' : 'deselect';
              ms.multiSelect(method, selectableFocused.first().attr('ms-value'));

            } else if (e.keyCode == 40){ // Down
              var nextIndex = (selectableFocusedIndex+1 != selectablesLength) ? selectableFocusedIndex+1 : 0,
                  nextSelectableLi = selectables.eq(nextIndex);

              nextSelectableLi.addClass('ms-hover');
              if (nextIndex > scrollStart){
                scroll += liHeight;
              } else if (nextIndex == 0){
                scroll = 0;
              }
              $('.'+keyContainer+' ul', container).scrollTop(scroll);
            } else if (e.keyCode == 38){ // Up
              var prevIndex = (selectableFocusedIndex-1 >= 0) ? selectableFocusedIndex-1 : selectablesLength-1,
                  prevSelectableLi = selectables.eq(prevIndex);
              selectables.removeClass('ms-hover');
              prevSelectableLi.addClass('ms-hover');
              if (selectablesLength-prevIndex+1 < scrollStart){
                scroll = liHeight*(selectablesLength-scrollStart);
              } else {
                scroll -= liHeight;
              }
              $('.'+keyContainer+' ul', container).scrollTop(scroll);
            } else if (e.keyCode == 37 || e.keyCode == 39){ // Right and Left
              if (selectableUl.hasClass('ms-focus')){
                selectableUl.focusout();
                selectionUl.focusin();
              } else {
                selectableUl.focusin();
                selectionUl.focusout();
              }
            }
          }

          ms.on('keydown', function(e){
            if (ms.is(':focus')){
              var keyContainer = selectableUl.hasClass('ms-focus') ? 'ms-selectable' : 'ms-selection';
              ms.onKeyDown(e, keyContainer);
            }
          });
        }
      });
    },
    'refresh' : function() {
      $("#ms-"+$(this).attr("id")).remove();
      $(this).multiSelect("init", $(this).data("settings"));
    },
    'select' : function(value, method){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul'),
          selectedOption = ms.find('option[value="'+value +'"]'),
          selectedLi = selectionUl.children('li[ms-value="'+value+'"]'),
          selectableLi = selectableUl.children('li[ms-value="'+value+'"]');

      if (method == 'init'){
        haveToSelect = !selectableLi.hasClass(ms.data('settings').disabledClass) && selectedOption.prop('selected');
      } else {
        haveToSelect = !selectableLi.hasClass(ms.data('settings').disabledClass);
        ms.focus();
      }

      if (haveToSelect && selectedLi.is(':hidden')){

        var selectableOptgroup = selectableLi.parent('.ms-optgroup');
        if (selectableOptgroup.length > 0)
          if (selectableOptgroup.children('.ms-elem-selectable:not(:hidden)').length == 1)
            selectableOptgroup.children('.ms-optgroup-label').hide();

        if (selectedLi.parent('.ms-optgroup').length > 0){
          selectedLi.prevAll('.ms-optgroup-label').show();
        }

        selectedOption.prop('selected', true);
        selectableLi.addClass('ms-selected').hide();
        selectedLi.addClass('ms-selected').show();
        if (method != 'init'){
          ms.trigger('change');
          if (typeof ms.data('settings').afterSelect == 'function') {
            ms.data('settings').afterSelect.call(this, value, selectedOption.text());
          }
        }
      }
    },
    'deselect' : function(value){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul'),
          selectedOption = ms.find('option[value="'+value +'"]'),
          selectedLi = selectionUl.children('li[ms-value="'+value+'"]');

      if (typeof value == 'string'){
        if (selectedLi){
          selectionUl.focusin();
          var selectableLi = selectableUl.children('li[ms-value="'+value+'"]'),
              selectedLi = selectionUl.children('li[ms-value="'+value+'"]');

          var selectableOptgroup = selectableLi.parent('.ms-optgroup');
          if (selectableOptgroup.length > 0){
            selectableOptgroup.children('.ms-optgroup-label').show();
            selectableOptgroup.children('.ms-elem-selectable:not(.ms-selected)').show();
          }

          selectedOption.prop('selected', false);
          selectableLi.show();
          selectableLi.removeClass('ms-selected');
          selectedLi.hide();

          var selectionOptgroup = selectedLi.parent('.ms-optgroup');

          if (selectionOptgroup.children(':visible').length == 1){
            selectionOptgroup.children('.ms-optgroup-label').hide();
          }

          ms.trigger('change');

          if (typeof ms.data('settings').afterDeselect == 'function') {
            ms.data('settings').afterDeselect.call(this, value, selectedLi.text());
          }
        }
      } else if (typeof value == 'object'){ // Is an array
        for (var cpt = 0; cpt < value.length; cpt++){
          ms.multiSelect('deselect', value[cpt]);
        }
      }
    },
    'select_all' : function(visible){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul');

      ms.find('option').prop('selected', true);
      selectableUl.find('.ms-elem-selectable').addClass('ms-selected').hide();
      selectionUl.find('.ms-optgroup-label').show();
      selectableUl.find('.ms-optgroup-label').hide();
      selectionUl.find('.ms-elem-selection').addClass('ms-selected').show();
    },
    'deselect_all' : function(){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul');

      ms.find('option').prop('selected', false);
      selectableUl.find('.ms-elem-selectable').removeClass('ms-selected').show();
      selectionUl.find('.ms-optgroup-label').hide();
      selectableUl.find('.ms-optgroup-label').show();
      selectionUl.find('.ms-elem-selection').removeClass('ms-selected').hide();
    }
  };

  $.fn.multiSelect = function(method){
    if ( msMethods[method] ) {
      return msMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return msMethods.init.apply( this, arguments );
    } else {
      if(console.log) console.log( 'Method ' +  method + ' does not exist on jquery.multiSelect' );
    }
    return false;
  };
})(jQuery);<commit_msg>delegate selectable/selection list elements binding event to parent UL<commit_after>/*
* MultiSelect v0.9
* Copyright (c) 2012 Louis Cuny
*
* This program is free software. It comes without any warranty, to
* the extent permitted by applicable law. You can redistribute it
* and/or modify it under the terms of the Do What The Fuck You Want
* To Public License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details.
*/

(function($){
  var msMethods = {
    'init' : function(options){
      this.settings = {
        disabledClass : 'disabled',
        dblClick : false
      };
      if(options) {
        this.settings = $.extend(this.settings, options);
      }
      var multiSelects = this;
      multiSelects.css('position', 'absolute').css('left', '-9999px');
      return multiSelects.each(function(){
        var ms = $(this),
            container,
            selectableContainer,
            selectionContainer,
            selectableUl,
            selectionUl;

        if (ms.next('.ms-container').length == 0){
          ms.attr('id', ms.attr('id') ? ms.attr('id') : 'ms-'+Math.ceil(Math.random()*1000));
          container = $('

'),
          selectableContainer = $('

'),
          selectionContainer = $('

'),
          selectableUl = $('

'),
          selectionUl = $('

');

          ms.data('settings', multiSelects.settings);

          var optgroupLabel = null,
              optgroupId = null,
              optgroupCpt = 0,
              scroll = 0;

          ms.find('optgroup, option').each(function(){
            if ($(this).is('optgroup')){
              optgroupLabel = $(this).attr('label');
              optgroupId = 'ms-'+ms.attr('id')+'-optgroup-'+optgroupCpt;

              selectableUl.append(
                '
\

\

'+optgroupLabel+'
\

\

');
              selectionUl.append(
                '
\

\

'+optgroupLabel+'
\

\

');
              optgroupCpt++;
            } else {

              var attributes = "";

              for (var cpt = 0; cpt < this.attributes.length; cpt++){
                var attr = this.attributes[cpt],
                    name = (attr.name == 'value') ? 'ms-value' : attr.name;

                attributes += name+'="'+attr.value+'" ';
              }
              var selectableLi = $('

'+$(this).text()+'

'),
                  selectedLi = selectableLi.clone();

              selectableLi.addClass('ms-elem-selectable');
              selectedLi.addClass('ms-elem-selection').hide();
              selectionUl.find('.ms-optgroup-label').hide();

              if ($(this).prop('disabled') || ms.prop('disabled')){
                selectableLi.prop('disabled', true);
                selectableLi.addClass(multiSelects.settings.disabledClass);
              }

              if (optgroupId){
                selectableUl.children('#'+optgroupId+'-selectable').find('ul').first().append(selectableLi);
                selectionUl.children('#'+optgroupId+'-selection').find('ul').first().append(selectedLi);
              } else {
                selectableUl.append(selectableLi);
                selectionUl.append(selectedLi);
              }
            }
          });

          if (multiSelects.settings.selectableHeader){
            selectableContainer.append(multiSelects.settings.selectableHeader);
          }
          selectableContainer.append(selectableUl);

          if (multiSelects.settings.selectionHeader){
            selectionContainer.append(multiSelects.settings.selectionHeader);
          }
          selectionContainer.append(selectionUl);

          container.append(selectableContainer);
          container.append(selectionContainer);

          ms.after(container);

          ms.find('option:selected').each(function(){
            ms.multiSelect('select', $(this).val(), 'init');
          });

          selectableUl.on('mouseenter', '.ms-elem-selectable', function(){
            $('li', container).removeClass('ms-hover');
            $(this).addClass('ms-hover');
          }).on('mouseout', function(){
            $('li', container).removeClass('ms-hover');
          });

          if(multiSelects.settings.dblClick) {
            selectableUl.on('dblclick', '.ms-elem-selectable', function(){
              ms.multiSelect('select', $(this).attr('ms-value'));
            });
            selectionUl.on('dblclick', '.ms-elem-selection', function(){
              ms.multiSelect('deselect', $(this).attr('ms-value'));
            });
          } else {
            selectableUl.on('click', '.ms-elem-selectable', function(){
              ms.multiSelect('select', $(this).attr('ms-value'));
            });
            selectionUl.on('click', '.ms-elem-selection', function(){
              ms.multiSelect('deselect', $(this).attr('ms-value'));
            });
          }

          selectionUl.on('mouseenter', '.ms-elem-selection', function(){
            $('li', selectionUl).removeClass('ms-hover');
            $(this).addClass('ms-hover');
          }).on('mouseout', function(){
            $('li', selectionUl).removeClass('ms-hover');
          });

          selectableUl.on('focusin', function(){
            $(this).addClass('ms-focus');
            selectionUl.focusout();
          }).on('focusout', function(){
            $(this).removeClass('ms-focus');
            $('li', container).removeClass('ms-hover');
          });

          selectionUl.on('focusin', function(){
            $(this).addClass('ms-focus');
          }).on('focusout', function(){
            $(this).removeClass('ms-focus');
            $('li', container).removeClass('ms-hover');
          });

          ms.on('focusin', function(){
            selectableUl.focus();
          }).on('focusout', function(){
            selectableUl.removeClass('ms-focus');
            selectionUl.removeClass('ms-focus');
          });

          ms.onKeyDown = function(e, keyContainer){
            var selectables = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', container),
                selectablesLength = selectables.length,
                selectableFocused = $('.'+keyContainer+' li.ms-hover', container),
                selectableFocusedIndex = $('.'+keyContainer+' li:visible:not(.ms-optgroup-label, .ms-optgroup-container)', container).index(selectableFocused),
                liHeight = selectables.first().outerHeight(),
                numberOfItemsDisplayed = Math.ceil(container.outerHeight()/liHeight),
                scrollStart = Math.ceil(numberOfItemsDisplayed/4);

            selectables.removeClass('ms-hover');
            if (e.keyCode == 32){ // space
              var method = keyContainer == 'ms-selectable' ? 'select' : 'deselect';
              ms.multiSelect(method, selectableFocused.first().attr('ms-value'));

            } else if (e.keyCode == 40){ // Down
              var nextIndex = (selectableFocusedIndex+1 != selectablesLength) ? selectableFocusedIndex+1 : 0,
                  nextSelectableLi = selectables.eq(nextIndex);

              nextSelectableLi.addClass('ms-hover');
              if (nextIndex > scrollStart){
                scroll += liHeight;
              } else if (nextIndex == 0){
                scroll = 0;
              }
              $('.'+keyContainer+' ul', container).scrollTop(scroll);
            } else if (e.keyCode == 38){ // Up
              var prevIndex = (selectableFocusedIndex-1 >= 0) ? selectableFocusedIndex-1 : selectablesLength-1,
                  prevSelectableLi = selectables.eq(prevIndex);
              selectables.removeClass('ms-hover');
              prevSelectableLi.addClass('ms-hover');
              if (selectablesLength-prevIndex+1 < scrollStart){
                scroll = liHeight*(selectablesLength-scrollStart);
              } else {
                scroll -= liHeight;
              }
              $('.'+keyContainer+' ul', container).scrollTop(scroll);
            } else if (e.keyCode == 37 || e.keyCode == 39){ // Right and Left
              if (selectableUl.hasClass('ms-focus')){
                selectableUl.focusout();
                selectionUl.focusin();
              } else {
                selectableUl.focusin();
                selectionUl.focusout();
              }
            }
          }

          ms.on('keydown', function(e){
            if (ms.is(':focus')){
              var keyContainer = selectableUl.hasClass('ms-focus') ? 'ms-selectable' : 'ms-selection';
              ms.onKeyDown(e, keyContainer);
            }
          });
        }
      });
    },
    'refresh' : function() {
      $("#ms-"+$(this).attr("id")).remove();
      $(this).multiSelect("init", $(this).data("settings"));
    },
    'select' : function(value, method){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul'),
          selectedOption = ms.find('option[value="'+value +'"]'),
          selectedLi = selectionUl.children('li[ms-value="'+value+'"]'),
          selectableLi = selectableUl.children('li[ms-value="'+value+'"]');

      if (method == 'init'){
        haveToSelect = !selectableLi.hasClass(ms.data('settings').disabledClass) && selectedOption.prop('selected');
      } else {
        haveToSelect = !selectableLi.hasClass(ms.data('settings').disabledClass);
        ms.focus();
      }

      if (haveToSelect && selectedLi.is(':hidden')){

        var selectableOptgroup = selectableLi.parent('.ms-optgroup');
        if (selectableOptgroup.length > 0)
          if (selectableOptgroup.children('.ms-elem-selectable:not(:hidden)').length == 1)
            selectableOptgroup.children('.ms-optgroup-label').hide();

        if (selectedLi.parent('.ms-optgroup').length > 0){
          selectedLi.prevAll('.ms-optgroup-label').show();
        }

        selectedOption.prop('selected', true);
        selectableLi.addClass('ms-selected').hide();
        selectedLi.addClass('ms-selected').show();
        if (method != 'init'){
          ms.trigger('change');
          if (typeof ms.data('settings').afterSelect == 'function') {
            ms.data('settings').afterSelect.call(this, value, selectedOption.text());
          }
        }
      }
    },
    'deselect' : function(value){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul'),
          selectedOption = ms.find('option[value="'+value +'"]'),
          selectedLi = selectionUl.children('li[ms-value="'+value+'"]');

      if (typeof value == 'string'){
        if (selectedLi){
          selectionUl.focusin();
          var selectableLi = selectableUl.children('li[ms-value="'+value+'"]'),
              selectedLi = selectionUl.children('li[ms-value="'+value+'"]');

          var selectableOptgroup = selectableLi.parent('.ms-optgroup');
          if (selectableOptgroup.length > 0){
            selectableOptgroup.children('.ms-optgroup-label').show();
            selectableOptgroup.children('.ms-elem-selectable:not(.ms-selected)').show();
          }

          selectedOption.prop('selected', false);
          selectableLi.show();
          selectableLi.removeClass('ms-selected');
          selectedLi.hide();

          var selectionOptgroup = selectedLi.parent('.ms-optgroup');

          if (selectionOptgroup.children(':visible').length == 1){
            selectionOptgroup.children('.ms-optgroup-label').hide();
          }

          ms.trigger('change');

          if (typeof ms.data('settings').afterDeselect == 'function') {
            ms.data('settings').afterDeselect.call(this, value, selectedLi.text());
          }
        }
      } else if (typeof value == 'object'){ // Is an array
        for (var cpt = 0; cpt < value.length; cpt++){
          ms.multiSelect('deselect', value[cpt]);
        }
      }
    },
    'select_all' : function(visible){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul');

      ms.find('option').prop('selected', true);
      selectableUl.find('.ms-elem-selectable').addClass('ms-selected').hide();
      selectionUl.find('.ms-optgroup-label').show();
      selectableUl.find('.ms-optgroup-label').hide();
      selectionUl.find('.ms-elem-selection').addClass('ms-selected').show();
    },
    'deselect_all' : function(){
      var ms = this,
          selectableUl = $('#ms-'+ms.attr('id')+' .ms-selectable ul'),
          selectionUl = $('#ms-'+ms.attr('id')+' .ms-selection ul');

      ms.find('option').prop('selected', false);
      selectableUl.find('.ms-elem-selectable').removeClass('ms-selected').show();
      selectionUl.find('.ms-optgroup-label').hide();
      selectableUl.find('.ms-optgroup-label').show();
      selectionUl.find('.ms-elem-selection').removeClass('ms-selected').hide();
    }
  };

  $.fn.multiSelect = function(method){
    if ( msMethods[method] ) {
      return msMethods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
    } else if ( typeof method === 'object' || ! method ) {
      return msMethods.init.apply( this, arguments );
    } else {
      if(console.log) console.log( 'Method ' +  method + ' does not exist on jquery.multiSelect' );
    }
    return false;
  };
})(jQuery);<|endoftext|>