<commit_before>var fs = require('fs')
  , path = require('path')
  , request = require('request')
  , url = require('url')

var githubConfigFile = path.resolve(__dirname, 'github-config.json')
  , githubConfig = {}
  , isConfigEnabled = false

// ^^^helps with the home page view; should we show the github dropdown?

if (fs.existsSync(githubConfigFile)) {
  githubConfig = require(githubConfigFile);
  isConfigEnabled = true;
} else if (process.env.github_client_id !== undefined) {
  githubConfig = {
    "client_id": process.env.github_client_id,
    "redirect_uri": process.env.github_redirect_uri,
    "client_secret": process.env.github_client_secret,
    "callback_url": process.env.github_callback_url
  };
  isConfigEnabled = true;
  console.log('Github config found in environment. Plugin enabled. (Key: "' + githubConfig.client_id +'")');
} else if (process.env.github_access_token !== undefined) {
  githubConfig = {
    "access_token": process.env.github_access_token
  };
  isConfigEnabled = true;
  console.log('Github config found in environment. Plugin enabled using a personal access_token.');
} else {
  githubConfig = {
    "client_id": "YOUR_ID"
  , "redirect_uri": "http://dillinger.io/"
  , "client_secret": "YOUR_SECRET"
  , "callback_url": "http://dillinger.io/oauth/github"
  }
  console.warn('Github config not found at ' + githubConfigFile + '. Plugin disabled.')
}

function arrayToRegExp(arr) {
  return new RegExp("(" + arr.map(function(e) { return e.replace('.','\\.'); }).join('|') + ")$", 'i');
}

exports.Github = (function() {

  var githubApi = 'https://api.github.com/'
    , headers = {
      "User-Agent": "X-Dillinger-App"
    }

  // String builder for auth url...
  function _buildAuthUrl() {
    return  'https://github.com/login/oauth/authorize?client_id='
            + githubConfig.client_id
            + '&scope=repo&redirect_uri='
            + githubConfig.callback_url
  }

  return {
    isConfigured: isConfigEnabled,
    githubConfig: githubConfig,
    generateAuthUrl: function(req, res) {
      return _buildAuthUrl()
    },
    getUsername: function(req, res, cb) {

      var uri = githubApi + 'user?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      console.log('getting username from github: ' + uri)

      request(options, function(e, r, d) {
        if (e) {
          console.error(e)
          return res.redirect(r.statusCode)
        }
        else if (!e && r.statusCode === 200) {
          d = JSON.parse(d)
          req.session.github.username = d.login
          cb && cb()
        }
      }) // end request.get()

    }, // end getUsername
    fetchOrgs: function(req, res) {
      var uri = githubApi + 'user/orgs?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.',
            data: r.statusCode
          })
        }
        else if (!e && r.statusCode == 200) {
          var set = []

          d = JSON.parse(d)

          d.forEach(function(el) {

            // Right now GitHub does not display a "Company Name" in user/orgs API route
            // Hopefully they will add it in later, for now use "login" name.

            var item = {
              url: el.url
            , name: el.login
            }

            set.push(item)
          })

          res.json(set)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch organizations from Github.' })
        }
      }) // end request callback

    }, // end fetchOrgs

    fetchRepos: function(req, res) {

      var uri;

      if (req.body.owner !== req.session.github.username) {
        uri = githubApi + 'orgs/' + req.body.owner + '/repos?access_token=' + req.session.github.oauth
      }
      else {
        uri = githubApi + 'user/repos?access_token=' + req.session.github.oauth
      }

      if (isFinite(req.body.page) && +req.body.page > 1) {
        uri += "&page=" + req.body.page
      }

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.',
            data: r.statusCode
          })
        }
        else if (!e && r.statusCode == 200) {
          var set = []

          d = JSON.parse(d)

          d.forEach(function(el) {

            var item = {
              url: el.url
            , name: el.name
            , private: el.private
            // future property we will need to pass so we can know whether we can "write" to repo
            //, permissions: el.permissions
            }

            set.push(item)
          })

          res.json(set)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch repos from Github.' })
        }
      }) // end request callback
    }, // end fetchRepos
    fetchBranches: function(req, res) {

      var uri = githubApi
        + 'repos/'
        + req.body.owner
        + '/'
        + req.body.repo
        +'/branches?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.'
          , d: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {
          res.send(d)
        } // end else if
        else {
          res.json({ error: 'Unable to fetch branches from Github.' })
        }
      }) // end request callback

    }, // end fetchBranches
    fetchTreeFiles: function(req, res) {
      // /repos/:user/:repo/git/trees/:sha

      var uri, options, fileExts, regExp

      uri = githubApi
        + 'repos/'
        + req.body.owner
        + '/'
        + req.body.repo
        + '/git/trees/'
        + req.body.sha + '?recursive=1&access_token=' + req.session.github.oauth
        ;

      options = {
        headers: headers
      , uri: uri
      };

      fileExts = req.body.fileExts.split("|");
      regExp = arrayToRegExp(fileExts);

      request(options, function(e, r, d) {

        if (e) {
          res.send({
            error: 'Request error.'
          , data: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {
          d = JSON.parse(d)
          d.branch = req.body.branch // inject branch info

          // overwrite d.tree to only return items that match regexp
          d.tree = d.tree.filter(function(item) { return regExp.test(item.path) });

          res.json(d)
        } // end else if
        else {
          res.json({ error: 'Unable to fetch files from Github.' })
        }
      }) // end request callback

    }, // end fetchTreeFiles
    fetchFile: function(req, res) {

      var uri = req.body.url
        , isPrivateRepo = /blob/.test(uri)

      // https://api.github.com/octocat/Hello-World/git/blobs/44b4fc6d56897b048c772eb4087f854f46256132
      // If it is a private repo, we need to make an API call, because otherwise it is the raw file.
      if (isPrivateRepo) {
        uri += '?access_token=' + req.session.github.oauth
      }

      var options = {
        headers: headers
      , uri: uri
      }

      console.dir(options)

      request(options, function(e, r, d) {
        if (e) {
          console.error(e)

          res.send({
            error: 'Request error.'
          , data: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {

          var jsonResp = {
            data: d
          , error: false
          }

          if (isPrivateRepo) {
            d = JSON.parse(d)
            jsonResp.data = (new Buffer(d.content, 'base64').toString('ascii'))
          }

          res.json(jsonResp)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch file from Github.' })
        }
      }) // end request callback

    }, // end fetchFile

    saveToGithub: function(req, res) {

      var data = req.body
      if (!data.uri) {
        res.json(400, { "error": "Requires Github URI" })
      }
      else {
        // uri = "https://api.github.com/repos/:owner/:repo/contents/:path"
        var options, uri, owner, repo, branch, sha, isPrivateRepo

        isPrivateRepo = /blob/.test(data.uri)

        branch = data.branch
        path = data.name
        sha = data.sha
        repo = data.repo
        owner = data.owner

        uri = githubApi + "repos/" + owner + '/' + repo + '/contents/' + path
        uri += '?access_token=' + req.session.github.oauth

        commit = {
          message: "Modified file using Dillinger" // Better commit messages?
        , path: path
        , branch: branch
        , content: data.data
        , sha: sha
        }

        options = {
          headers: headers
        , uri: uri
        , method: "PUT"
        , body: JSON.stringify(commit)
        }

        request(options, function(e, r, d) {
          // console.log(uri, e, r.statusCode, JSON.stringify(commit), d)
          if (!e && r.statusCode === 200) {
            return res.json(200, JSON.parse(d))
          }
          return res.json(400, { "error": "Unable to save file: " + (e || JSON.parse(d).message) })

        })

      }
    }
  }

})()
<commit_msg>Add back component for Stackato use lost in merge<commit_after>var fs = require('fs')
  , path = require('path')
  , request = require('request')
  , url = require('url')

var githubConfigFile = path.resolve(__dirname, 'github-config.json')
  , githubConfig = {}
  , isConfigEnabled = false

// ^^^helps with the home page view; should we show the github dropdown?

if (fs.existsSync(githubConfigFile)) {
  githubConfig = require(githubConfigFile);
  isConfigEnabled = true;
} else if (process.env.github_client_id !== undefined) {
  githubConfig = {
    "client_id": process.env.github_client_id,
    "redirect_uri": process.env.github_redirect_uri,
    "client_secret": process.env.github_client_secret,
    "callback_url": process.env.github_callback_url
  };
  isConfigEnabled = true;
  console.log('Github config found in environment. Plugin enabled. (Key: "' + githubConfig.client_id +'")');
} else if (process.env.github_access_token !== undefined) {
  githubConfig = {
    "access_token": process.env.github_access_token
  };
  isConfigEnabled = true;
  console.log('Github config found in environment. Plugin enabled using a personal access_token.');
} else {
  githubConfig = {
    "client_id": "YOUR_ID"
  , "redirect_uri": "http://dillinger.io/"
  , "client_secret": "YOUR_SECRET"
  , "callback_url": "http://dillinger.io/oauth/github"
  }
  console.warn('Github config not found at ' + githubConfigFile + '. Plugin disabled.')
}

var APP_URL="localhost";
if (process.env.VCAP_APPLICATION)
  APP_URL = JSON.parse(process.env.VCAP_APPLICATION).uris[0]

github_config.redirect_uri = "http://"+APP_URL;
github_config.callback_url = "http://"+APP_URL+"/oauth/github";

function arrayToRegExp(arr) {
  return new RegExp("(" + arr.map(function(e) { return e.replace('.','\\.'); }).join('|') + ")$", 'i');
}

exports.Github = (function() {

  var githubApi = 'https://api.github.com/'
    , headers = {
      "User-Agent": "X-Dillinger-App"
    }

  // String builder for auth url...
  function _buildAuthUrl() {
    return  'https://github.com/login/oauth/authorize?client_id='
            + githubConfig.client_id
            + '&scope=repo&redirect_uri='
            + githubConfig.callback_url
  }

  return {
    isConfigured: isConfigEnabled,
    githubConfig: githubConfig,
    generateAuthUrl: function(req, res) {
      return _buildAuthUrl()
    },
    getUsername: function(req, res, cb) {

      var uri = githubApi + 'user?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      console.log('getting username from github: ' + uri)

      request(options, function(e, r, d) {
        if (e) {
          console.error(e)
          return res.redirect(r.statusCode)
        }
        else if (!e && r.statusCode === 200) {
          d = JSON.parse(d)
          req.session.github.username = d.login
          cb && cb()
        }
      }) // end request.get()

    }, // end getUsername
    fetchOrgs: function(req, res) {
      var uri = githubApi + 'user/orgs?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.',
            data: r.statusCode
          })
        }
        else if (!e && r.statusCode == 200) {
          var set = []

          d = JSON.parse(d)

          d.forEach(function(el) {

            // Right now GitHub does not display a "Company Name" in user/orgs API route
            // Hopefully they will add it in later, for now use "login" name.

            var item = {
              url: el.url
            , name: el.login
            }

            set.push(item)
          })

          res.json(set)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch organizations from Github.' })
        }
      }) // end request callback

    }, // end fetchOrgs

    fetchRepos: function(req, res) {

      var uri;

      if (req.body.owner !== req.session.github.username) {
        uri = githubApi + 'orgs/' + req.body.owner + '/repos?access_token=' + req.session.github.oauth
      }
      else {
        uri = githubApi + 'user/repos?access_token=' + req.session.github.oauth
      }

      if (isFinite(req.body.page) && +req.body.page > 1) {
        uri += "&page=" + req.body.page
      }

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.',
            data: r.statusCode
          })
        }
        else if (!e && r.statusCode == 200) {
          var set = []

          d = JSON.parse(d)

          d.forEach(function(el) {

            var item = {
              url: el.url
            , name: el.name
            , private: el.private
            // future property we will need to pass so we can know whether we can "write" to repo
            //, permissions: el.permissions
            }

            set.push(item)
          })

          res.json(set)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch repos from Github.' })
        }
      }) // end request callback
    }, // end fetchRepos
    fetchBranches: function(req, res) {

      var uri = githubApi
        + 'repos/'
        + req.body.owner
        + '/'
        + req.body.repo
        +'/branches?access_token=' + req.session.github.oauth

      var options = {
        headers: headers
      , uri: uri
      }

      request(options, function(e, r, d) {
        if (e) {
          res.send({
            error: 'Request error.'
          , d: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {
          res.send(d)
        } // end else if
        else {
          res.json({ error: 'Unable to fetch branches from Github.' })
        }
      }) // end request callback

    }, // end fetchBranches
    fetchTreeFiles: function(req, res) {
      // /repos/:user/:repo/git/trees/:sha

      var uri, options, fileExts, regExp

      uri = githubApi
        + 'repos/'
        + req.body.owner
        + '/'
        + req.body.repo
        + '/git/trees/'
        + req.body.sha + '?recursive=1&access_token=' + req.session.github.oauth
        ;

      options = {
        headers: headers
      , uri: uri
      };

      fileExts = req.body.fileExts.split("|");
      regExp = arrayToRegExp(fileExts);

      request(options, function(e, r, d) {

        if (e) {
          res.send({
            error: 'Request error.'
          , data: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {
          d = JSON.parse(d)
          d.branch = req.body.branch // inject branch info

          // overwrite d.tree to only return items that match regexp
          d.tree = d.tree.filter(function(item) { return regExp.test(item.path) });

          res.json(d)
        } // end else if
        else {
          res.json({ error: 'Unable to fetch files from Github.' })
        }
      }) // end request callback

    }, // end fetchTreeFiles
    fetchFile: function(req, res) {

      var uri = req.body.url
        , isPrivateRepo = /blob/.test(uri)

      // https://api.github.com/octocat/Hello-World/git/blobs/44b4fc6d56897b048c772eb4087f854f46256132
      // If it is a private repo, we need to make an API call, because otherwise it is the raw file.
      if (isPrivateRepo) {
        uri += '?access_token=' + req.session.github.oauth
      }

      var options = {
        headers: headers
      , uri: uri
      }

      console.dir(options)

      request(options, function(e, r, d) {
        if (e) {
          console.error(e)

          res.send({
            error: 'Request error.'
          , data: r.statusCode
          })
        }
        else if (!e && r.statusCode === 200) {

          var jsonResp = {
            data: d
          , error: false
          }

          if (isPrivateRepo) {
            d = JSON.parse(d)
            jsonResp.data = (new Buffer(d.content, 'base64').toString('ascii'))
          }

          res.json(jsonResp)

        } // end else if
        else {
          res.json({ error: 'Unable to fetch file from Github.' })
        }
      }) // end request callback

    }, // end fetchFile

    saveToGithub: function(req, res) {

      var data = req.body
      if (!data.uri) {
        res.json(400, { "error": "Requires Github URI" })
      }
      else {
        // uri = "https://api.github.com/repos/:owner/:repo/contents/:path"
        var options, uri, owner, repo, branch, sha, isPrivateRepo

        isPrivateRepo = /blob/.test(data.uri)

        branch = data.branch
        path = data.name
        sha = data.sha
        repo = data.repo
        owner = data.owner

        uri = githubApi + "repos/" + owner + '/' + repo + '/contents/' + path
        uri += '?access_token=' + req.session.github.oauth

        commit = {
          message: "Modified file using Dillinger" // Better commit messages?
        , path: path
        , branch: branch
        , content: data.data
        , sha: sha
        }

        options = {
          headers: headers
        , uri: uri
        , method: "PUT"
        , body: JSON.stringify(commit)
        }

        request(options, function(e, r, d) {
          // console.log(uri, e, r.statusCode, JSON.stringify(commit), d)
          if (!e && r.statusCode === 200) {
            return res.json(200, JSON.parse(d))
          }
          return res.json(400, { "error": "Unable to save file: " + (e || JSON.parse(d).message) })

        })

      }
    }
  }

})()
<|endoftext|>


<commit_before>module.exports = function(Meteor) {
	var _ = Meteor.underscore;
	var Tracker = Meteor.Tracker;
	var ReactiveObjectMap;

	ReactiveObjectMap = function() {
		if (!(this instanceof ReactiveObjectMap))
		// called without `new`
			return new ReactiveObjectMap();

		this.map = {};
		this.dep = new Tracker.Dependency;
	};

	ReactiveObjectMap.prototype.assign = function(collection, iteratee) {
		this.map = _.indexBy(collection, iteratee);
		this.dep.changed();
	};

	ReactiveObjectMap.prototype.get = function(key) {
		if (Tracker.active)
			this.dep.depend();
		return this.map[key];
	};

	ReactiveObjectMap.prototype.set = function(key, value) {
		var old = this.map[key];
		this.map[key] = value;
		if (old !== value)
			this.dep.changed();
	};

	ReactiveObjectMap.prototype.has = function(key) {
		if (Tracker.active)
			this.dep.depend();
		return this.hasOwnProperty(key);
	};

	ReactiveObjectMap.prototype.clear = function(key, value) {
		this.map = {};
		this.dep.changed();
	};

	ReactiveObjectMap.prototype.delete = function(key, value) {
		if (delete this.map[key])
			this.dep.changed();
	};

	ReactiveObjectMap.prototype.setAttribute = function(key, attr, value) {
		var old = this.map[key][attr];
		this.map[key][attr] = value;
		if (old !== value)
			this.dep.changed();
	};

	ReactiveObjectMap.prototype.getAttribute = function(key, attr) {
		if (Tracker.active)
			this.dep.depend();
		return this.map[key][attr];
	};

	ReactiveObjectMap.prototype.keys = function() {
		if (Tracker.active)
			this.dep.depend();
		return Object.keys(this.map);
	};

	ReactiveObjectMap.prototype.values = function() {
		if (Tracker.active)
			this.dep.depend();
		return _.values(this.map);
	};

	ReactiveObjectMap.prototype.filter = function(predicate) {
		if (Tracker.active)
			this.dep.depend();
		return _.filter(this.map,predicate);
	};

	ReactiveObjectMap.prototype.sortBy = function(iteratee) {
		if (Tracker.active)
			this.dep.depend();
		return _.sortBy(this.map,iteratee);
	};

	ReactiveObjectMap.prototype.map = function(iteratee) {
		if (Tracker.active)
			this.dep.depend();
		return _.map(this.map,iteratee);
	};

	ReactiveObjectMap.prototype.size = function() {
		if (Tracker.active)
			this.dep.depend();
		return Object.keys(this.map).length;
	};

	ReactiveObjectMap.prototype.toString = function() {
		return 'ReactiveObjectMap{' + this.get() + '}';
	};

	ReactiveObjectMap.prototype._numListeners = function() {
		// Tests want to know.
		// Accesses a private field of Tracker.Dependency.
		var count = 0;
		for (var id in this.dep._dependentsById)
			count++;
		return count;
	};

	Meteor.ReactiveObjectMap = ReactiveObjectMap;

	module.exports = ReactiveObjectMap;
};
<commit_msg>make variables private<commit_after>module.exports = function(Meteor) {
	var _ = Meteor.underscore;
	var Tracker = Meteor.Tracker;
	var ReactiveObjectMap;

	ReactiveObjectMap = function() {
		if (!(this instanceof ReactiveObjectMap))
		// called without `new`
			return new ReactiveObjectMap();

		this._map = {};
		this._dep = new Tracker.Dependency;
	};

	ReactiveObjectMap.prototype.assign = function(collection, iteratee) {
		this._map = _.indexBy(collection, iteratee);
		this._dep.changed();
	};

	ReactiveObjectMap.prototype.get = function(key) {
		if (Tracker.active)
			this._dep.depend();
		return this._map[key];
	};

	ReactiveObjectMap.prototype.set = function(key, value) {
		var old = this._map[key];
		this._map[key] = value;
		if (old !== value)
			this._dep.changed();
	};

	ReactiveObjectMap.prototype.has = function(key) {
		if (Tracker.active)
			this._dep.depend();
		return this.hasOwnProperty(key);
	};

	ReactiveObjectMap.prototype.clear = function(key, value) {
		this._map = {};
		this._dep.changed();
	};

	ReactiveObjectMap.prototype.delete = function(key, value) {
		if (delete this._map[key])
			this._dep.changed();
	};

	ReactiveObjectMap.prototype.setAttribute = function(key, attr, value) {
		var old = this._map[key][attr];
		this._map[key][attr] = value;
		if (old !== value)
			this._dep.changed();
	};

	ReactiveObjectMap.prototype.getAttribute = function(key, attr) {
		if (Tracker.active)
			this._dep.depend();
		return this._map[key][attr];
	};

	ReactiveObjectMap.prototype.keys = function() {
		if (Tracker.active)
			this._dep.depend();
		return Object.keys(this._map);
	};

	ReactiveObjectMap.prototype.values = function() {
		if (Tracker.active)
			this._dep.depend();
		return _.values(this._map);
	};

	ReactiveObjectMap.prototype.filter = function(predicate) {
		if (Tracker.active)
			this._dep.depend();
		return _.filter(this._map,predicate);
	};

	ReactiveObjectMap.prototype.sortBy = function(iteratee) {
		if (Tracker.active)
			this._dep.depend();
		return _.sortBy(this._map,iteratee);
	};

	ReactiveObjectMap.prototype.map = function(iteratee) {
		if (Tracker.active)
			this._dep.depend();
		return _._map(this._map,iteratee);
	};

	ReactiveObjectMap.prototype.size = function() {
		if (Tracker.active)
			this._dep.depend();
		return Object.keys(this._map).length;
	};

	ReactiveObjectMap.prototype.toString = function() {
		return 'ReactiveObjectMap{' + this.get() + '}';
	};

	ReactiveObjectMap.prototype._numListeners = function() {
		// Tests want to know.
		// Accesses a private field of Tracker.Dependency.
		var count = 0;
		for (var id in this._dep._dependentsById)
			count++;
		return count;
	};

	Meteor.ReactiveObjectMap = ReactiveObjectMap;

	module.exports = ReactiveObjectMap;
};
<|endoftext|>


<commit_before>// Initializes (start) HTTP and RPC server listeners.
// Used as last step in `cli/server.js`
//

"use strict";

// stdlib
var fs    = require('fs');
var path  = require('path');
var http  = require('http');
var https = require('https');
var url   = require('url');
var querystring = require('querystring');

// 3rd-party
var _        = require('lodash');
var async    = require('async');
var parseURL = require('pointer').parseURL;

// internal
var env = require('../env');

////////////////////////////////////////////////////////////////////////////////

// Parse binding config and returns a parsed map:
//
//  {
//    '127.0.0.1:3000': {
//      address: '127.0.0.1',
//      port:    3000,
//      ssl:     null,
//      hosts:  [ 'localhost', ... ]
//    },
//    // ...
//  }
//
function extractBindings(config, mainApp) {
  var result = {};

  // Collect non-SSL bindings.
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.listen]) {
      result[options.listen] = {
        address: options.listen.split(':')[0] || '0.0.0.0'
      , port:    options.listen.split(':')[1] || 80
      , hosts:   []
      , ssl:     null
      };
    }
  });

  // Collect SSL bindings.
  _(config).select('ssl').forEach(function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.ssl.listen]) {
      result[options.ssl.listen] = {
        address: options.ssl.listen.split(':')[0] || '0.0.0.0'
      , port:    options.ssl.listen.split(':')[1] || 443
      , hosts:   []
      , ssl:     _.omit(options.ssl, 'listen')
      };

      // Read files.
      _(options.ssl).pick('pfx', 'key', 'cert').map(function (filename, key) {
        filename = path.resolve(mainApp.root, filename);
        result[options.ssl.listen].ssl[key] = fs.readFileSync(filename, 'utf8');
      });
    }
  });

  // Fill-in host lists that should be served by <address:port>
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    var host = parseURL(options.mount).host || '*';

    // For non-SSL.
    if (!_.contains(result[options.listen].hosts, host)) {
      result[options.listen].hosts.push(host);
    }

    // For SSL.
    if (options.ssl && !_.contains(result[options.ssl.listen].hosts, host)) {
      result[options.ssl.listen].hosts.push(host);
    }
  });

  return result;
}

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}

// Parses URL query string and resolves numbers and booleans.
//
function parseQueryString(inputQuery) {
  if (!inputQuery) {
    return {};
  }

  return castParamTypes(querystring.parse(String(inputQuery)));
}

// MODULE EXPORTS //////////////////////////////////////////////////////////////

// starts http(s) server(s) and attach application listener to it
//
module.exports = function (N) {

  N.wire.on("init:server", function models_init(N, cb) {
    var bindings = extractBindings(N.config.bind, N.runtime.mainApp),
        handle_invalid_host = N.config.bind._;

    //
    // set "host not found" handler if it was not set
    //

    if (!_.isFunction(handle_invalid_host)) {
      handle_invalid_host = function (req, res) {
        res.writeHead(N.io.NOT_FOUND, { 'Content-Type': 'text/plain' });
        res.end('Invalid host ' + req.headers.host);
      };
    }

    //
    // bind listeners
    //

    async.forEachSeries(_.keys(bindings), function (point, next) {
      var options = bindings[point],
          address = options.address,
          port    = options.port,
          server  = options.ssl ? https.createServer(options.ssl) : http.createServer(),
          host_is_valid;

      //
      // Create host validation function
      //

      if (_.include(options.hosts, '*')) {
        // if binding address:port has *any host* mount point,
        // skip host validation
        host_is_valid = function () {
          return true;
        };
      } else {
        host_is_valid = function (host) {
          return _.include(options.hosts, host);
        };
      }

      //
      // attach event handlers
      //

      server.on('error', function handle_startup_error(err) {
        var err_prefix = "Can't bind to <" + address + "> with port <" + port + ">: ";

        if ('EADDRINUSE' === err.code) {
          next(err_prefix + 'Address in use...');
          return;
        }

        if ('EADDRNOTAVAIL' === err.code) {
          // system has no such ip address
          next(err_prefix + 'Address is not available...');
          return;
        }

        if ('ENOENT' === err.code) {
          // failed resolve addressname to ip address
          next(err_prefix + "Failed to resolve IP address...");
          return;
        }

        // unexpected / unknown error
        next(err_prefix + err);
      });

      server.on('connection', function handle_connection(socket) {
        socket.setTimeout(15 * 1000);
        socket.setNoDelay();
      });

      server.on('request', function handle_request(req, res) {
        var isSecure, httpMethod, parsedUrl, type, routeMatch;

        if (!host_is_valid(req.headers.host)) {
          handle_invalid_host(req, res);
          return;
        }

        // Frontend server may handle HTTPS request from the client
        // but connect with us via plain HTTP.
        isSecure = (N.config.options.server.respect_forwarded_proto &&
                    'https' === req.headers['X-Forwarded-Proto']) ||
                   Boolean(options.ssl);

        httpMethod = req.method.toLowerCase();
        parsedUrl  = url.parse(req.url);

        req.query    = parseQueryString(parsedUrl.query);
        req.pathname = parsedUrl.pathname;
        req.fullUrl  = (isSecure ? 'https': 'http') + '://' +
                       (req.headers.host || '*') + req.pathname;

        //
        // Choose a responder to use.
        //

        if ('/io/rpc' === parsedUrl.pathname) {
          type = 'rpc';
        } else {
          type = 'http';

          routeMatch = _.find(N.runtime.router.matchAll(req.fullUrl), function (match) {
            return _.has(match.meta.methods, httpMethod);
          });

          if (routeMatch) {
            type = routeMatch.meta.responder;
            routeMatch.params = castParamTypes(routeMatch.params);
          }
        }

        //
        // Try to process request: create enviroment & run responder chain
        // Responders are totally pluggable, via hooks
        //

        var _env = env(N, {
          req:      req
        , res:      res
        , type:     type
        , isSecure: isSecure
        , matched:  routeMatch
        });

        N.wire.emit('responder:' + type, _env);
      });

      server.on('listening', function handle_listening() {
        // remove error and listening handlers,
        // once we successfully bounded on address:port
        server.removeAllListeners('error');
        server.removeAllListeners('listening');

        //
        // Notify that we started listening
        //

        N.logger.info('Listening on %s:%s %s',
                      options.address,
                      options.port,
                      options.ssl ? 'SSL' : 'NON-SSL');

        //
        // Successfully binded, process to the next mount point
        //

        next();
      });

      //
      // try to bind to the port
      //

      server.listen(port, address);
    },

    function (err) {
      if (err) {
        cb(err);
        return;
      }

      N.logger.info('Services init done');
      cb();
    });
  });
};
<commit_msg>Remove `pfx` SSL config option.<commit_after>// Initializes (start) HTTP and RPC server listeners.
// Used as last step in `cli/server.js`
//

"use strict";

// stdlib
var fs    = require('fs');
var path  = require('path');
var http  = require('http');
var https = require('https');
var url   = require('url');
var querystring = require('querystring');

// 3rd-party
var _        = require('lodash');
var async    = require('async');
var parseURL = require('pointer').parseURL;

// internal
var env = require('../env');

////////////////////////////////////////////////////////////////////////////////

// Parse binding config and returns a parsed map:
//
//  {
//    '127.0.0.1:3000': {
//      address: '127.0.0.1',
//      port:    3000,
//      ssl:     null,
//      hosts:  [ 'localhost', ... ]
//    },
//    // ...
//  }
//
function extractBindings(config, mainApp) {
  var result = {};

  // Collect non-SSL bindings.
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.listen]) {
      result[options.listen] = {
        address: options.listen.split(':')[0] || '0.0.0.0'
      , port:    options.listen.split(':')[1] || 80
      , hosts:   []
      , ssl:     null
      };
    }
  });

  // Collect SSL bindings.
  _(config).select('ssl').forEach(function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    if (!result[options.ssl.listen]) {
      result[options.ssl.listen] = {
        address: options.ssl.listen.split(':')[0] || '0.0.0.0'
      , port:    options.ssl.listen.split(':')[1] || 443
      , hosts:   []
      , ssl:     _.omit(options.ssl, 'listen')
      };

      // Read files.
      _(options.ssl).pick('key', 'cert').map(function (filename, key) {
        filename = path.resolve(mainApp.root, filename);
        result[options.ssl.listen].ssl[key] = fs.readFileSync(filename, 'utf8');
      });
    }
  });

  // Fill-in host lists that should be served by <address:port>
  _.forEach(config, function (options, apiPath) {
    if ('_' === apiPath) {
      return; // skip special case keyword
    }

    var host = parseURL(options.mount).host || '*';

    // For non-SSL.
    if (!_.contains(result[options.listen].hosts, host)) {
      result[options.listen].hosts.push(host);
    }

    // For SSL.
    if (options.ssl && !_.contains(result[options.ssl.listen].hosts, host)) {
      result[options.ssl.listen].hosts.push(host);
    }
  });

  return result;
}

// Takes a params hash (can be nested) and tries to parse each string-value as
// number or boolean. Returns a new hash with parsed values.
//
function castParamTypes(inputValue) {
  var parsedValue;

  if (_.isArray(inputValue)) {
    return _.map(inputValue, castParamTypes);

  } else if (_.isObject(inputValue)) {
    parsedValue = {};

    _.forEach(inputValue, function (value, key) {
      parsedValue[key] = castParamTypes(value);
    });

    return parsedValue;

  } else if ('true' === inputValue) {
    return true;

  } else if ('false' === inputValue) {
    return false;

  } else if (/^[0-9\.\-]+$/.test(inputValue)) {
    parsedValue = Number(inputValue);
    return String(parsedValue) === inputValue ? parsedValue : inputValue;

  } else {
    return inputValue;
  }
}

// Parses URL query string and resolves numbers and booleans.
//
function parseQueryString(inputQuery) {
  if (!inputQuery) {
    return {};
  }

  return castParamTypes(querystring.parse(String(inputQuery)));
}

// MODULE EXPORTS //////////////////////////////////////////////////////////////

// starts http(s) server(s) and attach application listener to it
//
module.exports = function (N) {

  N.wire.on("init:server", function models_init(N, cb) {
    var bindings = extractBindings(N.config.bind, N.runtime.mainApp),
        handle_invalid_host = N.config.bind._;

    //
    // set "host not found" handler if it was not set
    //

    if (!_.isFunction(handle_invalid_host)) {
      handle_invalid_host = function (req, res) {
        res.writeHead(N.io.NOT_FOUND, { 'Content-Type': 'text/plain' });
        res.end('Invalid host ' + req.headers.host);
      };
    }

    //
    // bind listeners
    //

    async.forEachSeries(_.keys(bindings), function (point, next) {
      var options = bindings[point],
          address = options.address,
          port    = options.port,
          server  = options.ssl ? https.createServer(options.ssl) : http.createServer(),
          host_is_valid;

      //
      // Create host validation function
      //

      if (_.include(options.hosts, '*')) {
        // if binding address:port has *any host* mount point,
        // skip host validation
        host_is_valid = function () {
          return true;
        };
      } else {
        host_is_valid = function (host) {
          return _.include(options.hosts, host);
        };
      }

      //
      // attach event handlers
      //

      server.on('error', function handle_startup_error(err) {
        var err_prefix = "Can't bind to <" + address + "> with port <" + port + ">: ";

        if ('EADDRINUSE' === err.code) {
          next(err_prefix + 'Address in use...');
          return;
        }

        if ('EADDRNOTAVAIL' === err.code) {
          // system has no such ip address
          next(err_prefix + 'Address is not available...');
          return;
        }

        if ('ENOENT' === err.code) {
          // failed resolve addressname to ip address
          next(err_prefix + "Failed to resolve IP address...");
          return;
        }

        // unexpected / unknown error
        next(err_prefix + err);
      });

      server.on('connection', function handle_connection(socket) {
        socket.setTimeout(15 * 1000);
        socket.setNoDelay();
      });

      server.on('request', function handle_request(req, res) {
        var isSecure, httpMethod, parsedUrl, type, routeMatch;

        if (!host_is_valid(req.headers.host)) {
          handle_invalid_host(req, res);
          return;
        }

        // Frontend server may handle HTTPS request from the client
        // but connect with us via plain HTTP.
        isSecure = (N.config.options.server.respect_forwarded_proto &&
                    'https' === req.headers['X-Forwarded-Proto']) ||
                   Boolean(options.ssl);

        httpMethod = req.method.toLowerCase();
        parsedUrl  = url.parse(req.url);

        req.query    = parseQueryString(parsedUrl.query);
        req.pathname = parsedUrl.pathname;
        req.fullUrl  = (isSecure ? 'https': 'http') + '://' +
                       (req.headers.host || '*') + req.pathname;

        //
        // Choose a responder to use.
        //

        if ('/io/rpc' === parsedUrl.pathname) {
          type = 'rpc';
        } else {
          type = 'http';

          routeMatch = _.find(N.runtime.router.matchAll(req.fullUrl), function (match) {
            return _.has(match.meta.methods, httpMethod);
          });

          if (routeMatch) {
            type = routeMatch.meta.responder;
            routeMatch.params = castParamTypes(routeMatch.params);
          }
        }

        //
        // Try to process request: create enviroment & run responder chain
        // Responders are totally pluggable, via hooks
        //

        var _env = env(N, {
          req:      req
        , res:      res
        , type:     type
        , isSecure: isSecure
        , matched:  routeMatch
        });

        N.wire.emit('responder:' + type, _env);
      });

      server.on('listening', function handle_listening() {
        // remove error and listening handlers,
        // once we successfully bounded on address:port
        server.removeAllListeners('error');
        server.removeAllListeners('listening');

        //
        // Notify that we started listening
        //

        N.logger.info('Listening on %s:%s %s',
                      options.address,
                      options.port,
                      options.ssl ? 'SSL' : 'NON-SSL');

        //
        // Successfully binded, process to the next mount point
        //

        next();
      });

      //
      // try to bind to the port
      //

      server.listen(port, address);
    },

    function (err) {
      if (err) {
        cb(err);
        return;
      }

      N.logger.info('Services init done');
      cb();
    });
  });
};
<|endoftext|>


<commit_before>/* ==========================================================
 * Collapse.js v1.1.0
 * ==========================================================
 * Copyright 2012 xsokev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */
define([
    './Support',
    'dojo/_base/declare',
    'dojo/query',
    'dojo/_base/lang',
    'dojo/_base/window',
    'dojo/on',
    'dojo/dom-class',
    'dojo/dom-attr',
    'dojo/dom-geometry',
    'dojo/dom-style',
    'dojo/NodeList-dom',
    'dojo/NodeList-traverse',
    'dojo/domReady!'
], function (support, declare, query, lang, win, on, domClass, domAttr, domGeom, domStyle) {
    "use strict";
    var collapseSelector = '[data-toggle=collapse]';
    var Collapse = declare([], {
        defaultOptions:{
            toggle:true
        },
        constructor:function (element, options) {
            this.options = lang.mixin(lang.clone(this.defaultOptions), (options || {}));
            this.domNode = element;
            if (this.options.parent) {
                this.parent = query(this.options.parent);
            }
            if (this.options.toggle) { this.toggle(); }
        },
        dimension:function () {
            return domClass.contains(this.domNode, 'width') ? 'width' : 'height';
        },
        show:function () {
            var dimension, scroll, actives, hasData;

            if (this.transitioning) {
                return;
            }
            if(this.parent && this.options.target) {
                query('[data-target=' + this.options.target + ']', this.parent[0]).forEach(function(el) {
                    domClass.remove(el, 'collapsed');
                });
            }

            dimension = this.dimension();
            scroll = support.toCamel(['scroll', dimension].join('-'));
            actives = this.parent && query('> .accordion-group > .in', this.parent[0]);

            if (actives && actives.length) {
                hasData = support.getData(actives[0], 'collapse');
                if (hasData && hasData.transitioning) {
                    return;
                }
                actives.collapse('hide');
                if (!hasData) { support.setData(actives[0], 'collapse', null); }
            }

            domStyle.set(this.domNode, dimension, '0px');
            this.transition('add', 'show', 'shown');
            if (support.trans) { domStyle.set(this.domNode, dimension, this.domNode[scroll] + 'px'); }
        },
        hide:function () {
            if (this.transitioning) {
                return;
            }
            if(this.parent && this.options.target) {
                query('[data-target=' + this.options.target + ']', this.parent[0]).forEach(function(el) {
                    domClass.add(el, 'collapsed');
                });
            }

            var dimension = this.dimension();
            this.reset(domStyle.get(this.domNode, dimension));
            this.transition('remove', 'hide', 'hidden');
            domStyle.set(this.domNode, dimension, '0px');
        },
        reset:function (size) {
            size = size ? parseFloat(size, 10) + 'px' : 'auto';
            var dimension = this.dimension();
            domClass.remove(this.domNode, 'collapse');
            domStyle.set(this.domNode, dimension, size);
            this._offsetWidth = this.domNode.offsetWidth;
            domClass[(size !== null ? 'add' : 'remove')](this.domNode, 'collapse');
            return this;
        },
        transition:function (method, startEvent, completeEvent) {
            var _this = this,
                _complete = function () {
                    if (startEvent === 'show') {
                        _this.reset();
                    }
                    _this.transitioning = 0;
                    on.emit(_this.domNode, completeEvent, {bubbles:false, cancelable:false});
                };

            on.emit(this.domNode, startEvent, {bubbles:false, cancelable:false});

            this.transitioning = 1;

            domClass[method](this.domNode, 'in');

            if (support.trans && domClass.contains(this.domNode, 'collapse')) {
                on.once(_this.domNode, support.trans.end, _complete);
            } else {
                _complete();
            }
        },
        toggle:function () {
            this[domClass.contains(this.domNode, 'in') ? 'hide' : 'show']();
        }
    });
    lang.extend(query.NodeList, {
        collapse:function (option) {
            var options = (lang.isObject(option)) ? option : false;
            return this.forEach(function (node) {
                var data = support.getData(node, 'collapse');
                if (!data) {
                    support.setData(node, 'collapse', (data = new Collapse(node, options)));
                }
                if (lang.isString(option)) {
                    data[option].call(data);
                }
            });
        }
    });

    on(win.body(), on.selector(collapseSelector, 'click'), function (e) {
        var node = this;
        if (support.getData(node, 'toggle') !== 'collapse') {
            node = query(this).closest('[data-toggle=collapse]')[0];
        }
        if (node) {
            var target = domAttr.get(node, 'data-target') || e.preventDefault() || support.hrefValue(node);
            if (target) {
                var option = support.getData(target, 'collapse') ? 'toggle' : support.getData(node);
                query(target).collapse(option);
            }
        }
    });
    return Collapse;
});<commit_msg>Minor refactor, using lang.hitch to fix scope<commit_after>/* ==========================================================
 * Collapse.js v1.1.0
 * ==========================================================
 * Copyright 2012 xsokev
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================== */
define([
    './Support',
    'dojo/_base/declare',
    'dojo/query',
    'dojo/_base/lang',
    'dojo/_base/window',
    'dojo/on',
    'dojo/dom-class',
    'dojo/dom-attr',
    'dojo/dom-geometry',
    'dojo/dom-style',
    'dojo/NodeList-dom',
    'dojo/NodeList-traverse',
    'dojo/domReady!'
], function (support, declare, query, lang, win, on, domClass, domAttr, domGeom, domStyle) {
    "use strict";
    var collapseSelector = '[data-toggle=collapse]';
    var Collapse = declare([], {
        defaultOptions:{
            toggle:true
        },
        constructor:function (element, options) {
            this.options = lang.mixin(lang.clone(this.defaultOptions), (options || {}));
            this.domNode = element;
            if (this.options.parent) {
                this.parent = query(this.options.parent);
            }
            if (this.options.toggle) { this.toggle(); }
        },
        dimension:function () {
            return domClass.contains(this.domNode, 'width') ? 'width' : 'height';
        },
        show:function () {
            var dimension, scroll, actives, hasData;

            if (this.transitioning) {
                return;
            }
            if(this.parent && this.options.target) {
                query('[data-target=' + this.options.target + ']', this.parent[0]).forEach(function(el) {
                    domClass.remove(el, 'collapsed');
                });
            }

            dimension = this.dimension();
            scroll = support.toCamel(['scroll', dimension].join('-'));
            actives = this.parent && query('> .accordion-group > .in', this.parent[0]);

            if (actives && actives.length) {
                hasData = support.getData(actives[0], 'collapse');
                if (hasData && hasData.transitioning) {
                    return;
                }
                actives.collapse('hide');
                if (!hasData) { support.setData(actives[0], 'collapse', null); }
            }

            domStyle.set(this.domNode, dimension, '0px');
            this.transition('add', 'show', 'shown');
            if (support.trans) { domStyle.set(this.domNode, dimension, this.domNode[scroll] + 'px'); }
        },
        hide:function () {
            if (this.transitioning) {
                return;
            }
            if(this.parent && this.options.target) {
                query('[data-target=' + this.options.target + ']', this.parent[0]).forEach(function(el) {
                    domClass.add(el, 'collapsed');
                });
            }

            var dimension = this.dimension();
            this.reset(domStyle.get(this.domNode, dimension));
            this.transition('remove', 'hide', 'hidden');
            domStyle.set(this.domNode, dimension, '0px');
        },
        reset:function (size) {
            size = size ? parseFloat(size, 10) + 'px' : 'auto';
            var dimension = this.dimension();
            domClass.remove(this.domNode, 'collapse');
            domStyle.set(this.domNode, dimension, size);
            this._offsetWidth = this.domNode.offsetWidth;
            domClass[(size !== null ? 'add' : 'remove')](this.domNode, 'collapse');
            return this;
        },
        transition:function (method, startEvent, completeEvent) {
            var _complete = lang.hitch(this, function () {
                if (startEvent === 'show') {
                    this.reset();
                }
                this.transitioning = 0;
                on.emit(this.domNode, completeEvent, {bubbles:false, cancelable:false});
            });

            on.emit(this.domNode, startEvent, {bubbles:false, cancelable:false});

            this.transitioning = 1;

            domClass[method](this.domNode, 'in');

            if (support.trans && domClass.contains(this.domNode, 'collapse')) {
                on.once(this.domNode, support.trans.end, _complete);
            } else {
                _complete();
            }
        },
        toggle:function () {
            this[domClass.contains(this.domNode, 'in') ? 'hide' : 'show']();
        }
    });
    lang.extend(query.NodeList, {
        collapse:function (option) {
            var options = (lang.isObject(option)) ? option : false;
            return this.forEach(function (node) {
                var data = support.getData(node, 'collapse');
                if (!data) {
                    support.setData(node, 'collapse', (data = new Collapse(node, options)));
                }
                if (lang.isString(option)) {
                    data[option].call(data);
                }
            });
        }
    });

    on(win.body(), on.selector(collapseSelector, 'click'), function (e) {
        var node = this;
        if (support.getData(node, 'toggle') !== 'collapse') {
            node = query(this).closest('[data-toggle=collapse]')[0];
        }
        if (node) {
            var target = domAttr.get(node, 'data-target') || e.preventDefault() || support.hrefValue(node);
            if (target) {
                var option = support.getData(target, 'collapse') ? 'toggle' : support.getData(node);
                query(target).collapse(option);
            }
        }
    });
    return Collapse;
});<|endoftext|>


<commit_before>var extend = require('xtend')
var getFormData = require('form-data-set/element')

var BaseEvent = require('./base-event.js');

var ENTER = 13

module.exports = BaseEvent(submitLambda);

function submitLambda(ev, broadcast) {
    var target = ev.target

    var isValid =
        (ev.type === 'click' && target.tagName === 'BUTTON') ||
        (ev.type === 'click' && target.type === 'submit') ||
        (
            (target.type === 'text') &&
            (ev.keyCode === ENTER && ev.type === 'keydown')
        )

    if (!isValid) {
        if (ev.startPropagation) {
            ev.startPropagation()
        }
        return
    }

    var value = getFormData(ev.currentTarget)
    var data = extend(value, this.data)

    if (ev.preventDefault) {
        ev.preventDefault();
    }

    broadcast(data);
}
<commit_msg>support submit events in form elements<commit_after>var extend = require('xtend')
var getFormData = require('form-data-set/element')

var BaseEvent = require('./base-event.js');

var ENTER = 13

module.exports = BaseEvent(submitLambda);

function submitLambda(ev, broadcast) {
    var target = ev.target

    var isValid =
        (ev.type === 'submit' && target.tagName === 'FORM') ||
        (ev.type === 'click' && target.tagName === 'BUTTON') ||
        (ev.type === 'click' && target.type === 'submit') ||
        (
            (target.type === 'text') &&
            (ev.keyCode === ENTER && ev.type === 'keydown')
        )

    if (!isValid) {
        if (ev.startPropagation) {
            ev.startPropagation()
        }
        return
    }

    var value = getFormData(ev.currentTarget)
    var data = extend(value, this.data)

    if (ev.preventDefault) {
        ev.preventDefault();
    }

    broadcast(data);
}
<|endoftext|>


<commit_before>const logger         = require('bole')('processor');
const db             = require('../db');
const botan          = require('./botan.js');
const TelegramClient = require('./client.js');
const geocoding      = require('../maps').geocoding;
const LocationsMap   = require('../maps').LocationsMap;
const stathat        = require('../stathat.js');

const tg = new TelegramClient();

class TelegramProcessor {
	constructor(update) {
		this.update = update;
	}

	process() {
		let tgChat;

		// Update type is message
		if (this.update['message']) {
			tgChat = this.update['message']['chat'];
		}
		// Update type is callback query (=> inline button press)
		else if (this.update['callback_query']) {
			tgChat = this.update['callback_query']['message']['chat'];
		}

		// Get chat details
		db.chats.get(tgChat, (err, chat) => {
			if (err) {
				logger.error(err, tgChat);
				return;
			}

			chat.send = (msg, callback) => {
				msg['chat'] = chat['id'];
				msg['type'] = msg['type'] || 'sendMessage';
				callback = callback || function() {};

				if (msg['type'] == 'sendMessage') {
					tg.sendMessage(msg, callback);
				}
				else if (msg['type'] == 'editMessage') {
					tg.editMessage(msg, callback);
				}
				else if (msg['type'] == 'sendPhoto') {
					tg.sendPhoto(msg, callback);
				}
				else if (msg['type'] == 'sendChatAction') {
					tg.sendChatAction(msg, callback);
				}
			};

			chat.error = (callback) => {
				let msg = {
					key: 'error',
					chat: chat['id'],
					type: 'sendMessage'
				};

				tg.sendMessage(msg);

				// ?
				callback && callback();
			};

			this.chat = chat;

			if (this.update['message']) {
				this._handleMessage(this.update['message']);
			}
			else if (this.update['callback_query']) {
				this._handleCallback(this.update['callback_query']);
			}
		});
	}

	_handleCallback(query) {
		logger.debug(`Incoming <query> from <${this.chat.id}>`);
		let data = query['data'].split(';');
		let messageId = query['message']['message_id'];

		if (data[0] == 'settings') {
			if (data[1] == 'radius') {
				let msg = {
					key: 'radius',
					keyboard: [
						[{ text: '25 km' }, { text: '50 km' }, { text: '75 km' }],
						[{ text: '100 km' }, { text: '150 km' }, { text: '200 km' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'magnitude') {
				let msg = {
					key: 'magnitude',
					keyboard: [
						[{ text: '2.0+' }, { text: '3.0+' }],
						[{ text: '4.0+' }, { text: '5.0+' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'broadcast') {
				if (data[2]) {
					// Enable or disable broadcast notifications
					let shouldEnable = !!+data[2]; // 0 becomes false, 1 becomes true. Horrible.

					db.chats.setBroadcast(this.chat['id'], shouldEnable, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setBroadcast error');
							return;
						}

						this.chat['settings']['broadcast'] = shouldEnable;
						this._sendBroadcastSettingMessage(messageId);
					});
				}
				else {
					this._sendBroadcastSettingMessage();
				}
			}
		}
		else if (data[0] == 'event') {
			let eventId = data[1];

			db.history.findById(eventId, (err, event) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'history.findById error');
					return;
				}
				else if (!event) {
					// TODO: answer callback query with error
					return;
				}

				let msg = {
					key: 'details',
					data: event,
					inline: [
						[{ text: '$$details_ingv', url: 'http://cnt.rm.ingv.it/event/' + event['id'] }]
					]
				};

				this.chat.send(msg);
			});
		}
	}

	_sendBroadcastSettingMessage(messageId) {
		let msg = {
			key: 'broadcast',
			inline: [
				[
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_enabled' : 'broadcast_enable'),
						callback_data: 'settings;broadcast;1'
					},
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_disable' : 'broadcast_disabled'),
						callback_data: 'settings;broadcast;0'
					}
				]
			]
		};

		if (messageId) {
			msg['type'] = 'editMessage';
			msg['messageId'] = messageId;
		}

		this.chat.send(msg);
	}

	_handleMessage(message) {
		this.message = message;
		message['date'] = new Date(message['date'] * 1000);
		db.incoming.insert(message);

		if (message['location']) {
			this._handleLocation();
		}
		else if (message['venue']) {
			this.message['location'] = this.message['venue']['location'];
			this._handleLocation();
		}
		else if (message['text']) {
			this._handleText();
		}
	}

	_handleLocation() {
		logger.debug(`Incoming <location> from <${this.chat.id}>`);
		botan.log(this.message, 'location');

		let loc = this.message['location'];
		let lat = loc['latitude'];
		let lon = loc['longitude'];

		geocoding.reverse(lat, lon, (err, result) => {
			if (err) {
				if (err['type'] == 'network' || err['type'] == 'response') {
					this.chat.error();
				}
				else if (err['type'] == 'invalid') {
					let msg = { key: 'location_invalid' };
					this.chat.send(msg);
				}
				else if (err['type'] == 'noresults') {
					let msg = { key: 'location_noresults' };
					this.chat.send(msg);
				}

				return;
			}

			let name = result['name'];

			db.locations.exists(this.chat['id'], name, (err, exists) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations exists');
					return;
				}

				if (exists) {
					let msg = { key: 'location_exists' };
					this.chat.send(msg);
				}
				else {
					db.locations.insert(this.chat['id'], lat, lon, name, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'locations insert');
							return;
						}

						let msg = {
							key: 'location_ok',
							data: { name }
						};

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});
				}
			});
		});
	}

	_handleText() {
		logger.debug(`Incoming <text> from <${this.chat.id}>`);
		let text = this.message['text'];
		let cmd = text.replace(/\/ /, '').toLowerCase();

		if (cmd.includes('start') || cmd.includes('avvia')) {
			let msg = {
				key: 'start',
				data: {
					name: this.chat['first_name']
				}
			};

			this.chat.send(msg);
		}
		else if (cmd.includes('chiudi') || cmd.includes('annulla')) {
			let msg = { key: 'done' };
			this.chat.send(msg);

			botan.log(this.message, 'cancel');
		}
		else if (cmd.includes('aiuto') || cmd.includes('help')) {
			let msg = { key: 'help' };
			this.chat.send(msg);

			botan.log(this.message, 'help');
		}
		else if (cmd.includes('posizioni')) {
			db.locations.find(this.chat.id, (err, locations) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations find');
					return;
				}

				if (locations.length == 0) {
					let msg = { key: 'locations_empty' };
					this.chat.send(msg);
				}

				let kb = [];
				locations.forEach((loc) => {
					kb.push([{ text: '📍 ' + loc['name'] }]);
				});

				kb.push([{ text: '$$close' }]);

				let msg = {
					key: 'locations',
					keyboard: kb
				};

				this.chat.send(msg);
			});
		}
		else if (cmd.includes('impostazioni')) {
			let msg = {
				key: 'settings',
				data: {
					radius: this.chat['settings']['radius'],
					magnitude: this.chat['settings']['magnitude'],
					broadcast: this.chat['settings']['broadcast']
				},
				inline: [
					[{ text: '$$settings_menu_radius', callback_data: 'settings;radius' }],
					[{ text: '$$settings_menu_magnitude', callback_data: 'settings;magnitude' }],
					[{ text: '$$settings_menu_broadcast', callback_data: 'settings;broadcast' }]
				]
			};

			this.chat.send(msg);

			botan.log(this.message, 'settings');
		}
		else if (cmd == 'debug') {
			this.chat.send({ text: JSON.stringify(this.chat) });
			this.chat.send({ text: new Date().toString() });
		}
		else {
			// Remove a location
			if (text.startsWith('📍')) {
				let name = text.slice(2).trim();

				db.locations.removeByName(this.chat['id'], name, (err, res) => {
					if (err) {
						this.chat.error();
						logger.error(err, 'locations remove');
						return;
					}

					if (res.n == 0) {
						this.chat.error();
						logger.error(`Couldn\'t remove '${name}' for ${this.chat.id}`);
						return;
					}

					let msg = {
						key: 'location_removed',
						data: { name }
					};

					this.chat.send(msg, () => {
						this._sendMap();
					});
				});
			}
			else {
				let radiusMatch = text.match(/^([0-9]{2,3}) ?(?:km|chilometri|kilometri)?/i);
				// http://regexr.com/3ev1b
				let magMatch = text.match(/^M? ?([0-9]{1}(?:[,\.]{1}[0-9]{1})?)\+?$/i);

				if (radiusMatch) {
					let value = +radiusMatch[1];

					if (value > 300) {
						let msg = { key: 'radius_big' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationRadius(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationRadius');
							return;
						}

						let msg = { key: 'radius_ok', data: { value: value } };

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});

					botan.log(this.message, 'radius');
				}
				else if (magMatch) {
					let value = +magMatch[1].replace(',', '.');

					if (value < 2) {
						let msg = { key: 'magnitude_small' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationMagnitude(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationMagnitude');
							return;
						}

						let msg = { key: 'magnitude_ok', data: { value: value.toFixed(1) } };
						this.chat.send(msg);
					});

					botan.log(this.message, 'magnitude');
				}
				else {
					let msg = { key: 'dunno' };
					this.chat.send(msg);

					botan.log(this.message, 'unknown');
				}
			}
		}
	}

	_sendMap() {
		logger.info(`Sending locations map to <${this.chat.id}>`);

		db.locations.find(this.chat['id'], (err, docs) => {
			if (err) {
				this.chat.error();
				logger.error(err, 'locations find');
				return;
			}

			if (docs.length == 0) {
				logger.debug(`No locations for map <${this.chat.id}>`);
				return;
			}

			this.chat.send({ type: 'sendChatAction', action: 'upload_photo' });
			stathat.count('terre map');

			let map = new LocationsMap(this.chat['settings']['radius']);

			map.addLocations(docs.map((loc) => loc['point']['coordinates']));

			map.generate((err, fn) => {
				if (err) {
					this.chat.error();
					return;
				}

				let msg = {
					type: 'sendPhoto',
					path: fn
				};

				this.chat.send(msg);
			});
		});
	}
}

module.exports = TelegramProcessor;
<commit_msg>[telegram] fix for map generating image with wrong radius<commit_after>const logger         = require('bole')('processor');
const db             = require('../db');
const botan          = require('./botan.js');
const TelegramClient = require('./client.js');
const geocoding      = require('../maps').geocoding;
const LocationsMap   = require('../maps').LocationsMap;
const stathat        = require('../stathat.js');

const tg = new TelegramClient();

class TelegramProcessor {
	constructor(update) {
		this.update = update;
	}

	process() {
		let tgChat;

		// Update type is message
		if (this.update['message']) {
			tgChat = this.update['message']['chat'];
		}
		// Update type is callback query (=> inline button press)
		else if (this.update['callback_query']) {
			tgChat = this.update['callback_query']['message']['chat'];
		}

		// Get chat details
		db.chats.get(tgChat, (err, chat) => {
			if (err) {
				logger.error(err, tgChat);
				return;
			}

			chat.send = (msg, callback) => {
				msg['chat'] = chat['id'];
				msg['type'] = msg['type'] || 'sendMessage';
				callback = callback || function() {};

				if (msg['type'] == 'sendMessage') {
					tg.sendMessage(msg, callback);
				}
				else if (msg['type'] == 'editMessage') {
					tg.editMessage(msg, callback);
				}
				else if (msg['type'] == 'sendPhoto') {
					tg.sendPhoto(msg, callback);
				}
				else if (msg['type'] == 'sendChatAction') {
					tg.sendChatAction(msg, callback);
				}
			};

			chat.error = (callback) => {
				let msg = {
					key: 'error',
					chat: chat['id'],
					type: 'sendMessage'
				};

				tg.sendMessage(msg);

				// ?
				callback && callback();
			};

			this.chat = chat;

			if (this.update['message']) {
				this._handleMessage(this.update['message']);
			}
			else if (this.update['callback_query']) {
				this._handleCallback(this.update['callback_query']);
			}
		});
	}

	_handleCallback(query) {
		logger.debug(`Incoming <query> from <${this.chat.id}>`);
		let data = query['data'].split(';');
		let messageId = query['message']['message_id'];

		if (data[0] == 'settings') {
			if (data[1] == 'radius') {
				let msg = {
					key: 'radius',
					keyboard: [
						[{ text: '25 km' }, { text: '50 km' }, { text: '75 km' }],
						[{ text: '100 km' }, { text: '150 km' }, { text: '200 km' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'magnitude') {
				let msg = {
					key: 'magnitude',
					keyboard: [
						[{ text: '2.0+' }, { text: '3.0+' }],
						[{ text: '4.0+' }, { text: '5.0+' }],
						[{ text: '$$close' }]
					]
				};

				this.chat.send(msg);
			}
			else if (data[1] == 'broadcast') {
				if (data[2]) {
					// Enable or disable broadcast notifications
					let shouldEnable = !!+data[2]; // 0 becomes false, 1 becomes true. Horrible.

					db.chats.setBroadcast(this.chat['id'], shouldEnable, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setBroadcast error');
							return;
						}

						this.chat['settings']['broadcast'] = shouldEnable;
						this._sendBroadcastSettingMessage(messageId);
					});
				}
				else {
					this._sendBroadcastSettingMessage();
				}
			}
		}
		else if (data[0] == 'event') {
			let eventId = data[1];

			db.history.findById(eventId, (err, event) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'history.findById error');
					return;
				}
				else if (!event) {
					// TODO: answer callback query with error
					return;
				}

				let msg = {
					key: 'details',
					data: event,
					inline: [
						[{ text: '$$details_ingv', url: 'http://cnt.rm.ingv.it/event/' + event['id'] }]
					]
				};

				this.chat.send(msg);
			});
		}
	}

	_sendBroadcastSettingMessage(messageId) {
		let msg = {
			key: 'broadcast',
			inline: [
				[
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_enabled' : 'broadcast_enable'),
						callback_data: 'settings;broadcast;1'
					},
					{
						text: '$$' + (this.chat['settings']['broadcast'] ? 'broadcast_disable' : 'broadcast_disabled'),
						callback_data: 'settings;broadcast;0'
					}
				]
			]
		};

		if (messageId) {
			msg['type'] = 'editMessage';
			msg['messageId'] = messageId;
		}

		this.chat.send(msg);
	}

	_handleMessage(message) {
		this.message = message;
		message['date'] = new Date(message['date'] * 1000);
		db.incoming.insert(message);

		if (message['location']) {
			this._handleLocation();
		}
		else if (message['venue']) {
			this.message['location'] = this.message['venue']['location'];
			this._handleLocation();
		}
		else if (message['text']) {
			this._handleText();
		}
	}

	_handleLocation() {
		logger.debug(`Incoming <location> from <${this.chat.id}>`);
		botan.log(this.message, 'location');

		let loc = this.message['location'];
		let lat = loc['latitude'];
		let lon = loc['longitude'];

		geocoding.reverse(lat, lon, (err, result) => {
			if (err) {
				if (err['type'] == 'network' || err['type'] == 'response') {
					this.chat.error();
				}
				else if (err['type'] == 'invalid') {
					let msg = { key: 'location_invalid' };
					this.chat.send(msg);
				}
				else if (err['type'] == 'noresults') {
					let msg = { key: 'location_noresults' };
					this.chat.send(msg);
				}

				return;
			}

			let name = result['name'];

			db.locations.exists(this.chat['id'], name, (err, exists) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations exists');
					return;
				}

				if (exists) {
					let msg = { key: 'location_exists' };
					this.chat.send(msg);
				}
				else {
					db.locations.insert(this.chat['id'], lat, lon, name, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'locations insert');
							return;
						}

						let msg = {
							key: 'location_ok',
							data: { name }
						};

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});
				}
			});
		});
	}

	_handleText() {
		logger.debug(`Incoming <text> from <${this.chat.id}>`);
		let text = this.message['text'];
		let cmd = text.replace(/\/ /, '').toLowerCase();

		if (cmd.includes('start') || cmd.includes('avvia')) {
			let msg = {
				key: 'start',
				data: {
					name: this.chat['first_name']
				}
			};

			this.chat.send(msg);
		}
		else if (cmd.includes('chiudi') || cmd.includes('annulla')) {
			let msg = { key: 'done' };
			this.chat.send(msg);

			botan.log(this.message, 'cancel');
		}
		else if (cmd.includes('aiuto') || cmd.includes('help')) {
			let msg = { key: 'help' };
			this.chat.send(msg);

			botan.log(this.message, 'help');
		}
		else if (cmd.includes('posizioni')) {
			db.locations.find(this.chat.id, (err, locations) => {
				if (err) {
					this.chat.error();
					logger.error(err, 'locations find');
					return;
				}

				if (locations.length == 0) {
					let msg = { key: 'locations_empty' };
					this.chat.send(msg);
				}

				let kb = [];
				locations.forEach((loc) => {
					kb.push([{ text: '📍 ' + loc['name'] }]);
				});

				kb.push([{ text: '$$close' }]);

				let msg = {
					key: 'locations',
					keyboard: kb
				};

				this.chat.send(msg);
			});
		}
		else if (cmd.includes('impostazioni')) {
			let msg = {
				key: 'settings',
				data: {
					radius: this.chat['settings']['radius'],
					magnitude: this.chat['settings']['magnitude'],
					broadcast: this.chat['settings']['broadcast']
				},
				inline: [
					[{ text: '$$settings_menu_radius', callback_data: 'settings;radius' }],
					[{ text: '$$settings_menu_magnitude', callback_data: 'settings;magnitude' }],
					[{ text: '$$settings_menu_broadcast', callback_data: 'settings;broadcast' }]
				]
			};

			this.chat.send(msg);

			botan.log(this.message, 'settings');
		}
		else if (cmd == 'debug') {
			this.chat.send({ text: JSON.stringify(this.chat) });
			this.chat.send({ text: new Date().toString() });
		}
		else {
			// Remove a location
			if (text.startsWith('📍')) {
				let name = text.slice(2).trim();

				db.locations.removeByName(this.chat['id'], name, (err, res) => {
					if (err) {
						this.chat.error();
						logger.error(err, 'locations remove');
						return;
					}

					if (res.n == 0) {
						this.chat.error();
						logger.error(`Couldn\'t remove '${name}' for ${this.chat.id}`);
						return;
					}

					let msg = {
						key: 'location_removed',
						data: { name }
					};

					this.chat.send(msg, () => {
						this._sendMap();
					});
				});
			}
			else {
				let radiusMatch = text.match(/^([0-9]{2,3}) ?(?:km|chilometri|kilometri)?/i);
				// http://regexr.com/3ev1b
				let magMatch = text.match(/^M? ?([0-9]{1}(?:[,\.]{1}[0-9]{1})?)\+?$/i);

				if (radiusMatch) {
					let value = +radiusMatch[1];

					if (value > 300) {
						let msg = { key: 'radius_big' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationRadius(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationRadius');
							return;
						}

						this.chat['settings']['radius'] = value;

						let msg = { key: 'radius_ok', data: { value: value } };

						this.chat.send(msg, () => {
							this._sendMap();
						});
					});

					botan.log(this.message, 'radius');
				}
				else if (magMatch) {
					let value = +magMatch[1].replace(',', '.');

					if (value < 2) {
						let msg = { key: 'magnitude_small' };
						this.chat.send(msg);
						return;
					}

					db.chats.setNotificationMagnitude(this.chat['id'], value, (err) => {
						if (err) {
							this.chat.error();
							logger.error(err, 'setNotificationMagnitude');
							return;
						}

						let msg = { key: 'magnitude_ok', data: { value: value.toFixed(1) } };
						this.chat.send(msg);
					});

					botan.log(this.message, 'magnitude');
				}
				else {
					let msg = { key: 'dunno' };
					this.chat.send(msg);

					botan.log(this.message, 'unknown');
				}
			}
		}
	}

	_sendMap() {
		logger.info(`Sending locations map to <${this.chat.id}>`);

		db.locations.find(this.chat['id'], (err, docs) => {
			if (err) {
				this.chat.error();
				logger.error(err, 'locations find');
				return;
			}

			if (docs.length == 0) {
				logger.debug(`No locations for map <${this.chat.id}>`);
				return;
			}

			this.chat.send({ type: 'sendChatAction', action: 'upload_photo' });
			stathat.count('terre map');

			let map = new LocationsMap(this.chat['settings']['radius']);

			map.addLocations(docs.map((loc) => loc['point']['coordinates']));

			map.generate((err, fn) => {
				if (err) {
					this.chat.error();
					return;
				}

				let msg = {
					type: 'sendPhoto',
					path: fn
				};

				this.chat.send(msg);
			});
		});
	}
}

module.exports = TelegramProcessor;
<|endoftext|>


<commit_before>var templates = [
    "root/externallib/text!root/plugins/mootes14/theme.css",
    "root/externallib/text!root/plugins/mootes14/login.html",
    "root/externallib/text!root/plugins/mootes14/program.html"
];

define(templates, function (theme, loginForm, program) {
    var plugin = {
        settings: {
            name: "mootes14",
            type: "general",
            menuURL: "#mootes14",
            icon: "plugins/events/icon.png",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["mootes14", "show_program", "showProgram"]
        ],

        showProgram: function() {
            var tpl = {};
            var html = MM.tpl.render(program, tpl);
            MM.panels.show('center', html, {title: MM.lang.s("mootes14")});
        }
    };

    // Inject allways our custom theme.
    $("#mobilecssurl").html(theme);

    // Replace the sign-up form with our custom template.
    $("#add-site_template").html(loginForm);

    // Automatically use the URL of the moodle moot without further checks.
    MM.checkSite = function(e) {
        MM.addSite(e);
    };

    // Inject allways our custom theme.
    MM.loadCachedRemoteCSS = function(e) {
        $("#mobilecssurl").html(theme);
    };

    // Do not display the manage accounts page.
    MM._displayManageAccounts = function() {
        MM._displayAddSite();
    };

    MM.registerPlugin(plugin);

});<commit_msg>no message<commit_after>var templates = [
    "root/externallib/text!root/plugins/mootes14/theme.css",
    "root/externallib/text!root/plugins/mootes14/login.html",
    "root/externallib/text!root/plugins/mootes14/program.html"
	"root/externallib/text!root/plugins/grades/activities.html",
    "root/externallib/text!root/plugins/grades/activities_total.html",
    "root/externallib/text!root/plugins/grades/grades_table.html"
];

define(templates, function (theme, loginForm, program) {
    var plugin = {
        settings: {
            name: "mootes14",
            type: "general",
            menuURL: "#mootes14",
            icon: "plugins/events/icon.png",
            lang: {
                component: "core"
            }
        },

        routes: [
            ["mootes14", "show_program", "showProgram"]
        ],

        showProgram: function() {
            var tpl = {};
            var html = MM.tpl.render(program, tpl);
            MM.panels.show('center', html, {title: MM.lang.s("mootes14")});
        }
    };

    // Inject allways our custom theme.
    $("#mobilecssurl").html(theme);

    // Replace the sign-up form with our custom template.
    $("#add-site_template").html(loginForm);

    // Automatically use the URL of the moodle moot without further checks.
    MM.checkSite = function(e) {
        MM.addSite(e);
    };

    // Inject allways our custom theme.
    MM.loadCachedRemoteCSS = function(e) {
        $("#mobilecssurl").html(theme);
    };

    // Do not display the manage accounts page.
    MM._displayManageAccounts = function() {
        MM._displayAddSite();
    };

    MM.registerPlugin(plugin);

});<|endoftext|>


<commit_before>// Copyright 2010 Michael Chaney
// Please see accompanying LICENSE file - MIT license.

// From page 22 in the Javascript: The Good Parts book from O'Reilly Press.
if (typeof Object.create !== 'function') {
	Object.create = function(o) {
		var F = function(){};
		F.prototype=o;
		return new F();
	}
}

// Each position on the puzzle is a "cell".  Each cell is a member
// of three sets: row (y), column (x), and group.  The groups are typically
// squares within the puzzle, but can be made of any contiguous set of
// cells.  Traditionally, a puzzle is 9x9, with the group inset as sets
// of cells each 3x3.
//
// Each cell must contain a value that is unique in all three sets of
// which it is a part.

// In following code, i is analogous to x, j is analogous to y
var Cell = {
	'puzzle': null,
	'group': null,
	'x': null,
	'y': null,
	'range': null,
	'div_id': null,
	'div': null,
	'value': null,
	'showing': null,
	'guesses': null,
	'locked': null,
	'guess_rows': null,
	'display_values': ['1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','0'],
	'init': function(puzzle, x, y, range) {
		this.puzzle = puzzle;
		this.x = x;
		this.y = y;
		this.range = range;
		this.div_id = "cell_" + x + "_" + y;
		this.guesses = new Array(range);
		this.guess_rows = Math.ceil(Math.sqrt(range));
	},
	'set_group': function(group) {
		this.group = group;
	},
	'set_value': function(value) {
		this.value = value;
	},
	'value_from_display': function(display) {
		for (var i=0; i<this.display_values.length; i++) {
			if (display == this.display_values[i]) {
				return i;
			}
		}
		return null;
	},
	'display': function() {
		var td= $("	").addClass('cell').addClass('x_'+this.x).addClass('y_'+this.y).addClass('group_'+this.group.id);
		if (this.x == 0 || this.puzzle.cell_at(this.x-1,this.y).group != this.group) {
			td.addClass('ldiff');
		}
		if (this.x == this.range-1 || this.puzzle.cell_at(this.x+1,this.y).group != this.group) {
			td.addClass('rdiff');
		}
		if (this.y == 0 || this.puzzle.cell_at(this.x,this.y-1).group != this.group) {
			td.addClass('tdiff');
		}
		if (this.y == this.range-1 || this.puzzle.cell_at(this.x,this.y+1).group != this.group) {
			td.addClass('bdiff');
		}
		this.div = td;
		return td;
	},
	'show_marker': function() {
		var marker = '' + this.group.id + "/" + this.x + "," + this.y;
		$('#' + this.div_id).text(marker);
	},
	'clear': function() {
		this.showing = null;
		$('#'+this.div_id+' *').remove();
	},
	'display_guesses': function() {
		this.clear();
		this.showing='guess';
		for (var i=0; i<this.range; i++) {
			if (this.guesses[i]) this.display_guess(i);
		}
	},
	'guess_div_id': function(guess) {
		return this.div_id + '_guess_' + guess;
	},
	'display_guess': function(guess) {
		if (this.showing!==null && this.showing!=='guess') this.clear();
		this.showing='guess';
		var guess_row = Math.floor(guess/this.guess_rows);
		var guess_col = guess % this.guess_rows;
		var guess_div = $('
'+this.display_values[guess]+'
')
		var pixel_padding = 2;
		var guess_width = guess_div.width();
		var cell_width = this.div.width() - pixel_padding * 2;
		var guess_height = guess_div.height();
		var cell_height = this.div.height() - pixel_padding * 2;
		if (guess_row == 0) {
			guess_div.css('top', '' + pixel_padding + 'px');
		} else if (guess_row == this.guess_rows - 1) {
			guess_div.css('bottom', '' + pixel_padding + 'px');
		} else {
			guess_div.css('top', '' + (cell_height * guess_row / (this.guess_rows-1) - guess_height/2.0 + pixel_padding) + 'px');
		}
		if (guess_col == 0) {
			guess_div.css('left', '' + pixel_padding + 'px');
		} else if (guess_col == this.guess_rows - 1) {
			guess_div.css('right', '' + pixel_padding + 'px');
		} else {
			guess_div.css('left', '' + (cell_width * guess_col / (this.guess_rows-1) - guess_width/2.0 + pixel_padding) + 'px');
		}
		guess_div.appendTo('#'+this.div_id);
	},
	'reveal': function() {
		if (this.value!==null && this.showing!=='answer') {
			this.clear();
			this.showing = 'answer';
			$('' + this.display_values[this.value] + '').appendTo('#'+this.div_id)
		}
	},
	'set_value': function(answer) {
		if (!this.locked) {
			this.value=answer;
			this.reveal();
		}
	}
};

var Group = {
	'size': null,
	'id': null,
	'cells': null,
	'init': function(id,size) {
		this.id = id;
		this.size = size;
		this.cells = new Array();
	},
	'add_cell': function(cell) {
		this.cells.push(cell);
		cell.set_group(this);
	}
};

var Sudoku = {
	'size': null,
	'groups': null,
	'rows': null,
	'cols': null,
	'cells': null,
	'group_map': null,
	'current_number': null,
	'element': null,
	'init': function(size) {
		this.size = size;
		this.cells = new Array(size)
		for (var i = 0; i < size; i++) {
			this.cells[i] = new Array(size);
			for (var j = 0; j < size; j++) {
				this.cells[i][j] = Object.create(Cell);
				this.cells[i][j].init(this,i,j,size);
			}
		}
		this.groups = new Array(size);
		for (var i = 0; i < size; i++) {
			this.groups[i] = Object.create(Group);
			this.groups[i].init(i, size);
		}
	},
	'simple_groups': function() {
		var s = Math.sqrt(this.size);
		if (Math.floor(s) == s) {
			this.group_map = new Array(this.size);
			for (i=0 ; i<this.size ; i++) {
				this.group_map[i] = new Array(this.size);
				for (j=0 ; j<this.size ; j++) {
					var group_number = Math.floor(i/s) * s + Math.floor(j/s);
					this.group_map[i][j] = group_number;
				}
			}
		} else {
			throw new Error("Simple groups are only possible if size is the square of an integer.");
		}
	},
	'gather_groups': function() {
		for (i=0 ; i<this.size ; i++) {
			for (j=0 ; j<this.size ; j++) {
				var group_number = this.group_map[i][j];
				this.groups[group_number].add_cell(this.cells[i][j]);
			}
		}
	},
	'make_simple': function() {
		var s = Math.sqrt(this.size);
		if (Math.floor(s) == s) {
			for (var x = 0 ; x < this.size ; x++) {
				for (var y = 0 ; y < this.size ; y++) {
					var new_val = ((x % s) * s + Math.floor(x / s) + y) % this.size;
					this.cells[x][y].set_value(new_val);
				}
			}
		} else {
			throw new Error("Simple groups are only possible if size is the square of an integer.");
		}
	},
	'display': function(div_id) {
		if (this.size) {
			var table = $('

');
			for (j=0 ; j<this.size ; j++) {
				var row = $("

");
				for (i=0 ; i<this.size ; i++) {
					var cell = this.cells[i][j];
					var col = cell.display();
					col.appendTo(row);
				}
				row.appendTo(table);
			}
			this.element = table;
			table.appendTo($(div_id));
		}
	},
	'cell_at': function(x,y) {
		return this.cells[x][y];
	},
	'show_markers': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].show_marker();
			}
		}
	},
	'reveal': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].reveal();
			}
		}
	},
	'show_guesses': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				for (var guess=0 ; guess < this.size; guess++) {
					this.cells[x][y].guesses[guess] = true;
				}
				this.cells[x][y].display_guesses();
			}
		}
	},
	'clear': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].clear();
			}
		}
	},
	'create_controls': function(div_id) {
		for (var i=0 ; i<size ; i++) {
			$('' + Cell.display_values[i] + '').appendTo('div#controls');
			$('#choose_'+i).click(this.handle_number_click);
		}
		$(document).keyup(function(event) {
			var clicked_item = String.fromCharCode(event.keyCode);
			var num = Cell.value_from_display(clicked_item);
			sudoku.set_number(num);
		});
		$(".cell").click(function(event) {
			if (!sudoku.current_number) { return; }
			var reg;
			// assignment on next line
			if (reg=this.id.match(/group_(\d+)_(\d+)_cell_(\d+)_(\d+)/)) {
				var cell=sudoku.groups[parseInt(reg[1])][parseInt(reg[2])].cells[parseInt(reg[3])][parseInt(reg[4])];
				if (cell) {
					if (event.shiftKey) {
						cell.display_guess(sudoku.current_number);
					} else {
						cell.set_value(sudoku.current_number);
					}
				}
			}
		});
	},
	'handle_number_click': function() {
		var reg;
		// next line is assignment, not comparison
		if (reg=this.id.match(/choose_(.*)$/)) {
			sudoku.set_number(parseInt(reg[1]));
		}
	},
	'set_number': function(num) {
		this.current_number = num;
		$('.chooser').removeClass('current');
		$('a#choose_'+num).addClass('current');
	},
	'solve': function() {
	}
};
<commit_msg>Made Row & Col handle more.<commit_after>// Copyright 2010 Michael Chaney
// Please see accompanying LICENSE file - MIT license.

// From page 22 in the Javascript: The Good Parts book from O'Reilly Press.
if (typeof Object.create !== 'function') {
	Object.create = function(o) {
		var F = function(){};
		F.prototype=o;
		return new F();
	}
}

// Each position on the puzzle is a "cell".  Each cell is a member
// of three sets: row (y), column (x), and group.  The groups are typically
// squares within the puzzle, but can be made of any contiguous set of
// cells.  Traditionally, a puzzle is 9x9, with the group inset as sets
// of cells each 3x3.
//
// Each cell must contain a value that is unique in all three sets of
// which it is a part.

// In following code, i is analogous to x, j is analogous to y
var Cell = {
	'puzzle': null,
	'group': null,
	'row': null,
	'col': null,
	'x': null,
	'y': null,
	'range': null,
	'div_id': null,
	'div': null,
	'value': null,
	'showing': null,
	'guesses': null,
	'locked': null,
	'guess_rows': null,
	'display_values': ['1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','0'],
	'init': function(puzzle, x, y, range) {
		this.puzzle = puzzle;
		this.x = x;
		this.y = y;
		this.range = range;
		this.div_id = "cell_" + x + "_" + y;
		this.guesses = new Array(range);
		this.guess_rows = Math.ceil(Math.sqrt(range));
	},
	'set_group': function(group) {
		this.group = group;
	},
	'set_row': function(row) {
		this.row = row;
	},
	'set_col': function(col) {
		this.col = col;
	},
	'set_value': function(value) {
		this.value = value;
	},
	'value_from_display': function(display) {
		for (var i=0; i<this.display_values.length; i++) {
			if (display == this.display_values[i]) {
				return i;
			}
		}
		return null;
	},
	'display': function() {
		var td= $("	").addClass('cell').addClass('x_'+this.x).addClass('y_'+this.y).addClass('group_'+this.group.id);
		if (this.x == 0 || this.row.cells[this.x-1].group != this.group) {
			td.addClass('ldiff');
		}
		if (this.x == this.range-1 || this.row.cells[this.x+1].group != this.group) {
			td.addClass('rdiff');
		}
		if (this.y == 0 || this.col.cells[this.y-1].group != this.group) {
			td.addClass('tdiff');
		}
		if (this.y == this.range-1 || this.col.cells[this.y+1].group != this.group) {
			td.addClass('bdiff');
		}
		this.div = td;
		return td;
	},
	'show_marker': function() {
		var marker = '' + this.group.id + "/" + this.x + "," + this.y;
		$('#' + this.div_id).text(marker);
	},
	'clear': function() {
		this.showing = null;
		$('#'+this.div_id+' *').remove();
	},
	'display_guesses': function() {
		this.clear();
		this.showing='guess';
		for (var i=0; i<this.range; i++) {
			if (this.guesses[i]) this.display_guess(i);
		}
	},
	'guess_div_id': function(guess) {
		return this.div_id + '_guess_' + guess;
	},
	'display_guess': function(guess) {
		if (this.showing!==null && this.showing!=='guess') this.clear();
		this.showing='guess';
		var guess_row = Math.floor(guess/this.guess_rows);
		var guess_col = guess % this.guess_rows;
		var guess_div = $('
'+this.display_values[guess]+'
')
		var pixel_padding = 2;
		var guess_width = guess_div.width();
		var cell_width = this.div.width() - pixel_padding * 2;
		var guess_height = guess_div.height();
		var cell_height = this.div.height() - pixel_padding * 2;
		if (guess_row == 0) {
			guess_div.css('top', '' + pixel_padding + 'px');
		} else if (guess_row == this.guess_rows - 1) {
			guess_div.css('bottom', '' + pixel_padding + 'px');
		} else {
			guess_div.css('top', '' + (cell_height * guess_row / (this.guess_rows-1) - guess_height/2.0 + pixel_padding) + 'px');
		}
		if (guess_col == 0) {
			guess_div.css('left', '' + pixel_padding + 'px');
		} else if (guess_col == this.guess_rows - 1) {
			guess_div.css('right', '' + pixel_padding + 'px');
		} else {
			guess_div.css('left', '' + (cell_width * guess_col / (this.guess_rows-1) - guess_width/2.0 + pixel_padding) + 'px');
		}
		guess_div.appendTo('#'+this.div_id);
	},
	'reveal': function() {
		if (this.value!==null && this.showing!=='answer') {
			this.clear();
			this.showing = 'answer';
			$('' + this.display_values[this.value] + '').appendTo('#'+this.div_id)
		}
	},
	'set_value': function(answer) {
		if (!this.locked) {
			this.value=answer;
			this.reveal();
		}
	}
};

var Group = {
	'puzzle': null,
	'size': null,
	'id': null,
	'cells': null,
	'init': function(puzzle,id,size) {
		this.puzzle = puzzle;
		this.id = id;
		this.size = size;
		this.cells = new Array();
	},
	'add_cell': function(cell) {
		this.cells.push(cell);
		cell.set_group(this);
	},
	'cell_at': function(pos) {
		return this.cells[pos];
	}
};

var Row = {
	'puzzle': null,
	'size': null,
	'id': null,
	'cells': null,
	'init': function(puzzle,id,size) {
		this.puzzle = puzzle;
		this.id = id;
		this.size = size;
		this.cells = new Array(size);
	},
	'add_cell': function(cell,pos) {
		this.cells[pos] = cell;
		cell.set_row(this);
	},
	'cell_at': function(pos) {
		return this.cells[pos];
	}
};

var Col = {
	'puzzle': null,
	'size': null,
	'id': null,
	'cells': null,
	'init': function(puzzle,id,size) {
		this.puzzle = puzzle;
		this.id = id;
		this.size = size;
		this.cells = new Array(size);
	},
	'add_cell': function(cell,pos) {
		this.cells[pos] = cell;
		cell.set_col(this);
	},
	'cell_at': function(pos) {
		return this.cells[pos];
	}
};

var Sudoku = {
	'size': null,
	'groups': null,
	'rows': null,
	'cols': null,
	'cells': null,
	'group_map': null,
	'current_number': null,
	'element': null,
	'init': function(size) {
		this.size = size;
		this.cells = new Array(size)
		for (var i = 0; i < size; i++) {
			this.cells[i] = new Array(size);
			for (var j = 0; j < size; j++) {
				this.cells[i][j] = Object.create(Cell);
				this.cells[i][j].init(this,i,j,size);
			}
		}
		this.groups = new Array(size);
		for (var i = 0; i < size; i++) {
			this.groups[i] = Object.create(Group);
			this.groups[i].init(this, i, size);
		}
		this.rows = new Array(size);
		for (var i = 0; i < size; i++) {
			this.rows[i] = Object.create(Row);
			this.rows[i].init(this, i, size);
			for (var j = 0; j < size; j++) {
				this.rows[i].add_cell(this.cells[j][i],j);
			}
		}
		this.cols = new Array(size);
		for (var i = 0; i < size; i++) {
			this.cols[i] = Object.create(Col);
			this.cols[i].init(this, i, size);
			for (var j = 0; j < size; j++) {
				this.cols[i].add_cell(this.cells[i][j],j);
			}
		}
	},
	'simple_groups': function() {
		var s = Math.sqrt(this.size);
		if (Math.floor(s) == s) {
			this.group_map = new Array(this.size);
			for (i=0 ; i<this.size ; i++) {
				this.group_map[i] = new Array(this.size);
				for (j=0 ; j<this.size ; j++) {
					var group_number = Math.floor(i/s) * s + Math.floor(j/s);
					this.group_map[i][j] = group_number;
				}
			}
		} else {
			throw new Error("Simple groups are only possible if size is the square of an integer.");
		}
	},
	'gather_groups': function() {
		for (i=0 ; i<this.size ; i++) {
			for (j=0 ; j<this.size ; j++) {
				var group_number = this.group_map[i][j];
				this.groups[group_number].add_cell(this.cells[i][j]);
			}
		}
	},
	'make_simple': function() {
		var s = Math.sqrt(this.size);
		if (Math.floor(s) == s) {
			for (var x = 0 ; x < this.size ; x++) {
				for (var y = 0 ; y < this.size ; y++) {
					var new_val = ((x % s) * s + Math.floor(x / s) + y) % this.size;
					this.cells[x][y].set_value(new_val);
				}
			}
		} else {
			throw new Error("Simple groups are only possible if size is the square of an integer.");
		}
	},
	'display': function(div_id) {
		if (this.size) {
			var table = $('

');
			for (j=0 ; j<this.size ; j++) {
				var row = $("

");
				for (i=0 ; i<this.size ; i++) {
					var cell = this.cells[i][j];
					var col = cell.display();
					col.appendTo(row);
				}
				row.appendTo(table);
			}
			this.element = table;
			table.appendTo($(div_id));
		}
	},
	'cell_at': function(x,y) {
		return this.cells[x][y];
	},
	'show_markers': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].show_marker();
			}
		}
	},
	'reveal': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].reveal();
			}
		}
	},
	'show_guesses': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				for (var guess=0 ; guess < this.size; guess++) {
					this.cells[x][y].guesses[guess] = true;
				}
				this.cells[x][y].display_guesses();
			}
		}
	},
	'clear': function() {
		for (var x=0 ; x < this.size; x++) {
			for (var y=0 ; y < this.size; y++) {
				this.cells[x][y].clear();
			}
		}
	},
	'create_controls': function(div_id) {
		for (var i=0 ; i<size ; i++) {
			$('' + Cell.display_values[i] + '').appendTo('div#controls');
			$('#choose_'+i).click(this.handle_number_click);
		}
		$(document).keyup(function(event) {
			var clicked_item = String.fromCharCode(event.keyCode);
			var num = Cell.value_from_display(clicked_item);
			sudoku.set_number(num);
		});
		$(".cell").click(function(event) {
			if (!sudoku.current_number) { return; }
			var reg;
			// assignment on next line
			if (reg=this.id.match(/group_(\d+)_(\d+)_cell_(\d+)_(\d+)/)) {
				var cell=sudoku.groups[parseInt(reg[1])][parseInt(reg[2])].cells[parseInt(reg[3])][parseInt(reg[4])];
				if (cell) {
					if (event.shiftKey) {
						cell.display_guess(sudoku.current_number);
					} else {
						cell.set_value(sudoku.current_number);
					}
				}
			}
		});
	},
	'handle_number_click': function() {
		var reg;
		// next line is assignment, not comparison
		if (reg=this.id.match(/choose_(.*)$/)) {
			sudoku.set_number(parseInt(reg[1]));
		}
	},
	'set_number': function(num) {
		this.current_number = num;
		$('.chooser').removeClass('current');
		$('a#choose_'+num).addClass('current');
	},
	'solve': function() {
	}
};
<|endoftext|>