<commit_before>
import { each, isBoolean, isEmpty, isPlainObject, isArray, isFunction, isString } from 'lodash'
import Criterion, { IsNull, Basic, IsIn, Raw, Between, Exists } from './index'
import { Raw as RawExpr, Column } from '../expression/index'

/**
 * @class CriteriaExpression
 */
export default class Criteria extends Criterion {

  /**
   *
   * @param {String} bool
   * @param {Boolean} negate
   * @constructor
   */
  constructor(bool = 'and', negate = false) {
    super(bool)

    this.builder = null
    this.components = []
    this.negate = negate ? 'not ' : ''
  }

  /**
   *
   * @param {Builder} value
   * @return this
   */
  setBuilder(value) {
    this.builder = value
    return this
  }

  /**
   *
   * @param {Criterion} value
   * @return this
   */
  add(value) {
    if (! (value instanceof Criterion) )
      throw new TypeError("Invalid condition expression")

    this.components.push(value)

    return this
  }

  /**
   *
   * @param {String} bool
   * @param {Boolean} not
   * @return Criteria instance
   */
  newCriteria(bool = 'and', not = false) {
    return new Criteria(bool, not)
  }

  /**
   *
   * @param {Compiler} compiler
   * @return string
   */
  compile(compiler) {
    // compile the conditions
    var conditions = this.components.map(child => {
      // compile a sub criteria
      if ( child instanceof Criteria ) {
        let conditions = child.compile(compiler)

        return `${child.bool} ${child.negate}(${conditions})`
      }

      return child.compile(compiler)
    })

    // remove the leading boolean and return the rest
    return conditions.join(' ').substr(3).trim()
  }

  /**
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  where(expr, operator, value, bool = 'and', not = false) {
    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    // supports `.where(Boolean)`
    if ( isBoolean(expr) )
      return this.raw('1 = ' + expr ? 1 : 0)

    // supports `.where(new RawExpr(...))`
    if ( expr instanceof RawExpr )
      return this.raw(expr, [], bool)

    // supports `.where({a: 1, b: 3})`
    if ( isPlainObject(expr) ) {
      let obj = expr

      expr = cr => each(obj, (val, key) => cr.where(key, '=', val))
    }

    // supports `.where(cr => { ... })`
    if ( isFunction(expr) ) {
      let fn = expr

      fn(expr = this.newCriteria(bool, not))

      return this.add(expr)
    }

    // format the operator
    operator = String(operator).toLowerCase().trim()

    // supports between operator
    if ( operator.indexOf('between') > -1 )
      return this.between(expr, value, bool, not)

    // escape percentages and underscores for like comparaison
    if ( operator.indexOf('like') > -1 )
      value = value.replace(/(_|%)/g, '\\$1')

    // supports `.where('column', null)`
    if ( value === null )
      return this.isNull(expr, bool, not)

    // supports `.where('column', [...])`
    if ( isArray(value) && operator === '=' )
      return this.in(expr, value, bool, not)

    // supports `.where('expr', 'operator', qb => {...})`
    if ( isFunction(value) )
      return this.sub(expr, operator, value, bool)

    return this.add(new Basic(expr, operator, value, bool, not))
  }

  /**
   * @alias `where()`
   */
  and() {
    return this.where(...arguments)
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @return this
   */
  or(expr, operator, value) {
    return this.where(expr, operator, value, 'or')
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @return this
   */
  not(expr, operator, value, bool = 'and') {
    var not = true

    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    // format the operator
    operator = String(operator).toLowerCase().trim()

    // supports `.whereNot('column', 'between', [...])`
    if ( operator === 'between' )
      return this.between(expr, operator, value, bool, not)

    // supports `.whereNot('column', 'in', [...])`
    if ( operator === 'in' )
      return this.in(expr, operator, value, bool, not)

    // supports `.whereNot('column', 'like', 'patern')`
    if ( operator === 'like' ) {
      operator = 'not like'
      not = false
    }

    return this.where(expr, operator, value, bool, not)
  }

  /**
   * @alias `not()`
   */
  andNot() {
    return this.not(...arguments)
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @return this
   */
  orNot(expr, operator, value) {
    return this.not(expr, operator, value, 'or')
  }

  /**
   *
   * @param {String|Raw} expr
   * @param {Array} bindings
   * @param {String} bool
   * @return this
   */
  raw(expr, bindings = [], bool = 'and') {
    return this.add(new Raw(this.builder.ensureRaw(expr, bindings), bool))
  }

  /**
   *
   * @param {String|Raw} expr
   * @param {Array} bindings
   * @return this
   */
  orRaw(expr, bindings = []) {
    return this.raw(expr, bindings, 'or')
  }

  /**
   * @alias `raw()`
   */
  andRaw() {
    return this.raw(...arguments)
  }

  /**
   *
   * @param {String|Column} expr
   * @param {Array} values
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  between(expr, values, bool = 'and', not = false) {
    return this.add(new Between(expr, values, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  isNull(expr, bool = 'and', not = false) {
    return this.add(new IsNull(expr, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {Any} query
   * @param {String} bool
   * @return this
   */
  sub(expr, operator, query, bool = 'and') {
    query = this.builder.ensureSubQuery(query)

    return this.add(new Basic(expr, operator, query, bool))
  }

  /**
   * @param {String|Column} expr
   * @param {Array|SubQuery} values
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  in(expr, values, bool = 'and', not = false) {
    // instead of an empty array of values, a boolean expression is used
    if ( isArray(values) && isEmpty(values) ) return this.where(not)

    // accepts sub queries
    if (! isArray(values) ) {
      let operator = not ? 'not ' : '' + 'in'

      return this.sub(expr, operator, values, bool)
    }

    return this.add(new IsIn(expr, values, bool, not))
  }

  /**
   *
   * @param {SubQuery} query
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  exists(query, bool = 'and', not = false) {
    query = this.ensureSubQuery(query)

    return this.add(new Exists(query, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {String|Column} value
   * @param {String} bool
   * @return this
   */
  on(expr, operator, value, bool = 'and') {
    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    if ( isString(value) ) expr = new Column(value)

    if ( value instanceof Column )
      throw new TypeError("Invalid column expression")

    return this.add(new Basic(expr, operator, value, bool))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {String|Column} value
   * @return this
   */
  orOn(expr, operator, value) {
    return this.on(expr, operator, value, 'or')
  }

  /**
   * @alias `on()`
   */
  andOn() {
    return this.on(...arguments)
  }

}<commit_msg>Fix criteria.where() remove the escape of parentheses and underscores for `like` operator<commit_after>
import { each, isBoolean, isEmpty, isPlainObject, isArray, isFunction, isString } from 'lodash'
import Criterion, { IsNull, Basic, IsIn, Raw, Between, Exists } from './index'
import { Raw as RawExpr, Column } from '../expression/index'

/**
 * @class CriteriaExpression
 */
export default class Criteria extends Criterion {

  /**
   *
   * @param {String} bool
   * @param {Boolean} negate
   * @constructor
   */
  constructor(bool = 'and', negate = false) {
    super(bool)

    this.builder = null
    this.components = []
    this.negate = negate ? 'not ' : ''
  }

  /**
   *
   * @param {Builder} value
   * @return this
   */
  setBuilder(value) {
    this.builder = value
    return this
  }

  /**
   *
   * @param {Criterion} value
   * @return this
   */
  add(value) {
    if (! (value instanceof Criterion) )
      throw new TypeError("Invalid condition expression")

    this.components.push(value)

    return this
  }

  /**
   *
   * @param {String} bool
   * @param {Boolean} not
   * @return Criteria instance
   */
  newCriteria(bool = 'and', not = false) {
    return new Criteria(bool, not)
  }

  /**
   *
   * @param {Compiler} compiler
   * @return string
   */
  compile(compiler) {
    // compile the conditions
    var conditions = this.components.map(child => {
      // compile a sub criteria
      if ( child instanceof Criteria ) {
        let conditions = child.compile(compiler)

        return `${child.bool} ${child.negate}(${conditions})`
      }

      return child.compile(compiler)
    })

    // remove the leading boolean and return the rest
    return conditions.join(' ').substr(3).trim()
  }

  /**
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  where(expr, operator, value, bool = 'and', not = false) {
    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    // supports `.where(Boolean)`
    if ( isBoolean(expr) )
      return this.raw('1 = ' + expr ? 1 : 0)

    // supports `.where(new RawExpr(...))`
    if ( expr instanceof RawExpr )
      return this.raw(expr, [], bool)

    // supports `.where({a: 1, b: 3})`
    if ( isPlainObject(expr) ) {
      let obj = expr

      expr = cr => each(obj, (val, key) => cr.where(key, '=', val))
    }

    // supports `.where(cr => { ... })`
    if ( isFunction(expr) ) {
      let fn = expr

      fn(expr = this.newCriteria(bool, not))

      return this.add(expr)
    }

    // format the operator
    operator = String(operator).toLowerCase().trim()

    // supports between operator
    if ( operator.indexOf('between') > -1 )
      return this.between(expr, value, bool, not)

    // supports `.where('column', null)`
    if ( value === null )
      return this.isNull(expr, bool, not)

    // supports `.where('column', [...])`
    if ( isArray(value) && operator === '=' )
      return this.in(expr, value, bool, not)

    // supports `.where('expr', 'operator', qb => {...})`
    if ( isFunction(value) )
      return this.sub(expr, operator, value, bool)

    return this.add(new Basic(expr, operator, value, bool, not))
  }

  /**
   * @alias `where()`
   */
  and() {
    return this.where(...arguments)
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @return this
   */
  or(expr, operator, value) {
    return this.where(expr, operator, value, 'or')
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @return this
   */
  not(expr, operator, value, bool = 'and') {
    var not = true

    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    // format the operator
    operator = String(operator).toLowerCase().trim()

    // supports `.whereNot('column', 'between', [...])`
    if ( operator === 'between' )
      return this.between(expr, operator, value, bool, not)

    // supports `.whereNot('column', 'in', [...])`
    if ( operator === 'in' )
      return this.in(expr, operator, value, bool, not)

    // supports `.whereNot('column', 'like', 'patern')`
    if ( operator === 'like' ) {
      operator = 'not like'
      not = false
    }

    return this.where(expr, operator, value, bool, not)
  }

  /**
   * @alias `not()`
   */
  andNot() {
    return this.not(...arguments)
  }

  /**
   *
   * @param {Any} expr
   * @param {String} operator
   * @param {Any} value
   * @param {String} bool
   * @return this
   */
  orNot(expr, operator, value) {
    return this.not(expr, operator, value, 'or')
  }

  /**
   *
   * @param {String|Raw} expr
   * @param {Array} bindings
   * @param {String} bool
   * @return this
   */
  raw(expr, bindings = [], bool = 'and') {
    return this.add(new Raw(this.builder.ensureRaw(expr, bindings), bool))
  }

  /**
   *
   * @param {String|Raw} expr
   * @param {Array} bindings
   * @return this
   */
  orRaw(expr, bindings = []) {
    return this.raw(expr, bindings, 'or')
  }

  /**
   * @alias `raw()`
   */
  andRaw() {
    return this.raw(...arguments)
  }

  /**
   *
   * @param {String|Column} expr
   * @param {Array} values
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  between(expr, values, bool = 'and', not = false) {
    return this.add(new Between(expr, values, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  isNull(expr, bool = 'and', not = false) {
    return this.add(new IsNull(expr, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {Any} query
   * @param {String} bool
   * @return this
   */
  sub(expr, operator, query, bool = 'and') {
    query = this.builder.ensureSubQuery(query)

    return this.add(new Basic(expr, operator, query, bool))
  }

  /**
   * @param {String|Column} expr
   * @param {Array|SubQuery} values
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  in(expr, values, bool = 'and', not = false) {
    // instead of an empty array of values, a boolean expression is used
    if ( isArray(values) && isEmpty(values) ) return this.where(not)

    // accepts sub queries
    if (! isArray(values) ) {
      let operator = not ? 'not ' : '' + 'in'

      return this.sub(expr, operator, values, bool)
    }

    return this.add(new IsIn(expr, values, bool, not))
  }

  /**
   *
   * @param {SubQuery} query
   * @param {String} bool
   * @param {Boolean} not
   * @return this
   */
  exists(query, bool = 'and', not = false) {
    query = this.ensureSubQuery(query)

    return this.add(new Exists(query, bool, not))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {String|Column} value
   * @param {String} bool
   * @return this
   */
  on(expr, operator, value, bool = 'and') {
    if ( value == null && operator != null ) {
      value = operator
      operator = '='
    }

    if ( isString(value) ) expr = new Column(value)

    if ( value instanceof Column )
      throw new TypeError("Invalid column expression")

    return this.add(new Basic(expr, operator, value, bool))
  }

  /**
   *
   * @param {String|Column} expr
   * @param {String} operator
   * @param {String|Column} value
   * @return this
   */
  orOn(expr, operator, value) {
    return this.on(expr, operator, value, 'or')
  }

  /**
   * @alias `on()`
   */
  andOn() {
    return this.on(...arguments)
  }

}<|endoftext|>


<commit_before>'use strict';

class SelectorFinder {

  constructor() {
    this.selectors = {
      _id: [],
      _class: [],
    };

    this.isCommentStarted = false;
    this.isCommentEnded = false;
    this.lineCounter = 0;
  }

  findClasses (text) {
    const classRegex = /(\.[^ ):]+)/g;
    const splitter = '.';
    const selector = '_class';

    if (classRegex.test(text)) {
      text = text.match(classRegex);

      var currentClass;

      // needs to be refactored
      text.forEach((classs)=> {
        currentClass = classs.match(/(\.[^ ):]+)/)[0];
        currentClass = currentClass.split('.');
        currentClass.forEach((c)=> {
          if (c && this.isCssSelectorValid(c)) {
            c = this.filterSelector(c);
            this.pushIntoSelectors(c, selector);
          }
        });

      });
    }

    return this.selectors;

  }

  findIdSelectors (text) {
    const idRegex = /(\#[^ ):]+)/g;
    const splitter = '#';
    const selector = '_id';
    if (idRegex.test(text)) {
      text = text.match(idRegex);

      var currentClass;
      text.forEach((_id) => {
        currentClass = _id.match(/(\#[^ .):]+)/)[0];
        currentClass = currentClass.split(splitter);

        currentClass.forEach((c) => {
          if (c && this.isCssSelectorValid(c)) {
            c = this.filterSelector(c);
            this.pushIntoSelectors(c, selector);
          }
        });
      });
    }

    return this.selectors;
  }

  findCssSelectors(text) {
    this.lineCounter++;
    text = text.trim();

    if (text.startsWith('src:') ||
      text.startsWith('@') ||
      text.indexOf('"') > -1 ||
      this.isInComment(text)
    ) {
      return this.selectors;
    }

    this.findClasses(text);
    this.findIdSelectors(text);

    return this.selectors;
  }

  isInComment (text) {
    if (text.indexOf('/*') > -1) {
      this.isCommentStarted = true;
      this.isCommentEnded = false;
      return true;
    }

    if (this.isCommentStarted && text.indexOf('*/') > -1) {
      this.isCommentEnded = true;
      this.isCommentStarted = false;
    }

    return this.isCommentStarted;
  }

  pushIntoSelectors (text, selector) {
    if (!(this.selectors[selector].indexOf(text) > -1)) {
      this.selectors[selector].push(text);
    }
  }

  isColor (text) {
    const colorRegex = /#[0-9a-f]{6}|#[0-9a-f]{3}/ig;
    return colorRegex.test(text);
  }

  isCssSelectorValid (selector) {
    return /^\D.*$/.test(selector) &&
      selector.indexOf('*') === -1 &&
      selector.indexOf(';') === -1 &&
      !this.isColor('#' + selector);
  }

  removeComma (text) {
    if (text.indexOf(',') > -1) {
      return text.substring(0, text.indexOf(','));
    }

    return text;
  }

  filterSelector (selector) {
    return this.removeComma(selector);
  }

}
module.exports = SelectorFinder;
<commit_msg>extended selector filtering<commit_after>'use strict';

class SelectorFinder {

  constructor() {
    this.selectors = {
      _id: [],
      _class: [],
    };

    this.isCommentStarted = false;
    this.isCommentEnded = false;
    this.lineCounter = 0;
  }

  findClasses (text) {
    const classRegex = /(\.[^ ):]+)/g;
    const splitter = '.';
    const selector = '_class';

    if (classRegex.test(text)) {
      text = text.match(classRegex);

      var currentClass;

      // needs to be refactored
      text.forEach((classs)=> {
        currentClass = classs.match(/(\.[^ ):]+)/)[0];
        currentClass = currentClass.split('.');
        currentClass.forEach((c)=> {
          if (c && this.isCssSelectorValid(c)) {
            c = this.filterSelector(c);
            this.pushIntoSelectors(c, selector);
          }
        });

      });
    }

    return this.selectors;

  }

  findIdSelectors (text) {
    const idRegex = /(\#[^ ):]+)/g;
    const splitter = '#';
    const selector = '_id';
    if (idRegex.test(text)) {
      text = text.match(idRegex);

      var currentClass;
      text.forEach((_id) => {
        currentClass = _id.match(/(\#[^ .):]+)/)[0];
        currentClass = currentClass.split(splitter);

        currentClass.forEach((c) => {
          if (c && this.isCssSelectorValid(c)) {
            c = this.filterSelector(c);
            this.pushIntoSelectors(c, selector);
          }
        });
      });
    }

    return this.selectors;
  }

  findCssSelectors(text) {
    this.lineCounter++;
    text = text.trim();

    if (text.startsWith('src:') ||
      text.startsWith('@') ||
      text.indexOf('"') > -1 ||
      this.isInComment(text)
    ) {
      return this.selectors;
    }

    this.findClasses(text);
    this.findIdSelectors(text);

    return this.selectors;
  }

  isInComment (text) {
    if (text.indexOf('/*') > -1) {
      this.isCommentStarted = true;
      this.isCommentEnded = false;
      return true;
    }

    if (this.isCommentStarted && text.indexOf('*/') > -1) {
      this.isCommentEnded = true;
      this.isCommentStarted = false;
    }

    return this.isCommentStarted;
  }

  pushIntoSelectors (text, selector) {
    if (!(this.selectors[selector].indexOf(text) > -1)) {
      this.selectors[selector].push(text);
    }
  }

  isColor (text) {
    const colorRegex = /#[0-9a-f]{6}|#[0-9a-f]{3}/ig;
    return colorRegex.test(text);
  }

  isCssSelectorValid (selector) {
    return /^\D.*$/.test(selector) &&
      selector.indexOf('*') === -1 &&
      selector.indexOf(';') === -1 &&
      !this.isColor('#' + selector);
  }

  removeSpecialChars (text) {
    let specialChars = [',', '{'];
    for (let char of specialChars) {
      if (text.indexOf(char) > -1) {
        text = text.substring(0, text.indexOf(char));
      }
    }

    return text;
  }

  filterSelector (selector) {
    return this.removeSpecialChars(selector);
  }

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


<commit_before>/*jslint devel: true, browser: true, indent: 2, nomen: true */
/*global define, $ */

(function ($) {
  'use strict';

  var oldVal;

  /**
   * m class
   * @author Brian McAllister - http://brianmcallister.com
   * @version 0.0.1
   */
  $.m = function (element, options) {
    var plugin = this,
      privateApi = {};

    // Set up options
    this.defaults = {
      classPrefix: 'm', // Pass a string to set your own, or false to have nothing.
      clickToOpen: false, // Click the title to open the dropdown list. Behaves like a regular select box.
      arrowString: false // Add a unicode arrow icon. Pass true, or your own unicode or even blank string.
    };
    this.originalOptions = options || {};
    this.options = $.extend(this.defaults, this.originalOptions);

    /**
     * Initialize.
     */
    this.initialize = function () {
      var pre = this.options.classPrefix;

      // Set up the correct class prefix.
      this.options.classPrefix = pre ? pre + '-' : '';

      privateApi.setElementReferences()
        .buildCustomHtml()
        .setPlaceholderText()
        .bindCustomEvents();
    };

    // Private methods.
    privateApi = {
      /**
       * Set up references to commonly used elements.
       * Handle the arrowString option.
       * @private
       */
      setElementReferences: function () {
        var icon;

        // Reference to main element.
        plugin.$select = $(element).addClass(this.prefix('select'));

        // Other common elements.
        plugin.$wrapper = $('

');
        plugin.$title = $('');
        plugin.$titleTarget = plugin.$title;
        plugin.$list = $('

');

        if (plugin.options.arrowString !== false) {
          icon = '▾';

          // Use a string if it's passed.
          if (typeof plugin.options.arrowString === 'string') {
            icon = plugin.options.arrowString;
          }

          // Update the title to accomodate the text and the icon.
          plugin.$title.attr('class', this.prefix('title-wrap'))
            .append('' + icon + '');

          // Update the new target.
          plugin.$titleTarget = plugin.$title.find('.' + this.prefix('title-target'));
        }

        return this;
      },

      /**
       * Create the custom markup for the list.
       * @private
       */
      buildCustomHtml: function () {
        var $options = plugin.$select.find('option'),
          privateApi = this;

        // Wrap the select and update the $wrapper element.
        plugin.$select.wrap(plugin.$wrapper);
        plugin.$wrapper = plugin.$select.parent();

        // Create the list.
        $options.each(function (index, option) {
          var $option = $(option),
            val = $option.val(),
            text = $option.text(),
            li = '
' + text + '
';

          // Add a classname for the placeholder.
          if (val === '') {
            li = li.substring(0, 4) + 'class="' + privateApi.prefix('placeholder') + '" ' + li.substring(4);
          }

          plugin.$list.append(li);
        });

        // Append the title and the list.
        plugin.$wrapper.append(plugin.$title, plugin.$list);

        return this;
      },

      /**
       * Can update the title with whatever is currently selected,
       * but is convenient for setting the placeholder text on initialize.
       * @private
       */
      setPlaceholderText: function () {
        this.updateTitle(this.getOptionData(this.getSelected()));
        return this;
      },

      /**
       * Set up custom events for handling the custom markup, and deal with
       * change events on select box.
       * @private
       */
      bindCustomEvents: function () {
        var privateApi = this;

        // Update m by clicking dropdown list items.
        plugin.$list.on('click', 'li', function (event) {
          var $target = $(event.target),
            data = privateApi.getOptionData($target);

          privateApi.updateSelect(data);
          $target.addClass('selected').siblings().removeClass('selected');
        });

        // Update m by changing the select box.
        plugin.$select.on('change', function () {
          var data = privateApi.getOptionData(privateApi.getSelected());
          privateApi.updateTitle(data);
        });

        if (plugin.options.clickToOpen) {
          this.clickToOpen();
        }

        return this;
      },

      /**
       * Show the dropdown list.
       * @private
       */
      showList: function () {
        plugin.$list.css('display', 'block');
        return this;
      },

      /**
       * Hide the dropdown list.
       * @private
       */
      hideList: function () {
        plugin.$list.css('display', 'none');
        return this;
      },

      /**
       * Bind the events that simulate regular select box behavior. Used for the
       * clickToOpen option.
       * @private
       */
      clickToOpen: function () {
        var $win = $(window),
          privateApi = this;

        plugin.$list.css('display', 'none') // You probably want this.
          // Clicking an li always closes the list.
          .on('click.m', 'li', function () {
            privateApi.hideList();
            $win.off('click.m'); // Remove this handler if it exists.
          });

        plugin.$title.on('click.m', function () {
          // Either show or hide the list.
          if (plugin.$list.css('display') !== 'block') {
            privateApi.showList();

            setTimeout(function () {
              $win.on('click.m', function (event) {
                event.preventDefault(); // This is not working?
                privateApi.hideList();
                $win.off('click.m');
              });
            }, 15);
          } else {
            privateApi.hideList();
            $win.off('click.m');
          }
        });
      },

      /**
       * Return a classname that includes the classPrefix option.
       * @private
       * @param {String} className Class name to prefix.
       */
      prefix: function (className) {
        return plugin.options.classPrefix + className;
      },

      /**
       * Return an object for updating the select box.
       * @private
       * @param {jQuery Object} $el Element to get data from.
       */
      getOptionData: function ($el) {
        return {text: $el.text(), value: $el.val() || $el.data('value')};
      },

      /**
       * Return the currently selected option.
       * @private
       */
      getSelected: function () {
        return plugin.$select.find(':selected');
      },

      /**
       * Update the title in the custom HTML.
       * @private
       * @param {Object} data Data to use for updating. Should contain a text property.
       */
      updateTitle: function (data) {
        plugin.$titleTarget.text(data.text);
        return this;
      },

      /**
       * Update the select box.
       * @private
       * @param {Object} data Data to use. Should contain a value property.
       */
      updateSelect: function (data) {
        plugin.$select.val(data.value);

        // Check for a patched val. Fire a 'change' event if it isn't
        if (!plugin.$select.data('m').patchedVal) {
          plugin.$select.trigger('change');
        }

        return this;
      }
    };

    this.initialize();
  };

  // Monkeypatch jQuery#val to fire an event on our select.
  oldVal = $.fn.val;

  $.fn.val = function (value) {
    var $this = $(this),
      elData = $this.data('m'),
      newVal;

    // Call the old val() function...
    newVal = oldVal.apply(this, arguments);

    // ...fire our custom event...
    if (elData && value) {
      elData.patchedVal = true;
      $this.data('m', elData);
      $this.trigger('change');
    }

    // ...return the value as the old val() does.
    return newVal;
  };

  // Add the function to jQuery.
  $.fn.m = function (options) {
    return this.each(function () {
      var $this = $(this),
        menu;

      if ($this.data('m') === undefined) {
        menu = new $.m(this, options);
        $this.data('m', menu);
      }
    });
  };
}($));

// define([
//     'jquery'
// ], function ($) {
//     'use strict';
//
//
// });<commit_msg>Implement the wrapperClass option.<commit_after>/*jslint devel: true, browser: true, indent: 2, nomen: true */
/*global define, $ */

(function ($) {
  'use strict';

  var oldVal;

  /**
   * m class
   * @author Brian McAllister - http://brianmcallister.com
   * @version 0.0.1
   */
  $.m = function (element, options) {
    var plugin = this,
      privateApi = {};

    // Set up options
    this.defaults = {
      classPrefix: 'm', // Pass a string to set your own, or false to have nothing.
      wrapperClass: null, // Pass a string to set a class on the wrapper element. Useful for custom themes.
      clickToOpen: false, // Click the title to open the dropdown list. Behaves like a regular select box.
      arrowString: false // Add a unicode arrow icon. Pass true, or your own unicode or even blank string.
    };
    this.originalOptions = options || {};
    this.options = $.extend(this.defaults, this.originalOptions);

    /**
     * Initialize.
     */
    this.initialize = function () {
      var pre = this.options.classPrefix;

      // Set up the correct class prefix.
      this.options.classPrefix = pre ? pre + '-' : '';

      privateApi.setElementReferences()
        .buildCustomHtml()
        .setPlaceholderText()
        .bindCustomEvents();
    };

    // Private methods.
    privateApi = {
      /**
       * Set up references to commonly used elements.
       * Handle the arrowString option.
       * @private
       */
      setElementReferences: function () {
        var icon;

        // Reference to main element.
        plugin.$select = $(element).addClass(this.prefix('select'));

        // Other common elements.
        plugin.$wrapper = $('

');
        plugin.$title = $('');
        plugin.$titleTarget = plugin.$title;
        plugin.$list = $('

');

        if (plugin.options.arrowString !== false) {
          icon = '▾';

          // Use a string if it's passed.
          if (typeof plugin.options.arrowString === 'string') {
            icon = plugin.options.arrowString;
          }

          // Update the title to accomodate the text and the icon.
          plugin.$title.attr('class', this.prefix('title-wrap'))
            .append('' + icon + '');

          // Update the new target.
          plugin.$titleTarget = plugin.$title.find('.' + this.prefix('title-target'));
        }

        return this;
      },

      /**
       * Create the custom markup for the list.
       * @private
       */
      buildCustomHtml: function () {
        var $options = plugin.$select.find('option'),
          privateApi = this;

        // Wrap the select and update the $wrapper element.
        plugin.$select.wrap(plugin.$wrapper);
        plugin.$wrapper = plugin.$select.parent();

        // Add the wrapper class.
        if (plugin.options.wrapperClass) {
          plugin.$wrapper.addClass(privateApi.prefix(plugin.options.wrapperClass));
        }

        // Create the list.
        $options.each(function (index, option) {
          var $option = $(option),
            val = $option.val(),
            text = $option.text(),
            li = '
' + text + '
';

          // Add a classname for the placeholder.
          if (val === '') {
            li = li.substring(0, 4) + 'class="' + privateApi.prefix('placeholder') + '" ' + li.substring(4);
          }

          plugin.$list.append(li);
        });

        // Append the title and the list.
        plugin.$wrapper.append(plugin.$title, plugin.$list);

        return this;
      },

      /**
       * Can update the title with whatever is currently selected,
       * but is convenient for setting the placeholder text on initialize.
       * @private
       */
      setPlaceholderText: function () {
        this.updateTitle(this.getOptionData(this.getSelected()));
        return this;
      },

      /**
       * Set up custom events for handling the custom markup, and deal with
       * change events on select box.
       * @private
       */
      bindCustomEvents: function () {
        var privateApi = this;

        // Update m by clicking dropdown list items.
        plugin.$list.on('click', 'li', function (event) {
          var $target = $(event.target),
            data = privateApi.getOptionData($target);

          privateApi.updateSelect(data);
          $target.addClass('selected').siblings().removeClass('selected');
        });

        // Update m by changing the select box.
        plugin.$select.on('change', function () {
          var data = privateApi.getOptionData(privateApi.getSelected());
          privateApi.updateTitle(data);
        });

        if (plugin.options.clickToOpen) {
          this.clickToOpen();
        }

        return this;
      },

      /**
       * Show the dropdown list.
       * @private
       */
      showList: function () {
        plugin.$list.css('display', 'block');
        return this;
      },

      /**
       * Hide the dropdown list.
       * @private
       */
      hideList: function () {
        plugin.$list.css('display', 'none');
        return this;
      },

      /**
       * Bind the events that simulate regular select box behavior. Used for the
       * clickToOpen option.
       * @private
       */
      clickToOpen: function () {
        var $win = $(window),
          privateApi = this;

        plugin.$list.css('display', 'none') // You probably want this.
          // Clicking an li always closes the list.
          .on('click.m', 'li', function () {
            privateApi.hideList();
            $win.off('click.m'); // Remove this handler if it exists.
          });

        plugin.$title.on('click.m', function () {
          // Either show or hide the list.
          if (plugin.$list.css('display') !== 'block') {
            privateApi.showList();

            setTimeout(function () {
              $win.on('click.m', function (event) {
                event.preventDefault(); // This is not working?
                privateApi.hideList();
                $win.off('click.m');
              });
            }, 15);
          } else {
            privateApi.hideList();
            $win.off('click.m');
          }
        });
      },

      /**
       * Return a classname that includes the classPrefix option.
       * @private
       * @param {String} className Class name to prefix.
       */
      prefix: function (className) {
        return plugin.options.classPrefix + className;
      },

      /**
       * Return an object for updating the select box.
       * @private
       * @param {jQuery Object} $el Element to get data from.
       */
      getOptionData: function ($el) {
        return {text: $el.text(), value: $el.val() || $el.data('value')};
      },

      /**
       * Return the currently selected option.
       * @private
       */
      getSelected: function () {
        return plugin.$select.find(':selected');
      },

      /**
       * Update the title in the custom HTML.
       * @private
       * @param {Object} data Data to use for updating. Should contain a text property.
       */
      updateTitle: function (data) {
        plugin.$titleTarget.text(data.text);
        return this;
      },

      /**
       * Update the select box.
       * @private
       * @param {Object} data Data to use. Should contain a value property.
       */
      updateSelect: function (data) {
        plugin.$select.val(data.value);

        // Check for a patched val. Fire a 'change' event if it isn't
        if (!plugin.$select.data('m').patchedVal) {
          plugin.$select.trigger('change');
        }

        return this;
      }
    };

    this.initialize();
  };

  // Monkeypatch jQuery#val to fire an event on our select.
  oldVal = $.fn.val;

  $.fn.val = function (value) {
    var $this = $(this),
      elData = $this.data('m'),
      newVal;

    // Call the old val() function...
    newVal = oldVal.apply(this, arguments);

    // ...fire our custom event...
    if (elData && value) {
      elData.patchedVal = true;
      $this.data('m', elData);
      $this.trigger('change');
    }

    // ...return the value as the old val() does.
    return newVal;
  };

  // Add the function to jQuery.
  $.fn.m = function (options) {
    return this.each(function () {
      var $this = $(this),
        menu;

      if ($this.data('m') === undefined) {
        menu = new $.m(this, options);
        $this.data('m', menu);
      }
    });
  };
}($));

// define([
//     'jquery'
// ], function ($) {
//     'use strict';
//
//
// });<|endoftext|>


<commit_before>/* Copyright (c) 2016 Dmitri Kourennyi
   See the file COPYING for copying permission.
*/

var darkColor;
var lightColor;

self.port.on("colors", function (colors) {
    darkColor = colors[0];
    lightColor = colors[1];
    allColors = colors[2];

    if (allColors == true) {
        // Now replace document colors
        checkElementContrast(document.all[0]);

        // Other checks required when browser is in quirks mode
        if (document.compatMode == "BackCompat") {
            // tables don't inherit color
            var tables = document.getElementsByTagName("table");
            for (var i = 0; i < tables.length; i++) {
                if (getComputedStyle(tables[i]).color ==
                    getDefaultComputedStyle(tables[i]).color) {
                    //if color has not been set explicitely, then force inherit
                    tables[i].style.color = "inherit";
                }
            }
        }
    }

    // Seperately check input-like elements
    ["input", "textarea", "select", "button", "toolbarbutton"].forEach(
        function (val) {
            var elements = document.getElementsByTagName(val),
                i;
            for (i = 0; i < elements.length; i += 1) {
                checkElementContrast(elements[i]);
            }
        });
});

function checkElementContrast(element) {
    try {
        var isFgUndefined = (getComputedStyle(element).color ==
            getDefaultComputedStyle(element).color);
        var isBgUndefined = (getComputedStyle(element).backgroundColor ==
            getDefaultComputedStyle(element).backgroundColor)

        var hasBgImg = (getComputedStyle(element).backgroundImage != 'none');
    } catch (e) {
        console.log("Got exception " + e.message + " when parsing element " +
            element.tagName + "." + element.className + "#" + element.id);
        return;
    }

    isBgUndefined = isBgUndefined && !hasBgImg;

    var fg_color_defined = getComputedStyle(element).color !=
        getDefaultComputedStyle(element).color;
    var bg_color_defined = getComputedStyle(element).backgroundColor !=
        getDefaultComputedStyle(element).backgroundColor;
    var bg_img_defined = getComputedStyle(element).backgroundImage != 'none';

    if (fg_color_defined && bg_color_defined) {
        //Both colors explicitely defined, nothing to do
        return;
    }

    if (!fg_color_defined && bg_color_defined) {
        // always set fg if only bg color defined
        element.style.color = darkColor;
        return;
    }

    if (fg_color_defined && !bg_color_defined) {
        //since bg images might be transparent, set the bg color no
        //matter what
        element.style.backgroundColor = lightColor;
        return;
    }

    if (bg_img_defined) {
        //No FG or BG color, but possibly transparent image, so need
        //to set both
        element.style.color = darkColor;
        element.style.backgroundColor = lightColor;
        return;
    }

    //Nothing defined, recurse through children
    var children = element.children
    for (var i = 0; i < children.length; i++) {
        // Don't look at non-renderable elements
        switch (element.children[i].nodeName) {
        case "HEAD":
        case "TITLE":
        case "META":
        case "SCRIPT":
        case "IMG":
        case "STYLE":
            break;
        default:
            checkElementContrast(element.children[i]);
        }
    }
    return;
}
<commit_msg>Cleanups in main code<commit_after>/* Copyright (c) 2016 Dmitri Kourennyi
   See the file COPYING for copying permission.
*/

var darkColor;
var lightColor;

self.port.on("colors", function (colors) {
    'use strict';

    darkColor = colors[0];
    lightColor = colors[1];
    allColors = colors[2];

    if (allColors === true) {
        // Now replace document colors
        checkElementContrast(document.all[0]);

        // Other checks required when browser is in quirks mode
        if (document.compatMode == "BackCompat") {
            // tables don't inherit color
            var tables = document.getElementsByTagName("table");
            for (var i = 0; i < tables.length; i++) {
                if (getComputedStyle(tables[i]).color ==
                    getDefaultComputedStyle(tables[i]).color) {
                    //if color has not been set explicitely, then force inherit
                    tables[i].style.color = "inherit";
                }
            }
        }
    }

    // Seperately check input-like elements
    ["input", "textarea", "select", "button", "toolbarbutton"].forEach(
        function (val) {
            var elements = document.getElementsByTagName(val),
                i;
            for (i = 0; i < elements.length; i += 1) {
                checkElementContrast(elements[i]);
            }
        });
});

function checkElementContrast(element) {
    'use strict';

    var isBgUndefined, hasBgImg;

    try {
        var isFgUndefined = (getComputedStyle(element).color ==
            getDefaultComputedStyle(element).color);
        isBgUndefined = (getComputedStyle(element).backgroundColor ==
            getDefaultComputedStyle(element).backgroundColor);

        hasBgImg = (getComputedStyle(element).backgroundImage != 'none');
    } catch (e) {
        console.log("Got exception " + e.message + " when parsing element " +
            element.tagName + "." + element.className + "#" + element.id);
        return;
    }

    isBgUndefined = isBgUndefined && !hasBgImg;

    var fg_color_defined = getComputedStyle(element).color !=
        getDefaultComputedStyle(element).color;
    var bg_color_defined = getComputedStyle(element).backgroundColor !=
        getDefaultComputedStyle(element).backgroundColor;
    var bg_img_defined = getComputedStyle(element).backgroundImage != 'none';

    if (fg_color_defined && bg_color_defined) {
        //Both colors explicitely defined, nothing to do
        return;
    }

    if (!fg_color_defined && bg_color_defined) {
        // always set fg if only bg color defined
        element.style.color = darkColor;
        return;
    }

    if (fg_color_defined && !bg_color_defined) {
        //since bg images might be transparent, set the bg color no
        //matter what
        element.style.backgroundColor = lightColor;
        return;
    }

    if (bg_img_defined) {
        //No FG or BG color, but possibly transparent image, so need
        //to set both
        element.style.color = darkColor;
        element.style.backgroundColor = lightColor;
        return;
    }

    //Nothing defined, recurse through children
    var children = element.children;
    for (var i = 0; i < children.length; i++) {
        // Don't look at non-renderable elements
        switch (element.children[i].nodeName) {
        case "HEAD":
        case "TITLE":
        case "META":
        case "SCRIPT":
        case "IMG":
        case "STYLE":
            break;
        default:
            checkElementContrast(element.children[i]);
        }
    }
    return;
}
<|endoftext|>


<commit_before>import React from 'react';
import t from 'tcomb';
import assign from 'lodash/object/assign';
import partialRight from 'lodash/function/partialRight';
import debug from 'debug';
import listener from './listener';
import isReactComponent from '../isReactComponent';

const hasOneKey = v => Object.keys(v).length === 1;
const StringDict = t.subtype(t.dict(t.Str, t.Str), hasOneKey, 'StringDict');
const FilterStateFn = t.Func; // revenge State -> t.Bool
const FilterDict = t.subtype(t.dict(t.Str,
  t.subtype(t.Obj, ({ query, filter }) => t.Str.is(query) && t.maybe(FilterStateFn).is(filter))
  // TODO(gio): not sure why this doesn't work:
  // t.struct({
  //   query: t.Str,
  //   filter: t.maybe(FilterStateFn)
  // })
), hasOneKey, 'FilterDict');
const DeclaredQ = t.union([
  t.Str, StringDict, FilterDict
], 'DeclaredQ');
DeclaredQ.dispatch = v => t.match(v,
  t.Str, () => t.Str,
  StringDict, () => StringDict,
  FilterDict, () => FilterDict
);
const Declared = t.list(DeclaredQ, 'Declared');

const log = debug('revenge:@queries');

export default function queries(declared) {

  if (process.env.NODE_ENV !== 'production') {
    t.assert(Declared.is(declared), `@queries decorator can only be configured with a list of query ids. Optionally rename a query like this: { propName: queryId }`);
  }

  return function (Component) {

    if (process.env.NODE_ENV !== 'production') {
      const name = Component.name;
      t.assert(isReactComponent(Component), `@queries decorator can only be applied to React.Component(s)`);
      t.assert(!Component.queries, `@queries decorator can not be applied to component ${name}, queries are already defined`);
    }

    const shouldHaveAppProp = ctx => {
      t.assert(t.Obj.is(ctx.props.app), `@queries decorator: missing app prop in component ${Component.name}`);
    };

    // TODO(gio): this is perf-relevant, should be optimized a bit
    const filterDeclared = (obj, defaultValue) => {
      return declared.reduce((ac, q) => {
        const {
          propName, queryId
        } = t.match(q,
          t.Str, query => ({
            queryId: query,
            propName: query
          }),
          StringDict, q => {
            const propName = Object.keys(q)[0];
            return {
              queryId: q[propName],
              propName: propName
            };
          },
          FilterDict, q => {
            const propName = Object.keys(q)[0];
            return {
              queryId: q[propName].query,
              propName
            };
          }
        );

        return {
          ...ac,
          [propName]: obj[queryId] || defaultValue
        };
      }, {});
    };

    @listener(QueriesWrapper.prototype.forceUpdate)
    class QueriesWrapper extends React.Component {

      get() {
        if (process.env.NODE_ENV !== 'production') {
          shouldHaveAppProp(this);
        }

        return filterDeclared(this.props.app.get(), null);
      }

      readyState() {
        if (process.env.NODE_ENV !== 'production') {
          shouldHaveAppProp(this);
        }

        const data = this.props.app.get();
        const filteredMeta = data && data.__meta ? filterDeclared(data.__meta, {
          loading: false, cache: false
        }) : {};
        return {
          readyState: assign(filteredMeta, {
            loading: Object.keys(filteredMeta).filter(({ loading }) => loading).length === declared.length,
            cache: Object.keys(filteredMeta).filter(({ cache }) => cache).length === declared.length
          })
        };
      }

      render() {
        return <Component {...this.props} {...this.get()} {...this.readyState()} />;
      }

    }

    const defaultFilter = () => true;
    const getFilter = partialRight(t.match,
      t.Str, query => ({ query, filter: defaultFilter }),
      StringDict, query => ({ query: query[Object.keys(query)[0]], filter: defaultFilter }),
      FilterDict, q => {
        const qq = q[Object.keys(q)[0]];
        const { query, filter } = qq;
        return { query, filter: FilterStateFn.is(filter) ? filter : defaultFilter };
      }
    );

    QueriesWrapper.queries = declared.reduce((ac, q) => {
      const query = t.Str.is(q) ? q : t.Str.is(q[Object.keys(q)[0]]) ? q[Object.keys(q)[0]] : q[Object.keys(q)[0]].query;
      return assign(ac, {
        [query]: getFilter(q)
      });
    }, {});
    log(`${Component.name} cleaned up queries: %o`, QueriesWrapper.queries);

    return QueriesWrapper;
  };
}
<commit_msg>fix readyState<commit_after>import React from 'react';
import t from 'tcomb';
import assign from 'lodash/object/assign';
import partialRight from 'lodash/function/partialRight';
import debug from 'debug';
import listener from './listener';
import isReactComponent from '../isReactComponent';

const hasOneKey = v => Object.keys(v).length === 1;
const StringDict = t.subtype(t.dict(t.Str, t.Str), hasOneKey, 'StringDict');
const FilterStateFn = t.Func; // revenge State -> t.Bool
const FilterDict = t.subtype(t.dict(t.Str,
  t.subtype(t.Obj, ({ query, filter }) => t.Str.is(query) && t.maybe(FilterStateFn).is(filter))
  // TODO(gio): not sure why this doesn't work:
  // t.struct({
  //   query: t.Str,
  //   filter: t.maybe(FilterStateFn)
  // })
), hasOneKey, 'FilterDict');
const DeclaredQ = t.union([
  t.Str, StringDict, FilterDict
], 'DeclaredQ');
DeclaredQ.dispatch = v => t.match(v,
  t.Str, () => t.Str,
  StringDict, () => StringDict,
  FilterDict, () => FilterDict
);
const Declared = t.list(DeclaredQ, 'Declared');

const log = debug('revenge:@queries');

export default function queries(declared) {

  if (process.env.NODE_ENV !== 'production') {
    t.assert(Declared.is(declared), `@queries decorator can only be configured with a list of query ids. Optionally rename a query like this: { propName: queryId }`);
  }

  return function (Component) {

    if (process.env.NODE_ENV !== 'production') {
      const name = Component.name;
      t.assert(isReactComponent(Component), `@queries decorator can only be applied to React.Component(s)`);
      t.assert(!Component.queries, `@queries decorator can not be applied to component ${name}, queries are already defined`);
    }

    const shouldHaveAppProp = ctx => {
      t.assert(t.Obj.is(ctx.props.app), `@queries decorator: missing app prop in component ${Component.name}`);
    };

    // TODO(gio): this is perf-relevant, should be optimized a bit
    const filterDeclared = (obj, defaultValue) => {
      return declared.reduce((ac, q) => {
        const {
          propName, queryId
        } = t.match(q,
          t.Str, query => ({
            queryId: query,
            propName: query
          }),
          StringDict, q => {
            const propName = Object.keys(q)[0];
            return {
              queryId: q[propName],
              propName: propName
            };
          },
          FilterDict, q => {
            const propName = Object.keys(q)[0];
            return {
              queryId: q[propName].query,
              propName
            };
          }
        );

        return {
          ...ac,
          [propName]: obj[queryId] || defaultValue
        };
      }, {});
    };

    @listener(QueriesWrapper.prototype.forceUpdate)
    class QueriesWrapper extends React.Component {

      get() {
        if (process.env.NODE_ENV !== 'production') {
          shouldHaveAppProp(this);
        }

        return filterDeclared(this.props.app.get(), null);
      }

      readyState() {
        if (process.env.NODE_ENV !== 'production') {
          shouldHaveAppProp(this);
        }

        const data = this.props.app.get();
        const filteredMeta = data && data.__meta ? filterDeclared(data.__meta, {
          loading: false, cache: false
        }) : {};
        return {
          readyState: assign(filteredMeta, {
            loading: Object.keys(filteredMeta).filter(key => filteredMeta[key].loading).length > 0,
            cache: Object.keys(filteredMeta).filter(key => filteredMeta[key].cache).length === declared.length
          })
        };
      }

      render() {
        return <Component {...this.props} {...this.get()} {...this.readyState()} />;
      }

    }

    const defaultFilter = () => true;
    const getFilter = partialRight(t.match,
      t.Str, query => ({ query, filter: defaultFilter }),
      StringDict, query => ({ query: query[Object.keys(query)[0]], filter: defaultFilter }),
      FilterDict, q => {
        const qq = q[Object.keys(q)[0]];
        const { query, filter } = qq;
        return { query, filter: FilterStateFn.is(filter) ? filter : defaultFilter };
      }
    );

    QueriesWrapper.queries = declared.reduce((ac, q) => {
      const query = t.Str.is(q) ? q : t.Str.is(q[Object.keys(q)[0]]) ? q[Object.keys(q)[0]] : q[Object.keys(q)[0]].query;
      return assign(ac, {
        [query]: getFilter(q)
      });
    }, {});
    log(`${Component.name} cleaned up queries: %o`, QueriesWrapper.queries);

    return QueriesWrapper;
  };
}
<|endoftext|>


<commit_before>import vuvuzela from 'vuvuzela';

function safeJsonParse(str) {
  try {
    return JSON.parse(str);
  } catch (e) {
    /* istanbul ignore next */
    return vuvuzela.parse(str);
  }
}

export default safeJsonParse;<commit_msg>(#4861) - faster json parse<commit_after>import vuvuzela from 'vuvuzela';

function slowJsonParse(str) {
  try {
    return JSON.parse(str);
  } catch (e) {
    /* istanbul ignore next */
    return vuvuzela.parse(str);
  }
}

function safeJsonParse(str) {
  // try/catch is deoptimized in V8, leading to slower
  // times than we'd like to have. Most documents are _not_
  // huge, and do not require a slower code path just to parse them.
  // We can be pretty sure that a document under 50000 characters
  // will not be so deeply nested as to throw a stack overflow error
  // (depends on the engine and available memory, though, so this is
  // just a hunch). 50000 was chosen based on the average length
  // of this string in our test suite, to try to find a number that covers
  // most of our test cases (26 over this size, 26378 under it).
  if (str.length < 50000) {
    return JSON.parse(str);
  }
  return slowJsonParse(str);
}

export default safeJsonParse;<|endoftext|>


<commit_before>import React from 'react'
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'

export default createDevTools(
  <DockMonitor toggleVisibilityKey="H" changePositionKey="Q">
    <LogMonitor />
  </DockMonitor>
)
<commit_msg>change dev_tools shortcut<commit_after>import React from 'react'
import { createDevTools } from 'redux-devtools'
import LogMonitor from 'redux-devtools-log-monitor'
import DockMonitor from 'redux-devtools-dock-monitor'

export default createDevTools(
  <DockMonitor toggleVisibilityKey="ctrl-H" changePositionKey="ctrl-Q">
    <LogMonitor />
  </DockMonitor>
)
<|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient;

var url = "mongodb://localhost:27017/designiot";

function MongoPersistence() {

}

MongoPersistence.prototype.insert = function (payload) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var insertDocuments = function (db, callback) {
            var collection = db.collection("documents");
            collection.insert(payload, function (err, result) {
                callback(result);
            });
        };
        insertDocuments(db, function () {
            db.close();
        });
    });
};

MongoPersistence.prototype.update = function (payload) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var updateDocument = function (db, callback) {
            var collection = db.collection("documents");
            var topic = {user: payload.user};
            collection.update(topic, {$set: {led: payload.led}}, function (err, result) {
                callback(result);
            });
        };
        updateDocument(db, function () {
            db.close();
        });
    });
};

MongoPersistence.prototype.query = function (queryOptions, queryCB) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var findDocuments = function (db, query, callback) {
            var collection = db.collection("documents");
            collection.find(query, {_id: false}).toArray(function (err, docs) {
                callback(docs);
            });
        };

        findDocuments(db, queryOptions, function (result) {
            db.close();
            queryCB(result);
        });
    });
};

module.exports = MongoPersistence;<commit_msg>update query method<commit_after>var MongoClient = require('mongodb').MongoClient;

var url = "mongodb://localhost:27017/designiot";

function MongoPersistence() {

}

MongoPersistence.prototype.insert = function (payload) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var insertDocuments = function (db, callback) {
            var collection = db.collection("documents");
            collection.insert(payload, function (err, result) {
                callback(result);
            });
        };
        insertDocuments(db, function () {
            db.close();
        });
    });
};

MongoPersistence.prototype.update = function (payload) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var updateDocument = function (db, callback) {
            var collection = db.collection("documents");
            var topic = {user: payload.user};
            collection.update(topic, {$set: {led: payload.led}}, function (err, result) {
                callback(result);
            });
        };
        updateDocument(db, function () {
            db.close();
        });
    });
};

MongoPersistence.prototype.query = function (queryOptions, queryCB) {
    'use strict';
    MongoClient.connect(url, function (err, db) {
        var findDocuments = function (db, query, callback) {
            var collection = db.collection("documents");
            collection.find(query).toArray(function (err, docs) {
                callback(docs);
            });
        };

        findDocuments(db, queryOptions, function (result) {
            db.close();
            queryCB(result);
        });
    });
};

module.exports = MongoPersistence;<|endoftext|>


<commit_before>function handleerrors(request, display) {
	request.success(function(result) {
		var formatted = result.split("\n").join("
");
		$(display).html(formatted);
		// $(display).text(result);
	});

	request.error(function(jqXHR, textStatus, errorThrown) {
		if (textStatus == 'timeout') {
			$(display).text("Server is not responding");
			return;
		}

		var msg = '
Error
';
		$(display).html(msg);
	});
}

function getplan() {
	var query = editor.getValue();
	var request = $.get("plan", {
		query : query
	});
	handleerrors(request, "#plan");
	var request = $.get("dot", {
		query : query,
		type : 'ra'
	});
	request.success(function(dot) {
		var result = Viz(dot, "svg");
		$('#relational_svg').html(result);
		$('svg').width('100%');
		$('svg').height('100%');
	})
};

function optimizeplan() {
	getplan(); // make sure the plan matches the query
	var query = editor.getValue();
	var request = $.get("optimize", {
		query : query,
	});
	handleerrors(request, "#optimized");
	var request = $.get("dot", {
		query : query,
		type : 'myria'
	});
	request.success(function(dot) {
		var result = Viz(dot, "svg");
		$('#myria_svg').html(result);
		$('svg').width('100%');
		$('svg').height('100%');
	})
}

function compileplan() {
	var query = editor.getValue();
	var url = "compile?" + $.param({
		query : query,
	});
	window.open(url, '_blank');
}

function displayQueryStatus(data) {
	var queryStatus = data['query_status'];
	var start_time = queryStatus['start_time'];
	var end_time = queryStatus['finish_time'];
	var elapsed = queryStatus['elapsed_nanos'] / 1e9;
	var status = queryStatus['status'];
	var query_id = queryStatus['query_id'];
	$("#executed").text(
			"#" + query_id + " status:" + status + " start:" + start_time
					+ " end:" + end_time + " elapsed: " + elapsed);
	if (!end_time) {
		setTimeout(function() {
			checkQueryStatus(query_id);
		}, 1000);
	}
}

function checkQueryStatus(query_id) {
	$.ajax("execute", {
		type : 'GET',
		data : {
			query_id : query_id
		},
		statusCode : {
			200 : displayQueryStatus,
			201 : displayQueryStatus,
			202 : displayQueryStatus,
			400 : displayQueryStatus
		}
	});
}

function executeplan() {
	$('#executed').text('...');
	optimizeplan(); // make sure the plan matches the query
	var query = editor.getValue();
	var request = $.ajax("execute", {
		type : 'POST',
		data : {
			query : query,
		},
		statusCode : {
			200 : displayQueryStatus,
			201 : displayQueryStatus,
			202 : displayQueryStatus,
		}
	});
	request.error(function(jqXHR, textStatus, errorThrown) {
		$('#executed').text(jqXHR.responseText);
	});
}

function resetResults() {
	$(".display").empty();
	$("svg").empty();
}

function updateExamples(language) {
	var doUpdateExamples = function(data) {
		var examplesList = $('#examples-list');
		examplesList.empty();
		if (data.length == 0) {
			examplesList.append('No ' + language +' examples found');
			return;
		}
		/* Populate the list of examples. */
		for (var i = 0; i < data.length; ++i) {
			examplesList.append('
'+data[i][0]+'
');
			examplesList.append('
'+data[i][1]+'
');
		}
		/* Restore the click functionality on the examples. */
		$(".example").click(function() {
			resetResults();
			var example_query = $(this).text();
			editor.setValue(example_query);
			optimizeplan();
		});
	}
	$.ajax("examples", {
		type : 'GET',
		data : {
			language : language
		},
		success : doUpdateExamples
	});
}

function changeLanguage() {
	/* First make sure it's a valid language. */
	var languages = ['Datalog', 'Myria'];
	var language = $(this).text();
	var i = languages.indexOf(language);
	if (i == -1) {
		return false;
	}

	/* Now let's update the UI around the language selector button. */
	languages.splice(i,1);
	$('#parse-btn').text("Parse "+language);
	var languageMenu = $('#language-menu');
	languageMenu.empty();
	for (var i=0; i < languages.length; ++i) {
		languageMenu.append('
'+languages[i]+'
');
	}
	$(".changer").click(changeLanguage);

	/* Now let's update the examples. */
	updateExamples(language);
}

$(document).ready(function() {
	editor.on("change", resetResults);
	editor.on("keydown", resetResults);
	editor.on("keypress", resetResults);
	$(".planner").click(optimizeplan);
	$(".compiler").click(compileplan);
	$(".executor").click(executeplan);
	$(".changer").click(changeLanguage);
	$(".example").click(function() {
		resetResults();
		var example_query = $(this).text();
		editor.setValue(example_query);
		optimizeplan();
	});
	optimizeplan();
});
<commit_msg>editor.js: submit the selected code language as a query parameter<commit_after>/* Setup the global language variable. */
var editorLanguage = 'Datalog';

function handleerrors(request, display) {
	request.success(function(result) {
		var formatted = result.split("\n").join("
");
		$(display).html(formatted);
	});

	request.error(function(jqXHR, textStatus, errorThrown) {
		if (textStatus == 'timeout') {
			$(display).text("Server is not responding");
			return;
		}

		var msg = '
Error
';
		$(display).html(msg);
	});
}

function getplan() {
	var query = editor.getValue();
	var request = $.get("plan", {
		query : query,
		language : editorLanguage
	});
	handleerrors(request, "#plan");
	var request = $.get("dot", {
		query : query,
		type : 'ra',
		language : editorLanguage
	});
	request.success(function(dot) {
		var result = Viz(dot, "svg");
		$('#relational_svg').html(result);
		$('svg').width('100%');
		$('svg').height('100%');
	})
};

function optimizeplan() {
	getplan(); // make sure the plan matches the query
	var query = editor.getValue();
	var request = $.get("optimize", {
		query : query,
		language : editorLanguage
	});
	handleerrors(request, "#optimized");
	var request = $.get("dot", {
		query : query,
		type : 'myria',
		language : editorLanguage
	});
	request.success(function(dot) {
		var result = Viz(dot, "svg");
		$('#myria_svg').html(result);
		$('svg').width('100%');
		$('svg').height('100%');
	})
}

function compileplan() {
	var query = editor.getValue();
	var url = "compile?" + $.param({
		query : query,
	});
	window.open(url, '_blank');
}

function displayQueryStatus(data) {
	var queryStatus = data['query_status'];
	var start_time = queryStatus['start_time'];
	var end_time = queryStatus['finish_time'];
	var elapsed = queryStatus['elapsed_nanos'] / 1e9;
	var status = queryStatus['status'];
	var query_id = queryStatus['query_id'];
	$("#executed").text(
			"#" + query_id + " status:" + status + " start:" + start_time
					+ " end:" + end_time + " elapsed: " + elapsed);
	if (!end_time) {
		setTimeout(function() {
			checkQueryStatus(query_id);
		}, 1000);
	}
}

function checkQueryStatus(query_id) {
	$.ajax("execute", {
		type : 'GET',
		data : {
			query_id : query_id,
			language : editorLanguage
		},
		statusCode : {
			200 : displayQueryStatus,
			201 : displayQueryStatus,
			202 : displayQueryStatus,
			400 : displayQueryStatus
		}
	});
}

function executeplan() {
	$('#executed').text('...');
	optimizeplan(); // make sure the plan matches the query
	var query = editor.getValue();
	var request = $.ajax("execute", {
		type : 'POST',
		data : {
			query : query,
			language : editorLanguage
		},
		statusCode : {
			200 : displayQueryStatus,
			201 : displayQueryStatus,
			202 : displayQueryStatus,
		}
	});
	request.error(function(jqXHR, textStatus, errorThrown) {
		$('#executed').text(jqXHR.responseText);
	});
}

function resetResults() {
	$(".display").empty();
	$("svg").empty();
}

function updateExamples(language) {
	var doUpdateExamples = function(data) {
		var examplesList = $('#examples-list');
		examplesList.empty();
		if (data.length == 0) {
			examplesList.append('No ' + language + ' examples found');
			return;
		}
		/* Populate the list of examples. */
		for ( var i = 0; i < data.length; ++i) {
			examplesList.append('
' + data[i][0] + '
');
			examplesList
					.append('
' + data[i][1] + '
');
		}
		/* Restore the click functionality on the examples. */
		$(".example").click(function() {
			resetResults();
			var example_query = $(this).text();
			editor.setValue(example_query);
			optimizeplan();
		});
		/*
		 * Finally, set the global variable editorLanguage to the new language.
		 * This makes all the API calls back use this query parameter.
		 */
		editorLanguage = language;
	}
	$.ajax("examples", {
		type : 'GET',
		data : {
			language : language
		},
		success : doUpdateExamples
	});
}

function changeLanguage() {
	/* First make sure it's a valid language. */
	var languages = [ 'Datalog', 'Myria' ];
	var language = $(this).text();
	var i = languages.indexOf(language);
	if (i == -1) {
		return false;
	}

	/* Now let's update the UI around the language selector button. */
	languages.splice(i, 1);
	$('#parse-btn').text("Parse " + language);
	var languageMenu = $('#language-menu');
	languageMenu.empty();
	for ( var i = 0; i < languages.length; ++i) {
		languageMenu.append('
' + languages[i]
				+ '
');
	}
	$(".changer").click(changeLanguage);

	/* Now let's update the examples. */
	updateExamples(language);
}

$(document).ready(function() {
	editor.on("change", resetResults);
	editor.on("keydown", resetResults);
	editor.on("keypress", resetResults);
	$(".planner").click(optimizeplan);
	$(".compiler").click(compileplan);
	$(".executor").click(executeplan);
	$(".changer").click(changeLanguage);
	$(".example").click(function() {
		resetResults();
		var example_query = $(this).text();
		editor.setValue(example_query);
		optimizeplan();
	});
	optimizeplan();
});
<|endoftext|>


<commit_before>/**
 * GitHub Alert Notifications
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This notifies a set of rooms of changes to specific repositor(y/ies).
 *
 * Much of this comes from Ecuacion's bot, which is based on xfix's bot,
 * and jd who originally converted this to PS server-side.
 *
 * @license MIT license
 */

'use strict';

let gitConfig = {};
const git = exports.github = require('githubhook')(gitConfig);

if (!Config.github) {
	return;
} else if (Config.github === {}) {
	gitConfig.port = 3420;
	gitConfig.secret = "Sna@vast34";
} else {
	gitConfig = {
		port: Config.github.port,
		secret: Config.github.secret,
	};
}

let updates = {};
let targetRooms = (Config.github.rooms && Config.github.rooms.length) ? Config.github.rooms : ['development'];
targetRooms = targetRooms.map(toId);
for (let i = 0; i < targetRooms.length; i++) {
	targetRooms[i] = Rooms(targetRooms[i]);
}
let gitBans = {};
if (targetRooms[0].chatRoomData) targetRooms[0].chatRoomData.gitBans = gitBans;

let sendReport = function (html) {
	for (let curRoom of targetRooms) {
		if (curRoom) curRoom.add(`|html|<div class="infobox">${html}</div>`).update();
	}
};

git.on('push', (repo, ref, result) => {
	let url = result.compare;
	let branch = /[^/]+$/.exec(ref)[0];
	let messages = [];
	let message = "";
	message += `[<font color='FF00FF'>${Chat.escapeHTML(repo)}</font>] `;
	message += `<font color='909090'>${Chat.escapeHTML(result.pusher.name)}</font> `;
	message += (result.forced ? 'force-pushed' : 'pushed') + " ";
	message += `<b>${Chat.escapeHTML(result.commits.length)}</b> `;
	message += `new commit${(result.commits.length === 1 ? '' : 's')} to `;
	message += `<font color='800080'>${Chat.escapeHTML(branch)}</font>: `;
	message += `<a href="${Chat.escapeHTML(url)}">View & compare</a>`;
	messages.push(message);
	result.commits.forEach(function (commit) {
		let commitMessage = commit.message;
		let shortCommit = /.+/.exec(commitMessage)[0];
		message = "";
		message += `<font color='FF00FF'>${Chat.escapeHTML(repo)}</font>/`;
		message += Chat.html`<font color='800080'>${branch}</font> `;
		message += Chat.html`<a href="${commit.url}">`;
		message += Chat.html`<font color='606060'>${commit.id.substring(0, 6)}</font></a> `;
		message += Chat.html`<font color='909090'>${commit.author.name}</font>: ${shortCommit}`;
		if (commitMessage !== shortCommit) message += '…';
		messages.push(message);
	});
	sendReport(messages.join('
'));
});

git.on('pull_request', function pullRequest(repo, ref, result) {
	let COOLDOWN = 10 * 60 * 1000;
	let requestUsername = toId(result.sender.login);
	if (requestUsername in gitBans) return;
	let requestNumber = result.pull_request.number;
	let url = result.pull_request.html_url;
	let action = result.action;
	if (!updates[repo]) updates[repo] = {};
	if (action === 'synchronize') {
		action = 'updated';
	}
	if (action === 'labeled' || action === 'unlabeled') {
		// Nobody cares about labels
		return;
	}
	let now = Date.now();
	if (updates[repo][requestNumber] && updates[repo][requestNumber] + COOLDOWN > now) {
		return;
	}
	updates[repo][requestNumber] = now;
	let message = "";
	message += `[<font color='FF00FF'>${repo}</font>] `;
	message += `<font color='909090'>${result.sender.login}</font> `;
	message += `${action} pull request <a href="${url}">#${requestNumber}</a>: `;
	message += result.pull_request.title;
	sendReport(message);
});

git.listen();

exports.commands = {
	gitban: function (target, room, user, connection, cmd) {
		let targetRoomIDs = targetRooms.map(room => {
			return room.id;
		});
		if (!targetRoomIDs.includes(toId(room))) return this.errorReply(`The command "/${cmd}" does not exist. To send a message starting with "/${cmd}", type "//${cmd}".`);
		if (!this.can('ban', null, room)) return false;
		if (!target) return this.parse('/help gitban');
		target = target.trim();
		if (gitBans[toId(target)]) return this.errorReply(`The GitHub Username '${target} already exists on the GitHub Alert Blacklist.'`);
		gitBans[toId(target)] = 1;
		this.privateModCommand(`(${target} was added to GitHub Alert Blacklist by ${user.name}.)`);
	},
	gitbanhelp: ["/gitban <github username>: Makes the GitHub Plugin ignore the github username's alerts. Requires: @ # & ~"],

	gitunban: function (target, room, user, connection, cmd) {
		let targetRoomIDs = targetRooms.map(room => {
			return room.id;
		});
		if (!targetRoomIDs.includes(toId(room))) return this.errorReply(`The command "/${cmd}" does not exist. To send a message starting with "/${cmd}", type "//${cmd}".`);
		if (!this.can('ban', null, room)) return false;
		if (!target) return this.parse('/help gitunban');
		target = target.trim();
		if (!gitBans[toId(target)]) return this.errorReply(`The GitHub Username '${target} does not exist on the GitHub Alert Blacklist.'`);
		delete gitBans[toId(target)];
		this.privateModCommand(`(${target} was removed from the GitHub Alert Blacklist by ${user.name}.)`);
	},
	gitunbanhelp: ["/gitunban <github username>: Removes the GitHub Username from GitHub Alert Blacklist. Requires: @ # & ~"],
};
<commit_msg>Bot test<commit_after>/**
 * GitHub Alert Notifications Test
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * This notifies a set of rooms of changes to specific repositor(y/ies).
 *
 * Much of this comes from Ecuacion's bot, which is based on xfix's bot,
 * and jd who originally converted this to PS server-side.
 *
 * @license MIT license
 */

'use strict';

let gitConfig = {};
const git = exports.github = require('githubhook')(gitConfig);

if (!Config.github) {
	return;
} else if (Config.github === {}) {
	gitConfig.port = 3420;
	gitConfig.secret = "Sna@vast34";
} else {
	gitConfig = {
		port: Config.github.port,
		secret: Config.github.secret,
	};
}

let updates = {};
let targetRooms = (Config.github.rooms && Config.github.rooms.length) ? Config.github.rooms : ['development'];
targetRooms = targetRooms.map(toId);
for (let i = 0; i < targetRooms.length; i++) {
	targetRooms[i] = Rooms(targetRooms[i]);
}
let gitBans = {};
if (targetRooms[0].chatRoomData) targetRooms[0].chatRoomData.gitBans = gitBans;

let sendReport = function (html) {
	for (let curRoom of targetRooms) {
		if (curRoom) curRoom.add(`|html|<div class="infobox">${html}</div>`).update();
	}
};

git.on('push', (repo, ref, result) => {
	let url = result.compare;
	let branch = /[^/]+$/.exec(ref)[0];
	let messages = [];
	let message = "";
	message += `[<font color='FF00FF'>${Chat.escapeHTML(repo)}</font>] `;
	message += `<font color='909090'>${Chat.escapeHTML(result.pusher.name)}</font> `;
	message += (result.forced ? 'force-pushed' : 'pushed') + " ";
	message += `<b>${Chat.escapeHTML(result.commits.length)}</b> `;
	message += `new commit${(result.commits.length === 1 ? '' : 's')} to `;
	message += `<font color='800080'>${Chat.escapeHTML(branch)}</font>: `;
	message += `<a href="${Chat.escapeHTML(url)}">View & compare</a>`;
	messages.push(message);
	result.commits.forEach(function (commit) {
		let commitMessage = commit.message;
		let shortCommit = /.+/.exec(commitMessage)[0];
		message = "";
		message += `<font color='FF00FF'>${Chat.escapeHTML(repo)}</font>/`;
		message += Chat.html`<font color='800080'>${branch}</font> `;
		message += Chat.html`<a href="${commit.url}">`;
		message += Chat.html`<font color='606060'>${commit.id.substring(0, 6)}</font></a> `;
		message += Chat.html`<font color='909090'>${commit.author.name}</font>: ${shortCommit}`;
		if (commitMessage !== shortCommit) message += '…';
		messages.push(message);
	});
	sendReport(messages.join('
'));
});

git.on('pull_request', function pullRequest(repo, ref, result) {
	let COOLDOWN = 10 * 60 * 1000;
	let requestUsername = toId(result.sender.login);
	if (requestUsername in gitBans) return;
	let requestNumber = result.pull_request.number;
	let url = result.pull_request.html_url;
	let action = result.action;
	if (!updates[repo]) updates[repo] = {};
	if (action === 'synchronize') {
		action = 'updated';
	}
	if (action === 'labeled' || action === 'unlabeled') {
		// Nobody cares about labels
		return;
	}
	let now = Date.now();
	if (updates[repo][requestNumber] && updates[repo][requestNumber] + COOLDOWN > now) {
		return;
	}
	updates[repo][requestNumber] = now;
	let message = "";
	message += `[<font color='FF00FF'>${repo}</font>] `;
	message += `<font color='909090'>${result.sender.login}</font> `;
	message += `${action} pull request <a href="${url}">#${requestNumber}</a>: `;
	message += result.pull_request.title;
	sendReport(message);
});

git.listen();

exports.commands = {
	gitban: function (target, room, user, connection, cmd) {
		let targetRoomIDs = targetRooms.map(room => {
			return room.id;
		});
		if (!targetRoomIDs.includes(toId(room))) return this.errorReply(`The command "/${cmd}" does not exist. To send a message starting with "/${cmd}", type "//${cmd}".`);
		if (!this.can('ban', null, room)) return false;
		if (!target) return this.parse('/help gitban');
		target = target.trim();
		if (gitBans[toId(target)]) return this.errorReply(`The GitHub Username '${target} already exists on the GitHub Alert Blacklist.'`);
		gitBans[toId(target)] = 1;
		this.privateModCommand(`(${target} was added to GitHub Alert Blacklist by ${user.name}.)`);
	},
	gitbanhelp: ["/gitban <github username>: Makes the GitHub Plugin ignore the github username's alerts. Requires: @ # & ~"],

	gitunban: function (target, room, user, connection, cmd) {
		let targetRoomIDs = targetRooms.map(room => {
			return room.id;
		});
		if (!targetRoomIDs.includes(toId(room))) return this.errorReply(`The command "/${cmd}" does not exist. To send a message starting with "/${cmd}", type "//${cmd}".`);
		if (!this.can('ban', null, room)) return false;
		if (!target) return this.parse('/help gitunban');
		target = target.trim();
		if (!gitBans[toId(target)]) return this.errorReply(`The GitHub Username '${target} does not exist on the GitHub Alert Blacklist.'`);
		delete gitBans[toId(target)];
		this.privateModCommand(`(${target} was removed from the GitHub Alert Blacklist by ${user.name}.)`);
	},
	gitunbanhelp: ["/gitunban <github username>: Removes the GitHub Username from GitHub Alert Blacklist. Requires: @ # & ~"],
};
<|endoftext|>


<commit_before>// ==UserScript==
// @name        chatwork_alert
// @version     1.1.1
// @namespace   http://fluidapp.com
// @description Better ChatWork notification. Supports Growl and dock badge when running in Fluid.
// @include     https://www.chat-work.com/*
// @include     https://www.chatwork.com/*
// @author      Marica Odagaki
// ==/UserScript==
//
// Released under the Simplified BSD License.

function main() {

// Favicon.js - Change favicon dynamically [http://ajaxify.com/run/favicon].
// Copyright (c) 2008 Michael Mahemoff. Icon updates only work in Firefox and Opera.
// Background and MIT License notice at end of file, see the homepage for more.

/* USAGE:

    favicon.change("/icon/active.ico", "new title"); // Cancels any animation/scrolling
    favicon.change("/icon/active.ico"); // leaves title alone. Cancels any animation.
    favicon.change(null, "new title"); // leaves icon alone. Cancels any scrolling.

    favicon.animate(["icon1.ico", "icon2.ico", ...]);
    favicon.animate(["icon1.ico", "icon2.ico", ...], {delay: 500} );
      // Tip: Use "" as the last element to make an empty icon between cycles.
      // Default delay is 2000ms
    // animate() cancels any previous animation

    favicon.scrollTitle("new title");
    favicon.scrollTitle("new title", { delay: 200, gap: "------"} )
      // delay is delay between each scroll unit
      // gap is string appended to title (default: "      ")
    // scrollTitle() cancels any previous scrolling

    favicon.unscroll();

    favicon.unanimate();
*/

var favicon = {

// -- "PUBLIC" ----------------------------------------------------------------

change: function(optionalIconURL, optionalDocTitle) {
  if (optionalIconURL) {
    clearTimeout(this.animateTimer);
    this.addLink(optionalIconURL, true);
  }
  if (optionalDocTitle) {
    clearTimeout(this.scrollTimer);
    document.title = optionalDocTitle;
  }
},

animate: function(iconSequence, options) {
  this.unanimate();
  options = options || {};
  options["delay"] = parseInt(options["delay"]) || 2000;
  this.preloadIcons(iconSequence);
  this.iconSequence = iconSequence;
  favicon.index = 0;
  favicon.change(iconSequence[0]);
  this.animateTimer = setInterval(function() {
    favicon.index = (favicon.index+1) % favicon.iconSequence.length;
    favicon.addLink(favicon.iconSequence[favicon.index], false);
  }, options["delay"]);
},

unanimate: function() {
  clearTimeout(this.animateTimer);
},

scrollTitle: function(title, options) {
  this.unscroll();
  options = options || {};
  options["delay"] = options["delay"] || 250;
  options["gap"]   = options["gap"]   || "     ";
  title = title+options["gap"];
  document.title = title;
  titleOffset = 0;
  this.scrollTimer = setInterval(function() {
    var startPos = (titleOffset++) % title.length;
    var newTitle = title.substr(startPos);
    newTitle += title.substr(0,startPos);
    document.title = newTitle;
  }, options["delay"]);
},

unscroll: function() {
  clearTimeout(this.scrollTimer);
},

changeTitle: function(title) {
  document.title = title;
},

// -- "PRIVATE" ---------------------------------------------------------------

scrollTimer: null,
animateTimer: null,

preloadIcons: function(iconSequence) {
  var dummyImageForPreloading = document.createElement("img");
  for (var i=0; i<iconSequence.length; i++) {
    dummyImageForPreloading.src = iconSequence[i];
  }
},

addLink: function(iconURL) {
  var link = document.createElement("link");
  link.type = "image/x-icon";
  link.rel = "shortcut icon";
  link.href = iconURL;
  this.removeLinkIfExists();
  this.docHead.appendChild(link);
},

removeLinkIfExists: function() {
  var links = this.docHead.getElementsByTagName("link");
  for (var i=0; i<links.length; i++) {
    var link = links[i];
    if (link.type=="image/x-icon" && link.rel=="shortcut icon") {
      this.docHead.removeChild(link);
      return; // Assuming only one match at most.
    }
  }
},

docHead:document.getElementsByTagName("head")[0]
}
// -- end of favicon.js ---------------------------------------------------------------

    var NORMAL_SECTION_HEADER_BG = "#769D03";
    var ALERT_SECTION_HEADER_BG = "#9D0303";
    var NORMAL_PAGE_HEADER_BG = "-webkit-gradient(linear, 0% 0%, 0% 100%, from(#3F5A86), to(#243553))";
    var ALERT_PAGE_HEADER_BG = "-webkit-gradient(linear, 0% 0%, 0% 100%, from(#CB5454), to(#7D2F2F))";
    var NORMAL_FAVICON_SRC = $('link[rel="shortcut icon"]').attr("href");
    var ALERT_FAVICON_SRC = 'data:image/png;base64,' +
		'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADC0lEQVQ4jYWTTWgcdQDFf/+ZndkPMtnd' +
		'aHa7aXZjuqlBtEpJYWkSUZB66bkVBAm2ED0VRagXhdykXvSSEq2goNC09iQebLEHP0pSKLTRhJZs1tim' +
		'ceMm3e/ZnY//zHgoFqsU3+3Bew8evCd4BD7rpS8UDp8SinpMOp0TRo25o+D9Wyf+SU6CkdHIdwRyz/Dw' +
		'3NiJdya03Vkqly+x9NXs6a7FWT0g9KfFtRloPxRwOsIL2cLkXPr5F/e2y2V36OVDkdTBcexaDa9rUTo3' +
		'T6eyRUhR+WPhxxvtjTuvTNusCoBTYOydKFw7fPbCk3p2kEBKpGnSrVax6nVkpwuui9NuITsm3ZUVrp/5' +
		'6MJUuX0kBCDC7OofKwxrAxl8x4EgoPz7bW5fXUR02ihCIRqPY/QYSMtCS6fR+zJPz5SLoRCA9OlzG3VX' +
		'mqYmhGCzVOLm+XmSiooiBIEAPwA/EkUAdq2GXS3fmgEZmo2SzTx34Nzoa1Mxq9EARWXt0kV6zA5SUdAS' +
		'CRKjo+g9BrJj4koJkQiRZGb/x81iWokZ4c+fOT49FN09gNtsYu5s01pepr1eQk0myb50iNjjKXzHwXOc' +
		'+xV1jYEDhSdiYd4NIcQ4kQh2vYEQgkAIOht3aK0usbNeolmtMjQxiaYo+LaD77r4lo2IJ9CF8rZ6OCyL' +
		'Uc04knx2H9LqoiBwbJvK4mVEp0l9aZHyjV/wtDBRw8C3bQLPw1xeorqx9oE61uWmvPtr0qt19xv5vOo5' +
		'DvHBQWQQprVexHcs/MYO0nSJj4yA51G58rN3d+Hi1+Y9/60HQzpj8MnB9z+c1nM5Ak8iVJV2uUx3awtF' +
		'04gmk/iOg99osPLlp4tTm7VxIFD+DpDwXWXhCkGriW+aONvbaLpOby5HLN573+w4dNeK2GbteyB4aMrn' +
		'QW0mOZnIPXUsCEVintXWRo6+2t9aLbJ59af1WGogo3qef++35W+Fa73xep36f84EMAMxHaK7Hgt905/f' +
		'V9gqXp9r13hPhElHwH/TpvioBz/A/Egq/0Wq54fZKJP/p/0LAq9nQqBdV08AAAAASUVORK5CYII=';

    var showingAlert = false;

    function updateAlert() {
        var unreadCount = getUnreadCount();
        updateDockBadge(unreadCount);

        prevShowingAlert = showingAlert;
        showingAlert = unreadCount > 0;

        if(showingAlert != prevShowingAlert) {
            updatePageHeaderBackground(showingAlert);
            if (showingAlert) {
                // rising edge: new message has just arrived
                showGrowlNotification("New message.");
                startFaviconNotification();
            } else {
                // falling edge: no more new messages
                hideFaviconNotification();
            }
        }
    }

    function getUnreadCount() {
        if (typeof RL == 'undefined') {
            return 0;
        }
        return countNewMessages(RL.rooms);
    }

    function updatePageHeaderBackground(isAlert) {
        var background =  isAlert ? ALERT_PAGE_HEADER_BG : NORMAL_PAGE_HEADER_BG;
        $(".tm_header_bg").css("background-image", background);
    }

    function updateDockBadge(unreadCount) {
        if (window.fluid) {
            fluid.dockBadge = unreadCount > 0 ? unreadCount : null;
        }
    }

    function showGrowlNotification(description) {
        if (window.fluid) {
            fluid.showGrowlNotification({
		title: "ChatWork",
		description: description,
		priority: 0,
		sticky: false
            });
        }
    }

    function startFaviconNotification() {
        favicon.animate([ALERT_FAVICON_SRC, NORMAL_FAVICON_SRC], {delay: 1000});
        setTimeout(function() {
            if (showingAlert) {
                showFaviconNotification();
            }
        }, 10000);
    }

    function showFaviconNotification() {
        favicon.change(ALERT_FAVICON_SRC);
    }

    function hideFaviconNotification() {
        favicon.change(NORMAL_FAVICON_SRC);
    }

    function countNewMessages(rooms) {
        var count = 0
        for(var i in rooms) {
            count += rooms[i].getUnreadNum();
        }
        return count;
    }

    // do yer thang!
    var updateAlertInterval = setInterval(updateAlert, 500);
}

// warp through Chrome's isolated world
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
<commit_msg>Follow changes in DOM structure around the global nav<commit_after>// ==UserScript==
// @name        chatwork_alert
// @version     1.1.1
// @namespace   http://fluidapp.com
// @description Better ChatWork notification. Supports Growl and dock badge when running in Fluid.
// @include     https://www.chat-work.com/*
// @include     https://www.chatwork.com/*
// @author      Marica Odagaki
// ==/UserScript==
//
// Released under the Simplified BSD License.

function main() {

// Favicon.js - Change favicon dynamically [http://ajaxify.com/run/favicon].
// Copyright (c) 2008 Michael Mahemoff. Icon updates only work in Firefox and Opera.
// Background and MIT License notice at end of file, see the homepage for more.

/* USAGE:

    favicon.change("/icon/active.ico", "new title"); // Cancels any animation/scrolling
    favicon.change("/icon/active.ico"); // leaves title alone. Cancels any animation.
    favicon.change(null, "new title"); // leaves icon alone. Cancels any scrolling.

    favicon.animate(["icon1.ico", "icon2.ico", ...]);
    favicon.animate(["icon1.ico", "icon2.ico", ...], {delay: 500} );
      // Tip: Use "" as the last element to make an empty icon between cycles.
      // Default delay is 2000ms
    // animate() cancels any previous animation

    favicon.scrollTitle("new title");
    favicon.scrollTitle("new title", { delay: 200, gap: "------"} )
      // delay is delay between each scroll unit
      // gap is string appended to title (default: "      ")
    // scrollTitle() cancels any previous scrolling

    favicon.unscroll();

    favicon.unanimate();
*/

var favicon = {

// -- "PUBLIC" ----------------------------------------------------------------

change: function(optionalIconURL, optionalDocTitle) {
  if (optionalIconURL) {
    clearTimeout(this.animateTimer);
    this.addLink(optionalIconURL, true);
  }
  if (optionalDocTitle) {
    clearTimeout(this.scrollTimer);
    document.title = optionalDocTitle;
  }
},

animate: function(iconSequence, options) {
  this.unanimate();
  options = options || {};
  options["delay"] = parseInt(options["delay"]) || 2000;
  this.preloadIcons(iconSequence);
  this.iconSequence = iconSequence;
  favicon.index = 0;
  favicon.change(iconSequence[0]);
  this.animateTimer = setInterval(function() {
    favicon.index = (favicon.index+1) % favicon.iconSequence.length;
    favicon.addLink(favicon.iconSequence[favicon.index], false);
  }, options["delay"]);
},

unanimate: function() {
  clearTimeout(this.animateTimer);
},

scrollTitle: function(title, options) {
  this.unscroll();
  options = options || {};
  options["delay"] = options["delay"] || 250;
  options["gap"]   = options["gap"]   || "     ";
  title = title+options["gap"];
  document.title = title;
  titleOffset = 0;
  this.scrollTimer = setInterval(function() {
    var startPos = (titleOffset++) % title.length;
    var newTitle = title.substr(startPos);
    newTitle += title.substr(0,startPos);
    document.title = newTitle;
  }, options["delay"]);
},

unscroll: function() {
  clearTimeout(this.scrollTimer);
},

changeTitle: function(title) {
  document.title = title;
},

// -- "PRIVATE" ---------------------------------------------------------------

scrollTimer: null,
animateTimer: null,

preloadIcons: function(iconSequence) {
  var dummyImageForPreloading = document.createElement("img");
  for (var i=0; i<iconSequence.length; i++) {
    dummyImageForPreloading.src = iconSequence[i];
  }
},

addLink: function(iconURL) {
  var link = document.createElement("link");
  link.type = "image/x-icon";
  link.rel = "shortcut icon";
  link.href = iconURL;
  this.removeLinkIfExists();
  this.docHead.appendChild(link);
},

removeLinkIfExists: function() {
  var links = this.docHead.getElementsByTagName("link");
  for (var i=0; i<links.length; i++) {
    var link = links[i];
    if (link.type=="image/x-icon" && link.rel=="shortcut icon") {
      this.docHead.removeChild(link);
      return; // Assuming only one match at most.
    }
  }
},

docHead:document.getElementsByTagName("head")[0]
}
// -- end of favicon.js ---------------------------------------------------------------

    var $globalHeader = $('.globalHeader');
    var NORMAL_GLOBAL_HEADER_STYLE = {
      "background":  $globalHeader.css('background'),
      "border-top-color": $globalHeader.css('border-top-color'),
      "border-bottom-color": $globalHeader.css('border-bottom-color')
    };
    var ALERT_GLOBAL_HEADER_STYLE = {
      "background":  '#fd0006', // normal: #0058a8
      "border-top-color": "#fd0006", // normal: #0062bb
      "border-bottom-color": "#830000" // normal: #002d55
    };
    $('<style type="text/css">' +
      '.globalHeader.globalHeaderAlert {' +
      'background:' + ALERT_GLOBAL_HEADER_STYLE['background'] + ';' +
      'border-top-color:' + ALERT_GLOBAL_HEADER_STYLE['border-top-color'] + ';' +
      'border-bottom-color:' + ALERT_GLOBAL_HEADER_STYLE['border-bottom-color'] + ';' +
      '}' +
      '.globalHeader.globalHeaderAlert:after {' +
      'border-bottom-color:' + ALERT_GLOBAL_HEADER_STYLE['border-top-color'] + ';' +
      '}</style>').appendTo($("head"));
    var NORMAL_FAVICON_SRC = $('link[rel="shortcut icon"]').attr("href");
    var ALERT_FAVICON_SRC = 'data:image/png;base64,' +
		'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAADC0lEQVQ4jYWTTWgcdQDFf/+ZndkPMtnd' +
		'aHa7aXZjuqlBtEpJYWkSUZB66bkVBAm2ED0VRagXhdykXvSSEq2goNC09iQebLEHP0pSKLTRhJZs1tim' +
		'ceMm3e/ZnY//zHgoFqsU3+3Bew8evCd4BD7rpS8UDp8SinpMOp0TRo25o+D9Wyf+SU6CkdHIdwRyz/Dw' +
		'3NiJdya03Vkqly+x9NXs6a7FWT0g9KfFtRloPxRwOsIL2cLkXPr5F/e2y2V36OVDkdTBcexaDa9rUTo3' +
		'T6eyRUhR+WPhxxvtjTuvTNusCoBTYOydKFw7fPbCk3p2kEBKpGnSrVax6nVkpwuui9NuITsm3ZUVrp/5' +
		'6MJUuX0kBCDC7OofKwxrAxl8x4EgoPz7bW5fXUR02ihCIRqPY/QYSMtCS6fR+zJPz5SLoRCA9OlzG3VX' +
		'mqYmhGCzVOLm+XmSiooiBIEAPwA/EkUAdq2GXS3fmgEZmo2SzTx34Nzoa1Mxq9EARWXt0kV6zA5SUdAS' +
		'CRKjo+g9BrJj4koJkQiRZGb/x81iWokZ4c+fOT49FN09gNtsYu5s01pepr1eQk0myb50iNjjKXzHwXOc' +
		'+xV1jYEDhSdiYd4NIcQ4kQh2vYEQgkAIOht3aK0usbNeolmtMjQxiaYo+LaD77r4lo2IJ9CF8rZ6OCyL' +
		'Uc04knx2H9LqoiBwbJvK4mVEp0l9aZHyjV/wtDBRw8C3bQLPw1xeorqx9oE61uWmvPtr0qt19xv5vOo5' +
		'DvHBQWQQprVexHcs/MYO0nSJj4yA51G58rN3d+Hi1+Y9/60HQzpj8MnB9z+c1nM5Ak8iVJV2uUx3awtF' +
		'04gmk/iOg99osPLlp4tTm7VxIFD+DpDwXWXhCkGriW+aONvbaLpOby5HLN573+w4dNeK2GbteyB4aMrn' +
		'QW0mOZnIPXUsCEVintXWRo6+2t9aLbJ59af1WGogo3qef++35W+Fa73xep36f84EMAMxHaK7Hgt905/f' +
		'V9gqXp9r13hPhElHwH/TpvioBz/A/Egq/0Wq54fZKJP/p/0LAq9nQqBdV08AAAAASUVORK5CYII=';

    var showingAlert = false;

    function updateAlert() {
        var unreadCount = getUnreadCount();
        updateDockBadge(unreadCount);

        prevShowingAlert = showingAlert;
        showingAlert = unreadCount > 0;

        if(showingAlert != prevShowingAlert) {
            updateGlobalHeaderBackground(showingAlert);
            if (showingAlert) {
                // rising edge: new message has just arrived
                showGrowlNotification("New message.");
                startFaviconNotification();
            } else {
                // falling edge: no more new messages
                hideFaviconNotification();
            }
        }
    }

    function getUnreadCount() {
        if (typeof RL == 'undefined') {
            return 0;
        }
        return countNewMessages(RL.rooms);
    }

    function updateGlobalHeaderBackground(isAlert) {
        var op =  isAlert ? 'addClass' : 'removeClass';
        $globalHeader[op]('globalHeaderAlert');
    }

    function updateDockBadge(unreadCount) {
        if (window.fluid) {
            fluid.dockBadge = unreadCount > 0 ? unreadCount : null;
        }
    }

    function showGrowlNotification(description) {
        if (window.fluid) {
            fluid.showGrowlNotification({
		title: "ChatWork",
		description: description,
		priority: 0,
		sticky: false
            });
        }
    }

    function startFaviconNotification() {
        favicon.animate([ALERT_FAVICON_SRC, NORMAL_FAVICON_SRC], {delay: 1000});
        setTimeout(function() {
            if (showingAlert) {
                showFaviconNotification();
            }
        }, 10000);
    }

    function showFaviconNotification() {
        favicon.change(ALERT_FAVICON_SRC);
    }

    function hideFaviconNotification() {
        favicon.change(NORMAL_FAVICON_SRC);
    }

    function countNewMessages(rooms) {
        var count = 0;
        for(var i in rooms) {
            count += rooms[i].getUnreadNum();
        }
        return count;
    }

    // do yer thang!
    var updateAlertInterval = setInterval(updateAlert, 500);
}

// warp through Chrome's isolated world
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
<|endoftext|>