<commit_before>"use strict";

/**
 * Format of a todo:
 * * file:  String - Relative path
 * * sha:   String - Commit's sha
 * * line:  Number - File line number
 * * title: String - Issue's title
 * * label: String - Issue's label
 * * issue: Number - (optional) Issue's number
 **/

var path = require("path");
var fs = require("fs");
var async = require("async");
var _ = require("lodash");
var inquirer = require("inquirer");

var config = require("./config");
var github = require("./github");
var git = require("./git");

module.exports = {
  "fromDiff": fromDiff
};

var SKIP_FILE_NAME = ".github-todos-ignore";

function readIgnoreFile (cb) {
  git.dir(path.join("..", SKIP_FILE_NAME), function (err, skipFile) {
    if (err) {
      return cb(err);
    }

    fs.readFile(skipFile, {encoding: "utf8"}, function (err, content) {
      if (err && err.code === "ENOENT") {
        return cb(null, [], skipFile);
      } else if (err) {
        return cb(err);
      }

      cb(null, _.filter(_.invoke(content.split("\n"), "trim")), skipFile);
    });
  });
}

function shouldSkip (title, caseSensitive, cb) {
  title = String(title || "").trim();
  if (caseSensitive) {
    title = title.toLowerCase();
  }

  readIgnoreFile(function (err, ignores) {
    if (err) {
      return cb(err);
    }

    if (caseSensitive) {
      ignores = _.invoke(ignores, "toLowerCase");
    }

    cb(null, _.contains(ignores, title));
  });
}

function createOrCommentIssue (repo, todo, conf, cb) {
  if (todo.issue) {
    commentIssue(todo, conf, cb);
  } else {
    shouldSkip(todo.title, conf["case-sensitive"], function (err, skip) {
      if (err) {
        return cb(err);
      }

      if (skip) {
        return cb();
      }

      github.findIssueByTitle(repo, todo.title, function (err, issue) {
        if (err) {
          cb(err);
        } else if (issue) {

          todo.issue = issue.number;

          var ops = {
            "comment": _.partial(commentIssue, repo, todo, conf)
          };
          if (!_.contains(issue.labels, todo.label)) {
            ops.tag = _.partial(github.tagIssue, repo, todo.issue, todo.label);
          }

          async.parallel(ops, function (err, res) {
            if (err) {
              return cb(err);
            }

            cb(null, res.comment);
          });

        } else {
          createIssue(repo, todo, conf, cb);
        }
      });
    });
  }
}

// Add line to github-todos-ignore
function rememberSkip (title, cb) {
  readIgnoreFile(function (err, ignores, skipFile) {
    if (err) {
      return cb(err);
    }

    if (_.contains(ignores, title)) {
      return cb();
    }

    fs.writeFile(skipFile, ignores.concat([title]).join("\n"), {encoding: "utf8"}, cb);
  });
}

function createIssue (repo, todo, conf, cb) {
  if (conf["confirm-create"]) {
    inquirer.prompt([{
      "type": "expand",
      "message": "Create new issue \"" + todo.title + "\"",
      "name": "choice",
      "choices": [
        {"key": "y", "name": "Create issue", "value": "create"},
        {"key": "n", "name": "Do not create issue", "value": "skip"},
        {"key": "r", "name": "Do not create issue and remember for next times", "value": "skip_and_remember"},
        {"key": "q", "name": "Abort", "value": "abort"}
      ],
      "default": 0
    }], function (answers) {
      switch (answers.choice) {
        case "create":
          create();
          break;
        case "skip_and_remember":
          rememberSkip(todo.title, function (err) {
            if (err) {
              console.error("[Github-Todos] Failed adding info to '" + SKIP_FILE_NAME + "'");
            }
            cb();
          });
          break;
        case "abort":
          cb(new Error("User aborted"));
          break;
        case "skip":
        default:
          cb();
          break;
      }
    });
  } else {
    create();
  }

  function create () {
    getCommentText(repo, todo, conf, function (err, text) {
      if (err) {
        return cb(err);
      }

      github.createIssue(repo, todo.title, text, [todo.label], cb);
    });
  }
}

function commentIssue (repo, todo, conf, cb) {
  getCommentText(repo, todo, conf, function (err, text) {
    if (err) {
      return cb(err);
    }

    github.commentIssue(repo, todo.issue, text, cb);
  });
}

function getCommentText (repo, todo, conf, cb) {
  var text = "";

  // Link to file
  text += "Ref. [" + todo.file + ":" + todo.line + "](" + github.getFileUrl(repo, todo.file, todo.sha, todo.line) + ")";

  // Add code information
  git.dir(path.join("..", todo.file), function (err, file) {
    if (err) {
      return cb(err);
    }

    fs.readFile(file, {encoding: "utf8"}, function (err, content) {
      if (err) {
        return cb(err);
      }

      var lines = content.split(/\r\n|\r|\n/);

      // Remove trailing new lines
      while (lines[lines.length - 1] === "") {
        lines.pop();
      }
      while (lines[0] === "") {
        lines.shift();
      }

      if (conf.context > 0) {
        // Extract: line to line + conf.context
        var extract = lines.slice(todo.line - 1, todo.line + conf.context).join("\n");
        if (todo.line + conf.context < lines.length) {
          extract += "\n…";
        }

        // TODO syntax highlighting?
        text += "\n\n```\n" + extract + "\n```\n";
      }

      if (conf.signature) {
        text += "\n" + conf.signature;
      }

      cb(null, text);
    });
  });
}

function fromDiff (repo, diff, sha, conf, cb) {
  if (_.isFunction(conf)) {
    cb = conf;
    conf = null;
  }
  conf = _.merge({
    "onProgress": _.noop
  }, conf || {}, config.defaults);

  var todos = _.flatten(_.map(diff, function (file) {
    var addedLines = _.filter(file.lines, "add");
    var lineTodos = _.map(addedLines, lineToTodoMapper(file.to, sha, conf));
    // keep only those with required field
    return _.filter(lineTodos, "title");
  }));

  async.mapSeries(todos, function (todo, cb) {
    createOrCommentIssue(repo, todo, conf, function (err, result) {
      conf.onProgress(err, result, todo);
      cb(err, result);
    });
  }, function (err, results) {
    cb(err, results, todos);
  });
}

// String, Sha → String → {file, sha, line, title, label}
function lineToTodoMapper (filename, sha, conf) {
  return function lineToTodo (line) {
    return _.merge({
      "file":   filename,
      "sha":    sha,
      "line":   line.ln
    }, extractTodoTitle(line.content, conf));
  };
}

// String → {title, label}
function extractTodoTitle (content, conf) {
  var result = null;

  var labels = {};
  _.each(conf, function (value, key) {
    if (value && key.match(/^label\./)) {
      var trigger = key.substring(6);
      if (conf["label-whitespace"]) {
        trigger += " ";
      }
      labels[trigger] = value;
    }
  });

  if (_.isString(content)) {
    _.find(Object.keys(labels), function (trigger) {
      var index;
      if (conf["case-sensitive"]) {
        index = content.indexOf(trigger);
      } else {
        index = content.toUpperCase().indexOf(trigger.toUpperCase());
      }

      if (index !== -1) {
        var title = content.substring(index + trigger.length).trim();
        var issue = null;
        if (title && !isCode(title)) {
          var match = title.match(/^\s+#(\d+)\s+/);
          if (match) {
            issue = match[1];
            title = title.substring(match[0].length);
          }
          result = {
            "title":  title,
            "label":  labels[trigger],
            "issue":  Number(issue)
          };
        }
        return true; // break
      }
    });
  }

  return result;
}

// TODO Better heuristic for code vs words detection

// Simple heuristic to detect if a title is really a title or some valid code
// String → Boolean
function isCode (string) {
  // If symbols are more than 20% of the code, it may be code more than human text
  var symbols = _.filter(string, isSymbol);

  return symbols.length / string.length > 0.20;
}

var RE_SYMBOL = /[^\sa-z0-9\u00E0-\u00FC]/i;
// Matches a symbol: non alphanumeric character
// Character → Boolean
function isSymbol (character) {
  return RE_SYMBOL.test(character);
}
<commit_msg>Show file & line in prompt<commit_after>"use strict";

/**
 * Format of a todo:
 * * file:  String - Relative path
 * * sha:   String - Commit's sha
 * * line:  Number - File line number
 * * title: String - Issue's title
 * * label: String - Issue's label
 * * issue: Number - (optional) Issue's number
 **/

var path = require("path");
var fs = require("fs");
var async = require("async");
var _ = require("lodash");
var inquirer = require("inquirer");

var config = require("./config");
var github = require("./github");
var git = require("./git");

module.exports = {
  "fromDiff": fromDiff
};

var SKIP_FILE_NAME = ".github-todos-ignore";

function readIgnoreFile (cb) {
  git.dir(path.join("..", SKIP_FILE_NAME), function (err, skipFile) {
    if (err) {
      return cb(err);
    }

    fs.readFile(skipFile, {encoding: "utf8"}, function (err, content) {
      if (err && err.code === "ENOENT") {
        return cb(null, [], skipFile);
      } else if (err) {
        return cb(err);
      }

      cb(null, _.filter(_.invoke(content.split("\n"), "trim")), skipFile);
    });
  });
}

function shouldSkip (title, caseSensitive, cb) {
  title = String(title || "").trim();
  if (caseSensitive) {
    title = title.toLowerCase();
  }

  readIgnoreFile(function (err, ignores) {
    if (err) {
      return cb(err);
    }

    if (caseSensitive) {
      ignores = _.invoke(ignores, "toLowerCase");
    }

    cb(null, _.contains(ignores, title));
  });
}

function createOrCommentIssue (repo, todo, conf, cb) {
  if (todo.issue) {
    commentIssue(todo, conf, cb);
  } else {
    shouldSkip(todo.title, conf["case-sensitive"], function (err, skip) {
      if (err) {
        return cb(err);
      }

      if (skip) {
        return cb();
      }

      github.findIssueByTitle(repo, todo.title, function (err, issue) {
        if (err) {
          cb(err);
        } else if (issue) {

          todo.issue = issue.number;

          var ops = {
            "comment": _.partial(commentIssue, repo, todo, conf)
          };
          if (!_.contains(issue.labels, todo.label)) {
            ops.tag = _.partial(github.tagIssue, repo, todo.issue, todo.label);
          }

          async.parallel(ops, function (err, res) {
            if (err) {
              return cb(err);
            }

            cb(null, res.comment);
          });

        } else {
          createIssue(repo, todo, conf, cb);
        }
      });
    });
  }
}

// Add line to github-todos-ignore
function rememberSkip (title, cb) {
  readIgnoreFile(function (err, ignores, skipFile) {
    if (err) {
      return cb(err);
    }

    if (_.contains(ignores, title)) {
      return cb();
    }

    fs.writeFile(skipFile, ignores.concat([title]).join("\n"), {encoding: "utf8"}, cb);
  });
}

function createIssue (repo, todo, conf, cb) {
  if (conf["confirm-create"]) {
    inquirer.prompt([{
      "type": "expand",
      "message": "Create new issue \"" + todo.title + "\" (" + todo.file + ":" + todo.line + ")",
      "name": "choice",
      "choices": [
        {"key": "y", "name": "Create issue", "value": "create"},
        {"key": "n", "name": "Do not create issue", "value": "skip"},
        {"key": "r", "name": "Do not create issue and remember for next times", "value": "skip_and_remember"},
        {"key": "q", "name": "Abort", "value": "abort"}
      ],
      "default": 0
    }], function (answers) {
      switch (answers.choice) {
        case "create":
          create();
          break;
        case "skip_and_remember":
          rememberSkip(todo.title, function (err) {
            if (err) {
              console.error("[Github-Todos] Failed adding info to '" + SKIP_FILE_NAME + "'");
            }
            cb();
          });
          break;
        case "abort":
          cb(new Error("User aborted"));
          break;
        case "skip":
        default:
          cb();
          break;
      }
    });
  } else {
    create();
  }

  function create () {
    getCommentText(repo, todo, conf, function (err, text) {
      if (err) {
        return cb(err);
      }

      github.createIssue(repo, todo.title, text, [todo.label], cb);
    });
  }
}

function commentIssue (repo, todo, conf, cb) {
  getCommentText(repo, todo, conf, function (err, text) {
    if (err) {
      return cb(err);
    }

    github.commentIssue(repo, todo.issue, text, cb);
  });
}

function getCommentText (repo, todo, conf, cb) {
  var text = "";

  // Link to file
  text += "Ref. [" + todo.file + ":" + todo.line + "](" + github.getFileUrl(repo, todo.file, todo.sha, todo.line) + ")";

  // Add code information
  git.dir(path.join("..", todo.file), function (err, file) {
    if (err) {
      return cb(err);
    }

    fs.readFile(file, {encoding: "utf8"}, function (err, content) {
      if (err) {
        return cb(err);
      }

      var lines = content.split(/\r\n|\r|\n/);

      // Remove trailing new lines
      while (lines[lines.length - 1] === "") {
        lines.pop();
      }
      while (lines[0] === "") {
        lines.shift();
      }

      if (conf.context > 0) {
        // Extract: line to line + conf.context
        var extract = lines.slice(todo.line - 1, todo.line + conf.context).join("\n");
        if (todo.line + conf.context < lines.length) {
          extract += "\n…";
        }

        // TODO syntax highlighting?
        text += "\n\n```\n" + extract + "\n```\n";
      }

      if (conf.signature) {
        text += "\n" + conf.signature;
      }

      cb(null, text);
    });
  });
}

function fromDiff (repo, diff, sha, conf, cb) {
  if (_.isFunction(conf)) {
    cb = conf;
    conf = null;
  }
  conf = _.merge({
    "onProgress": _.noop
  }, conf || {}, config.defaults);

  var todos = _.flatten(_.map(diff, function (file) {
    var addedLines = _.filter(file.lines, "add");
    var lineTodos = _.map(addedLines, lineToTodoMapper(file.to, sha, conf));
    // keep only those with required field
    return _.filter(lineTodos, "title");
  }));

  async.mapSeries(todos, function (todo, cb) {
    createOrCommentIssue(repo, todo, conf, function (err, result) {
      conf.onProgress(err, result, todo);
      cb(err, result);
    });
  }, function (err, results) {
    cb(err, results, todos);
  });
}

// String, Sha → String → {file, sha, line, title, label}
function lineToTodoMapper (filename, sha, conf) {
  return function lineToTodo (line) {
    return _.merge({
      "file":   filename,
      "sha":    sha,
      "line":   line.ln
    }, extractTodoTitle(line.content, conf));
  };
}

// String → {title, label}
function extractTodoTitle (content, conf) {
  var result = null;

  var labels = {};
  _.each(conf, function (value, key) {
    if (value && key.match(/^label\./)) {
      var trigger = key.substring(6);
      if (conf["label-whitespace"]) {
        trigger += " ";
      }
      labels[trigger] = value;
    }
  });

  if (_.isString(content)) {
    _.find(Object.keys(labels), function (trigger) {
      var index;
      if (conf["case-sensitive"]) {
        index = content.indexOf(trigger);
      } else {
        index = content.toUpperCase().indexOf(trigger.toUpperCase());
      }

      if (index !== -1) {
        var title = content.substring(index + trigger.length).trim();
        var issue = null;
        if (title && !isCode(title)) {
          var match = title.match(/^\s+#(\d+)\s+/);
          if (match) {
            issue = match[1];
            title = title.substring(match[0].length);
          }
          result = {
            "title":  title,
            "label":  labels[trigger],
            "issue":  Number(issue)
          };
        }
        return true; // break
      }
    });
  }

  return result;
}

// TODO Better heuristic for code vs words detection

// Simple heuristic to detect if a title is really a title or some valid code
// String → Boolean
function isCode (string) {
  // If symbols are more than 20% of the code, it may be code more than human text
  var symbols = _.filter(string, isSymbol);

  return symbols.length / string.length > 0.20;
}

var RE_SYMBOL = /[^\sa-z0-9\u00E0-\u00FC]/i;
// Matches a symbol: non alphanumeric character
// Character → Boolean
function isSymbol (character) {
  return RE_SYMBOL.test(character);
}
<|endoftext|>


<commit_before>/* Class: T
 * Main tylus namespace.
 */
var T = (function () {
   var exports = {},
       styles = {},
       device = Ti.Platform.osname,
       ctorMap,
       debugMode = false;

   // map nice names to ugly titanium factory names
   ctorMap = {
      Matrix2D    : 'create2DMatrix',
      Matrix3D    : 'create3DMatrix',
      Indicator   : 'createActivityIndicator',
      AlertDialog : 'createAlertDialog',
      Animation   : 'createAnimation',
      Button      : 'createButton',
      ButtonBar   : 'createButtonBar',
      CoverFlow   : 'createCoverFlow',
      DashItem    : 'createDashboardItem',
      Dash        : 'createDashboardView',
      EmailDialog : 'createEmailDialog',
      Image       : 'createImageView',
      OptionDialog: 'createOptionDialog',
      Label       : 'createLabel',
      Picker      : 'createPicker',
      PickerRow   : 'createPickerRow',
      PickerCol   : 'createPickerCol',
      ProgressBar : 'createProgressBar',
      Scroll      : 'createScrollView',
      Scrollable  : 'createScrollableView',
      SearchBar   : 'createSearchBar',
      Slider      : 'createSlider',
      Switch      : 'createSwitch',
      Tab         : 'createTab',
      TabGroup    : 'createTabGroup',
      TabbedBar   : 'createTabbedBar',
      Table       : 'createTableView',
      TableRow    : 'createTableViewRow',
      TableSection: 'createTableViewSection',
      TextArea    : 'createTextArea',
      TextField   : 'createTextField',
      Toolbar     : 'createToolbar',
      View        : 'createView',
      Web         : 'createWebView',
      Window      : 'createWindow'
   };

   /* Function: combineSelectors
    * Combines selectors into a string to use for searching with getProps
    *
    * Parameters:
    *    type     - *string* type of the item, such as Label, DashItem, Window,
    *               etc
    *    args     - *object* with id, className, classes, or classNames
    *               properties which will be used to resolve to styles
    *
    * Returns:
    *    *array* of selectors to be searched in the style tree
    */
   function combineSelectors (type, args) {
      var out = [], c;
      out.push(type);
      if (args.id) {
         out.push('#' + args.id);
      }
      if (args.className) {
         out.push('.' + args.className);
      }
      if (c = args.classes || args.classNames) {
         c.forEach(function (value) {
            out.push('.' + value);
         });
      }
      return out;
   }

   /* Function: extend
    * Extends an object with the properties of another.
    *
    * Parameters:
    *    to    - *object* to put properties into
    *    from  - *object* to take properties from
    */

   function extend (to, from) {
      var item;
      for (item in from) {
         if (from.hasOwnProperty(item)) {
            if (typeof from[item] === 'object' && from[item] !== null) {
               to[item] = to[item] || {};
               extend(to[item], from[item]); // recurse
            } else {
               to[item] = from[item];
            }
         }
      }
   }

   /* Function: weakExtend
    * Extends an object with the properties of another.  Does not recurse,
    * which is important because recursing into Titanium proxies will break
    * those proxies.
    *
    * Parameters:
    *    to    - *object* to put properties into
    *    from  - *object* to take properties from
    */

   function weakExtend (to, from) {
      var item;
      for (item in from) {
         if (from.hasOwnProperty(item)) {
            to[item] = from[item];
         }
      }
   }

   /* Function: getProps
    * Gets the properties for a given type and argument string.  If args.style
    * is specified, uses that for the search (should look like
    * '#id .class1 .class2'.  If args.style is undefined, uses combineSelectors
    * to find a list of selectors to use.  Then, searches the tree for styles
    * and returns them.
    *
    * Parameters:
    *    type     - *string* type of the item, such as Label, DashItem, Window
    *    args     - *string* args passed to factory, should have either style
    *               property or id/classes/classNames/className for style
    *               retreival.
    *
    * Returns:
    *    *object* properties to be added into the new object.
    */

   function getProps (type, args) {
      var selectors, out = {}, i, j, curr, style, selStr, styleVal;
      if (typeof args.style === 'string') {
         selectors = args.style.split(' ');
         selectors.unshift(type);
      } else {
         selectors = combineSelectors(type, args);
      }

      selStr = selectors.join('|');
      getProps.memo = getProps.memo || {};
      if (getProps.memo[selStr]) {
         return getProps.memo[selStr];
      }

      for (i = 0; i < selectors.length; i++) {
         j = i;
         curr = selectors[i];
         style = styles;
         while (style !== undefined) {
            if (style.self !== undefined) {
               extend(out, style.self);
            }
            if (style.device && style.device[device]) {
               if (style.device[device].self) {
                  extend(out, style.device[device].self);
               }
               // if we just hit a device conditional, we want to continue
               // walking down the tree, but we don't want to advance the
               // selector yet since a device conditional isn't matched by a
               // selector but is instead matched by the system itself.
               style = style.device[device];
               continue;
            }
            // walk down the tree
            style = style[curr];
            // move to the next selector
            j += 1;
            curr = selectors[j];
         }
      }

      for (style in out) {
         if (out.hasOwnProperty(style) && typeof style == 'string') {
            styleVal = out[style];
            if (styleVal.charAt &&
                styleVal.charAt(0) == '`' &&
                styleVal.charAt(styleVal.length-1) == '`') {
               styleVal = styleVal.slice(1, styleVal.length - 1);
                out[style] = eval(styleVal);
            }
         }
      }

      if (out.text) {
         out.text = out.text.replace("
", "\n");
      }

      getProps.memo[selStr] = out;
      return out;
   }

   /* Function: load
    * Loads styles out of a .js file via commonjs require
    *
    * Parameters:
    *    name     - *string* name of the .js file
    */

   function load (name) {
      styles = require(name).styles;
   }

   /* Function: makeCtor
    * Makes a functional constructor for a Titanium object, getting properties
    * first from the style information
    *
    * Parameters:
    *    name     - *string* nice name of the Titanium object
    *
    * Returns:
    *    *function (args)* factory method which tries to get properties first
    */

   function makeCtor (name) {
      return function (args) {
         var ret, computedStyle, newStyle;
         args = args || {};
         computedStyle = getProps(name, args);
         newStyle = {};

         // copy styles from the computed styles first, then from the args
         // this way, computed styles are the 'defaults' and can be
         // overwritten by arguments

         // we can't just extend style with args because that would
         // overwrite the computed style in the memo of the getProps function

         // we have to use weakExtend on Titanium objects because recursing
         // into them will likely break them.

         extend(newStyle, computedStyle);

         // for now, we do this so that it does not conflict with any
         // properties that happened to be called 'style'
         args.styl = args.style;
         delete args.style;

         weakExtend(newStyle, args);

         // actually construct the damn thing
         ret = Ti.UI[ctorMap[name]](newStyle);

         // wrap .on
         ret.on = function (event, handler) {
            var wrapper = function (e) {
               try {
                  handler(e);
               } catch (ex) {
                  if (debugMode) {
                     alert(JSON.stringify(ex, null, 3));
                  }
               }
            };
            ret.addEventListener(event, wrapper);
         };

         // ret.fire = ret.removeEventListner doesnt work, I've tried it
         ret.fire = function (name, data) {
            ret.fireEvent(name, data);
         };
         ret.off = function (name, handler) {
            ret.removeEventListener(name, handler);
         };
         return ret;
      };
   }

   /* Function: apply
    * Applies style information into obj.
    *
    * Parameters:
    *    obj      - *object* to add properties into
    *    type     - *string* type of ti object, like Label
    *    style    - *object* describing styles to be inserted.  Should look
    *               like {style: '#id .class1'} or {id: 'someid', className:
    *               'class1'}.
    */
   function apply (obj, type, style) {
      style = getProps(type, style);
      extend(obj, style);
   }

   function setDebugMode (value) {
      value = value || true;
      debugMode = value;
   }

   var name;
   for (name in ctorMap) if (ctorMap.hasOwnProperty(name)) {
      exports[name] = makeCtor(name);
   }

   exports.apply = apply;
   exports.load = load;
   exports.getProps = getProps;
   exports.setDebugMode = setDebugMode;

   try {
      styles = require('style').styles;
   } catch(e) {
      // do nothing; the user must have his own plan
   }

   return exports;
}());

<commit_msg>changing tylus to use the 'tyle' property<commit_after>/* Class: T
 * Main tylus namespace.
 */
var T = (function () {
   var exports = {},
       styles = {},
       device = Ti.Platform.osname,
       ctorMap,
       debugMode = false;

   // map nice names to ugly titanium factory names
   ctorMap = {
      Matrix2D    : 'create2DMatrix',
      Matrix3D    : 'create3DMatrix',
      Indicator   : 'createActivityIndicator',
      AlertDialog : 'createAlertDialog',
      Animation   : 'createAnimation',
      Button      : 'createButton',
      ButtonBar   : 'createButtonBar',
      CoverFlow   : 'createCoverFlow',
      DashItem    : 'createDashboardItem',
      Dash        : 'createDashboardView',
      EmailDialog : 'createEmailDialog',
      Image       : 'createImageView',
      OptionDialog: 'createOptionDialog',
      Label       : 'createLabel',
      Picker      : 'createPicker',
      PickerRow   : 'createPickerRow',
      PickerCol   : 'createPickerCol',
      ProgressBar : 'createProgressBar',
      Scroll      : 'createScrollView',
      Scrollable  : 'createScrollableView',
      SearchBar   : 'createSearchBar',
      Slider      : 'createSlider',
      Switch      : 'createSwitch',
      Tab         : 'createTab',
      TabGroup    : 'createTabGroup',
      TabbedBar   : 'createTabbedBar',
      Table       : 'createTableView',
      TableRow    : 'createTableViewRow',
      TableSection: 'createTableViewSection',
      TextArea    : 'createTextArea',
      TextField   : 'createTextField',
      Toolbar     : 'createToolbar',
      View        : 'createView',
      Web         : 'createWebView',
      Window      : 'createWindow'
   };

   /* Function: combineSelectors
    * Combines selectors into a string to use for searching with getProps
    *
    * Parameters:
    *    type     - *string* type of the item, such as Label, DashItem, Window,
    *               etc
    *    args     - *object* with id, className, classes, or classNames
    *               properties which will be used to resolve to styles
    *
    * Returns:
    *    *array* of selectors to be searched in the style tree
    */
   function combineSelectors (type, args) {
      var out = [], c;
      out.push(type);
      if (args.id) {
         out.push('#' + args.id);
      }
      if (args.className) {
         out.push('.' + args.className);
      }
      if (c = args.classes || args.classNames) {
         c.forEach(function (value) {
            out.push('.' + value);
         });
      }
      return out;
   }

   /* Function: extend
    * Extends an object with the properties of another.
    *
    * Parameters:
    *    to    - *object* to put properties into
    *    from  - *object* to take properties from
    */

   function extend (to, from) {
      var item;
      for (item in from) {
         if (from.hasOwnProperty(item)) {
            if (typeof from[item] === 'object' && from[item] !== null) {
               to[item] = to[item] || {};
               extend(to[item], from[item]); // recurse
            } else {
               to[item] = from[item];
            }
         }
      }
   }

   /* Function: weakExtend
    * Extends an object with the properties of another.  Does not recurse,
    * which is important because recursing into Titanium proxies will break
    * those proxies.
    *
    * Parameters:
    *    to    - *object* to put properties into
    *    from  - *object* to take properties from
    */

   function weakExtend (to, from) {
      var item;
      for (item in from) {
         if (from.hasOwnProperty(item)) {
            to[item] = from[item];
         }
      }
   }

   /* Function: getProps
    * Gets the properties for a given type and argument string.  If args.style
    * is specified, uses that for the search (should look like
    * '#id .class1 .class2'.  If args.style is undefined, uses combineSelectors
    * to find a list of selectors to use.  Then, searches the tree for styles
    * and returns them.
    *
    * Parameters:
    *    type     - *string* type of the item, such as Label, DashItem, Window
    *    args     - *string* args passed to factory, should have either style
    *               property or id/classes/classNames/className for style
    *               retreival.
    *
    * Returns:
    *    *object* properties to be added into the new object.
    */

   function getProps (type, args) {
      var selectors, out = {}, i, j, curr, style, selStr, styleVal;
      if (typeof args.tyle === 'string') {
         selectors = args.tyle.split(' ');
         selectors.unshift(type);
      } else {
         selectors = combineSelectors(type, args);
      }

      selStr = selectors.join('|');
      getProps.memo = getProps.memo || {};
      if (getProps.memo[selStr]) {
         return getProps.memo[selStr];
      }

      for (i = 0; i < selectors.length; i++) {
         j = i;
         curr = selectors[i];
         style = styles;
         while (style !== undefined) {
            if (style.self !== undefined) {
               extend(out, style.self);
            }
            if (style.device && style.device[device]) {
               if (style.device[device].self) {
                  extend(out, style.device[device].self);
               }
               // if we just hit a device conditional, we want to continue
               // walking down the tree, but we don't want to advance the
               // selector yet since a device conditional isn't matched by a
               // selector but is instead matched by the system itself.
               style = style.device[device];
               continue;
            }
            // walk down the tree
            style = style[curr];
            // move to the next selector
            j += 1;
            curr = selectors[j];
         }
      }

      for (style in out) {
         if (out.hasOwnProperty(style) && typeof style == 'string') {
            styleVal = out[style];
            if (styleVal.charAt &&
                styleVal.charAt(0) == '`' &&
                styleVal.charAt(styleVal.length-1) == '`') {
               styleVal = styleVal.slice(1, styleVal.length - 1);
                out[style] = eval(styleVal);
            }
         }
      }

      if (out.text) {
         out.text = out.text.replace("
", "\n");
      }

      getProps.memo[selStr] = out;
      return out;
   }

   /* Function: load
    * Loads styles out of a .js file via commonjs require
    *
    * Parameters:
    *    name     - *string* name of the .js file
    */

   function load (name) {
      styles = require(name).styles;
   }

   /* Function: makeCtor
    * Makes a functional constructor for a Titanium object, getting properties
    * first from the style information
    *
    * Parameters:
    *    name     - *string* nice name of the Titanium object
    *
    * Returns:
    *    *function (args)* factory method which tries to get properties first
    */

   function makeCtor (name) {
      return function (args) {
         var ret, computedStyle, newStyle;
         args = args || {};
         computedStyle = getProps(name, args);
         newStyle = {};

         // copy styles from the computed styles first, then from the args
         // this way, computed styles are the 'defaults' and can be
         // overwritten by arguments

         // we can't just extend style with args because that would
         // overwrite the computed style in the memo of the getProps function

         // we have to use weakExtend on Titanium objects because recursing
         // into them will likely break them.

         extend(newStyle, computedStyle);

         weakExtend(newStyle, args);

         // actually construct the damn thing
         ret = Ti.UI[ctorMap[name]](newStyle);

         // wrap .on
         ret.on = function (event, handler) {
            var wrapper = function (e) {
               try {
                  handler(e);
               } catch (ex) {
                  if (debugMode) {
                     alert(JSON.stringify(ex, null, 3));
                  }
               }
            };
            ret.addEventListener(event, wrapper);
         };

         // ret.fire = ret.removeEventListner doesnt work, I've tried it
         ret.fire = function (name, data) {
            ret.fireEvent(name, data);
         };
         ret.off = function (name, handler) {
            ret.removeEventListener(name, handler);
         };
         return ret;
      };
   }

   /* Function: apply
    * Applies style information into obj.
    *
    * Parameters:
    *    obj      - *object* to add properties into
    *    type     - *string* type of ti object, like Label
    *    style    - *object* describing styles to be inserted.  Should look
    *               like {style: '#id .class1'} or {id: 'someid', className:
    *               'class1'}.
    */
   function apply (obj, type, style) {
      style = getProps(type, style);
      extend(obj, style);
   }

   function setDebugMode (value) {
      value = value || true;
      debugMode = value;
   }

   var name;
   for (name in ctorMap) if (ctorMap.hasOwnProperty(name)) {
      exports[name] = makeCtor(name);
   }

   exports.apply = apply;
   exports.load = load;
   exports.getProps = getProps;
   exports.setDebugMode = setDebugMode;

   try {
      styles = require('style').styles;
   } catch(e) {
      // do nothing; the user must have his own plan
   }

   return exports;
}());

<|endoftext|>


<commit_before>
function langFilter(str){
	return /[><]/.test(str);
}
var UnifiedSQL = function(databasePath) {
	var sqlite3 = require('sqlite3').verbose();
  	var db;

	return {
		setDb: function(newPath){
			databasePath = newPath
		},
		linkDb: function(){
			db = new sqlite3.Database(databasePath);
		},
		closeDb: function(){
			db.close();
		},
		findAll: function(){

		},
		findOne: function(con){
			var tableName = con.table;	// "tableName"
			var fieldName = con.fields.join(',');	//["id", "name"]
			var sqlang = "SELECT " + fieldName + " FROM " + tableName + " WHERE 1 AND";
			var conPart = "";
			for(var keyWords in con){
				if(keyWords == "conditions"){
					conPart = " (";
				}else if (keyWords == "OR"){
					conPart = " OR ( ";
				}else{
					continue;
				}
				var count = 0;
				for(var field in con[keyWords]){
					count ++;
					var nameField = field;
					if(!langFilter(field)){
						nameField += "=";
					}
					if(count > 1){
						conPart += " AND ";
					}
					conPart = conPart + nameField + "'" + con[keyWords][field] + "'";
				}
				conPart = conPart + ") ";
			}
			sqlang = sqlang + conPart;
			console.log(sqlang);
			db.get(sqlang, function(err, row){
				console.log(row);
			});
		}
	}
}

var findChar = UnifiedSQL('Chinese_charater.db');
findChar.linkDb();
var con = {
	"table": "chaizi",
	"fields": ["id", "chaifen"],
	"conditions": {
		"id": 1,
		"chaifen":"口阿"
	}
}
findChar.findOne(con);
findChar.closeDb();<commit_msg>fix sql lang template<commit_after>
function langFilter(str){
	return /[><]/.test(str);
}
var UnifiedSQL = function(databasePath) {
	var sqlite3 = require('sqlite3').verbose();
  	var db;

	var andSql = function(key, objAnd){
		var nameField = key;
		if(!langFilter(nameField)){
			nameField += "=";
		}
		return nameField + "'" + objAnd.key + "'" ;
	};
	var orSql = function(objOr){
		var orPArt = "(";
		for(var key in objOr){
			if(key == "OR"){
				orSql(objOr.key);
			}else{
				var nameField = key;
				if(!langFilter(nameField)){
					nameField += "=";
				}
				orPArt = orPArt + nameField + "'" + objOr.key + "'" + " OR ";
			}
		}
		return orPArt + "0)";
	}
	var connectLang = function(conditions){
		var lang = "";
		for(var key in conditions){
			if(key == "OR"){
				lang += orSql(conditions.key) + " AND ";
			}else{
				lang += andSql(key, conditions) + " AND ";
			}
		}
		return lang + "1"
	}
	var preSql = function(){
		var tableName = con.table;	// "tableName"
		var fieldName = con.fields.join(',');	//["id", "name"]
		var sqlang = "SELECT " + fieldName + " FROM " + tableName + " WHERE 1 ";
		return sqlang;
	}

	return {
		setDb: function(newPath){
			databasePath = newPath
		},
		linkDb: function(){
			db = new sqlite3.Database(databasePath);
		},
		closeDb: function(){
			db.close();
		},
		findAll: function(){

		},
		findOne: function(con){
			// for(var key in con.conditions){
			// 	if(key != "OR"){
			// 		conPart += andSql(key, con.conditions);
			// 	}else if (key == "OR"){
			// 		var orConditions = con.conditions['OR'];
			// 		conPart += orSql(orConditions);
			// 	}

			// }
			// sqlang = sqlang + conPart;
			// console.log(sqlang);
			// db.get(sqlang, function(err, row){
			// 	console.log(row);
			// });
		}
	}
}

var findChar = UnifiedSQL('Chinese_charater.db');
findChar.linkDb();
var con = {
	"table": "chaizi",
	"fields": ["id", "chaifen"],
	"conditions": {
		"id": 1,
		"chaifen":"口阿"
		"OR":{

		}
	}
}
findChar.findOne(con);
findChar.closeDb();<|endoftext|>


<commit_before>/* wheatonDice is an opensource project to study the legendary dice curse of Wil Wheaton
 * and get to the bottom of bad juju. At the moment, it's just a dice roller, but later
 * functions will include a rolls database that can track any sinking feeling that the kind of
 * cheetos you're eating is influencing your rolls. Also v.1.0 is aiming at 5th edition D&D rules
 * usage. Later versions will be more system agnostic. This is intended for mobile release, but
 * for now, this is just the engine. Minus the actual engine. Broad strokes to get the system
 * working. Much to be built! -Ian
 */

//public variables
var displaySides = 0,
    displayNumDc = 0,
    displayRolls = [],
    displayTotal = 0;

/*the physics engine (to be built: a different abstract function than builtin Math.random().
It will access the device's accelerometer and add physics + chaos to generate the roll.
it'll be totally sweet!*/
function physics(diceNumber, diceSides) {

    var rolls = [];

    displaySides = diceSides;
    displayNumDc = diceNumber;

    //warning! no edge case protection! You gotta use it just right! Todo: handle edge cases

    //rolls each die separately then pushes to "rolls" array.
    for (var i = 0; i < diceNumber; i++) {
	    rolls.push(Math.ceil( Math.random() * diceSides ));
    }

    displayRolls = rolls;

    //folds the array into a neat total
    var rollTotal = rolls.reduce(function(a ,b) {
	return a + b;
    });

    displayTotal = rollTotal;
    return rollOut(displayNumDc, displaySides, displayRolls, displayTotal);
}

//Separate output function, so that code is easy to delete.
//make it so every lowest possible roll that's rolled displays "Wheaton!"
    //instead of the number
//comment out any portion if you don't want Wil distracting you so much.
//uncomment Percy if you're an optimist.
function rollOut(diceNumber, diceSides, rolls, rollTotal) {
    if (rollTotal === diceNumber) {
	rollTotal = "Wheaton!";
    } else if (rollTotal < (0.33 * (diceSides * diceNumber))){
	rollTotal += " lesser Wheaton.";
    } /*else if (rollTotal === (diceSides * diceNumber)) {
	rollTotal = "Percy!";
    } else if (rollTotal > (0.66 * (diceSides * diceNumber))) {
	rollTotal += " lesser Percy.";
    }*/

    //returns a neat string that shows what you rolled, how each roll went, and the total.
    //This will probably be numberfied and separated out when the actual UI gets built
    if (diceNumber === 1) {
	return ("1 d" + diceSides + ": " + rollTotal);
    } else {
	return (diceNumber + " d" + diceSides + ": " + rolls.join(", ") + " Total: " + rollTotal);}
}

//input functions.
//uses public variables to access physics() function.
function roll(diceNumber, diceSides) {
    return physics(diceNumber, diceSides);
}

/*Mixed dice rolls. There are 6 Standard dice in D&D.
d4, d6, d8, d10, d12, and d20. It would be quite unusual for all these
types of dice to be rolled at the same time, but just in case,
rollMix will be able to handle up to 6 different kinds of dice rolls. */
function rollMix(dNum1, dSid1, dNum2, dSid2, dNum3, dSid3,
		 dNum4, dSid4, dNum5, dSid5, dNum6, dSid6) {
    var diceMix = [],
	mixTotal = 0;
	diceRold = [];

    physics(dNum1, dSid1);
    diceRold.push(dNum1 + " d" + dSid1);
    for (var i = 0; i < displayRolls.length; i++) {
	diceMix.push(displayRolls[i]);
    }

    if (dNum2 > 0) {
	physics(dNum2, dSid2);
	diceRold.push(dNum2 + " d" + dSid2);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum3 > 0) {
	physics(dNum3, dSid3);
	diceRold.push(dNum3 + " d" + dSid3);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum4 > 0) {
	physics(dNum4, dSid4);
	diceRold.push(dNum4 + " d" + dSid4);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum5 > 0) {
	physics(dNum5, dSid5);
	diceRold.push(dNum5 + " d" + dSid5);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum6 > 0) {
	physics(dNum6, dSid6);
	diceRold.push(dNum6 + " d" + dSid6);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }

    mixTotal = diceMix.reduce(function(a ,b) {
	return a + b;
    });

    var dispString = (diceRold.join(", ") + ": "
	+ diceMix.join(", ") + " Total: " + mixTotal);

    return dispString;
}

//Special kinds of D&D rolls, Advantage, Disadvantage, Percent, Inspiration and mixed rolls
function rollAdvantage() {
    roll(2, 20);
    displayTotal = Math.max(displayRolls[0], displayRolls[1]);
    return (displayRolls.join(" & ") + ". Result: " + displayTotal);
}

function rollDisadvantage() {
    roll(2, 20);
    displayTotal = Math.min(displayRolls[0], displayRolls[1]);
    return (displayRolls.join(" & ") + ". Result: " + displayTotal);
}

function rollPercent() {
    physics(2, 10);

    dicePercent = 0;

    dicePercent = ((displayRolls[0] % 10) * 10) + (displayRolls[1] % 10);

    if (dicePercent < 1) {
	dicePercent = 100;
    }

    displayTotal = dicePercent;

    return (displayRolls.join(" & ") + " %Score: " + displayTotal);
}

//Accesses the last most recent roll, check if it is a d20 roll,
//then add itself to that roll.
//Handles Advantage, & Disadvantage rolls as well.
function rollInspiration() {
    var lastRoll = 0;

    if (displaySides === 20) {
	lastRoll = displayTotal;
	physics(1, 10);
	return ("+" + displayTotal + " to " + lastRoll + ": "
		+ (lastRoll + displayTotal));
    } else {
	return ("You can only add an inspiration die to a d20 roll.");
    }
}

//input for manual rolls (like if you're using real dice). changes public variables directly.
function rollManual(diceNumber, diceSides, rolls) {
    displayNumDc = diceNumber;
    displaySides = diceSides;
    //todo: create an if statement that checks that rolls is an Array.
    displayRolls = rolls;

    displayTotal = rolls.reduce(function(a ,b) {
	return a + b;
    });

    return rollOut(displayNumDc, displaySides, displayRolls, displayTotal);
}

//manual testing, no edge cases yet.
console.log(roll(1,20));
console.log(rollInspiration());
console.log(roll(2,12));
console.log(rollInspiration());
console.log(roll(10,6));
console.log(rollAdvantage());
console.log(rollInspiration());
console.log(rollDisadvantage());
console.log(rollInspiration());
console.log(rollManual(10, 6, [4, 4, 4, 2, 5, 2, 3, 2, 1, 6]));
console.log(rollManual(2, 12, [5, 2]));
console.log(rollInspiration());
console.log(rollManual(1, 20, [20]));
console.log(rollInspiration());
console.log(rollMix(2, 12));
console.log(rollMix(2, 12, 3, 10));
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());

<commit_msg>add basic DB scaffolding<commit_after>/* wheatonDice is an opensource project to study the legendary dice curse of Wil Wheaton
 * and get to the bottom of bad juju. At the moment, it's just a dice roller, but later
 * functions will include a rolls database that can track any sinking feeling that the kind of
 * cheetos you're eating is influencing your rolls. Also v.1.0 is aiming at 5th edition D&D rules
 * usage. Later versions will be more system agnostic. This is intended for mobile release, but
 * for now, this is just the engine. Minus the actual engine. Broad strokes to get the system
 * working. Much to be built! -Ian
 */

//public variables
var displaySides = 0,
    displayNumDc = 0,
    displayRolls = [],
    displayTotal = 0;

//saved rolls go here so they can be accessed and analyzed (unitl a proper DB is made)
//currnet version forgets after the battery of console.log()s are made. Need a proper DB,
//but this is proof of concept. todo: db that can store previous rolls.
var rollDB = [];

/*the physics engine (to be built: a different abstract function than builtin Math.random().
It will access the device's accelerometer and add physics + chaos to generate the roll.
it'll be totally sweet!*/
function physics(diceNumber, diceSides) {

    var rolls = [];

    displaySides = diceSides;
    displayNumDc = diceNumber;

    //warning! no edge case protection! You gotta use it just right! Todo: handle edge cases

    //rolls each die separately then pushes to "rolls" array.
    for (var i = 0; i < diceNumber; i++) {
	    rolls.push(Math.ceil( Math.random() * diceSides ));
    }

    displayRolls = rolls;

    //folds the array into a neat total
    var rollTotal = rolls.reduce(function(a ,b) {
	return a + b;
    });

    displayTotal = rollTotal;
    return rollDBizer();
}

//function between physics and rollOut that passes roll results to rollDB.
function rollDBizer() {
    var rollTime = new Date(),
	rollEntry = [],

    //rollTime set to Now in ms. Date function, roll stats organized to rollEntry
    rollEntry = [rollTime.getTime(), displayNumDc, displaySides, displayRolls, displayTotal];

    rollDB.push(rollEntry);
    //rollDB += rollEntry???

    return rollOut(displayNumDc, displaySides, displayRolls, displayTotal);
}

//Separate output function, so that code is easy to delete.
//make it so every lowest possible roll that's rolled displays "Wheaton!"
    //instead of the number
//comment out any portion if you don't want Wil distracting you so much.
//uncomment Percy if you're an optimist.
function rollOut(diceNumber, diceSides, rolls, rollTotal) {
    if (rollTotal === diceNumber) {
	rollTotal = "Wheaton!";
    } else if (rollTotal < (0.33 * (diceSides * diceNumber))){
	rollTotal += " lesser Wheaton.";
    } /*else if (rollTotal === (diceSides * diceNumber)) {
	rollTotal = "Percy!";
    } else if (rollTotal > (0.66 * (diceSides * diceNumber))) {
	rollTotal += " lesser Percy.";
    }*/

    //returns a neat string that shows what you rolled, how each roll went, and the total.
    //This will probably be numberfied and separated out when the actual UI gets built
    if (diceNumber === 1) {
	return ("1 d" + diceSides + ": " + rollTotal);
    } else {
	return (diceNumber + " d" + diceSides + ": " + rolls.join(", ") + " Total: " + rollTotal);}
}

//input functions.
//uses public variables to access physics() function.
function roll(diceNumber, diceSides) {
    return physics(diceNumber, diceSides);
}

/*Mixed dice rolls. There are 6 Standard dice in D&D.
d4, d6, d8, d10, d12, and d20. It would be quite unusual for all these
types of dice to be rolled at the same time, but just in case,
rollMix will be able to handle up to 6 different kinds of dice rolls. */
function rollMix(dNum1, dSid1, dNum2, dSid2, dNum3, dSid3,
		 dNum4, dSid4, dNum5, dSid5, dNum6, dSid6) {
    var diceMix = [],
	mixTotal = 0;
	diceRold = [];

    physics(dNum1, dSid1);
    diceRold.push(dNum1 + " d" + dSid1);
    for (var i = 0; i < displayRolls.length; i++) {
	diceMix.push(displayRolls[i]);
    }

    if (dNum2 > 0) {
	physics(dNum2, dSid2);
	diceRold.push(dNum2 + " d" + dSid2);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum3 > 0) {
	physics(dNum3, dSid3);
	diceRold.push(dNum3 + " d" + dSid3);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum4 > 0) {
	physics(dNum4, dSid4);
	diceRold.push(dNum4 + " d" + dSid4);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum5 > 0) {
	physics(dNum5, dSid5);
	diceRold.push(dNum5 + " d" + dSid5);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }
    if (dNum6 > 0) {
	physics(dNum6, dSid6);
	diceRold.push(dNum6 + " d" + dSid6);
	for (var i = 0; i < displayRolls.length; i++) {
		diceMix.push(displayRolls[i]);
	}
    }

    mixTotal = diceMix.reduce(function(a ,b) {
	return a + b;
    });

    var dispString = (diceRold.join(", ") + ": "
	+ diceMix.join(", ") + " Total: " + mixTotal);

    return dispString;
}

//Special kinds of D&D rolls, Advantage, Disadvantage, Percent, Inspiration and mixed rolls
//todo: make sure rollDB gets the proper displayTotal
function rollAdvantage() {
    roll(2, 20);
    displayTotal = Math.max(displayRolls[0], displayRolls[1]);
    return (displayRolls.join(" & ") + ". Result: " + displayTotal);
}

//todo: make sure rollDB gets the proper displayTotal
function rollDisadvantage() {
    roll(2, 20);
    displayTotal = Math.min(displayRolls[0], displayRolls[1]);
    return (displayRolls.join(" & ") + ". Result: " + displayTotal);
}

//todo : make sure rollDB gets the proper displayTotal
function rollPercent() {
    physics(2, 10);

    dicePercent = 0;

    dicePercent = ((displayRolls[0] % 10) * 10) + (displayRolls[1] % 10);

    if (dicePercent < 1) {
	dicePercent = 100;
    }

    displayTotal = dicePercent;

    return (displayRolls.join(" & ") + " %Score: " + displayTotal);
}

//Accesses the last most recent roll, check if it is a d20 roll,
//then add itself to that roll.
//Handles Advantage, & Disadvantage rolls as well.
function rollInspiration() {
    var lastRoll = 0;

    if (displaySides === 20) {
	lastRoll = displayTotal;
	physics(1, 10);
	return ("+" + displayTotal + " to " + lastRoll + ": "
		+ (lastRoll + displayTotal));
    } else {
	return ("You can only add an inspiration die to a d20 roll.");
    }
}

//input for manual rolls (like if you're using real dice). changes public variables directly.
//todo: make sure manual rolls get pushed to rollDB - currently no entry.
function rollManual(diceNumber, diceSides, rolls) {
    displayNumDc = diceNumber;
    displaySides = diceSides;
    //todo: create an if statement that checks that rolls is an Array.
    displayRolls = rolls;

    displayTotal = rolls.reduce(function(a ,b) {
	return a + b;
    });

    return rollOut(displayNumDc, displaySides, displayRolls, displayTotal);
}

//manual testing, no edge cases yet.
console.log(roll(1,20));
console.log(rollInspiration());
console.log(roll(2,12));
console.log(rollInspiration());
console.log(roll(10,6));
console.log(rollAdvantage());
console.log(rollInspiration());
console.log(rollDisadvantage());
console.log(rollInspiration());
console.log(rollManual(10, 6, [4, 4, 4, 2, 5, 2, 3, 2, 1, 6]));
console.log(rollManual(2, 12, [5, 2]));
console.log(rollInspiration());
console.log(rollManual(1, 20, [20]));
console.log(rollInspiration());
console.log(rollMix(2, 12));
console.log(rollMix(2, 12, 3, 10));
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());
console.log(rollPercent());
console.log(rollDB);
<|endoftext|>


<commit_before>/*
Project Name: Fancy Forms
URL: https://github.com/keobrien/Fancy-Forms
Author: Kevin O'Brien
Company: Clockwork Acive Media Systems
Company Site: clockwork.net
License: MIT
Copyright (C) 2012 Clockwork Active Media Systems
Version: 0.2
**************************************/

(function ($) {

	"use strict";

	// These locally scoped variables help minification by aliasing strings
	var data_key               =  'CW_fancy_forms',
		event_suffix           =  '.CW_fancy_forms',
		class_prefix           =  'fancy-',
	// Custom events
		component_initialized  =  'component_initialized' + event_suffix,
		$elements              =  $(), // Used to filter propHooks to plugin elements only
		$radios                =  $(); // Used to make the radio updater faster

	var methods  =  {

		_init_el  :  function (settings) {

			if($(this).hasClass(class_prefix + 'active')) { return this; }

			var $el  =  $(this),
				type =  $el.prop("tagName").toLowerCase(),
				type =  type === 'select' ? type : $el.attr('type'),
				options  =  $.extend({
					// Settings for selects, checkboxes and radios
					selected_class  :  class_prefix + type + '-checked',
					wrap_class      :  class_prefix + type +'-wrap',
					wrap_id         :  $el.attr('id') ? class_prefix + type +'-' + $el.attr('id') : '',
					wrap_tag        :  'div',
					type            :  type
				}, settings);
			options.wrap_html       =  '<' + options.wrap_tag + ' class="' + options.wrap_class + '" id="' + options.wrap_id + '"></' + options.wrap_tag + '>';
			$elements = $elements.add(this);

			if(type === 'select') {
				// Settings specific to selects
				options  =  $.extend({
					selected        :  $el.find('option:selected'),
					visible_class   :  class_prefix + type +'-visible',
					fallback        :  '

',
					visible_tag     :  'div'
				}, options);
				options.visible_html   =  '<' + options.visible_tag + ' class="' + options.visible_class + '">' + options.selected.html() + '</' + options.visible_tag + '>';
			}else {
				// Makes sure change event is fired when jQuery change is called on element
				$.propHooks.checked = {
					set: function(el, value) {
						if ($elements.filter(el).length !== 0) {
							el.checked = value;
							$(el).trigger('change');
						}
					}
				};
				if(type === 'radio') {
					$radios = $radios.add(this);
				}
			}

			$el.data(data_key, options);
			methods._make.apply(this);

			return this;
		},

		_make  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key);

			options.$wrap = $el.wrap(options.wrap_html).parent()
				.css({
					'width'          :  $el.outerWidth() + 'px',
					'height'          :  $el.outerHeight() + 'px'
				})
				.addClass(data_key + '-' + $el.attr('class'))
				.attr('id', data_key + '-' + $el.attr('id' ));

			if(options.type === 'select') {
				options.$visible  =  $(options.visible_html).appendTo(options.$wrap);
				if ($.browser.msie  && parseInt($.browser.version, 10) < 8) {
					$(options.fallback).appendTo(options.$wrap);
				}
			}else if(options.type === 'radio') {
				// Need to check all since checking one in a group doesn't fire change on the element that looses checked
				$el.change(function(){ $radios.each(function(){ methods.update.apply(this) }); });
			}

			$el
				.change(methods.update)
				.each(methods.update)
				.trigger(component_initialized, options.$wrap, options)
				.addClass(class_prefix +'active');

			return this;
		},

		/* usage: jQuery( element ).cw_fancy_forms("update") */
		update  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key),
				checked  =  $el.prop('checked');

			if((options.type === 'select') && options.$visible) {
				options.selected  =  options.$wrap.find('option:selected');
				options.$visible.html(options.selected.html());
			}else {
				checked === true ? $el.parent().addClass(options.selected_class) : $el.parent().removeClass(options.selected_class);
			}

		}
	};

	$.fn.cw_fancy_forms  =  function (method) {

		if (methods[method] && method.charAt(0) !== '_') {
			return $(this).map(function(i, val) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); });
		} else if (typeof method === 'object' || !method) {
			var args  =  arguments;
			return $(this).map(function(i, val) { return methods._init_el.apply(this, args); });
		}
	};

}(jQuery));<commit_msg>added class to disabled elements<commit_after>/*
Project Name: Fancy Forms
URL: https://github.com/keobrien/Fancy-Forms
Author: Kevin O'Brien
Company: Clockwork Acive Media Systems
Company Site: clockwork.net
License: MIT
Copyright (C) 2012 Clockwork Active Media Systems
Version: 0.2
**************************************/

(function ($) {

	"use strict";

	// These locally scoped variables help minification by aliasing strings
	var data_key               =  'CW_fancy_forms',
		event_suffix           =  '.CW_fancy_forms',
		class_prefix           =  'fancy-',
	// Custom events
		component_initialized  =  'component_initialized' + event_suffix,
		$elements              =  $(), // Used to filter propHooks to plugin elements only
		$radios                =  $(); // Used to make the radio updater faster

	var methods  =  {

		_init_el  :  function (settings) {

			if($(this).hasClass(class_prefix + 'active')) { return this; }

			var $el  =  $(this),
				type =  $el.prop("tagName").toLowerCase(),
				type =  type === 'select' ? type : $el.attr('type'),
				options  =  $.extend({
					// Settings for selects, checkboxes and radios
					selected_class  :  class_prefix + type + '-checked',
					disabled_class  :  class_prefix + type + '-disabled',
					wrap_class      :  class_prefix + type +'-wrap',
					wrap_id         :  $el.attr('id') ? class_prefix + type +'-' + $el.attr('id') : '',
					wrap_tag        :  'div',
					type            :  type
				}, settings);
			options.wrap_html       =  '<' + options.wrap_tag + ' class="' + options.wrap_class + '" id="' + options.wrap_id + '"></' + options.wrap_tag + '>';
			$elements = $elements.add(this);

			if(type === 'select') {
				// Settings specific to selects
				options  =  $.extend({
					selected        :  $el.find('option:selected'),
					visible_class   :  class_prefix + type +'-visible',
					fallback        :  '

',
					visible_tag     :  'div'
				}, options);
				options.visible_html   =  '<' + options.visible_tag + ' class="' + options.visible_class + '">' + options.selected.html() + '</' + options.visible_tag + '>';
			}else {
				// Makes sure change event is fired when jQuery change is called on element
				$.propHooks.checked = {
					set: function(el, value) {
						if ($elements.filter(el).length !== 0) {
							el.checked = value;
							$(el).trigger('change');
						}
					}
				};
				if(type === 'radio') {
					$radios = $radios.add(this);
				}
			}

			$el.data(data_key, options);
			methods._make.apply(this);

			return this;
		},

		_make  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key);

			options.$wrap = $el.wrap(options.wrap_html).parent()
				.css({
					'width'          :  $el.outerWidth() + 'px',
					'height'          :  $el.outerHeight() + 'px'
				})
				.addClass(data_key + '-' + $el.attr('class'))
				.attr('id', data_key + '-' + $el.attr('id' ));

			if(options.type === 'select') {
				options.$visible  =  $(options.visible_html).appendTo(options.$wrap);
				if ($.browser.msie  && parseInt($.browser.version, 10) < 8) {
					$(options.fallback).appendTo(options.$wrap);
				}
			}else if(options.type === 'radio') {
				// Need to check all since checking one in a group doesn't fire change on the element that looses checked
				$el.change(function(){ $radios.each(function(){ methods.update.apply(this) }); });
			}

			$el
				.change(methods.update)
				.each(methods.update)
				.trigger(component_initialized, options.$wrap, options)
				.addClass(class_prefix +'active');

			return this;
		},

		/* usage: jQuery( element ).cw_fancy_forms("update") */
		update  :  function (evt) {

			var	$el      =  $(this),
				options  =  evt ? $(evt.currentTarget).data(data_key) : $(this).data(data_key),
				checked  =  $el.prop('checked'),
				disabled =  $el.prop('disabled');

			if((options.type === 'select') && options.$visible) {
				options.selected  =  options.$wrap.find('option:selected');
				options.$visible.html(options.selected.html());
			}else {
				checked === true ? $el.parent().addClass(options.selected_class) : $el.parent().removeClass(options.selected_class);
			}
			if(disabled) {
				$el.parent().addClass(options.disabled_class);
			}

		}
	};

	$.fn.cw_fancy_forms  =  function (method) {

		if (methods[method] && method.charAt(0) !== '_') {
			return $(this).map(function(i, val) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); });
		} else if (typeof method === 'object' || !method) {
			var args  =  arguments;
			return $(this).map(function(i, val) { return methods._init_el.apply(this, args); });
		}
	};

}(jQuery));<|endoftext|>


<commit_before>/**
 * Display a nice easy to use multiselect list
 * @Version: 1.2
 * @Author: Patrick Springstubbe
 * @Contact: @JediNobleclem
 * @Website: springstubbe.us
 * @Source: https://github.com/nobleclem/jQuery-MultiSelect
 * @Notes: If select list is hidden on page load use the jquery.actual plugin
 *         to resolve issues with preselected items placeholder text
 *         https://github.com/dreamerslab/jquery.actual
 *
 * Usage:
 *     $('select[multiple]').multiselect();
 *     $('select[multiple]').multiselect({ placeholder: 'Select options' });
 *
 **/
(function($){
    var msCounter = 1;
    $.fn.multiselect = function( options ){
        var defaults = {
            placeholder   : '',    // text to use in dummy input
            columns       : 1,     // how many columns should be use to show options
            maxWidth      : null,  // maximum width of option overlay (or selector)
            minHeight     : 200,   // minimum height of option overlay
            maxHeight     : null,  // maximum height of option overlay
            showCheckbox  : true,  // display the checkbox to the user
            jqActualOpts  : {},    // options for jquery.actual

            // @NOTE: these are for future development
            minSelect     : false, // minimum number of items that can be selected
            maxSelect     : false, // maximum number of items that can be selected
            groupSelect   : false, // select entire optgroup
            selectAllText : false, // add select all option
            searchOptions : false, // enable option search/filtering
        }
        options = $.extend( defaults, options );

        var func = {
            addOption: function( container, option )
            {
                container.text( $(option).text() );
                container.prepend(
                    $('<input type="checkbox" value="" title="" />')
                        .val( $(option).val() )
                        .attr( 'title', $(option).text() )
                        .attr( 'id', 'ms-opt-'+ msCounter )
                );

                if( $(option).attr( 'selected' ) ) {
                    container.addClass('selected');
                    container.find( 'input[type="checkbox"]' ).attr( 'checked', 'checked' );
                }

                var label = $('').attr( 'for', 'ms-opt-'+ msCounter );
                container.wrapInner( label );

                if( !options.showCheckbox ) {
                    container.find('input[id="ms-opt-'+ msCounter +'"]').hide();
                }

                msCounter = msCounter + 1;
            },

            ieVersion: function()
            {
                var myNav = navigator.userAgent.toLowerCase();
                return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
            }
        };

        // menuize each list
        return this.each(function(){
            // make sure this is a select list
            if( this.nodeName != 'SELECT' ) {
                return false;
            }

            // add option container
            $(this).after('
<button>None Selected</button>

');
            var placeholder = $(this).next('.ms-options-wrap').find('> button:first-child');
            var optionsWrap = $(this).next('.ms-options-wrap').find('> .ms-options');
            var optionsList = optionsWrap.find('> ul');
            var hasOptGroup = $(this).find('optgroup').length ? true : false;

            var maxWidth = null;
            if( typeof options.width == 'number' ) {
                optionsWrap.parent().css( 'position', 'relative' );
                maxWidth = options.width;
            }
            else if( typeof options.width == 'string' ) {
                $( options.width ).css( 'position', 'relative' );
                maxWidth = '100%';
            }
            else {
                optionsWrap.parent().css( 'position', 'relative' );
            }

            var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
            if( options.maxHeight ) {
                maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                maxHeight = maxHeight < options.minHeight ? options.minHeight : maxheight;
            }

            maxHeight = maxHeight < options.minHeight ? options.minHeight : maxHeight;

            optionsWrap.css({
                maxWidth : maxWidth,
                minHeight: options.minHeight,
                maxHeight: maxHeight,
                overflow : 'auto'
            }).hide();

            // isolate options scroll
            // @source: https://github.com/nobleclem/jQuery-IsolatedScroll
            optionsWrap.bind( 'touchmove mousewheel DOMMouseScroll', function ( e ) {
                if( ($(this).outerHeight() < $(this)[0].scrollHeight) ) {
                    var e0 = e.originalEvent,
                        delta = e0.wheelDelta || -e0.detail;

                    if( ($(this).outerHeight() + $(this)[0].scrollTop) > $(this)[0].scrollHeight ) {
                        e.preventDefault();
                        this.scrollTop += ( delta < 0 ? 1 : -1 );
                    }
                }
            });

            // hide options menus if click happens off of the list placeholder button
            $(window).off('click.ms-hideopts').on('click.ms-hideopts', function( event ){
                if( !$(event.target).closest('.ms-options-wrap').length ) {
                    $('.ms-options-wrap > .ms-options:visible').hide();
                }
            });

            // disable button action
            placeholder.bind('mousedown',function(){
                // hide other menus before showing this one
                $('.ms-options-wrap > .ms-options:visible').each(function(){
                    if( $(this).parent().prev()[0] != optionsWrap.parent().prev()[0] ) {
                        $(this).hide();
                    }
                });

                // show/hide options
                optionsWrap.toggle();

                // recalculate height
                if( optionsWrap.is(':visible') ) {
                    optionsWrap.css( 'maxHeight', '' );

                    var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                    if( options.maxHeight ) {
                        maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                        maxHeight = maxHeight < options.minHeight ? options.minHeight : maxheight;
                    }
                    maxHeight = maxHeight < options.minHeight ? options.minHeight : maxHeight;

                    optionsWrap.css( 'maxHeight', maxHeight );
                }
            }).click(function( event ){ event.preventDefault(); });

            // add placeholder copy
            if( options.placeholder ) {
                placeholder.text( options.placeholder );
            }

            // hide native select list
            $(this).hide();

            // add options to wrapper
            $(this).children().each(function(){
                var container = $('

');

                // add group of items
                if( this.nodeName == 'OPTGROUP' ) {
                    container.addClass('optgroup');
                    container.append(''+ $(this).attr('label') +'');
                    container.find('> .label').css({
                        clear: 'both'
                    });
                    container.append('

');

                    $(this).children('option').each(function(){
                        var gContainer = $('

');
                        func.addOption( gContainer, this );

                        container.find('> ul').append( gContainer );
                    });
                }
                else if( this.nodeName == 'OPTION' ) {
                    func.addOption( container, this );
                }
                else {
                    // bad option
                    return true;
                }

                optionsList.append( container );
            });

            // COLUMNIZE
            if( hasOptGroup ) {
                // float non grouped options
                optionsList.find('> li:not(.optgroup)').css({
                    float: 'left',
                    width: (100 / options.columns) +'%'
                });

                // add CSS3 column styles
                optionsList.find('li.optgroup').css({
                    clear: 'both'
                }).find('> ul').css({
                    'column-count'        : options.columns,
                    'column-gap'          : 0,
                    '-webkit-column-count': options.columns,
                    '-webkit-column-gap'  : 0,
                    '-moz-column-count'   : options.columns,
                    '-moz-column-gap'     : 0
                });

                // for crappy IE versions float grouped options
                if( func.ieVersion() && func.ieVersion() < 10 ) {
                    optionsList.find('li.optgroup > ul > li').css({
                        float: 'left',
                        width: (100 / options.columns) +'%'
                    });
                }
            }
            else {
                // add CSS3 column styles
                optionsList.css({
                    'column-count'        : options.columns,
                    'column-gap'          : 0,
                    '-webkit-column-count': options.columns,
                    '-webkit-column-gap'  : 0,
                    '-moz-column-count'   : options.columns,
                    '-moz-column-gap'     : 0
                });

                // for crappy IE versions float grouped options
                if( func.ieVersion() && func.ieVersion() < 10 ) {
                    optionsList.find('> li').css({
                        float: 'left',
                        width: (100 / options.columns) +'%'
                    });
                }
            }

            // BIND SELECT ACTION
            optionsWrap.find('input[type="checkbox"]').click(function(){
                $(this).closest( 'li' ).toggleClass( 'selected' );

                var select = optionsWrap.parent().prev();

                // toggle clicked option
                select.find('option[value="'+ $(this).val() +'"]').attr(
                    'selected', $(this).is(':checked')
                ).closest('select').trigger('change');

                updatePlaceholderText();
            }).each(function( idx ){
                if( $(this).css('display').match(/block$/) ) {
                    var checkboxWidth = $(this).outerWidth();
                        checkboxWidth = checkboxWidth ? checkboxWidth : 15;

                    $(this).closest('label').css(
                        'padding-left',
                        (parseInt( $(this).closest('label').css('padding-left') ) * 2) + checkboxWidth
                    );
                }
            });

            // update selected placeholder text
            var updatePlaceholderText = function(){
                var select = optionsWrap.parent().prev();

                // get selected options
                var selOpts = [];
                select.find('option[selected="selected"]').each(function(){
                    selOpts.push( $(this).text() );
                });

                // UPDATE PLACEHOLDER TEXT WITH OPTIONS SELECTED
                placeholder.text( selOpts.join( ', ' ) );
                var copy = placeholder.clone().css({
                    display   : 'inline',
                    width     : 'auto',
                    visibility: 'hidden'
                }).appendTo( optionsWrap.parent() );

                // if the jquery.actual plugin is loaded use it to get the widths
                var copyWidth  = (typeof $.fn.actual !== 'undefined') ? copy.actual('width',options.jqActualOpts) : copy.width();
                var placeWidth = (typeof $.fn.actual !== 'undefined') ? placeholder.actual('width',options.jqActualOpts) : placeholder.width();

                // if copy is larger than button width use "# selected"
                if( copyWidth > placeWidth ) {
                    placeholder.text( selOpts.length +' selected' );
                }
                // if options selected then use those
                else if( selOpts.length ) {
                    placeholder.text( selOpts.join( ', ' ) );
                }
                // replace placeholder text
                else {
                    placeholder.text( options.placeholder );
                }

                // remove dummy element
                copy.remove();
            };

            updatePlaceholderText();
        });
    };
}(jQuery));
<commit_msg>added check if select has been loaded<commit_after>/**
 * Display a nice easy to use multiselect list
 * @Version: 1.3
 * @Author: Patrick Springstubbe
 * @Contact: @JediNobleclem
 * @Website: springstubbe.us
 * @Source: https://github.com/nobleclem/jQuery-MultiSelect
 * @Notes: If select list is hidden on page load use the jquery.actual plugin
 *         to resolve issues with preselected items placeholder text
 *         https://github.com/dreamerslab/jquery.actual
 *
 * Usage:
 *     $('select[multiple]').multiselect();
 *     $('select[multiple]').multiselect({ placeholder: 'Select options' });
 *
 **/
(function($){
    var msCounter = 1;
    $.fn.multiselect = function( options ){
        var defaults = {
            placeholder   : '',    // text to use in dummy input
            columns       : 1,     // how many columns should be use to show options
            maxWidth      : null,  // maximum width of option overlay (or selector)
            minHeight     : 200,   // minimum height of option overlay
            maxHeight     : null,  // maximum height of option overlay
            showCheckbox  : true,  // display the checkbox to the user
            jqActualOpts  : {},    // options for jquery.actual

            // @NOTE: these are for future development
            minSelect     : false, // minimum number of items that can be selected
            maxSelect     : false, // maximum number of items that can be selected
            groupSelect   : false, // select entire optgroup
            selectAllText : false, // add select all option
            searchOptions : false, // enable option search/filtering
        }
        options = $.extend( defaults, options );

        var func = {
            addOption: function( container, option )
            {
                container.text( $(option).text() );
                container.prepend(
                    $('<input type="checkbox" value="" title="" />')
                        .val( $(option).val() )
                        .attr( 'title', $(option).text() )
                        .attr( 'id', 'ms-opt-'+ msCounter )
                );

                if( $(option).attr( 'selected' ) ) {
                    container.addClass('selected');
                    container.find( 'input[type="checkbox"]' ).attr( 'checked', 'checked' );
                }

                var label = $('').attr( 'for', 'ms-opt-'+ msCounter );
                container.wrapInner( label );

                if( !options.showCheckbox ) {
                    container.find('input[id="ms-opt-'+ msCounter +'"]').hide();
                }

                msCounter = msCounter + 1;
            },

            ieVersion: function()
            {
                var myNav = navigator.userAgent.toLowerCase();
                return (myNav.indexOf('msie') != -1) ? parseInt(myNav.split('msie')[1]) : false;
            }
        };

        // menuize each list
        return this.each(function(){
            // make sure this is a select list and not loaded
            if( this.nodeName != 'SELECT' || $(this).hasClass('jqmsLoaded') ) {
                return false;
            }

            // sanity check so we don't double load on a select element
            $(this).addClass('jqmsLoaded');

            // add option container
            $(this).after('
<button>None Selected</button>

');
            var placeholder = $(this).next('.ms-options-wrap').find('> button:first-child');
            var optionsWrap = $(this).next('.ms-options-wrap').find('> .ms-options');
            var optionsList = optionsWrap.find('> ul');
            var hasOptGroup = $(this).find('optgroup').length ? true : false;

            var maxWidth = null;
            if( typeof options.width == 'number' ) {
                optionsWrap.parent().css( 'position', 'relative' );
                maxWidth = options.width;
            }
            else if( typeof options.width == 'string' ) {
                $( options.width ).css( 'position', 'relative' );
                maxWidth = '100%';
            }
            else {
                optionsWrap.parent().css( 'position', 'relative' );
            }

            var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
            if( options.maxHeight ) {
                maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                maxHeight = maxHeight < options.minHeight ? options.minHeight : maxheight;
            }

            maxHeight = maxHeight < options.minHeight ? options.minHeight : maxHeight;

            optionsWrap.css({
                maxWidth : maxWidth,
                minHeight: options.minHeight,
                maxHeight: maxHeight,
                overflow : 'auto'
            }).hide();

            // isolate options scroll
            // @source: https://github.com/nobleclem/jQuery-IsolatedScroll
            optionsWrap.bind( 'touchmove mousewheel DOMMouseScroll', function ( e ) {
                if( ($(this).outerHeight() < $(this)[0].scrollHeight) ) {
                    var e0 = e.originalEvent,
                        delta = e0.wheelDelta || -e0.detail;

                    if( ($(this).outerHeight() + $(this)[0].scrollTop) > $(this)[0].scrollHeight ) {
                        e.preventDefault();
                        this.scrollTop += ( delta < 0 ? 1 : -1 );
                    }
                }
            });

            // hide options menus if click happens off of the list placeholder button
            $(window).off('click.ms-hideopts').on('click.ms-hideopts', function( event ){
                if( !$(event.target).closest('.ms-options-wrap').length ) {
                    $('.ms-options-wrap > .ms-options:visible').hide();
                }
            });

            // disable button action
            placeholder.bind('mousedown',function(){
                // hide other menus before showing this one
                $('.ms-options-wrap > .ms-options:visible').each(function(){
                    if( $(this).parent().prev()[0] != optionsWrap.parent().prev()[0] ) {
                        $(this).hide();
                    }
                });

                // show/hide options
                optionsWrap.toggle();

                // recalculate height
                if( optionsWrap.is(':visible') ) {
                    optionsWrap.css( 'maxHeight', '' );

                    var maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                    if( options.maxHeight ) {
                        maxHeight = ($(window).height() - optionsWrap.offset().top - 20);
                        maxHeight = maxHeight < options.minHeight ? options.minHeight : maxheight;
                    }
                    maxHeight = maxHeight < options.minHeight ? options.minHeight : maxHeight;

                    optionsWrap.css( 'maxHeight', maxHeight );
                }
            }).click(function( event ){ event.preventDefault(); });

            // add placeholder copy
            if( options.placeholder ) {
                placeholder.text( options.placeholder );
            }

            // hide native select list
            $(this).hide();

            // add options to wrapper
            $(this).children().each(function(){
                var container = $('

');

                // add group of items
                if( this.nodeName == 'OPTGROUP' ) {
                    container.addClass('optgroup');
                    container.append(''+ $(this).attr('label') +'');
                    container.find('> .label').css({
                        clear: 'both'
                    });
                    container.append('

');

                    $(this).children('option').each(function(){
                        var gContainer = $('

');
                        func.addOption( gContainer, this );

                        container.find('> ul').append( gContainer );
                    });
                }
                else if( this.nodeName == 'OPTION' ) {
                    func.addOption( container, this );
                }
                else {
                    // bad option
                    return true;
                }

                optionsList.append( container );
            });

            // COLUMNIZE
            if( hasOptGroup ) {
                // float non grouped options
                optionsList.find('> li:not(.optgroup)').css({
                    float: 'left',
                    width: (100 / options.columns) +'%'
                });

                // add CSS3 column styles
                optionsList.find('li.optgroup').css({
                    clear: 'both'
                }).find('> ul').css({
                    'column-count'        : options.columns,
                    'column-gap'          : 0,
                    '-webkit-column-count': options.columns,
                    '-webkit-column-gap'  : 0,
                    '-moz-column-count'   : options.columns,
                    '-moz-column-gap'     : 0
                });

                // for crappy IE versions float grouped options
                if( func.ieVersion() && func.ieVersion() < 10 ) {
                    optionsList.find('li.optgroup > ul > li').css({
                        float: 'left',
                        width: (100 / options.columns) +'%'
                    });
                }
            }
            else {
                // add CSS3 column styles
                optionsList.css({
                    'column-count'        : options.columns,
                    'column-gap'          : 0,
                    '-webkit-column-count': options.columns,
                    '-webkit-column-gap'  : 0,
                    '-moz-column-count'   : options.columns,
                    '-moz-column-gap'     : 0
                });

                // for crappy IE versions float grouped options
                if( func.ieVersion() && func.ieVersion() < 10 ) {
                    optionsList.find('> li').css({
                        float: 'left',
                        width: (100 / options.columns) +'%'
                    });
                }
            }

            // BIND SELECT ACTION
            optionsWrap.find('input[type="checkbox"]').click(function(){
                $(this).closest( 'li' ).toggleClass( 'selected' );

                var select = optionsWrap.parent().prev();

                // toggle clicked option
                select.find('option[value="'+ $(this).val() +'"]').attr(
                    'selected', $(this).is(':checked')
                ).closest('select').trigger('change');

                updatePlaceholderText();
            }).each(function( idx ){
                if( $(this).css('display').match(/block$/) ) {
                    var checkboxWidth = $(this).outerWidth();
                        checkboxWidth = checkboxWidth ? checkboxWidth : 15;

                    $(this).closest('label').css(
                        'padding-left',
                        (parseInt( $(this).closest('label').css('padding-left') ) * 2) + checkboxWidth
                    );
                }
            });

            // update selected placeholder text
            var updatePlaceholderText = function(){
                var select = optionsWrap.parent().prev();

                // get selected options
                var selOpts = [];
                select.find('option[selected="selected"]').each(function(){
                    selOpts.push( $(this).text() );
                });

                // UPDATE PLACEHOLDER TEXT WITH OPTIONS SELECTED
                placeholder.text( selOpts.join( ', ' ) );
                var copy = placeholder.clone().css({
                    display   : 'inline',
                    width     : 'auto',
                    visibility: 'hidden'
                }).appendTo( optionsWrap.parent() );

                // if the jquery.actual plugin is loaded use it to get the widths
                var copyWidth  = (typeof $.fn.actual !== 'undefined') ? copy.actual('width',options.jqActualOpts) : copy.width();
                var placeWidth = (typeof $.fn.actual !== 'undefined') ? placeholder.actual('width',options.jqActualOpts) : placeholder.width();

                // if copy is larger than button width use "# selected"
                if( copyWidth > placeWidth ) {
                    placeholder.text( selOpts.length +' selected' );
                }
                // if options selected then use those
                else if( selOpts.length ) {
                    placeholder.text( selOpts.join( ', ' ) );
                }
                // replace placeholder text
                else {
                    placeholder.text( options.placeholder );
                }

                // remove dummy element
                copy.remove();
            };

            updatePlaceholderText();
        });
    };
}(jQuery));
<|endoftext|>