<commit_before>/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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.
 */

'use strict';

var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var spell = require('../util/spell');
var Type = require('./types').Type;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;

/**
 * A selection allows the user to pick a value from known set of options.
 * An option is made up of a name (which is what the user types) and a value
 * (which is passed to exec)
 * @param typeSpec Object containing properties that describe how this
 * selection functions. Properties include:
 * - lookup: An array of objects, one for each option, which contain name and
 *   value properties. lookup can be a function which returns this array
 * - data: An array of strings - alternative to 'lookup' where the valid values
 *   are strings. i.e. there is no mapping between what is typed and the value
 *   that is used by the program
 * - stringifyProperty: Conversion from value to string is generally a process
 *   of looking through all the valid options for a matching value, and using
 *   the associated name. However the name maybe available directly from the
 *   value using a property lookup. Setting 'stringifyProperty' allows
 *   SelectionType to take this shortcut.
 * - cacheable: If lookup is a function, then we normally assume that
 *   the values fetched can change. Setting 'cacheable:true' enables internal
 *   caching.
 */
function SelectionType(typeSpec) {
  if (typeSpec) {
    Object.keys(typeSpec).forEach(function(key) {
      this[key] = typeSpec[key];
    }, this);
  }

  if (this.name !== 'selection' &&
      this.lookup == null && this.data == null) {
    throw new Error(this.name + ' has no lookup or data');
  }

  this._dataToLookup = this._dataToLookup.bind(this);
}

SelectionType.prototype = Object.create(Type.prototype);

SelectionType.prototype.getSpec = function(commandName, paramName) {
  var spec = { name: 'selection' };
  if (this.lookup != null && typeof this.lookup !== 'function') {
    spec.lookup = this.lookup;
  }
  if (this.data != null && typeof this.data !== 'function') {
    spec.data = this.data;
  }
  if (this.stringifyProperty != null) {
    spec.stringifyProperty = this.stringifyProperty;
  }
  if (this.cacheable) {
    spec.cacheable = true;
  }
  if (typeof this.lookup === 'function' || typeof this.data === 'function') {
    spec.commandName = commandName;
    spec.paramName = paramName;
    spec.remoteLookup = (typeof this.lookup === 'function');
    spec.remoteData = (typeof this.data === 'function');
  }
  return spec;
};

SelectionType.prototype.stringify = function(value, context) {
  if (value == null) {
    return '';
  }
  if (this.stringifyProperty != null) {
    return value[this.stringifyProperty];
  }

  return this.getLookup(context).then(function(lookup) {
    var name = null;
    lookup.some(function(item) {
      if (item.value === value) {
        name = item.name;
        return true;
      }
      return false;
    }, this);
    return name;
  }.bind(this));
};

/**
 * If typeSpec contained cacheable:true then calls to parse() work on cached
 * data. clearCache() enables the cache to be cleared.
 */
SelectionType.prototype.clearCache = function() {
  this._cachedLookup = undefined;
};

/**
 * There are several ways to get selection data. This unifies them into one
 * single function.
 * @return An array of objects with name and value properties.
 */
SelectionType.prototype.getLookup = function(context) {
  if (this._cachedLookup != null) {
    return this._cachedLookup;
  }

  var reply;

  if (this.remoteLookup) {
    reply = this.connection.call('selectioninfo', {
      action: 'lookup',
      commandName: this.commandName,
      paramName: this.paramName
    });
    reply = resolve(reply, context);
  }
  else if (this.remoteData) {
    reply = this.connection.call('selectioninfo', {
      action: 'data',
      commandName: this.commandName,
      paramName: this.paramName
    });
    reply = resolve(reply, context).then(this._dataToLookup);
  }
  else if (typeof this.lookup === 'function') {
    reply = resolve(this.lookup.bind(this), context);
  }
  else if (this.lookup != null) {
    reply = resolve(this.lookup, context);
  }
  else if (this.data != null) {
    reply = resolve(this.data, context).then(this._dataToLookup);
  }
  else {
    throw new Error(this.name + ' has no lookup or data');
  }

  if (this.cacheable) {
    this._cachedLookup = reply;
  }

  if (reply == null) {
    console.error(arguments);
  }
  return reply;
};

/**
 * Both 'lookup' and 'data' properties (see docs on SelectionType constructor)
 * in addition to being real data can be a function or a promise, or even a
 * function which returns a promise of real data, etc. This takes a thing and
 * returns a promise of actual values.
 */
function resolve(thing, context) {
  return Promise.resolve(thing).then(function(resolved) {
    if (typeof resolved === 'function') {
      return resolve(resolved(context), context);
    }
    return resolved;
  });
}

/**
 * Selection can be provided with either a lookup object (in the 'lookup'
 * property) or an array of strings (in the 'data' property). Internally we
 * always use lookup, so we need a way to convert a 'data' array to a lookup.
 */
SelectionType.prototype._dataToLookup = function(data) {
  if (!Array.isArray(data)) {
    throw new Error('data for ' + this.name + ' resolved to non-array');
  }

  return data.map(function(option) {
    return { name: option, value: option };
  });
};

/**
 * Return a list of possible completions for the given arg.
 * @param arg The initial input to match
 * @return A trimmed array of string:value pairs
 */
exports.findPredictions = function(arg, lookup) {
  var predictions = [];
  var i, option;
  var maxPredictions = Conversion.maxPredictions;
  var match = arg.text.toLowerCase();

  // If the arg has a suffix then we're kind of 'done'. Only an exact match
  // will do.
  if (arg.suffix.length > 0) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option.name === arg.text) {
        predictions.push(option);
      }
    }

    return predictions;
  }

  // Cache lower case versions of all the option names
  for (i = 0; i < lookup.length; i++) {
    option = lookup[i];
    if (option._gcliLowerName == null) {
      option._gcliLowerName = option.name.toLowerCase();
    }
  }

  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
  // All the tests after here check that !option.value.hidden
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option.name === arg.text) {
      predictions.push(option);
    }
  }

  // Start with prefix matching
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option._gcliLowerName.indexOf(match) === 0 && !option.value.hidden) {
      if (predictions.indexOf(option) === -1) {
        predictions.push(option);
      }
    }
  }

  // Try infix matching if we get less half max matched
  if (predictions.length < (maxPredictions / 2)) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option._gcliLowerName.indexOf(match) !== -1 && !option.value.hidden) {
        if (predictions.indexOf(option) === -1) {
          predictions.push(option);
        }
      }
    }
  }

  // Try fuzzy matching if we don't get a prefix match
  if (predictions.length === 0) {
    var names = [];
    lookup.forEach(function(opt) {
      if (!opt.value.hidden) {
        names.push(opt.name);
      }
    });
    var corrected = spell.correct(match, names);
    if (corrected) {
      lookup.forEach(function(opt) {
        if (opt.name === corrected) {
          predictions.push(opt);
        }
      }, this);
    }
  }

  return predictions;
};

SelectionType.prototype.parse = function(arg, context) {
  return Promise.resolve(this.getLookup(context)).then(function(lookup) {
    var predictions = exports.findPredictions(arg, lookup);
    return exports.convertPredictions(arg, predictions);
  }.bind(this));
};

/**
 * Decide what sort of conversion to return based on the available predictions
 * and how they match the passed arg
 */
exports.convertPredictions = function(arg, predictions) {
  if (predictions.length === 0) {
    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
    return new Conversion(undefined, arg, Status.ERROR, msg,
                          Promise.resolve(predictions));
  }

  if (predictions[0].name === arg.text) {
    var value = predictions[0].value;
    return new Conversion(value, arg, Status.VALID, '',
                          Promise.resolve(predictions));
  }

  return new Conversion(undefined, arg, Status.INCOMPLETE, '',
                        Promise.resolve(predictions));
};

SelectionType.prototype.getBlank = function(context) {
  var predictFunc = function(context2) {
    return Promise.resolve(this.getLookup(context2)).then(function(lookup) {
      return lookup.filter(function(option) {
        return !option.value.hidden;
      }).slice(0, Conversion.maxPredictions - 1);
    });
  }.bind(this);

  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '',
                        predictFunc);
};

/**
 * For selections, up is down and black is white. It's like this, given a list
 * [ a, b, c, d ], it's natural to think that it starts at the top and that
 * going up the list, moves towards 'a'. However 'a' has the lowest index, so
 * for SelectionType, up is down and down is up.
 * Sorry.
 */
SelectionType.prototype.decrement = function(value, context) {
  return this.getLookup(context).then(function(lookup) {
    var index = this._findValue(lookup, value);
    if (index === -1) {
      index = 0;
    }
    index++;
    if (index >= lookup.length) {
      index = 0;
    }
    return lookup[index].value;
  }.bind(this));
};

/**
 * See note on SelectionType.decrement()
 */
SelectionType.prototype.increment = function(value, context) {
  return this.getLookup(context).then(function(lookup) {
    var index = this._findValue(lookup, value);
    if (index === -1) {
      // For an increment operation when there is nothing to start from, we
      // want to start from the top, i.e. index 0, so the value before we
      // 'increment' (see note above) must be 1.
      index = 1;
    }
    index--;
    if (index < 0) {
      index = lookup.length - 1;
    }
    return lookup[index].value;
  }.bind(this));
};

/**
 * Walk through an array of { name:.., value:... } objects looking for a
 * matching value (using strict equality), returning the matched index (or -1
 * if not found).
 * @param lookup Array of objects with name/value properties to search through
 * @param value The value to search for
 * @return The index at which the match was found, or -1 if no match was found
 */
SelectionType.prototype._findValue = function(lookup, value) {
  var index = -1;
  for (var i = 0; i < lookup.length; i++) {
    var pair = lookup[i];
    if (pair.value === value) {
      index = i;
      break;
    }
  }
  return index;
};

/**
 * This is how we indicate to SelectionField that we have predictions that
 * might work in a menu.
 */
SelectionType.prototype.hasPredictions = true;

SelectionType.prototype.name = 'selection';

exports.SelectionType = SelectionType;
exports.items = [ SelectionType ];
<commit_msg>runat-1128988: Check for hidden options in a function<commit_after>/*
 * Copyright 2012, Mozilla Foundation and contributors
 *
 * 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.
 */

'use strict';

var Promise = require('../util/promise').Promise;
var l10n = require('../util/l10n');
var spell = require('../util/spell');
var Type = require('./types').Type;
var Status = require('./types').Status;
var Conversion = require('./types').Conversion;
var BlankArgument = require('./types').BlankArgument;

/**
 * A selection allows the user to pick a value from known set of options.
 * An option is made up of a name (which is what the user types) and a value
 * (which is passed to exec)
 * @param typeSpec Object containing properties that describe how this
 * selection functions. Properties include:
 * - lookup: An array of objects, one for each option, which contain name and
 *   value properties. lookup can be a function which returns this array
 * - data: An array of strings - alternative to 'lookup' where the valid values
 *   are strings. i.e. there is no mapping between what is typed and the value
 *   that is used by the program
 * - stringifyProperty: Conversion from value to string is generally a process
 *   of looking through all the valid options for a matching value, and using
 *   the associated name. However the name maybe available directly from the
 *   value using a property lookup. Setting 'stringifyProperty' allows
 *   SelectionType to take this shortcut.
 * - cacheable: If lookup is a function, then we normally assume that
 *   the values fetched can change. Setting 'cacheable:true' enables internal
 *   caching.
 */
function SelectionType(typeSpec) {
  if (typeSpec) {
    Object.keys(typeSpec).forEach(function(key) {
      this[key] = typeSpec[key];
    }, this);
  }

  if (this.name !== 'selection' &&
      this.lookup == null && this.data == null) {
    throw new Error(this.name + ' has no lookup or data');
  }

  this._dataToLookup = this._dataToLookup.bind(this);
}

SelectionType.prototype = Object.create(Type.prototype);

SelectionType.prototype.getSpec = function(commandName, paramName) {
  var spec = { name: 'selection' };
  if (this.lookup != null && typeof this.lookup !== 'function') {
    spec.lookup = this.lookup;
  }
  if (this.data != null && typeof this.data !== 'function') {
    spec.data = this.data;
  }
  if (this.stringifyProperty != null) {
    spec.stringifyProperty = this.stringifyProperty;
  }
  if (this.cacheable) {
    spec.cacheable = true;
  }
  if (typeof this.lookup === 'function' || typeof this.data === 'function') {
    spec.commandName = commandName;
    spec.paramName = paramName;
    spec.remoteLookup = (typeof this.lookup === 'function');
    spec.remoteData = (typeof this.data === 'function');
  }
  return spec;
};

SelectionType.prototype.stringify = function(value, context) {
  if (value == null) {
    return '';
  }
  if (this.stringifyProperty != null) {
    return value[this.stringifyProperty];
  }

  return this.getLookup(context).then(function(lookup) {
    var name = null;
    lookup.some(function(item) {
      if (item.value === value) {
        name = item.name;
        return true;
      }
      return false;
    }, this);
    return name;
  }.bind(this));
};

/**
 * If typeSpec contained cacheable:true then calls to parse() work on cached
 * data. clearCache() enables the cache to be cleared.
 */
SelectionType.prototype.clearCache = function() {
  this._cachedLookup = undefined;
};

/**
 * There are several ways to get selection data. This unifies them into one
 * single function.
 * @return An array of objects with name and value properties.
 */
SelectionType.prototype.getLookup = function(context) {
  if (this._cachedLookup != null) {
    return this._cachedLookup;
  }

  var reply;

  if (this.remoteLookup) {
    reply = this.connection.call('selectioninfo', {
      action: 'lookup',
      commandName: this.commandName,
      paramName: this.paramName
    });
    reply = resolve(reply, context);
  }
  else if (this.remoteData) {
    reply = this.connection.call('selectioninfo', {
      action: 'data',
      commandName: this.commandName,
      paramName: this.paramName
    });
    reply = resolve(reply, context).then(this._dataToLookup);
  }
  else if (typeof this.lookup === 'function') {
    reply = resolve(this.lookup.bind(this), context);
  }
  else if (this.lookup != null) {
    reply = resolve(this.lookup, context);
  }
  else if (this.data != null) {
    reply = resolve(this.data, context).then(this._dataToLookup);
  }
  else {
    throw new Error(this.name + ' has no lookup or data');
  }

  if (this.cacheable) {
    this._cachedLookup = reply;
  }

  if (reply == null) {
    console.error(arguments);
  }
  return reply;
};

/**
 * Both 'lookup' and 'data' properties (see docs on SelectionType constructor)
 * in addition to being real data can be a function or a promise, or even a
 * function which returns a promise of real data, etc. This takes a thing and
 * returns a promise of actual values.
 */
function resolve(thing, context) {
  return Promise.resolve(thing).then(function(resolved) {
    if (typeof resolved === 'function') {
      return resolve(resolved(context), context);
    }
    return resolved;
  });
}

/**
 * Selection can be provided with either a lookup object (in the 'lookup'
 * property) or an array of strings (in the 'data' property). Internally we
 * always use lookup, so we need a way to convert a 'data' array to a lookup.
 */
SelectionType.prototype._dataToLookup = function(data) {
  if (!Array.isArray(data)) {
    throw new Error('data for ' + this.name + ' resolved to non-array');
  }

  return data.map(function(option) {
    return { name: option, value: option };
  });
};

/**
 * Return a list of possible completions for the given arg.
 * @param arg The initial input to match
 * @return A trimmed array of string:value pairs
 */
exports.findPredictions = function(arg, lookup) {
  var predictions = [];
  var i, option;
  var maxPredictions = Conversion.maxPredictions;
  var match = arg.text.toLowerCase();

  // If the arg has a suffix then we're kind of 'done'. Only an exact match
  // will do.
  if (arg.suffix.length > 0) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option.name === arg.text) {
        predictions.push(option);
      }
    }

    return predictions;
  }

  // Cache lower case versions of all the option names
  for (i = 0; i < lookup.length; i++) {
    option = lookup[i];
    if (option._gcliLowerName == null) {
      option._gcliLowerName = option.name.toLowerCase();
    }
  }

  // Exact hidden matches. If 'hidden: true' then we only allow exact matches
  // All the tests after here check that !isHidden(option)
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option.name === arg.text) {
      predictions.push(option);
    }
  }

  // Start with prefix matching
  for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
    option = lookup[i];
    if (option._gcliLowerName.indexOf(match) === 0 && !isHidden(option)) {
      if (predictions.indexOf(option) === -1) {
        predictions.push(option);
      }
    }
  }

  // Try infix matching if we get less half max matched
  if (predictions.length < (maxPredictions / 2)) {
    for (i = 0; i < lookup.length && predictions.length < maxPredictions; i++) {
      option = lookup[i];
      if (option._gcliLowerName.indexOf(match) !== -1 && !isHidden(option)) {
        if (predictions.indexOf(option) === -1) {
          predictions.push(option);
        }
      }
    }
  }

  // Try fuzzy matching if we don't get a prefix match
  if (predictions.length === 0) {
    var names = [];
    lookup.forEach(function(opt) {
      if (!isHidden(opt)) {
        names.push(opt.name);
      }
    });
    var corrected = spell.correct(match, names);
    if (corrected) {
      lookup.forEach(function(opt) {
        if (opt.name === corrected) {
          predictions.push(opt);
        }
      }, this);
    }
  }

  return predictions;
};

SelectionType.prototype.parse = function(arg, context) {
  return Promise.resolve(this.getLookup(context)).then(function(lookup) {
    var predictions = exports.findPredictions(arg, lookup);
    return exports.convertPredictions(arg, predictions);
  }.bind(this));
};

/**
 * Decide what sort of conversion to return based on the available predictions
 * and how they match the passed arg
 */
exports.convertPredictions = function(arg, predictions) {
  if (predictions.length === 0) {
    var msg = l10n.lookupFormat('typesSelectionNomatch', [ arg.text ]);
    return new Conversion(undefined, arg, Status.ERROR, msg,
                          Promise.resolve(predictions));
  }

  if (predictions[0].name === arg.text) {
    var value = predictions[0].value;
    return new Conversion(value, arg, Status.VALID, '',
                          Promise.resolve(predictions));
  }

  return new Conversion(undefined, arg, Status.INCOMPLETE, '',
                        Promise.resolve(predictions));
};

/**
 * Checking that an option is hidden involves messing in properties on the
 * value right now (which isn't a good idea really) we really should be marking
 * that on the option, so this encapsulates the problem
 */
function isHidden(option) {
  return option.hidden === true ||
         (option.value != null && option.value.hidden);
}

SelectionType.prototype.getBlank = function(context) {
  var predictFunc = function(context2) {
    return Promise.resolve(this.getLookup(context2)).then(function(lookup) {
      return lookup.filter(function(option) {
        return !isHidden(option);
      }).slice(0, Conversion.maxPredictions - 1);
    });
  }.bind(this);

  return new Conversion(undefined, new BlankArgument(), Status.INCOMPLETE, '',
                        predictFunc);
};

/**
 * For selections, up is down and black is white. It's like this, given a list
 * [ a, b, c, d ], it's natural to think that it starts at the top and that
 * going up the list, moves towards 'a'. However 'a' has the lowest index, so
 * for SelectionType, up is down and down is up.
 * Sorry.
 */
SelectionType.prototype.decrement = function(value, context) {
  return this.getLookup(context).then(function(lookup) {
    var index = this._findValue(lookup, value);
    if (index === -1) {
      index = 0;
    }
    index++;
    if (index >= lookup.length) {
      index = 0;
    }
    return lookup[index].value;
  }.bind(this));
};

/**
 * See note on SelectionType.decrement()
 */
SelectionType.prototype.increment = function(value, context) {
  return this.getLookup(context).then(function(lookup) {
    var index = this._findValue(lookup, value);
    if (index === -1) {
      // For an increment operation when there is nothing to start from, we
      // want to start from the top, i.e. index 0, so the value before we
      // 'increment' (see note above) must be 1.
      index = 1;
    }
    index--;
    if (index < 0) {
      index = lookup.length - 1;
    }
    return lookup[index].value;
  }.bind(this));
};

/**
 * Walk through an array of { name:.., value:... } objects looking for a
 * matching value (using strict equality), returning the matched index (or -1
 * if not found).
 * @param lookup Array of objects with name/value properties to search through
 * @param value The value to search for
 * @return The index at which the match was found, or -1 if no match was found
 */
SelectionType.prototype._findValue = function(lookup, value) {
  var index = -1;
  for (var i = 0; i < lookup.length; i++) {
    var pair = lookup[i];
    if (pair.value === value) {
      index = i;
      break;
    }
  }
  return index;
};

/**
 * This is how we indicate to SelectionField that we have predictions that
 * might work in a menu.
 */
SelectionType.prototype.hasPredictions = true;

SelectionType.prototype.name = 'selection';

exports.SelectionType = SelectionType;
exports.items = [ SelectionType ];
<|endoftext|>


<commit_before>// 在 Cloud code 里初始化 Express 框架
var express = require('express')
var app = express()
var path = require('path')

// App 全局配置
app.set('views','cloud/views')   // 设置模板目录
app.set('view engine', 'ejs')    // 设置 template 引擎
app.use(express.bodyParser())

// 使用 Express 路由 API 服务 /hello 的 HTTP GET 请求
app.get('/hello', function(req, res) {
  res.render('hello.ejs', {message:'002'})
})

app.get('/chatroom', function(req, res) {
	res.render('chatroom.ejs', {title: 'Chat Room'})
})

var server = require('http').Server(app)
var io = require('socket.io')(server)

io.on('connection', function(socket) {
	socket.broadcast.emit('hi', 'asdfasdf');
	socket.on('chat msg', function(msg) {
		io.emit('chat msg', msg)
	})
})

// 最后，必须有这行代码来使 express 响应 HTTP 请求
server.listen(8000)<commit_msg>remove port<commit_after>// 在 Cloud code 里初始化 Express 框架
var express = require('express')
var app = express()
var path = require('path')

// App 全局配置
app.set('views','cloud/views')   // 设置模板目录
app.set('view engine', 'ejs')    // 设置 template 引擎
app.use(express.bodyParser())

// 使用 Express 路由 API 服务 /hello 的 HTTP GET 请求
app.get('/hello', function(req, res) {
  res.render('hello.ejs', {message:'002'})
})

app.get('/chatroom', function(req, res) {
	res.render('chatroom.ejs', {title: 'Chat Room'})
})

var server = require('http').Server(app)
var io = require('socket.io')(server)

io.on('connection', function(socket) {
	socket.broadcast.emit('hi', 'asdfasdf');
	socket.on('chat msg', function(msg) {
		io.emit('chat msg', msg)
	})
})

// 最后，必须有这行代码来使 express 响应 HTTP 请求
server.listen()<|endoftext|>


<commit_before>/*
* @Author: Jiyun
* @Date:   2015-06-25 03:35:03
* @Last Modified by:   Jiyun
* @Last Modified time: 2015-06-28 02:02:14
*/

// jshint ignore:start

var express = require('express');

var request = require('request');
var schedule = require('node-schedule');
var session = require('express-session');
var everyauthCN = require('everyauth-cn');
everyauthCN.douban.scope('douban_basic_common,shuo_basic_w,shuo_basic_r');
var conf = require('./config/sns-config');
var authSettings = require('./config/auth-settings');
var nodemailer = require('nodemailer');

var app = express();
app.set('views','cloud/views');// 设置模板目录
app.set('view engine', 'jade');

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var cookieSession = require('cookie-session');

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.use(express.static('./public'));

// 如果打开这俩，应用重启后，还会保持登录状态
// 我觉得不太保险，所以注释掉了
// app.use(cookieParser());
// app.use(cookieSession({secret: 'doubananimalclock'}));

app.use(session({
    secret: 'everyauth-cn',
    resave: false,
    saveUninitialized: true,
    // cookie: { secure: true }
}));
app.use(everyauthCN.middleware());

everyauthCN.debug = false;

var now; // 当前是第几个小时
// var image; // 广播配图

// 为字符串添加 repeat 方法:
// 判断是否存在这个方法
if (!String.repeat) {
    // 创建repeat方法
    String.prototype.repeat = function (l) {
        // 创建元素值为空、个数为重复次数+1的数组，用字符串自身做为分隔符连接起来，返回连接后的值。
        return new Array(l + 1).join(this);
    }
}

// 创建邮件发送器
var smtpTransport = nodemailer.createTransport('SMTP',{
    service: 'Gmail',
    auth: {
        user: conf.mailer.user,
        pass: conf.mailer.pass
    }
});

app.get('/', function (req, res) {

    // 判断是不是拿到了 token
    if (typeof req.session.auth !== 'undefined') {

        // console.log('req.session = ', req.session);
        // console.log('req.session.auth.douban.user = ', req.session.auth.douban.user);

        // 判断是不是豆瓣大笨鸡的 uid
        // todo: 通过 config 增加多个帐
        // 68576413 是本地测试的一个帐号
        if (req.session.auth.douban.user.id === '67736974' || req.session.auth.douban.user.id === '68576413') {
            // 取得 token
            var accessToken = req.session.auth.douban.accessToken;
            var refresh_token = req.session.auth.douban.user.accessTokenExtra.refresh_token;

            // 提前获取广播配图
            // image = request.get('http://7bv90p.com1.z0.glb.clouddn.com/333.png');

            // 定义自动定时任务的规则
            var rule = new schedule.RecurrenceRule();
            rule.minute = [55]; // 会有延迟

            /* just for testing */
            // var text = generateText();
            // console.log(text);
            /* test */

            // 开始自动定时任务
            var autoTask = schedule.scheduleJob(rule, function () {

                var text = generateText();

                // console.log('text = ', text);

                postToDouban(accessToken, refresh_token, text, function (err, httpResponse, body) {
                    // if (!err) {
                    //     console.log('豆瓣广播发布成功！偶也');
                    // } else {
                    //     console.error(error, body);
                    // }
                });
            });

            res.render('hello', {message: '欢迎豆瓣大笨鸡！'});
            return;

        } else {
            res.render('hello', {message: '骗谁呢？你根本不是豆瓣大笨鸡！'});
            return;
        }

    } else {
        res.render('hello', {message: '请问，你是豆瓣大笨鸡吗？'});
    }
});

// 生成广播内容
function generateText () {
    // todo:
    // 多种文字形式 如：支持前端页面中 input 传值，或者通过 json 配置
    var string = '咯~';

    // 调整时区
    var d = new Date(); //创建一个Date对象
    var localTime = d.getTime();
    var localOffset = d.getTimezoneOffset() * 60000; //获得当地时间偏移的毫秒数
    var utc = localTime + localOffset; //utc即GMT时间
    var offset = 8; //以北京时间为例，东8区
    var beijing = utc + (3600000 * offset);
    var date = new Date(beijing); // 得到最终的准确时间
    now = date.getHours(); // 得到当前小时

    if (now === 0) {
        now = 24;
    }

    // 之前的版本
    // if (now < 12 && now > 6 || now === 6) {
    //     half = '早上';
    // } else if (now === 12) {
    //     half = '中午';
    // } else if (now > 12 && now < 18 || now === 18) {
    //     half = '下午';
    // } else if (now > 18 && now < 23 || now === 23) {
    //     half = '晚上';
    // } else if (now < 6) {
    //     half = '凌晨';
    // }
    // var text = half + now + '点: \r\n' + string.repeat(now);

    // 新版
    var text;
    var repeatString = string.repeat(now);

    // todo: 优化逻辑，减少 hardcode
    switch (now) {
        case 1:
            text = '凌晨1点。\r\n' + '呼~'.repeat(now);
            break;
        case 2:
            text = '凌晨2点。\r\n' + '呼~'.repeat(now);
            break;
        case 3:
            text = '凌晨3点。\r\n' + '啪~'.repeat(now);
            break;
        case 4:
            text = '凌晨4点。\r\n' + '呼~'.repeat(now);
            break;
        case 5:
            text = '凌晨5点。\r\n' + '呼~'.repeat(now);
            break;
        case 6:
            text = '早上6点。\r\n' + '呼~'.repeat(now);
            break;
        case 7:
            text = '早上7点。\r\n起床打卡。\r\n' + '噗~'.repeat(now);
            break;
        case 8:
            text = '早上8点。\r\n早餐打卡。\r\n' + '饿~'.repeat(now);
            break;
        case 9:
            text = '早上9点。\r\n和大笨鸭吵了一架。\r\n' + repeatString;
            break;
        case 10:
            text = '早上10点。\r\n想做一只猫。\r\n' + '喵~'.repeat(now);
            break;
        case 11:
            text = '早上11点。\r\n' + repeatString;
            break;
        case 12:
            text = '中午12点。\r\n无可奈何鸡睡去，\r\n似曾相识喵归来。\r\n' + '喵~'.repeat(now);
            break;
        case 13:
            text = '下午1点。\r\n' + string.repeat(1);
            break;
        case 14:
            text = '下午2点。\r\n' + string.repeat(2);
            break;
        case 15:
            text = '下午3点。\r\n' + '哼！'.repeat(3);
            break;
        case 16:
            text = '下午4点。\r\n' + string.repeat(4);
            break;
        case 17:
            text = '下午5点。\r\n' + '嗷~'.repeat(5);
            break;
        case 18:
            text = '下午6点。\r\n' + string.repeat(6);
            break;
        case 19:
            text = '晚上7点。\r\n' + string.repeat(7);
            break;
        case 20:
            text = '晚上8点。\r\n背单词打卡。\r\n' + string.repeat(8);
            break;
        case 21:
            text = '晚上9点。\r\n健身打卡。\r\n' + string.repeat(9);
            break;
        case 22:
            text = '晚上10点。\r\n跑步打卡。\r\n' + string.repeat(10);
            break;
        case 23:
            text = '晚上11点。\r\n晚安。\r\n' + '呼~'.repeat(11);
            break;
        case 24:
            text = '零点。\r\n' + '呼~'.repeat(12);
            break;
    }

    return text;
}

// 发送豆瓣广播
function postToDouban (accessToken, refresh_token, text, callback) {
    var r = request.post('https://api.douban.com/shuo/v2/statuses/', {
            method: 'POST',
            headers: {'Authorization': 'Bearer ' + accessToken},
            timeout: 3000
        }, function (err, httpResponse, body) {
            // 判断如果 106 错误 token 过期 (access_token_has_expired)
            // 则去刷新获取 token (refresh_token)
            if (err && err.code === 106) {
                console.error('HOT fuck！Clock fail! We need to refresh token!', err);
                // mailSender('紧急！豆瓣大笨鸡报时失败！需要重新授权！', err, function (mailError, mailResponse) {
                //     console.log('sender feecback:', mailError, mailResponse);
                // });

                refreshToken(refresh_token);

            } else if (err) {
                console.error('Fuck! Clock fail!, Error:', err, '\r\n Body:', body);
                mailSender('操！豆瓣大笨鸡报时失败！', err, function (mailError, mailResponse) {
                    console.log('Sender feedback:', mailError, mailResponse);
                });
            } else {
                console.log('LOL clock success!');
                // mailSender('哈哈！豆瓣大笨鸡报时成功！', text, function (mailError, mailResponse) {
                //     console.log('sender feecback:', mailError, mailResponse);
                // });
            }

            if (callback && typeof callback === 'function') {
                callback(err, httpResponse, body);
            }
        });

    var form = r.form();
    form.append('text', text);

    // // 22点 广播里增加一张配图
    // if (now === 22) {
    //     form.append('image', image);
    // }
    // // todo: 可增加配图
}

// 刷新获取 token
function refreshToken (refresh_token) {
    var client_id = conf.douban.apiKey;
    var client_secret = conf.douban.Secret;
    var redirect_uri = conf.douban.redirect_uri;

    request.post({
        url: 'https://www.douban.com/service/auth2/token?client_id=' + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirect_uri + '&grant_type=refresh_token&refresh_token=' + refresh_token,
    }, function (error, response, resBody) {
        if (!error && response.statusCode == 200) {
            // 拿到新的 access_token 和 refresh_token，再次发送豆瓣广播
            postToDouban(resBody.access_token, resBody.refresh_token, text, callback);
        } else {
            console.error('refresh_token fail！', error, resBody);
        }
    });
}

// 发送邮件
function mailSender (subject, text, callback) {
    console.log('sendMail...');
    var mailOptions = {
        from: '豆瓣大笨鸡 <'+ conf.mailer.user + '>',
        to: conf.mailer.recipient.join(','), // list of receivers
        subject: subject,
        text: text
    };

    smtpTransport.sendMail(mailOptions, function(mailError, mailResponse){
        if (callback && typeof callback === 'function') {
            callback(mailError, mailResponse);
        }
    });
}

app.listen(8080);

// jshint ignore:end<commit_msg>fix bug<commit_after>/*
* @Author: Jiyun
* @Date:   2015-06-25 03:35:03
* @Last Modified by:   Jiyun
* @Last Modified time: 2015-06-28 03:01:11
*/

// jshint ignore:start

var express = require('express');

var request = require('request');
var schedule = require('node-schedule');
var session = require('express-session');
var everyauthCN = require('everyauth-cn');
everyauthCN.douban.scope('douban_basic_common,shuo_basic_w,shuo_basic_r');
var conf = require('./config/sns-config');
var authSettings = require('./config/auth-settings');
var nodemailer = require('nodemailer');

var app = express();
app.set('views','cloud/views');// 设置模板目录
app.set('view engine', 'jade');

var bodyParser = require('body-parser');
var cookieParser = require('cookie-parser');
var cookieSession = require('cookie-session');

app.use(bodyParser.urlencoded({
    extended: true
}));
app.use(bodyParser.json());

app.use(express.static('./public'));

// 如果打开这俩，应用重启后，还会保持登录状态
// 我觉得不太保险，所以注释掉了
// app.use(cookieParser());
// app.use(cookieSession({secret: 'doubananimalclock'}));

app.use(session({
    secret: 'everyauth-cn',
    resave: false,
    saveUninitialized: true,
    // cookie: { secure: true }
}));
app.use(everyauthCN.middleware());

everyauthCN.debug = false;

var now; // 当前是第几个小时
// var image; // 广播配图

// 为字符串添加 repeat 方法:
// 判断是否存在这个方法
if (!String.repeat) {
    // 创建repeat方法
    String.prototype.repeat = function (l) {
        // 创建元素值为空、个数为重复次数+1的数组，用字符串自身做为分隔符连接起来，返回连接后的值。
        return new Array(l + 1).join(this);
    }
}

// 创建邮件发送器
var smtpTransport = nodemailer.createTransport('SMTP',{
    service: 'Gmail',
    auth: {
        user: conf.mailer.user,
        pass: conf.mailer.pass
    }
});

app.get('/', function (req, res) {

    // 判断是不是拿到了 token
    if (typeof req.session.auth !== 'undefined') {

        // console.log('req.session = ', req.session);
        // console.log('req.session.auth.douban.user = ', req.session.auth.douban.user);

        // 判断是不是豆瓣大笨鸡的 uid
        // todo: 通过 config 增加多个帐
        // 68576413 是本地测试的一个帐号
        if (req.session.auth.douban.user.id === '67736974' || req.session.auth.douban.user.id === '68576413') {
            // 取得 token
            var accessToken = req.session.auth.douban.accessToken;
            var refresh_token = req.session.auth.douban.user.accessTokenExtra.refresh_token;

            // 提前获取广播配图
            // image = request.get('http://7bv90p.com1.z0.glb.clouddn.com/333.png');

            // 定义自动定时任务的规则
            var rule = new schedule.RecurrenceRule();
            rule.minute = [2]; // 会有延迟

            /* just for testing */
            // var text = generateText();
            // console.log(text);
            /* test */

            // 开始自动定时任务
            var autoTask = schedule.scheduleJob(rule, function () {

                var text = generateText();

                // console.log('text = ', text);

                postToDouban(accessToken, refresh_token, text, function (err, httpResponse, body) {
                    // if (!err) {
                    //     console.log('豆瓣广播发布成功！偶也');
                    // } else {
                    //     console.error(error, body);
                    // }
                });
            });

            res.render('hello', {message: '欢迎豆瓣大笨鸡！'});
            return;

        } else {
            res.render('hello', {message: '骗谁呢？你根本不是豆瓣大笨鸡！'});
            return;
        }

    } else {
        res.render('hello', {message: '请问，你是豆瓣大笨鸡吗？'});
    }
});

// 生成广播内容
function generateText () {
    // todo:
    // 多种文字形式 如：支持前端页面中 input 传值，或者通过 json 配置
    var string = '咯~';

    // 调整时区
    var d = new Date(); //创建一个Date对象
    var localTime = d.getTime();
    var localOffset = d.getTimezoneOffset() * 60000; //获得当地时间偏移的毫秒数
    var utc = localTime + localOffset; //utc即GMT时间
    var offset = 8; //以北京时间为例，东8区
    var beijing = utc + (3600000 * offset);
    var date = new Date(beijing); // 得到最终的准确时间
    now = date.getHours(); // 得到当前小时

    if (now === 0) {
        now = 24;
    }

    // 之前的版本
    // if (now < 12 && now > 6 || now === 6) {
    //     half = '早上';
    // } else if (now === 12) {
    //     half = '中午';
    // } else if (now > 12 && now < 18 || now === 18) {
    //     half = '下午';
    // } else if (now > 18 && now < 23 || now === 23) {
    //     half = '晚上';
    // } else if (now < 6) {
    //     half = '凌晨';
    // }
    // var text = half + now + '点: \r\n' + string.repeat(now);

    // 新版
    var text;
    var repeatString = string.repeat(now);

    // todo: 优化逻辑，减少 hardcode
    switch (now) {
        case 1:
            text = '凌晨1点。\r\n' + '呼~'.repeat(now);
            break;
        case 2:
            text = '凌晨2点。\r\n' + '呼~'.repeat(now);
            break;
        case 3:
            text = '凌晨3点。\r\n' + '啪~'.repeat(now);
            break;
        case 4:
            text = '凌晨4点。\r\n' + '呼~'.repeat(now);
            break;
        case 5:
            text = '凌晨5点。\r\n' + '呼~'.repeat(now);
            break;
        case 6:
            text = '早上6点。\r\n' + '呼~'.repeat(now);
            break;
        case 7:
            text = '早上7点。\r\n起床打卡。\r\n' + '噗~'.repeat(now);
            break;
        case 8:
            text = '早上8点。\r\n早餐打卡。\r\n' + '饿~'.repeat(now);
            break;
        case 9:
            text = '早上9点。\r\n和大笨鸭吵了一架。\r\n' + repeatString;
            break;
        case 10:
            text = '早上10点。\r\n想做一只猫。\r\n' + '喵~'.repeat(now);
            break;
        case 11:
            text = '早上11点。\r\n' + repeatString;
            break;
        case 12:
            text = '中午12点。\r\n无可奈何鸡睡去，\r\n似曾相识喵归来。\r\n' + '喵~'.repeat(now);
            break;
        case 13:
            text = '下午1点。\r\n' + string.repeat(1);
            break;
        case 14:
            text = '下午2点。\r\n' + string.repeat(2);
            break;
        case 15:
            text = '下午3点。\r\n' + '哼！'.repeat(3);
            break;
        case 16:
            text = '下午4点。\r\n' + string.repeat(4);
            break;
        case 17:
            text = '下午5点。\r\n' + '嗷~'.repeat(5);
            break;
        case 18:
            text = '下午6点。\r\n' + string.repeat(6);
            break;
        case 19:
            text = '晚上7点。\r\n' + string.repeat(7);
            break;
        case 20:
            text = '晚上8点。\r\n背单词打卡。\r\n' + string.repeat(8);
            break;
        case 21:
            text = '晚上9点。\r\n健身打卡。\r\n' + string.repeat(9);
            break;
        case 22:
            text = '晚上10点。\r\n跑步打卡。\r\n' + string.repeat(10);
            break;
        case 23:
            text = '晚上11点。\r\n晚安。\r\n' + '呼~'.repeat(11);
            break;
        case 24:
            text = '零点。\r\n' + '呼~'.repeat(12);
            break;
    }

    return text;
}

// 发送豆瓣广播
function postToDouban (accessToken, refresh_token, text, callback) {
    var r = request.post('https://api.douban.com/shuo/v2/statuses/', {
            method: 'POST',
            headers: {'Authorization': 'Bearer ' + accessToken},
            timeout: 3000
        }, function (err, httpResponse, body) {
            // 判断如果 106 错误 token 过期 (access_token_has_expired)
            // 则去刷新获取 token (refresh_token)
            if (err && err.code === 106) {
                console.error('HOT fuck！Clock fail! We need to refresh token!', err);
                // mailSender('紧急！豆瓣大笨鸡报时失败！需要重新授权！', err, function (mailError, mailResponse) {
                //     console.log('sender feecback:', mailError, mailResponse);
                // });

                refreshToken(refresh_token);

            } else if (err) {
                console.error('Fuck! Clock fail!, Error:', err, '\r\n Body:', body);
                mailSender('操！豆瓣大笨鸡报时失败！', err, function (mailError, mailResponse) {
                    console.log('Sender feedback:', mailError, mailResponse);
                });
            } else {
                console.log('LOL clock success!');
                // mailSender('哈哈！豆瓣大笨鸡报时成功！', text, function (mailError, mailResponse) {
                //     console.log('sender feecback:', mailError, mailResponse);
                // });
            }

            if (callback && typeof callback === 'function') {
                callback(err, httpResponse, body);
            }
        });

    var form = r.form();
    form.append('text', text);

    // // 22点 广播里增加一张配图
    // if (now === 22) {
    //     form.append('image', image);
    // }
    // // todo: 可增加配图
}

// 刷新获取 token
function refreshToken (refresh_token) {
    var client_id = conf.douban.apiKey;
    var client_secret = conf.douban.Secret;
    var redirect_uri = conf.douban.redirect_uri;

    request.post({
        url: 'https://www.douban.com/service/auth2/token?client_id=' + client_id + '&client_secret=' + client_secret + '&redirect_uri=' + redirect_uri + '&grant_type=refresh_token&refresh_token=' + refresh_token,
    }, function (error, response, resBody) {
        if (!error && response.statusCode == 200) {
            // 拿到新的 access_token 和 refresh_token，再次发送豆瓣广播
            postToDouban(resBody.access_token, resBody.refresh_token, text, callback);
        } else {
            console.error('refresh_token fail！', error, resBody);
        }
    });
}

// 发送邮件
function mailSender (subject, text, callback) {
    console.log('sendMail...');
    var mailOptions = {
        from: '豆瓣大笨鸡 <'+ conf.mailer.user + '>',
        to: conf.mailer.recipient.join(','), // list of receivers
        subject: subject,
        text: text
    };

    smtpTransport.sendMail(mailOptions, function(mailError, mailResponse){
        if (callback && typeof callback === 'function') {
            callback(mailError, mailResponse);
        }
    });
}

app.listen(8080);

// jshint ignore:end<|endoftext|>


<commit_before>var _ = require('lodash');
var util = require('sails-util');
var Err = require('../../../errors');

module.exports = function(sails) {

  /**
   * Expose `policies` hook definition
   */
  var policyHookDef = {

    defaults: {

      // Default policy mappings (allow all)
      policies: { '*': true }
    },

    configure: function () {
      this.middleware || (this.middleware = { });
    },

    /**
     * Initialize is fired first thing when the hook is loaded
     *
     * @api public
     */
    initialize: function(cb) {
      // Callback is optional
      cb = util.optional(cb);

      // Grab policies config & policy modules and trigger callback
      this.loadMiddleware(function (err) {
        if (err) return cb(err);

        sails.log.verbose('Finished loading policy middleware logic.');
        cb();
      }.bind(this));

      // Before routing, curry controller functions with appropriate policy chains
      sails.on('router:before', this.bindPolicies);
    },

    /**
     * Wipe everything and (re)load middleware from policies
     * (policies.js config is already loaded at this point)
     *
     * @api private
     */
    loadMiddleware: function(cb) {
      // Load policy modules
      sails.log.verbose('Loading policy modules from app...');
      sails.modules.loadPolicies(function modulesLoaded (err, modules) {
        if (err) return cb(err);
        _.extend(this.middleware, modules);
        //console.log('sails.middleware.policies', sails.middleware.policies);
        return cb();
      }.bind(this));
    },

    /**
     * Curry the policy chains into the appropriate controller functions
     *
     * @api private
     */
    bindPolicies: function() {
      // Build / normalize policy config
      this.mapping = this.buildPolicyMap();
      _bindPolicies(this.mapping, sails.middleware.controllers);

      // Emit event to let other hooks know we're ready to go
      sails.log.verbose('Policy-controller bindings complete!');
      sails.emit('hook:policies:bound');
    },

    /**
     * Build normalized, hook-internal representation of policy mapping
     * by performing a non-destructive parse of `sails.config.policies`
     *
     * @returns {Object} mapping
     * @api private
     */
    buildPolicyMap: function () {
      var mapping = { };
      _.each(sails.config.policies, function (_policy, controllerId) {

        // Accept `FooController` or `foo`
        // Case-insensitive
        controllerId = util.normalizeControllerId(controllerId);

        // Controller-level policy ::
        // Just map the policy to the controller directly
        if (!util.isDictionary(_policy)) {
          mapping[controllerId] = policyHookDef.normalizePolicy(_policy);
          return;
        }

        // Policy mapping contains a sub-object ::
        // So we need to dive in and build/normalize the policy mapping from here
        // Mapping each policy to each action for this controller
        mapping[controllerId] = {};
        _.each( _policy, function (__policy, actionId) {

          // Case-insensitive
          actionId = actionId.toLowerCase();

          mapping[controllerId][actionId] = policyHookDef.normalizePolicy(__policy);
        });
      });

      return mapping;
    },

    /**
     * Convert a single policy into shallow array notation
     * (look up string policies using middleware in this hook)
     *
     * @param {Array|String|Function|Boolean} policy
     * @api private
     */
    normalizePolicy: function (policy) {
      // Recursively normalize lists of policies
      if (_.isArray(policy)) {
        // Normalize each policy in the chain
        return _.flatten(
          _.map(policy, function normalize_each_policy (policy) {
            return policyHookDef.normalizePolicy(policy);
          }));
      }

      // Look up the policy in the policy registry
      if (_.isString(policy)) {
        var policyFn = this.lookupFn(policy, 'config.policies');
        // Set the "policy" key on the policy function to the policy name, for debugging
        policyFn._middlewareType = 'POLICY: '+policy;
        return [policyFn];
      }

      // An explicitly defined, anonymous policy middleware can be directly attached
      if (_.isFunction(policy)) {
        var anonymousPolicy = policy.bind({ });
        // Set the "policy" key on the function name (if any) for debugging
        anonymousPolicy._middlewareType = 'POLICY: '+ (anonymousPolicy.name || 'anonymous');
        return [anonymousPolicy];
      }

      // A false or null policy means NEVER allow any requests
      if (policy === false || policy === null) {
        var neverAllow = function neverAllow (req, res, next) {
          res.send(403);
        };
        neverAllow._middlewareType = 'POLICY: neverAllow';
        return [neverAllow];
      }

      // A true policy means ALWAYS allow requests
      if (policy === true) {
        var alwaysAllow = function alwaysAllow (req, res, next) {
          next();
        };
        alwaysAllow._middlewareType  = 'POLICY: alwaysAllow';
        return [alwaysAllow];
      }

      // If we made it here, the policy is invalid
      sails.log.error('Cannot map invalid policy: ', policy);
      return [function(req, res) {
        throw new Error('Invalid policy: ' + policy);
      }];
    },

    /**
     * Bind a route directly to a policy
     */
    bindDirectlyToRoute: function (event) {
      // Only pay attention to delegated route events
      // if `policy` is declared in event.target
      if ( !event.target || !event.target.policy ) {
        return;
      }

      // Bind policy function to route
      var fn = this.lookupFn(event.target.policy, 'config.routes');
      sails.router.bind(event.path, fn, event.verb, _.merge(event.options, event.target));
    },

    /**
     * @param {String} policyId
     * @param {String} referencedIn [optional]
     *  - where the policy identity is being referenced, for providing better error msg
     * @returns {Function} the appropriate policy middleware
     */
    lookupFn: function (policyId, referencedIn) {
      policyId = policyId.toLowerCase();

      // Policy doesn't exist
      if (!this.middleware[policyId] ) {
        return Err.fatal.__UnknownPolicy__ (policyId, referencedIn, sails.config.paths.policies);
      }

      // Policy found
      return this.middleware[policyId];
    }
  };

  /**
   * Bind routes for manually-mapped policies from `config/routes.js`
   */
  sails.on('route:typeUnknown', function (ev) {
    policyHookDef.bindDirectlyToRoute(ev);
  });

  return policyHookDef;
};

/**
 * Bind a set of policies to a set of controllers
 * (prepend policy chains to original middleware)
 */
function _bindPolicies (mapping, middlewareSet) {
  _.each(middlewareSet, function (_c, id) {

    var topLevelPolicyId = mapping[id];
    var actions, actionFn;
    var controller = middlewareSet[id];

    // If a policy doesn't exist for this controller, use '*'
    if (_.isUndefined(topLevelPolicyId) ) {
      topLevelPolicyId = mapping['*'];
    }

    // Build list of actions
    if (util.isDictionary(controller) ) {
      actions = _.functions(controller);
    }

    // If this is a controller policy, apply it immediately
    if (!util.isDictionary(topLevelPolicyId) ) {

      // :: Controller is a container object
      // -> apply the policy to all the actions
      if (util.isDictionary(controller) ) {
        // sails.log.verbose('Applying policy (' + topLevelPolicyId + ') to controller\'s (' + id + ') actions...');
        _.each(actions, function(actionId) {
          actionFn = controller[actionId];
          controller[actionId] = topLevelPolicyId.concat([actionFn]);
          // sails.log.verbose('Applying policy to ' + id + '.' + actionId + '...', controller[actionId]);
        });
        return;
      }

      // :: Controller is a function
      // -> apply the policy directly
      // sails.log.verbose('Applying policy (' + topLevelPolicyId + ') to top-level controller middleware fn (' + id + ')...');
      middlewareSet[id] = topLevelPolicyId.concat(controller);
    }

    // If this is NOT a top-level policy, and merely a container of other policies,
    // iterate through each of this controller's actions and apply policies in a way that makes sense
    else {
      _.each(actions, function(actionId) {

        var actionPolicy = mapping[id][actionId];
        // sails.log.verbose('Mapping policies to actions.... ', actions);

        // If a policy doesn't exist for this controller/action, use the controller-local '*'
        if (_.isUndefined(actionPolicy) ) {
          actionPolicy = mapping[id]['*'];
        }

        // if THAT doesn't exist, use the global '*' policy
        if (_.isUndefined(actionPolicy)) {
          actionPolicy = mapping['*'];
        }
        // sails.log.verbose('Applying policy (' + actionPolicy + ') to action (' + id + '.' + actionId + ')...');

        actionFn = controller[actionId];
        controller[actionId] = actionPolicy.concat([actionFn]);
      });
    }
  });
}
<commit_msg>Cleanup pass in policies hook; get rid of unnecessary helper function that's only being used in one place.<commit_after>var _ = require('lodash');
var util = require('sails-util');
var Err = require('../../../errors');

module.exports = function(sails) {

  /**
   * Expose `policies` hook definition
   */
  var policyHookDef = {

    defaults: {

      // Default policy mappings (allow all)
      policies: { '*': true }
    },

    configure: function () {
      this.middleware || (this.middleware = { });
    },

    /**
     * Initialize is fired first thing when the hook is loaded
     *
     * @api public
     */
    initialize: function(cb) {
      // Callback is optional
      cb = util.optional(cb);

      // Grab policies config & policy modules and trigger callback
      this.loadMiddleware(function (err) {
        if (err) { return cb(err); }

        sails.log.verbose('Finished loading policy middleware logic.');
        cb();
      }.bind(this));

      // Before routing, curry controller functions with appropriate policy chains
      sails.on('router:before', this.bindPolicies);
    },

    /**
     * Wipe everything and (re)load middleware from policies
     * (policies.js config is already loaded at this point)
     *
     * @api private
     */
    loadMiddleware: function(cb) {
      // Load policy modules
      sails.log.verbose('Loading policy modules from app...');
      sails.modules.loadPolicies(function modulesLoaded (err, modules) {
        if (err) { return cb(err); }
        _.extend(this.middleware, modules);

        return cb();
      }.bind(this));
    },

    /**
     * Curry the policy chains into the appropriate controller functions
     *
     * @api private
     */
    bindPolicies: function() {
      // Build / normalize policy config
      this.mapping = this.buildPolicyMap();

      // Bind a set of policies to a set of controllers
      // (prepend policy chains to original middleware)
      var mapping = this.mapping;
      var middlewareSet = sails.middleware.controllers;
      _.each(middlewareSet, function (_c, id) {

        var topLevelPolicyId = mapping[id];
        var actions, actionFn;
        var controller = middlewareSet[id];

        // If a policy doesn't exist for this controller, use '*'
        if (_.isUndefined(topLevelPolicyId) ) {
          topLevelPolicyId = mapping['*'];
        }

        // Build list of actions
        if (util.isDictionary(controller) ) {
          actions = _.functions(controller);
        }

        // If this is a controller policy, apply it immediately
        if (!util.isDictionary(topLevelPolicyId) ) {

          // :: Controller is a container object
          // -> apply the policy to all the actions
          if (util.isDictionary(controller) ) {
            // sails.log.verbose('Applying policy (' + topLevelPolicyId + ') to controller\'s (' + id + ') actions...');
            _.each(actions, function(actionId) {
              actionFn = controller[actionId];
              controller[actionId] = topLevelPolicyId.concat([actionFn]);
              // sails.log.verbose('Applying policy to ' + id + '.' + actionId + '...', controller[actionId]);
            });
            return;
          }

          // :: Controller is a function
          // -> apply the policy directly
          // sails.log.verbose('Applying policy (' + topLevelPolicyId + ') to top-level controller middleware fn (' + id + ')...');
          middlewareSet[id] = topLevelPolicyId.concat(controller);
        }

        // If this is NOT a top-level policy, and merely a container of other policies,
        // iterate through each of this controller's actions and apply policies in a way that makes sense
        else {
          _.each(actions, function(actionId) {

            var actionPolicy = mapping[id][actionId];
            // sails.log.verbose('Mapping policies to actions.... ', actions);

            // If a policy doesn't exist for this controller/action, use the controller-local '*'
            if (_.isUndefined(actionPolicy) ) {
              actionPolicy = mapping[id]['*'];
            }

            // if THAT doesn't exist, use the global '*' policy
            if (_.isUndefined(actionPolicy)) {
              actionPolicy = mapping['*'];
            }
            // sails.log.verbose('Applying policy (' + actionPolicy + ') to action (' + id + '.' + actionId + ')...');

            actionFn = controller[actionId];
            controller[actionId] = actionPolicy.concat([actionFn]);
          });
        }
      });//</each in middlewareSet>

      // Emit event to let other hooks know we're ready to go
      sails.log.verbose('Policy-controller bindings complete!');
      sails.emit('hook:policies:bound');
    },

    /**
     * Build normalized, hook-internal representation of policy mapping
     * by performing a non-destructive parse of `sails.config.policies`
     *
     * @returns {Object} mapping
     * @api private
     */
    buildPolicyMap: function () {
      var mapping = { };
      _.each(sails.config.policies, function (_policy, controllerId) {

        // Accept `FooController` or `foo`
        // Case-insensitive
        controllerId = util.normalizeControllerId(controllerId);

        // Controller-level policy ::
        // Just map the policy to the controller directly
        if (!util.isDictionary(_policy)) {
          mapping[controllerId] = policyHookDef.normalizePolicy(_policy);
          return;
        }

        // Policy mapping contains a sub-object ::
        // So we need to dive in and build/normalize the policy mapping from here
        // Mapping each policy to each action for this controller
        mapping[controllerId] = {};
        _.each( _policy, function (__policy, actionId) {

          // Case-insensitive
          actionId = actionId.toLowerCase();

          mapping[controllerId][actionId] = policyHookDef.normalizePolicy(__policy);
        });
      });

      return mapping;
    },

    /**
     * Convert a single policy into shallow array notation
     * (look up string policies using middleware in this hook)
     *
     * @param {Array|String|Function|Boolean} policy
     * @api private
     */
    normalizePolicy: function (policy) {
      // Recursively normalize lists of policies
      if (_.isArray(policy)) {
        // Normalize each policy in the chain
        return _.flatten(
          _.map(policy, function normalize_each_policy (policy) {
            return policyHookDef.normalizePolicy(policy);
          }));
      }

      // Look up the policy in the policy registry
      if (_.isString(policy)) {
        var policyFn = this.lookupFn(policy, 'config.policies');
        // Set the "policy" key on the policy function to the policy name, for debugging
        policyFn._middlewareType = 'POLICY: '+policy;
        return [policyFn];
      }

      // An explicitly defined, anonymous policy middleware can be directly attached
      if (_.isFunction(policy)) {
        var anonymousPolicy = policy.bind({ });
        // Set the "policy" key on the function name (if any) for debugging
        anonymousPolicy._middlewareType = 'POLICY: '+ (anonymousPolicy.name || 'anonymous');
        return [anonymousPolicy];
      }

      // A false or null policy means NEVER allow any requests
      if (policy === false || policy === null) {
        var neverAllow = function neverAllow (req, res, next) {
          res.send(403);
        };
        neverAllow._middlewareType = 'POLICY: neverAllow';
        return [neverAllow];
      }

      // A true policy means ALWAYS allow requests
      if (policy === true) {
        var alwaysAllow = function alwaysAllow (req, res, next) {
          next();
        };
        alwaysAllow._middlewareType  = 'POLICY: alwaysAllow';
        return [alwaysAllow];
      }

      // If we made it here, the policy is invalid
      sails.log.error('Cannot map invalid policy: ', policy);
      return [function(req, res) {
        throw new Error('Invalid policy: ' + policy);
      }];
    },

    /**
     * Bind a route directly to a policy
     */
    bindDirectlyToRoute: function (event) {
      // Only pay attention to delegated route events
      // if `policy` is declared in event.target
      if ( !event.target || !event.target.policy ) {
        return;
      }

      // Bind policy function to route
      var fn = this.lookupFn(event.target.policy, 'config.routes');
      sails.router.bind(event.path, fn, event.verb, _.merge(event.options, event.target));
    },

    /**
     * @param {String} policyId
     * @param {String} referencedIn [optional]
     *  - where the policy identity is being referenced, for providing better error msg
     * @returns {Function} the appropriate policy middleware
     */
    lookupFn: function (policyId, referencedIn) {
      policyId = policyId.toLowerCase();

      // Policy doesn't exist
      if (!this.middleware[policyId] ) {
        return Err.fatal.__UnknownPolicy__ (policyId, referencedIn, sails.config.paths.policies);
      }

      // Policy found
      return this.middleware[policyId];
    }
  };

  /**
   * Bind routes for manually-mapped policies from `config/routes.js`
   */
  sails.on('route:typeUnknown', function (ev) {
    policyHookDef.bindDirectlyToRoute(ev);
  });

  return policyHookDef;
};

<|endoftext|>


<commit_before>var Initializer = require("../initializer");

Initializer.add('startup', 'stex.express', ['stex.config', 'stex.logging', 'stex.db', 'stex.newrelic'], function(stex) {
  var express    = require("express");
  var bodyParser = require('body-parser');
  var cors       = require("cors");
  var urlPattern = require("url-pattern");
  var _          = require("lodash");

  stex.app    = express();
  stex.router = express.Router();
  global.app  = stex.app;

  app.enable('trust proxy');

  // As part of the spec for CORS, we need to make sure we include
  // Vary: Origin since we're now inspecting the origin to decide
  // whether CORS is allowed or not
  app.use(function(req, res, next) {
    res.vary("Origin");
    next();
  });

  var allowedPatternStrings = stex.conf.get("allowedOrigins") || ["*"];
  var allowedPatterns = _.map(allowedPatternStrings, function(pattern) {
    return urlPattern.newPattern(pattern, '.');
  });

  app.use(cors({
    origin: function(origin, callback){
      if(typeof origin !== 'string') {
        return callback(null, false);
      }
      var allowed = _.any(allowedPatterns, function(pattern) {
        return !!pattern.match(origin);
      });

      callback(null, allowed);
    }
  }));

  var bodyLimit = stex.conf.get("bodyLimit") || "128kb";
  var parser    = bodyParser.json({limit: bodyLimit});

  // collect the raw body in to req.rawBody.
  // NOTE: we nest the bodyParser underneath this middleware since this
  // middleware _MUST_ be immediately before bodyParser to work properly
  app.use(function(req, res, next) {
    req.rawBody = '';

    req.on('data', function(chunk) {
      req.rawBody += chunk.toString('utf-8');
    });

    parser(req, res, next);
  });
});<commit_msg>Only consider hostname when checking CORS origin<commit_after>var Initializer = require("../initializer");

Initializer.add('startup', 'stex.express', ['stex.config', 'stex.logging', 'stex.db', 'stex.newrelic'], function(stex) {
  var express    = require("express");
  var bodyParser = require('body-parser');
  var cors       = require("cors");
  var urlPattern = require("url-pattern");
  var url        = require("url");
  var _          = require("lodash");

  stex.app    = express();
  stex.router = express.Router();
  global.app  = stex.app;

  app.enable('trust proxy');

  // As part of the spec for CORS, we need to make sure we include
  // Vary: Origin since we're now inspecting the origin to decide
  // whether CORS is allowed or not
  app.use(function(req, res, next) {
    res.vary("Origin");
    next();
  });

  var allowedPatternStrings = stex.conf.get("allowedOrigins") || ["*"];
  var allowedPatterns = _.map(allowedPatternStrings, function(pattern) {
    return urlPattern.newPattern(pattern, '.');
  });

  app.use(cors({
    origin: function(origin, callback){
      if(typeof origin !== 'string') {
        return callback(null, false);
      }

      origin = url.parse(origin).hostname;

      var allowed = _.any(allowedPatterns, function(pattern) {
        return !!pattern.match(origin);
      });

      callback(null, allowed);
    }
  }));

  var bodyLimit = stex.conf.get("bodyLimit") || "128kb";
  var parser    = bodyParser.json({limit: bodyLimit});

  // collect the raw body in to req.rawBody.
  // NOTE: we nest the bodyParser underneath this middleware since this
  // middleware _MUST_ be immediately before bodyParser to work properly
  app.use(function(req, res, next) {
    req.rawBody = '';

    req.on('data', function(chunk) {
      req.rawBody += chunk.toString('utf-8');
    });

    parser(req, res, next);
  });
});<|endoftext|>


<commit_before>'use strict';

const mongoose = require('mongoose');

const projectSchema = mongoose.Schema({
  name: {
    type: String,
    unique: true
  },
  description: String,
  privacy: String,
  managerName: String,
  devSkills: [String],
  organization: String,
  employees: [{
    employeeName: String,
    employeeId: String,
    role: String,
    hoursWorked: Number,
    tasksCompleted: [String]
  }],
  tasks: [String],
  isDeleted: {
    type: Boolean,
    default: false
  }
});

let Project;
projectSchema.statics.getProject = (project) => {
  return new Project({
    name: project.name,
    description: project.description,
    privacy: project.privacy,
    managerName: project.managerName,
    devSkills: project.devSkills,
    emploeyees: project.emploeyees,
    tasks: project.tasks
  });
};
mongoose.model('Project', projectSchema);
Project = mongoose.model('Project');
module.exports = mongoose.model('Project');<commit_msg>project-model.js owner field and appliers field added<commit_after>'use strict';

const mongoose = require('mongoose');

const projectSchema = mongoose.Schema({
  name: {
    type: String,
    unique: true
  },
  description: String,
  owner: String,
  privacy: String,
  managerName: String,
  devSkills: [String],
  organization: String,
  applyers: [String],
  employees: [{
    employeeName: String,
    employeeId: String,
    role: String,
    hoursWorked: Number,
    tasksCompleted: [String]
  }],
  tasks: [String],
  isDeleted: {
    type: Boolean,
    default: false
  }
});

let Project;
projectSchema.statics.getProject = (project) => {
  return new Project({
    name: project.name,
    description: project.description,
    owner: project.owner,
    privacy: project.privacy,
    managerName: project.managerName,
    devSkills: project.devSkills,
    applyers: project.applyers,
    emploeyees: project.emploeyees,
    tasks: project.tasks
  });
};
mongoose.model('Project', projectSchema);
Project = mongoose.model('Project');
module.exports = mongoose.model('Project');<|endoftext|>


<commit_before> var solar_tilt=45, lamp_height=12, lamp_width=3, draw_construction = false, sheet_thickness=1/12, sheet_width=192, sheet_height=384, sheets=[], sheet_count = 0;

 $(window).load(function(){
	var dial_width = Math.round($('#slider_container').width()*.5);
	$('#construction_button').click(function() {
		if ($(this).hasClass('active')){
			$(this).removeClass('active');
			draw_construction = false;
		} else {
			$(this).addClass('active');
			draw_construction = true;
		}
		updateScenes();
    });

	var map_width = $('#map').width()
    var map_height = $('#map').height()

	$('#map_pin').draggable({
		containment:'parent'
		, drag: function(event, ui){
			// calculate geographic coordinates from pointer position on map
            var offset = $(this).offset();
            var xPos = ui.position.left;
            var yPos = ui.position.top;
        	var relative_pos = get_relative_position(
	        		xPos
	        		, yPos
	        		, map_width-20
	        		, map_height-20)
        	var coors = get_geo_coordinates_from_relative_coordinates(
						relative_pos.x
						, relative_pos.y)

        	// get solar noon from map angle
            solarNoon = get_solar_noon(coors.lat, coors.lng)

            // naive assumption: set tilt to this angle (original range was -15 to 89)
            solar_tilt = solarNoon
            updateScenes()
        }
	})

	// $("#dial").knob({
	// 	'width':dial_width,
	// 	'height':dial_width,
	// 	fgColor:"orange",
	// 	min:-15,
	// 	max:89,
	// 	angleArc:104,
	// 	angleOffset:255,
	//       change: function(v) {
	// 		solar_tilt = v;
	// 		updateScenes();
	// 	}
	// });
    $("#slider").slider({
      orientation: "vertical",
      range: "min",
      min: 7,
      max: 18,
      value: lamp_height,
      slide: function(event,ui) {
		lamp_height = ui.value;
		updateScenes();
		$("#height_label").html(lamp_height + " ft");
      }
    });
	$("#width_slider").slider({
		range: "min",
		min: 12,
		max: 72,
		value: lamp_width*12,
		slide: function(event,ui){
			lamp_width = ui.value/12;
			updateScenes();
			$("#width_value").val(Math.floor(ui.value/12).toString()+"\' "+(ui.value%12).toString()+"\"");
		}
	});
	$("#sheet_thickness_slider").slider({
		range: "min",
		min: 1,
		max: 32,
		value: sheet_thickness*32*12, //convert from feet to 1/32"
		slide: function(event,ui){
			sheet_thickness = ui.value/32/12;
			updateScenes();
			$("#sheet_thickness_value").val(toFractionString32(ui.value)+"\"");
		}
	});
	$("#sheet_width_slider").slider({
		range: "min",
		min: 1,
		max: 576,
		value: sheet_width,
		slide: function(event,ui){
			sheet_width = ui.value;
			$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
		}
	});
	$("#sheet_height_slider").slider({
		range: "min",
		min: 1,
		max: 576,
		value: sheet_height,
		slide: function(event,ui){
			sheet_height = ui.value;
			$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
		}
	});
	$("#add_sheet_button").click(function(event){
		var sheet = {width:sheet_width/12, height:sheet_height/12, id:sheet_count};
		sheets.push(sheet);
		updateSheets();
		addSheet(sheet);
		updateScenes();
		sheet_count += 1;
	});
	$("#sheet_list").on('click','.close_button',function(){
		var index = this.getAttribute('data-index');
		var id = sheets[index].id;
		removeSheet(index);
		sheets.splice(index,1);
		updateSheets();
		updateScenes();
	});
	$("#download_button").click(function(event){
		createSVG();
	});
	$(".unclick").mouseup(function(event){
		$(this).blur();
	});
	$("#sheet_width_value").change(function(event){
		sheet_width = $(this).val();
		$("#sheet_width_slider").slider("value",$(this).val());
		$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
		$("#sheet_width_value").blur();
	});
	$("#sheet_height_value").change(function(event){
		sheet_height = $(this).val();
		$("#sheet_height_slider").slider("value",$(this).val());
		$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
		$("#sheet_height_value").blur();
	});
	drawElements();
	model_init();
	cut_init();
	animate();
	updateScenes();
});

window.addEventListener('resize',function(){
	drawElements();
});

function drawElements(){
	var dial_width = Math.round($('#slider_container').width()*.5);
	 // $('#dial').trigger(
        // 'configure',
        // {
            // 'width':dial_width
        // }
    // );
	$("#height_label").html(lamp_height + " ft");
	$("#slider").height((window.innerHeight-$('#construction_button').outerHeight(true)-$('#dial').siblings().outerHeight(true)-$('#height_label').outerHeight(true))*.2);
	$("#sheet_thickness_value").val(toFractionString32(sheet_thickness*12*32)+"\"");
	$("#width_value").val(Math.floor(lamp_width).toString()+"\' "+(lamp_width%1*12).toString()+"\"");
	$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
	$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
	$("#sheet_width_value").css('width',$("#sheet_width_value").parent().width()-$("#sheet_width_value").prev().width()-6);
	$("#sheet_height_value").css('width',$("#sheet_height_value").parent().width()-$("#sheet_height_value").prev().width()-6);
	$(".geometry_super_container").css('height',window.innerHeight);
	$("#download_button").css('position','absolute');
	$("#download_button").css('left',$("#cut_container").width()-$("#download_button").width()-40);
	$("#download_button").css('top',$("#cut_container").height());
}

function updateSheets(){
	$("#sheet_list").empty();
	for (var i = 0; i < sheets.length; i++){
		$("#sheet_list").append("
Sheet "+sheets[i].id+": "+Math.floor(sheets[i].width).toString()+"\' "+(sheets[i].width*12%12).toString()+"\" x "+Math.floor(sheets[i].height).toString()+"\' "+(sheets[i].height*12%12).toString()+"\"
");
	}
}

function toFractionString32(numerator){
	var whole = Math.floor(numerator/32);
	var top=numerator-whole*32, bottom=32;
	if (numerator%32 == 0){
		return whole.toString();
	} else if(numerator%16 == 0){
		top = numerator/16;
		bottom = 2;
	} else if (numerator%8 == 0){
		top = numerator/8;
		bottom = 4;
	} else if (numerator%4 == 0){
		top = numerator/4;
		bottom = 8;
	} else if (numerator%2 == 0){
		top = numerator/2;
		bottom = 16;
	}
	if (whole > 0){
		return whole.toString()+" "+top.toString()+"/"+bottom.toString();
	} else {
		return top.toString()+"/"+bottom.toString();
	}
}

//// -----
//// map pointer --> sun angles
//// -----

// takes x and y (which scale from 0 to 100)
// and returns geographic coordinates
function get_geo_coordinates_from_relative_coordinates (x,y) {
	// if y is less than 50
	// , give a southern hemisphere lat
	var lng = y.map(0,100,-90,90)
	var lat = y.map(0,100,-90,90)

	return {
		lat:lat
		, lng: lng
	}
}

// find the date of the sunniest equinox at given latitude
function find_sunniest_equinox (lat) {
	var d = new Date()
	// if southern altitude, sunnier equinox is march 20
	if (lat<0) return d.setMonth(2,20)
	// if northern, september 23
	return d.setMonth(8,23)
}

// returns an object with three sun angles
// 1. sunriseEnd (bottom edge of sun touches horizon)
// 2. solarNoon (sun's highest position)
// 3. sunsetStart (bottom edge of sun touches horizon)
function get_solar_noon (lat, lng) {

	var times = SunCalc.getTimes(
		// new Date(find_sunniest_equinox(lat))
		new Date()
		, lat
		, lng)

	// get altitude at solar noon
	return SunCalc.getPosition(times.solarNoon, lat, lng)
		//convert radians to degrees
		.altitude * (180 / Math.PI)

	// return _.zipObject(
	// 	// keys
	// 	['sunriseEnd', 'solarNoon', 'sunriseStart']
	// 	// values
	// 	, _.map([times.sunriseEnd, times.solarNoon, times.sunsetStart], function (time) {

	// 	}))
}

// returns an object with x, y
// where x and y scale from 0 (min) to 100 (max)
function get_relative_position (x,y,max_x,max_y) {
	return {
		x: x.map(0, max_x, 0, 100)
		, y: y.map(0, max_y, 0, 100)
	}
}

Number.prototype.map = function ( in_min , in_max , out_min , out_max ) {
  return ( this - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min;
}

<commit_msg>fixed a bug in get_geo_coordinates_from_relative_coordinates in paralamptric_scripts.js. added some debug statements on lines 32, 38 and 43.<commit_after> var solar_tilt=45, lamp_height=12, lamp_width=3, draw_construction = false, sheet_thickness=1/12, sheet_width=192, sheet_height=384, sheets=[], sheet_count = 0;

 $(window).load(function(){
	var dial_width = Math.round($('#slider_container').width()*.5);
	$('#construction_button').click(function() {
		if ($(this).hasClass('active')){
			$(this).removeClass('active');
			draw_construction = false;
		} else {
			$(this).addClass('active');
			draw_construction = true;
		}
		updateScenes();
    });

	var map_width = $('#map').width()
    var map_height = $('#map').height()

	$('#map_pin').draggable({
		containment:'parent'
		, drag: function(event, ui){
			// calculate geographic coordinates from pointer position on map
            var offset = $(this).offset();
            var xPos = ui.position.left;
            var yPos = ui.position.top;
        	var relative_pos = get_relative_position(
	        		xPos
	        		, yPos
	        		, map_width-40
	        		, map_height-40)

        	console.log('relative pos of pointer in map', relative_pos)

        	var coors = get_geo_coordinates_from_relative_coordinates(
						relative_pos.x
						, relative_pos.y)

        	console.log('coordinates i think the pin is at:', coors)

        	// get solar noon from map angle
            solarNoon = get_solar_noon(coors.lat, coors.lng)

        	console.log('what angle i think solarNoon is', solarNoon)

            // naive assumption: set tilt to this angle (original range was -15 to 89)
            solar_tilt = solarNoon
            updateScenes()
        }
	})

	// $("#dial").knob({
	// 	'width':dial_width,
	// 	'height':dial_width,
	// 	fgColor:"orange",
	// 	min:-15,
	// 	max:89,
	// 	angleArc:104,
	// 	angleOffset:255,
	//       change: function(v) {
	// 		solar_tilt = v;
	// 		updateScenes();
	// 	}
	// });
    $("#slider").slider({
      orientation: "vertical",
      range: "min",
      min: 7,
      max: 18,
      value: lamp_height,
      slide: function(event,ui) {
		lamp_height = ui.value;
		updateScenes();
		$("#height_label").html(lamp_height + " ft");
      }
    });
	$("#width_slider").slider({
		range: "min",
		min: 12,
		max: 72,
		value: lamp_width*12,
		slide: function(event,ui){
			lamp_width = ui.value/12;
			updateScenes();
			$("#width_value").val(Math.floor(ui.value/12).toString()+"\' "+(ui.value%12).toString()+"\"");
		}
	});
	$("#sheet_thickness_slider").slider({
		range: "min",
		min: 1,
		max: 32,
		value: sheet_thickness*32*12, //convert from feet to 1/32"
		slide: function(event,ui){
			sheet_thickness = ui.value/32/12;
			updateScenes();
			$("#sheet_thickness_value").val(toFractionString32(ui.value)+"\"");
		}
	});
	$("#sheet_width_slider").slider({
		range: "min",
		min: 1,
		max: 576,
		value: sheet_width,
		slide: function(event,ui){
			sheet_width = ui.value;
			$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
		}
	});
	$("#sheet_height_slider").slider({
		range: "min",
		min: 1,
		max: 576,
		value: sheet_height,
		slide: function(event,ui){
			sheet_height = ui.value;
			$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
		}
	});
	$("#add_sheet_button").click(function(event){
		var sheet = {width:sheet_width/12, height:sheet_height/12, id:sheet_count};
		sheets.push(sheet);
		updateSheets();
		addSheet(sheet);
		updateScenes();
		sheet_count += 1;
	});
	$("#sheet_list").on('click','.close_button',function(){
		var index = this.getAttribute('data-index');
		var id = sheets[index].id;
		removeSheet(index);
		sheets.splice(index,1);
		updateSheets();
		updateScenes();
	});
	$("#download_button").click(function(event){
		createSVG();
	});
	$(".unclick").mouseup(function(event){
		$(this).blur();
	});
	$("#sheet_width_value").change(function(event){
		sheet_width = $(this).val();
		$("#sheet_width_slider").slider("value",$(this).val());
		$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
		$("#sheet_width_value").blur();
	});
	$("#sheet_height_value").change(function(event){
		sheet_height = $(this).val();
		$("#sheet_height_slider").slider("value",$(this).val());
		$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
		$("#sheet_height_value").blur();
	});
	drawElements();
	model_init();
	cut_init();
	animate();
	updateScenes();
});

window.addEventListener('resize',function(){
	drawElements();
});

function drawElements(){
	var dial_width = Math.round($('#slider_container').width()*.5);
	 // $('#dial').trigger(
        // 'configure',
        // {
            // 'width':dial_width
        // }
    // );
	$("#height_label").html(lamp_height + " ft");
	$("#slider").height((window.innerHeight-$('#construction_button').outerHeight(true)-$('#dial').siblings().outerHeight(true)-$('#height_label').outerHeight(true))*.2);
	$("#sheet_thickness_value").val(toFractionString32(sheet_thickness*12*32)+"\"");
	$("#width_value").val(Math.floor(lamp_width).toString()+"\' "+(lamp_width%1*12).toString()+"\"");
	$("#sheet_width_value").val(Math.floor(sheet_width/12).toString()+"\' "+(sheet_width%12).toString()+"\"");
	$("#sheet_height_value").val(Math.floor(sheet_height/12).toString()+"\' "+(sheet_height%12).toString()+"\"");
	$("#sheet_width_value").css('width',$("#sheet_width_value").parent().width()-$("#sheet_width_value").prev().width()-6);
	$("#sheet_height_value").css('width',$("#sheet_height_value").parent().width()-$("#sheet_height_value").prev().width()-6);
	$(".geometry_super_container").css('height',window.innerHeight);
	$("#download_button").css('position','absolute');
	$("#download_button").css('left',$("#cut_container").width()-$("#download_button").width()-40);
	$("#download_button").css('top',$("#cut_container").height());
}

function updateSheets(){
	$("#sheet_list").empty();
	for (var i = 0; i < sheets.length; i++){
		$("#sheet_list").append("
Sheet "+sheets[i].id+": "+Math.floor(sheets[i].width).toString()+"\' "+(sheets[i].width*12%12).toString()+"\" x "+Math.floor(sheets[i].height).toString()+"\' "+(sheets[i].height*12%12).toString()+"\"
");
	}
}

function toFractionString32(numerator){
	var whole = Math.floor(numerator/32);
	var top=numerator-whole*32, bottom=32;
	if (numerator%32 == 0){
		return whole.toString();
	} else if(numerator%16 == 0){
		top = numerator/16;
		bottom = 2;
	} else if (numerator%8 == 0){
		top = numerator/8;
		bottom = 4;
	} else if (numerator%4 == 0){
		top = numerator/4;
		bottom = 8;
	} else if (numerator%2 == 0){
		top = numerator/2;
		bottom = 16;
	}
	if (whole > 0){
		return whole.toString()+" "+top.toString()+"/"+bottom.toString();
	} else {
		return top.toString()+"/"+bottom.toString();
	}
}

//// -----
//// map pointer --> sun angles
//// -----

// takes x and y (which scale from 0 to 100)
// and returns geographic coordinates
function get_geo_coordinates_from_relative_coordinates (x,y) {
	// if y is > than 50
	// , give a southern hemisphere lat
	var lat = y.map(0,100,80,-80)
	var lng = x.map(0,100,-171,176)

	return {
		lat:lat
		, lng: lng
	}
}

// find the date of the sunniest equinox at given latitude
function find_sunniest_equinox (lat) {
	var d = new Date()
	// if southern altitude, sunnier equinox is march 20
	if (lat<0) return d.setMonth(2,20)
	// if northern, september 23
	return d.setMonth(8,23)
}

// returns an object with three sun angles
// 1. sunriseEnd (bottom edge of sun touches horizon)
// 2. solarNoon (sun's highest position)
// 3. sunsetStart (bottom edge of sun touches horizon)
function get_solar_noon (lat, lng) {

	var times = SunCalc.getTimes(
		// new Date(find_sunniest_equinox(lat))
		new Date()
		, lat
		, lng)

	// get altitude at solar noon
	return SunCalc.getPosition(times.solarNoon, lat, lng)
		//convert radians to degrees
		.altitude * (180 / Math.PI)

	// return _.zipObject(
	// 	// keys
	// 	['sunriseEnd', 'solarNoon', 'sunriseStart']
	// 	// values
	// 	, _.map([times.sunriseEnd, times.solarNoon, times.sunsetStart], function (time) {

	// 	}))
}

// returns an object with x, y
// where x and y scale from 0 (min) to 100 (max)
function get_relative_position (x,y,max_x,max_y) {
	return {
		x: x.map(0, max_x, 0, 100)
		, y: y.map(0, max_y, 0, 100)
	}
}

Number.prototype.map = function ( in_min , in_max , out_min , out_max ) {
  return ( this - in_min ) * ( out_max - out_min ) / ( in_max - in_min ) + out_min;
}

<|endoftext|>


<commit_before>var swig = require('swig');
var path = require('path');

var defaultPath = 'templates';

var loadedFilters;

module.exports = function(item, pipe, config, filters, callback){

  var templatesPath = config.templatesPath || defaultPath;

  if (!loadedFilters) {
    var x, filter;
    for (x in filters){
      filter = filters[x];
      filter.safe = true;
      swig.setFilter(x, filter);
    }
    loadedFilters = true;
  }

  var templates = {};

  var template = item.template || pipe.template;

  if (templates[template] == null) {
    templates[template] = swig.compileFile(path.join(config.projectDir, templatesPath, template + '.html'));
  }

  item.renderedContent = templates[template]({
    page: item,
    site: config
  });
  return callback(null, item);
};<commit_msg>allow to specify template path per pipe<commit_after>var swig = require('swig');
var path = require('path');

var defaultPath = 'templates';

var loadedFilters;

module.exports = function(item, pipe, config, filters, callback){

  var templatesPath = pipe.templatesPath || config.templatesPath || defaultPath;

  if (!loadedFilters) {
    var x, filter;
    for (x in filters){
      filter = filters[x];
      filter.safe = true;
      swig.setFilter(x, filter);
    }
    loadedFilters = true;
  }

  var templates = {};

  var template = item.template || pipe.template;

  if (templates[template] == null) {
    templates[template] = swig.compileFile(path.join(config.projectDir, templatesPath, template + '.html'));
  }

  item.renderedContent = templates[template]({
    page: item,
    site: config
  });
  return callback(null, item);
};<|endoftext|>