<commit_before>class GA {
  constructor() { //initial values
    this.populationLimit = 20;

    var a, b, c, queueSize;

    this.population = [];

    for(var i = 0; i < this.populationLimit; i++) { // todo: fill by random?
      a = this.getRandomInt(0, 65535);
      b = this.getRandomInt(0, 65535);
      c = this.getRandomInt(0, 65535);
      queueSize = this.getRandomInt(1, 15);

      this.population.push({a : a, b: b, c: c, queueSize: queueSize, target: 0});
    }
    this.nextItemToUse = 0;
  }

  getRandomInt(min, max)
  {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  toFloating(intNum) {
    return (intNum/65536) * 10 - 5;
  }

  getItem() {
    if(this.nextItemToUse >= this.populationLimit) {
      this.proceedGAPart();
      this.nextItemToUse = this.population.length / 2; // first half of population already has target value. We are not interested to repeat it.
    }

//    console.log("Going to check population member " + this.nextItemToUse + "; queueSize is " + this.population[this.nextItemToUse].queueSize);
//    console.log("a/b/c/queueSize " + this.population[this.nextItemToUse].a + "/" + this.population[this.nextItemToUse].b + "/"
//      + this.population[this.nextItemToUse].c + "/" + this.population[this.nextItemToUse].queueSize);

//    this.dumpPopulation();
    return {a: this.toFloating(this.population[this.nextItemToUse].a),
            b: this.toFloating(this.population[this.nextItemToUse].b),
            c: this.toFloating(this.population[this.nextItemToUse].c),
            queueSize: this.population[this.nextItemToUse].queueSize};
  }

  setItemTargetValue(value) {
    this.population[this.nextItemToUse].target = value;
    this.nextItemToUse++;
  }

  proceedGAPart() { // use all those GA staff
    this.removeWorse();
    this.crossover();
    this.mutate();
  }

  executeCrossover(str1, str2) {
    if(str1.length != str2.length) {
      console.log("ERROROROROROOORRRR!");
    }
    var res1 = '';
    var res2 = '';
    for(var k = 0; k < str1.length; k++) {
      if((k % 2) == 0) {
        res1 += str1[k];
        res2 += str2[k];
      }
      else {
        res1 += str2[k];
        res2 += str1[k];
      }
    }

    return {first : res1, second : res2};
  }

  dumpPopulation() {
    for(var i = 0; i < this.population.length; i++) {
      console.log("population[" + i + "]: a/b/c/queueSize/target " + this.toFloating(this.population[i].a) + "/" + this.toFloating(this.population[i].b) + "/" + this.toFloating(this.population[i].c) + "/" + this.population[i].queueSize + "/" + this.population[i].target);
    }
  }

  binaryRepresentation(num) {
    var asString = num.toString(2);

    var leadingZeroesToAdd = 16 - asString.length;
    var toAdd = '';
    for(var i = 0; i < leadingZeroesToAdd; i++) {
      toAdd += '0';
    }
    return toAdd + asString;
  }

  crossover() {
    //console.log("starting crossover. length" + this.population.length);
    var itemsToCrossover = this.population.length / 2;
    for(var i = 0; i < itemsToCrossover; i++) {
      var j = i + itemsToCrossover;

//      console.log("item " + i + " + item " + j);

      var firstBinaryStringA = this.binaryRepresentation(this.population[i].a);
      var secondBinaryStringA = this.binaryRepresentation(this.population[j].a);

      var firstBinaryStringB = this.binaryRepresentation(this.population[i].b);
      var secondBinaryStringB = this.binaryRepresentation(this.population[j].b);

      var firstBinaryStringC = this.binaryRepresentation(this.population[i].c);
      var secondBinaryStringC = this.binaryRepresentation(this.population[j].c);

      var firstQueueSize = this.binaryRepresentation(this.population[i].queueSize);
      var secondQueueSize = this.binaryRepresentation(this.population[j].queueSize);

      var res1 = this.executeCrossover(firstBinaryStringA, secondBinaryStringA);
      var res2 = this.executeCrossover(firstBinaryStringB, secondBinaryStringB);
      var res3 = this.executeCrossover(firstBinaryStringC, secondBinaryStringC);
      var resQueue = this.executeCrossover(firstQueueSize, secondQueueSize);

  //    console.log("first/second/crossresult1/crossres2: " + firstQueueSize + " " + secondQueueSize + " " + resQueue.first + " " + resQueue.second);

      var a1 = parseInt(res1.first, 2);
      var a2 = parseInt(res1.second, 2);
      var b1 = parseInt(res2.first, 2);
      var b2 = parseInt(res2.second, 2);
      var c1 = parseInt(res3.first, 2);
      var c2 = parseInt(res3.second, 2);
      var queueSize1 = parseInt(resQueue.first, 2);
      var queueSize2 = parseInt(resQueue.second, 2);

      if(queueSize1 == 0) {
        queueSize1 = queueSize2;
  //      console.log("setting to queueSize2 " + queueSize2);
      }

      if(queueSize2 == 0) {
        queueSize2 = queueSize1;
  //      console.log("setting to queueSize1 " + queueSize1);
      }

      this.population.push({a:a1, b:b1,c:c1,queueSize: queueSize1, target:0});
      this.population.push({a:a2, b:b2,c:c2,queueSize: queueSize2, target:0});
    }

  //  console.log("after crossover:");
//    this.dumpPopulation();
  }

  mutateString(str) {
    var pos = this.getRandomInt(0, 15);
    var res = '';

    for(var i = 0; i < 16; i++) {
      if(i == pos) {
        if(str[i] == '0')
          res += '1';
        else
          res += '0';
      }
      else
        res += str[i];
    }
    return res;
  }

  mutate() {
//    console.log("before mutate");
//    this.dumpPopulation();
    for(var i = 0; i < this.population.length; i++) {
      if (this.getRandomInt(0, 20) <= 3) {
        var res = this.mutateString(this.binaryRepresentation(this.population[i].a));
        this.population[i].a = parseInt(res, 2);
      }
      if (this.getRandomInt(0, 20) <= 3) {

        this.population[i].b = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].b)), 2);
      }
      if(this.getRandomInt(0, 20) <= 3) {
        this.population[i].c = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].c)), 2);
      }
      if(this.getRandomInt(0, 20) <= 3) {
        var origQueueSize = this.population[i].queueSize;
        this.population[i].queueSize = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].queueSize)), 2);
        if(this.population[i].queueSize == 0)
          this.population[i].queueSize = origQueueSize;
      }
    }
//    console.log("after mutate");
//    this.dumpPopulation();
  }

  removeWorse(){ //removes half worst elements
    this.population.sort(function(a,b) {
      return a.target < b.target ? 1 : -1;
    });
//    console.log("now population length is: " + this.population.length);
//    console.log("firstA: " + this.population[0].a);

    this.population = this.population.splice(0, this.population.length/2);

//    console.log("now population length is: " + this.population.length);
//    console.log("firstA: " + this.population[0].a);
  }
}

module.exports = GA;
<commit_msg>genetics algorithm improvements<commit_after>class GA {
  constructor() { //initial values
    this.populationLimit = 20;
    this.mutationCounter = 0;
    var a, b, c, queueSize;

    this.population = [];

    for(var i = 0; i < this.populationLimit; i++) { // todo: fill by random?
      a = this.getRandomInt(0, 65535);
      b = this.getRandomInt(0, 65535);
      c = this.getRandomInt(0, 65535);
      queueSize = this.getRandomInt(1, 15);

      this.population.push({a : a, b: b, c: c, queueSize: queueSize, target: 0});
    }
    this.nextItemToUse = 0;
  }

  getRandomInt(min, max)
  {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }

  toFloating(intNum) {
    return (intNum/65536) * 10 - 5;
  }

  getItem() {
    if(this.nextItemToUse >= this.populationLimit) {
      this.proceedGAPart();
      this.nextItemToUse = this.population.length / 2; // first half of population already has target value. We are not interested to repeat it.
    }

//    console.log("Going to check population member " + this.nextItemToUse + "; queueSize is " + this.population[this.nextItemToUse].queueSize);
//    console.log("a/b/c/queueSize " + this.population[this.nextItemToUse].a + "/" + this.population[this.nextItemToUse].b + "/"
//      + this.population[this.nextItemToUse].c + "/" + this.population[this.nextItemToUse].queueSize);

//    this.dumpPopulation();
    return {a: this.toFloating(this.population[this.nextItemToUse].a),
            b: this.toFloating(this.population[this.nextItemToUse].b),
            c: this.toFloating(this.population[this.nextItemToUse].c),
            queueSize: this.population[this.nextItemToUse].queueSize};
  }

  setItemTargetValue(value) {
    this.population[this.nextItemToUse].target = value;
    this.nextItemToUse++;
  }

  proceedGAPart() { // use all those GA staff
    this.removeWorse();
    this.crossover();
    this.mutate();
  }

  executeCrossover(str1, str2) {
    if(str1.length != str2.length) {
      console.log("ERROROROROROOORRRR!");
    }
    var res1 = '';
    var res2 = '';
    for(var k = 0; k < str1.length; k++) {
      if((k % 2) == 0) {
        res1 += str1[k];
        res2 += str2[k];
      }
      else {
        res1 += str2[k];
        res2 += str1[k];
      }
    }

    return {first : res1, second : res2};
  }

  dumpPopulation() {
    for(var i = 0; i < this.population.length; i++) {
      console.log("population[" + i + "]: a/b/c/queueSize/target " + this.toFloating(this.population[i].a) + "/" + this.toFloating(this.population[i].b) + "/" + this.toFloating(this.population[i].c) + "/" + this.population[i].queueSize + "/" + this.population[i].target);
    }
  }

  binaryRepresentation(num) {
    var asString = num.toString(2);

    var leadingZeroesToAdd = 16 - asString.length;
    var toAdd = '';
    for(var i = 0; i < leadingZeroesToAdd; i++) {
      toAdd += '0';
    }
    return toAdd + asString;
  }

  crossover() {
    //console.log("starting crossover. length" + this.population.length);
    var itemsToCrossover = this.population.length / 2;
    for(var i = 0; i < itemsToCrossover; i++) {
      var j = i + itemsToCrossover;

//      console.log("item " + i + " + item " + j);

      var firstBinaryStringA = this.binaryRepresentation(this.population[i].a);
      var secondBinaryStringA = this.binaryRepresentation(this.population[j].a);

      var firstBinaryStringB = this.binaryRepresentation(this.population[i].b);
      var secondBinaryStringB = this.binaryRepresentation(this.population[j].b);

      var firstBinaryStringC = this.binaryRepresentation(this.population[i].c);
      var secondBinaryStringC = this.binaryRepresentation(this.population[j].c);

      var firstQueueSize = this.binaryRepresentation(this.population[i].queueSize);
      var secondQueueSize = this.binaryRepresentation(this.population[j].queueSize);

      var res1 = this.executeCrossover(firstBinaryStringA, secondBinaryStringA);
      var res2 = this.executeCrossover(firstBinaryStringB, secondBinaryStringB);
      var res3 = this.executeCrossover(firstBinaryStringC, secondBinaryStringC);
      var resQueue = this.executeCrossover(firstQueueSize, secondQueueSize);

  //    console.log("first/second/crossresult1/crossres2: " + firstQueueSize + " " + secondQueueSize + " " + resQueue.first + " " + resQueue.second);

      var a1 = parseInt(res1.first, 2);
      var a2 = parseInt(res1.second, 2);
      var b1 = parseInt(res2.first, 2);
      var b2 = parseInt(res2.second, 2);
      var c1 = parseInt(res3.first, 2);
      var c2 = parseInt(res3.second, 2);
      var queueSize1 = parseInt(resQueue.first, 2);
      var queueSize2 = parseInt(resQueue.second, 2);

      if(queueSize1 == 0) {
        queueSize1 = queueSize2;
  //      console.log("setting to queueSize2 " + queueSize2);
      }

      if(queueSize2 == 0) {
        queueSize2 = queueSize1;
  //      console.log("setting to queueSize1 " + queueSize1);
      }

      this.population.push({a:a1, b:b1,c:c1,queueSize: queueSize1, target:0});
      this.population.push({a:a2, b:b2,c:c2,queueSize: queueSize2, target:0});
    }

  //  console.log("after crossover:");
//    this.dumpPopulation();
  }

  mutateString(str, bits) {
    var pos = this.getRandomInt(0, 15);
    var res = '';

    for(var i = 0; i < 16; i++) {
      if(i >= (16-bits) && i == pos) {
        if(str[i] == '0')
          res += '1';
        else
          res += '0';
      }
      else
        res += str[i];
    }
    return res;
  }

  mutate() {
//    console.log("before mutate");
//    this.dumpPopulation();
    for(var i = 0; i < this.population.length; i++) {
      if (this.getRandomInt(0, 20) <= 10) {
        var res = this.mutateString(this.binaryRepresentation(this.population[i].a), 16);
        this.population[i].a = parseInt(res, 2);
      }
      if (this.getRandomInt(0, 20) <= 10) {

        this.population[i].b = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].b), 16), 2);
      }
      if(this.getRandomInt(0, 20) <= 10) {
        this.population[i].c = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].c), 16), 2);
      }
      if(this.getRandomInt(0, 20) <= 10) {
        var origQueueSize = this.population[i].queueSize;
        this.population[i].queueSize = parseInt(this.mutateString(this.binaryRepresentation(this.population[i].queueSize), 4), 2);
        if(this.population[i].queueSize == 0 || this.population[i].queueSize > 15)
          this.population[i].queueSize = origQueueSize;
      }
    }
//    console.log("after mutate");
//    this.dumpPopulation();
    this.mutationCounter++;
    if(this.mutationCounter % 3 == 0) {
      console.log(" " + this.mutationCounter + "mutations passed, checking:");
      this.dumpPopulation();
    }

  }

  removeWorse(){ //removes half worst elements
    this.population.sort(function(a,b) {
      return a.target < b.target ? 1 : -1;
    });
//    console.log("now population length is: " + this.population.length);
//    console.log("firstA: " + this.population[0].a);

    this.population = this.population.splice(0, this.population.length/2);

//    console.log("now population length is: " + this.population.length);
//    console.log("firstA: " + this.population[0].a);
  }
}

module.exports = GA;
<|endoftext|>


<commit_before>//IE8 不兼容数组的indexOf方法
if (typeof Array.prototype.indexOf !== 'function') {
    Array.prototype.indexOf = function(item) {
        for (var i = 0; i < this.length; i++) {
            if (this[i] == item) {
                return i;
            }
        }
        return -1;
    }
}
/**
 * MZ 移动端框架库
 * @author Heaven
 * @E-mail lichenbuliren@gmail.com
 * @version 0.0.1
 * @return {[type]} [description]
 */
window.MZ = (function(w,d) {

    /**
     * 对dom元素封装
     * @param {[type]} els [description]
     */
    function Dom(els) {
        for (var i = 0, len = els.length; i < len; i++) {
            this[i] = els[i];
        }
        this.length = els.length;
    }

    //=========== UTILs =============
    Dom.prototype.map = function(callback) {
        var results = [],
            i = 0;
        for (; i < this.length; i++) {
            results.push(callback.call(this, this[i], i));
        }
        return results;
    }

    Dom.prototype.forEach = function(callback) {
        this.map(callback);
        // 返回this.链式调用
        return this;
    }

    /**
     * 返回一个或者多个
     * @param  {Function} callback [description]
     * @return {[type]}            [description]
     */
    Dom.prototype.mapOne = function(callback) {
        var m = this.map(callback);
        return m.length > 1 ? m : m[0];
    }

    // ============ DOM MANIPULATION
    Dom.prototype.text = function(text) {
        if (typeof text !== 'undefined') {
            return this.forEach(function(el) {
                el[(el.textContent) ? 'textContent' : 'innerText'] = text;
            });
        } else {
            return this.mapOne(function(el) {
                return el.textContent || el.innerText;
            });
        }
    }

    Dom.prototype.html = function(html) {
        if (typeof html !== 'undefined') {
            return this.forEach(function(el) {
                el.innerHTML = html;
            });
        } else {
            return this.mapOne(function(el) {
                return el.innerHTML;
            });
        }
    }

    Dom.prototype.addClass = function(classes) {
        var className = '';
        if (typeof classes !== 'string') {
            for (var i = 0, len = classes.length; i < len; i++) {
                className += ' ' + classes[i];
            }
        } else {
            className = ' ' + classes;
        }

        return this.forEach(function(el) {
            el.className += className;
        });
    }

    Dom.prototype.removeClass = function(clazz) {
        return this.forEach(function(el) {
            var cs = el.className.split(' '),
                i;
            while ((i = cs.indexOf(clazz)) > -1) {
                // slice，截取数组
                cs = cs.slice(0, i).concat(cs.slice(++i));
            }
            el.className = cs.join(' ');
        });
    }

    Dom.prototype.attr = function(attr, val) {
        if (typeof val !== 'undefined') {
            return this.forEach(function(el) {
                el.setAttribute(attr, val);
            });
        } else {
            return this.mapOne(function(el) {
                return el.getAttribute(attr);
            });
        }
    }

    Dom.prototype.append = function(els) {
        // 当前dom对象
        this.forEach(function(parEl, i) {
            // 需要添加的子节点对象
            els.forEach(function(childEl) {
                // 当需要将子节点添加到多个父节点中，从第二个父节点开始，使用els的副本
                if (i > 0) {
                    childEl = childEl.cloneNode(true);
                }
                parEl.appendChild(childEl);
            });
        });
    }

    Dom.prototype.prepend = function(els) {
        // 当前dom对象
        this.forEach(function(parEl, i) {
            // 这里使用for循环，是为了保证插入的顺序，如果使用forEach，将会得到一个倒序的子节点
            for (var j = els.length - 1; j > -1; j--) {
                childEl = (i > 0) ? els[j].cloneNode(true) : els[j];
                parEl.insertBefore(childEl, parEl.firstChild);
            }
        });
    }

    /**
     * 删除节点
     * @return {[type]} [description]
     */
    Dom.prototype.remove = function() {
        return this.forEach(function(el) {
            return el.parentNode.removeChild(el);
        });
    }

    Dom.prototype.on = (function() {
        if (d.addEventListener) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.addEventListener(evt, fn, false);
                });
            };
        } else if (d.attachEvent) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.attachEvent('on' + evt, fn);
                });
            };
        } else {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el['on' + evt] = fn;
                });
            }
        }
    }());

    Dom.prototype.off = (function() {
        if (d.addEventListener) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.removeEventListener(evt, fn, false);
                });
            };
        } else if (d.detachEvent) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.detachEvent('on' + evt, fn);
                });
            };
        } else {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el['on' + evt] = null;
                });
            }
        }
    }());

    var MZ = {
        $: function(selector) {
            var els;
            if (typeof selector == 'string') {
                els = d.querySelectorAll(selector);
            } else if (selector.length) {
                els = selector;
            } else {
                els = [selector];
            }
            return new Dom(els);
        },

        create: function(tagName, attrs) {
            var el = new Dom([d.createElement(tagName)]);
            if (attrs) {
                if (attrs.className) {
                    el.addClass(attrs.className);
                    delete attrs.className;
                }

                if (attrs.text) {
                    el.text(attrs.text);
                    delete attrs.text;
                }

                for (var key in attrs) {
                    if (attrs.hasOwnProperty(key)) {
                        el.attr(key, attrs[key]);
                    }
                }
            }
            return el;
        }
    }
    return MZ;
})(window,document);<commit_msg>内容修改，修改添加类方式<commit_after>//IE8 不兼容数组的indexOf方法
if (typeof Array.prototype.indexOf !== 'function') {
    Array.prototype.indexOf = function(item) {
        for (var i = 0; i < this.length; i++) {
            if (this[i] == item) {
                return i;
            }
        }
        return -1;
    }
}
/**
 * MZ 移动端框架库
 * @author Heaven
 * @E-mail lichenbuliren@gmail.com
 * @version 0.0.1
 * @return {[type]} [description]
 */
window.MZ = (function(w, d) {

    /**
     * 对dom元素封装
     * @param {[type]} els [description]
     */
    function Dom(els) {
        for (var i = 0, len = els.length; i < len; i++) {
            this[i] = els[i];
        }
        this.length = els.length;
    }

    //=========== UTILs =============
    Dom.prototype.map = function(callback) {
        var results = [],
            i = 0;
        for (; i < this.length; i++) {
            results.push(callback.call(this, this[i], i));
        }
        return results;
    }

    Dom.prototype.forEach = function(callback) {
        this.map(callback);
        // 返回this.链式调用
        return this;
    }

    /**
     * 返回一个或者多个
     * @param  {Function} callback [description]
     * @return {[type]}            [description]
     */
    Dom.prototype.mapOne = function(callback) {
        var m = this.map(callback);
        return m.length > 1 ? m : m[0];
    }

    //  TODO dom元素定位
    Dom.prototype.offset = function(){

    }

    // TODO 判断元素是否在可视区域内
    Dom.prototype.isInViewport = function(){

    }

    // ============ DOM MANIPULATION
    Dom.prototype.text = function(text) {
        if (typeof text !== 'undefined') {
            return this.forEach(function(el) {
                el[(el.textContent) ? 'textContent' : 'innerText'] = text;
            });
        } else {
            return this.mapOne(function(el) {
                return el.textContent || el.innerText;
            });
        }
    }

    Dom.prototype.html = function(html) {
        if (typeof html !== 'undefined') {
            return this.forEach(function(el) {
                el.innerHTML = html;
            });
        } else {
            return this.mapOne(function(el) {
                return el.innerHTML;
            });
        }
    }

    Dom.prototype.addClass = function(classes) {
        return this.forEach(function(el) {
            el.classList.add(classes);
        });
    }

    Dom.prototype.removeClass = function(clazz) {
        return this.forEach(function(el) {
            var cs = el.className.split(' ');
            for (var i = 0, len = cs.length; i < len; i++) {
                el.classList.remove(cs[i]);
            }
        });
    }

    Dom.prototype.attr = function(attr, val) {
        if (typeof val !== 'undefined') {
            return this.forEach(function(el) {
                el.setAttribute(attr, val);
            });
        } else {
            return this.mapOne(function(el) {
                return el.getAttribute(attr);
            });
        }
    }

    Dom.prototype.append = function(els) {
        // 当前dom对象
        this.forEach(function(parEl, i) {
            // 需要添加的子节点对象
            els.forEach(function(childEl) {
                // 当需要将子节点添加到多个父节点中，从第二个父节点开始，使用els的副本
                if (i > 0) {
                    childEl = childEl.cloneNode(true);
                }
                parEl.appendChild(childEl);
            });
        });
    }

    Dom.prototype.prepend = function(els) {
        // 当前dom对象
        this.forEach(function(parEl, i) {
            // 这里使用for循环，是为了保证插入的顺序，如果使用forEach，将会得到一个倒序的子节点
            for (var j = els.length - 1; j > -1; j--) {
                childEl = (i > 0) ? els[j].cloneNode(true) : els[j];
                parEl.insertBefore(childEl, parEl.firstChild);
            }
        });
    }

    /**
     * 删除节点
     * @return {[type]} [description]
     */
    Dom.prototype.remove = function() {
        return this.forEach(function(el) {
            return el.parentNode.removeChild(el);
        });
    }

    Dom.prototype.on = (function() {
        if (d.addEventListener) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.addEventListener(evt, fn, false);
                });
            };
        } else if (d.attachEvent) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.attachEvent('on' + evt, fn);
                });
            };
        } else {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el['on' + evt] = fn;
                });
            }
        }
    }());

    Dom.prototype.off = (function() {
        if (d.addEventListener) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.removeEventListener(evt, fn, false);
                });
            };
        } else if (d.detachEvent) {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el.detachEvent('on' + evt, fn);
                });
            };
        } else {
            return function(evt, fn) {
                return this.forEach(function(el) {
                    el['on' + evt] = null;
                });
            }
        }
    }());

    var MZ = {
        $: function(selector) {
            var els;
            if (typeof selector == 'string') {
                els = d.querySelectorAll(selector);
            } else if (selector.length) {
                els = selector;
            } else {
                els = [selector];
            }
            return new Dom(els);
        },

        create: function(tagName, attrs) {
            var el = new Dom([d.createElement(tagName)]);
            if (attrs) {
                if (attrs.className) {
                    el.addClass(attrs.className);
                    delete attrs.className;
                }

                if (attrs.text) {
                    el.text(attrs.text);
                    delete attrs.text;
                }

                for (var key in attrs) {
                    if (attrs.hasOwnProperty(key)) {
                        el.attr(key, attrs[key]);
                    }
                }
            }
            return el;
        }
    }
    return MZ;
})(window, document);<|endoftext|>


<commit_before>SVG.FX = SVG.invent({
  // Initialize FX object
  create: function(element) {
    /* store target element */
    this.target = element
  }

  // Add class methods
, extend: {
    // Add animation parameters and start animation
    animate: function(d, ease, delay) {
      var akeys, tkeys, skeys, key
        , element = this.target
        , fx = this

      /* dissect object if one is passed */
      if (typeof d == 'object') {
        delay = d.delay
        ease = d.ease
        d = d.duration
      }

      /* ensure default duration and easing */
      d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf()
      ease = ease || '<>'

      /* process values */
      fx.to = function(pos) {
        var i

        /* normalise pos */
        pos = pos < 0 ? 0 : pos > 1 ? 1 : pos

        /* collect attribute keys */
        if (akeys == null) {
          akeys = []
          for (key in fx.attrs)
            akeys.push(key)

          /* make sure morphable elements are scaled, translated and morphed all together */
          if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) {
            /* get destination */
            var box
              , p = new element.morphArray(fx._plot || fx.attrs.points || element.array)

            /* add size */
            if (fx._size) p.size(fx._size.width.to, fx._size.height.to)

            /* add movement */
            box = p.bbox()
            if (fx._x) p.move(fx._x.to, box.y)
            else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y)

            box = p.bbox()
            if (fx._y) p.move(box.x, fx._y.to)
            else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2)

            /* delete element oriented changes */
            delete fx._x
            delete fx._y
            delete fx._cx
            delete fx._cy
            delete fx._size

            fx._plot = element.array.morph(p)
          }
        }

        /* collect transformation keys */
        if (tkeys == null) {
          tkeys = []
          for (key in fx.trans)
            tkeys.push(key)
        }

        /* collect style keys */
        if (skeys == null) {
          skeys = []
          for (key in fx.styles)
            skeys.push(key)
        }

        /* apply easing */
        pos = ease == '<>' ?
          (-Math.cos(pos * Math.PI) / 2) + 0.5 :
        ease == '>' ?
          Math.sin(pos * Math.PI / 2) :
        ease == '<' ?
          -Math.cos(pos * Math.PI / 2) + 1 :
        ease == '-' ?
          pos :
        typeof ease == 'function' ?
          ease(pos) :
          pos

        /* run plot function */
        if (fx._plot) {
          element.plot(fx._plot.at(pos))

        } else {
          /* run all x-position properties */
          if (fx._x)
            element.x(fx._x.at(pos))
          else if (fx._cx)
            element.cx(fx._cx.at(pos))

          /* run all y-position properties */
          if (fx._y)
            element.y(fx._y.at(pos))
          else if (fx._cy)
            element.cy(fx._cy.at(pos))

          /* run all size properties */
          if (fx._size)
            element.size(fx._size.width.at(pos), fx._size.height.at(pos))
        }

        /* run all viewbox properties */
        if (fx._viewbox)
          element.viewbox(
            fx._viewbox.x.at(pos)
          , fx._viewbox.y.at(pos)
          , fx._viewbox.width.at(pos)
          , fx._viewbox.height.at(pos)
          )

        /* run leading property */
        if (fx._leading)
          element.leading(fx._leading.at(pos))

        /* animate attributes */
        for (i = akeys.length - 1; i >= 0; i--)
          element.attr(akeys[i], at(fx.attrs[akeys[i]], pos))

        /* animate transformations */
        for (i = tkeys.length - 1; i >= 0; i--)
          element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos))

        /* animate styles */
        for (i = skeys.length - 1; i >= 0; i--)
          element.style(skeys[i], at(fx.styles[skeys[i]], pos))

        /* callback for each keyframe */
        if (fx._during)
          fx._during.call(element, pos, function(from, to) {
            return at({ from: from, to: to }, pos)
          })
      }

      if (typeof d === 'number') {
        /* delay animation */
        this.timeout = setTimeout(function() {
          var start = new Date().getTime()

          /* initialize situation object */
          fx.situation = {
            interval: 1000 / 60
          , start:    start
          , play:     true
          , finish:   start + d
          , duration: d
          }

          /* render function */
          fx.render = function() {

            if (fx.situation.play === true) {
              // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby.
              var time = new Date().getTime()
                , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d

              /* process values */
              fx.to(pos)

              /* finish off animation */
              if (time > fx.situation.finish) {
                if (fx._plot)
                  element.plot(new SVG.PointArray(fx._plot.destination).settle())

                if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) {
                  if (typeof fx._loop == 'number')
                    --fx._loop
                  fx.animate(d, ease, delay)
                } else {
                  fx._after ? fx._after.apply(element, [fx]) : fx.stop()
                }

              } else {
                requestAnimFrame(fx.render)
              }
            } else {
              requestAnimFrame(fx.render)
            }

          }

          /* start animation */
          fx.render()

        }, new SVG.Number(delay).valueOf())
      }

      return this
    }
    // Get bounding box of target element
  , bbox: function() {
      return this.target.bbox()
    }
    // Add animatable attributes
  , attr: function(a, v) {
      if (typeof a == 'object') {
        for (var key in a)
          this.attr(key, a[key])

      } else {
        var from = this.target.attr(a)

        this.attrs[a] = SVG.Color.isColor(from) ?
          new SVG.Color(from).morph(v) :
        SVG.regex.unit.test(from) ?
          new SVG.Number(from).morph(v) :
          { from: from, to: v }
      }

      return this
    }
    // Add animatable transformations
  , transform: function(o, v) {
      if (arguments.length == 1) {
        /* parse matrix string */
        o = parseMatrix(o)

        /* dlete matrixstring from object */
        delete o.matrix

        /* store matrix values */
        for (v in o)
          this.trans[v] = { from: this.target.trans[v], to: o[v] }

      } else {
        /* apply transformations as object if key value arguments are given*/
        var transform = {}
        transform[o] = v

        this.transform(transform)
      }

      return this
    }
    // Add animatable styles
  , style: function(s, v) {
      if (typeof s == 'object')
        for (var key in s)
          this.style(key, s[key])

      else
        this.styles[s] = { from: this.target.style(s), to: v }

      return this
    }
    // Animatable x-axis
  , x: function(x) {
      this._x = new SVG.Number(this.target.x()).morph(x)

      return this
    }
    // Animatable y-axis
  , y: function(y) {
      this._y = new SVG.Number(this.target.y()).morph(y)

      return this
    }
    // Animatable center x-axis
  , cx: function(x) {
      this._cx = new SVG.Number(this.target.cx()).morph(x)

      return this
    }
    // Animatable center y-axis
  , cy: function(y) {
      this._cy = new SVG.Number(this.target.cy()).morph(y)

      return this
    }
    // Add animatable move
  , move: function(x, y) {
      return this.x(x).y(y)
    }
    // Add animatable center
  , center: function(x, y) {
      return this.cx(x).cy(y)
    }
    // Add animatable size
  , size: function(width, height) {
      if (this.target instanceof SVG.Text) {
        /* animate font size for Text elements */
        this.attr('font-size', width)

      } else {
        /* animate bbox based size for all other elements */
        var box = this.target.bbox()

        this._size = {
          width:  new SVG.Number(box.width).morph(width)
        , height: new SVG.Number(box.height).morph(height)
        }
      }

      return this
    }
    // Add animatable plot
  , plot: function(p) {
      this._plot = p

      return this
    }
    // Add leading method
  , leading: function(value) {
      if (this.target._leading)
        this._leading = new SVG.Number(this.target._leading).morph(value)

      return this
    }
    // Add animatable viewbox
  , viewbox: function(x, y, width, height) {
      if (this.target instanceof SVG.Container) {
        var box = this.target.viewbox()

        this._viewbox = {
          x:      new SVG.Number(box.x).morph(x)
        , y:      new SVG.Number(box.y).morph(y)
        , width:  new SVG.Number(box.width).morph(width)
        , height: new SVG.Number(box.height).morph(height)
        }
      }

      return this
    }
    // Add animateable gradient update
  , update: function(o) {
      if (this.target instanceof SVG.Stop) {
        if (o.opacity != null) this.attr('stop-opacity', o.opacity)
        if (o.color   != null) this.attr('stop-color', o.color)
        if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
      }

      return this
    }
    // Add callback for each keyframe
  , during: function(during) {
      this._during = during

      return this
    }
    // Callback after animation
  , after: function(after) {
      this._after = after

      return this
    }
    // Make loopable
  , loop: function(times) {
      this._loop = times || true

      return this
    }
    // Stop running animation
  , stop: function(fulfill) {
      /* fulfill animation */
      if (fulfill === true) {
        this.animate(0);
        if (this._after) {
          this._after.apply(this);
        }
      } else {
        /* stop current animation */
        clearTimeout(this.timeout)

        /* reset storage for properties that need animation */
        this.attrs     = {}
        this.trans     = {}
        this.styles    = {}
        this.situation = {}

        /* delete destinations */
        delete this._x
        delete this._y
        delete this._cx
        delete this._cy
        delete this._size
        delete this._plot
        delete this._loop
        delete this._after
        delete this._during
        delete this._leading
        delete this._viewbox
      }

      return this
    }
    // Pause running animation
  , pause: function() {
      if (this.situation.play === true) {
        this.situation.play  = false
        this.situation.pause = new Date().getTime()
      }

      return this
    }
    // Play running animation
  , play: function() {
      if (this.situation.play === false) {
        var pause = new Date().getTime() - this.situation.pause

        this.situation.finish += pause
        this.situation.start  += pause
        this.situation.play    = true
      }

      return this
    }

  }

  // Define parent class
, parent: SVG.Element

  // Add method to parent elements
, construct: {
    // Get fx module or create a new one, then animate with given duration and ease
    animate: function(d, ease, delay) {
      return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay)
    }
    // Stop current animation; this is an alias to the fx instance
  , stop: function(fulfill) {
      if (this.fx)
        this.fx.stop(fulfill)

      return this
    }
    // Pause current animation
  , pause: function() {
      if (this.fx)
        this.fx.pause()

      return this
    }
    // Play paused current animation
  , play: function() {
      if (this.fx)
        this.fx.play()

      return this
    }

  }
})
<commit_msg>Calling after() on stop(fulfill)<commit_after>SVG.FX = SVG.invent({
  // Initialize FX object
  create: function(element) {
    /* store target element */
    this.target = element
  }

  // Add class methods
, extend: {
    // Add animation parameters and start animation
    animate: function(d, ease, delay) {
      var akeys, tkeys, skeys, key
        , element = this.target
        , fx = this

      /* dissect object if one is passed */
      if (typeof d == 'object') {
        delay = d.delay
        ease = d.ease
        d = d.duration
      }

      /* ensure default duration and easing */
      d = d == '=' ? d : d == null ? 1000 : new SVG.Number(d).valueOf()
      ease = ease || '<>'

      /* process values */
      fx.to = function(pos) {
        var i

        /* normalise pos */
        pos = pos < 0 ? 0 : pos > 1 ? 1 : pos

        /* collect attribute keys */
        if (akeys == null) {
          akeys = []
          for (key in fx.attrs)
            akeys.push(key)

          /* make sure morphable elements are scaled, translated and morphed all together */
          if (element.morphArray && (fx._plot || akeys.indexOf('points') > -1)) {
            /* get destination */
            var box
              , p = new element.morphArray(fx._plot || fx.attrs.points || element.array)

            /* add size */
            if (fx._size) p.size(fx._size.width.to, fx._size.height.to)

            /* add movement */
            box = p.bbox()
            if (fx._x) p.move(fx._x.to, box.y)
            else if (fx._cx) p.move(fx._cx.to - box.width / 2, box.y)

            box = p.bbox()
            if (fx._y) p.move(box.x, fx._y.to)
            else if (fx._cy) p.move(box.x, fx._cy.to - box.height / 2)

            /* delete element oriented changes */
            delete fx._x
            delete fx._y
            delete fx._cx
            delete fx._cy
            delete fx._size

            fx._plot = element.array.morph(p)
          }
        }

        /* collect transformation keys */
        if (tkeys == null) {
          tkeys = []
          for (key in fx.trans)
            tkeys.push(key)
        }

        /* collect style keys */
        if (skeys == null) {
          skeys = []
          for (key in fx.styles)
            skeys.push(key)
        }

        /* apply easing */
        pos = ease == '<>' ?
          (-Math.cos(pos * Math.PI) / 2) + 0.5 :
        ease == '>' ?
          Math.sin(pos * Math.PI / 2) :
        ease == '<' ?
          -Math.cos(pos * Math.PI / 2) + 1 :
        ease == '-' ?
          pos :
        typeof ease == 'function' ?
          ease(pos) :
          pos

        /* run plot function */
        if (fx._plot) {
          element.plot(fx._plot.at(pos))

        } else {
          /* run all x-position properties */
          if (fx._x)
            element.x(fx._x.at(pos))
          else if (fx._cx)
            element.cx(fx._cx.at(pos))

          /* run all y-position properties */
          if (fx._y)
            element.y(fx._y.at(pos))
          else if (fx._cy)
            element.cy(fx._cy.at(pos))

          /* run all size properties */
          if (fx._size)
            element.size(fx._size.width.at(pos), fx._size.height.at(pos))
        }

        /* run all viewbox properties */
        if (fx._viewbox)
          element.viewbox(
            fx._viewbox.x.at(pos)
          , fx._viewbox.y.at(pos)
          , fx._viewbox.width.at(pos)
          , fx._viewbox.height.at(pos)
          )

        /* run leading property */
        if (fx._leading)
          element.leading(fx._leading.at(pos))

        /* animate attributes */
        for (i = akeys.length - 1; i >= 0; i--)
          element.attr(akeys[i], at(fx.attrs[akeys[i]], pos))

        /* animate transformations */
        for (i = tkeys.length - 1; i >= 0; i--)
          element.transform(tkeys[i], at(fx.trans[tkeys[i]], pos))

        /* animate styles */
        for (i = skeys.length - 1; i >= 0; i--)
          element.style(skeys[i], at(fx.styles[skeys[i]], pos))

        /* callback for each keyframe */
        if (fx._during)
          fx._during.call(element, pos, function(from, to) {
            return at({ from: from, to: to }, pos)
          })
      }

      if (typeof d === 'number') {
        /* delay animation */
        this.timeout = setTimeout(function() {
          var start = new Date().getTime()

          /* initialize situation object */
          fx.situation = {
            interval: 1000 / 60
          , start:    start
          , play:     true
          , finish:   start + d
          , duration: d
          }

          /* render function */
          fx.render = function() {

            if (fx.situation.play === true) {
              // This code was borrowed from the emile.js micro framework by Thomas Fuchs, aka MadRobby.
              var time = new Date().getTime()
                , pos = time > fx.situation.finish ? 1 : (time - fx.situation.start) / d

              /* process values */
              fx.to(pos)

              /* finish off animation */
              if (time > fx.situation.finish) {
                if (fx._plot)
                  element.plot(new SVG.PointArray(fx._plot.destination).settle())

                if (fx._loop === true || (typeof fx._loop == 'number' && fx._loop > 1)) {
                  if (typeof fx._loop == 'number')
                    --fx._loop
                  fx.animate(d, ease, delay)
                } else {
                  fx._after ? fx._after.apply(element, [fx]) : fx.stop()
                }

              } else {
                requestAnimFrame(fx.render)
              }
            } else {
              requestAnimFrame(fx.render)
            }

          }

          /* start animation */
          fx.render()

        }, new SVG.Number(delay).valueOf())
      }

      return this
    }
    // Get bounding box of target element
  , bbox: function() {
      return this.target.bbox()
    }
    // Add animatable attributes
  , attr: function(a, v) {
      if (typeof a == 'object') {
        for (var key in a)
          this.attr(key, a[key])

      } else {
        var from = this.target.attr(a)

        this.attrs[a] = SVG.Color.isColor(from) ?
          new SVG.Color(from).morph(v) :
        SVG.regex.unit.test(from) ?
          new SVG.Number(from).morph(v) :
          { from: from, to: v }
      }

      return this
    }
    // Add animatable transformations
  , transform: function(o, v) {
      if (arguments.length == 1) {
        /* parse matrix string */
        o = parseMatrix(o)

        /* dlete matrixstring from object */
        delete o.matrix

        /* store matrix values */
        for (v in o)
          this.trans[v] = { from: this.target.trans[v], to: o[v] }

      } else {
        /* apply transformations as object if key value arguments are given*/
        var transform = {}
        transform[o] = v

        this.transform(transform)
      }

      return this
    }
    // Add animatable styles
  , style: function(s, v) {
      if (typeof s == 'object')
        for (var key in s)
          this.style(key, s[key])

      else
        this.styles[s] = { from: this.target.style(s), to: v }

      return this
    }
    // Animatable x-axis
  , x: function(x) {
      this._x = new SVG.Number(this.target.x()).morph(x)

      return this
    }
    // Animatable y-axis
  , y: function(y) {
      this._y = new SVG.Number(this.target.y()).morph(y)

      return this
    }
    // Animatable center x-axis
  , cx: function(x) {
      this._cx = new SVG.Number(this.target.cx()).morph(x)

      return this
    }
    // Animatable center y-axis
  , cy: function(y) {
      this._cy = new SVG.Number(this.target.cy()).morph(y)

      return this
    }
    // Add animatable move
  , move: function(x, y) {
      return this.x(x).y(y)
    }
    // Add animatable center
  , center: function(x, y) {
      return this.cx(x).cy(y)
    }
    // Add animatable size
  , size: function(width, height) {
      if (this.target instanceof SVG.Text) {
        /* animate font size for Text elements */
        this.attr('font-size', width)

      } else {
        /* animate bbox based size for all other elements */
        var box = this.target.bbox()

        this._size = {
          width:  new SVG.Number(box.width).morph(width)
        , height: new SVG.Number(box.height).morph(height)
        }
      }

      return this
    }
    // Add animatable plot
  , plot: function(p) {
      this._plot = p

      return this
    }
    // Add leading method
  , leading: function(value) {
      if (this.target._leading)
        this._leading = new SVG.Number(this.target._leading).morph(value)

      return this
    }
    // Add animatable viewbox
  , viewbox: function(x, y, width, height) {
      if (this.target instanceof SVG.Container) {
        var box = this.target.viewbox()

        this._viewbox = {
          x:      new SVG.Number(box.x).morph(x)
        , y:      new SVG.Number(box.y).morph(y)
        , width:  new SVG.Number(box.width).morph(width)
        , height: new SVG.Number(box.height).morph(height)
        }
      }

      return this
    }
    // Add animateable gradient update
  , update: function(o) {
      if (this.target instanceof SVG.Stop) {
        if (o.opacity != null) this.attr('stop-opacity', o.opacity)
        if (o.color   != null) this.attr('stop-color', o.color)
        if (o.offset  != null) this.attr('offset', new SVG.Number(o.offset))
      }

      return this
    }
    // Add callback for each keyframe
  , during: function(during) {
      this._during = during

      return this
    }
    // Callback after animation
  , after: function(after) {
      this._after = after

      return this
    }
    // Make loopable
  , loop: function(times) {
      this._loop = times || true

      return this
    }
    // Stop running animation
  , stop: function(fulfill) {
      /* fulfill animation */
      if (fulfill === true) {

        this.animate(0)

        if (this._after)
          this._after.apply(this.target, [this])

      } else {
        /* stop current animation */
        clearTimeout(this.timeout)

        /* reset storage for properties that need animation */
        this.attrs     = {}
        this.trans     = {}
        this.styles    = {}
        this.situation = {}

        /* delete destinations */
        delete this._x
        delete this._y
        delete this._cx
        delete this._cy
        delete this._size
        delete this._plot
        delete this._loop
        delete this._after
        delete this._during
        delete this._leading
        delete this._viewbox
      }

      return this
    }
    // Pause running animation
  , pause: function() {
      if (this.situation.play === true) {
        this.situation.play  = false
        this.situation.pause = new Date().getTime()
      }

      return this
    }
    // Play running animation
  , play: function() {
      if (this.situation.play === false) {
        var pause = new Date().getTime() - this.situation.pause

        this.situation.finish += pause
        this.situation.start  += pause
        this.situation.play    = true
      }

      return this
    }

  }

  // Define parent class
, parent: SVG.Element

  // Add method to parent elements
, construct: {
    // Get fx module or create a new one, then animate with given duration and ease
    animate: function(d, ease, delay) {
      return (this.fx || (this.fx = new SVG.FX(this))).stop().animate(d, ease, delay)
    }
    // Stop current animation; this is an alias to the fx instance
  , stop: function(fulfill) {
      if (this.fx)
        this.fx.stop(fulfill)

      return this
    }
    // Pause current animation
  , pause: function() {
      if (this.fx)
        this.fx.pause()

      return this
    }
    // Play paused current animation
  , play: function() {
      if (this.fx)
        this.fx.play()

      return this
    }

  }
})
<|endoftext|>


<commit_before>KonOpas.Part = function(list, opt) {
	this.list = list || [];
	this.list.forEach(function(p){
		p.sortname = ((p.name[1] || '') + '  ' + p.name[0]).toLowerCase().replace(/^ +/, '');
		if (!opt.non_ascii_people) p.sortname = p.sortname.make_ascii();
	});
	this.list.sort(opt.non_ascii_people
		? function(a, b) { return a.sortname.localeCompare(b.sortname, opt.lc); }
		: function(a, b) { return a.sortname  b.sortname; });
	this.set_ranges(opt.people_per_screen || 0);
}

KonOpas.Part.prototype.set_ranges = function(bin_size) {
	var	self = this,
		_ranges = function(a, bin_size) {
			var	ends = [], start = 'A',
				n_bins = bin_size ? Math.round(a.length / bin_size) : 0,
				_prev_matches = function(a, i) { return (i > 0) && (a[i - 1] == a[i]); },
				_next_matches = function(a, i) { return (i < a.length - 1) && (a[i + 1] == a[i]); };
			for (var i = 1; i <= n_bins; ++i) {
				var e = Math.round(i * a.length / n_bins), n_up = 0, n_down = 0;
				if (e < 0) e = 0;
				if (e >= a.length) e = a.length - 1;
				while (_next_matches(a, e + n_up)) ++n_up;
				if (n_up) while (_prev_matches(a, e - n_down)) ++n_down;
				if (n_up <= n_down) e += n_up;
				else if (e > n_down) e -= n_down + 1;
				if (!ends.length || (ends[ends.length - 1] != a[e])) ends.push(a[e]);
			}
			ends.forEach(function(e, i){
				if (e > start) ends[i] = start + e;
				if (e >= start) start = String.fromCharCode(e.charCodeAt(0) + 1);
			});
			return ends;
		},
		_range_set = function(div, nr) {
			var ul = _new_elem('ul', 'name-list');
			nr.forEach(function(n){
				var li = _new_elem('li', '', n.charAt(0));
				if (n.length > 1) li.textContent += ' - ' + n.charAt(1);
				li.setAttribute('data-range', n);
				ul.appendChild(li);
			});
			div.appendChild(ul);
			div.onclick = (function(ev) {
				var name_range = (ev || window.event).target.getAttribute('data-range');
				if (name_range) {
					konopas.store.set('part', { 'name_range': name_range, 'participant': '' });
					window.location.hash = '#part';
					this.update_view(name_range, '');
				}
			}).bind(self);
		};

	var fn = [], ln = [];
	this.list.forEach(function(p){
		if (p.name && p.name.length) {
			fn.push(p.name[0].trim().charAt(0).toUpperCase());
			if (p.name.length >= 2) ln.push(p.name[1].trim().charAt(0).toUpperCase());
		}
	});

	this.ranges = _ranges(ln.length ? ln : fn, bin_size);
	_range_set(_el('part-sidebar'), this.ranges);
	_range_set(_el('part-narrow'), this.ranges);

	var nl_type =  fn.length &&  ln.length ? ''
	            :  fn.length && !ln.length ? 'fn-only'
	            : !fn.length &&  ln.length ? 'ln-only'
	            : 'error';
	if (nl_type) _el('part_names').classList.add(nl_type);
}

KonOpas.Part.name_in_range = function(n0, range) {
	switch (range.length) {
		case 1:  return (n0 == range[0]);
		case 2:  return konopas.non_ascii_people
			? (n0.localeCompare(range[0], konopas.lc) >= 0) && (n0.localeCompare(range[1], konopas.lc) <= 0)
			: ((n0 >= range[0]) && (n0 <= range[1]));
		default: return (range.indexOf(n0) >= 0);
	}
}

KonOpas.Part.prototype.show_one = function(i) {
	var p = this.list[i],
	    p_name = KonOpas.clean_name(p, false),
	    links = '',
	    img = '',
	    pl = KonOpas.clean_links(p);
	if (pl) {
		links += '
';
		for (var type in pl) if (type != 'img') {
			var tgt = pl[type].tgt, txt = pl[type].txt || tgt;
			links += '
' + type + ':
' + '' + txt + '';
		}
		links += '
';
		if (pl.img && navigator.onLine) {
			img = '';
		}
	}
	_el("part_names").innerHTML = '';
	_el("part_info").innerHTML =
		  '
' + p_name + '
'
		+ ((p.bio || img) ? ('
' + img + p.bio) : '')
		+ links;
	KonOpas.Item.show_list(konopas.program.list.filter(function(it) { return p.prog.indexOf(it.id) >= 0; }));
	_el("top").scrollIntoView();
}

KonOpas.Part.prototype.show_list = function(name_range) {
	var lp = !name_range ? this.list : this.list.filter(function(p) {
		var n0 = p.sortname[0].toUpperCase();
		return KonOpas.Part.name_in_range(n0, name_range);
	});
	_el('part_names').innerHTML = lp.map(function(p) {
		return '
' + KonOpas.clean_name(p, true) + '
';
	}).join('');
	_el('part_info').innerHTML = '';
	_el('prog_ls').innerHTML = '';
}

KonOpas.Part.prototype.update_view = function(name_range, participant) {
	var	p_id = participant.substr(1),
		ll = document.querySelectorAll('.name-list > li');
	if (!name_range) name_range = this.ranges && this.ranges[0] || '';
	for (var i = 0; i < ll.length; ++i) {
		var cmd = (ll[i].getAttribute('data-range') == name_range) ? 'add' : 'remove';
		ll[i].classList[cmd]('selected');
	}
	if (!p_id || !this.list.some(function(p,i){
		if (p.id == p_id) { this.show_one(i); return true; }
	}, this)) {
		participant = '';
		this.show_list(name_range);
	}
	konopas.store.set('part', { 'name_range': name_range, 'participant': participant });
}

KonOpas.Part.prototype.show = function(hash) {
	if (!this.list.length) { window.location.hash = ''; return; }
	var store = konopas.store.get('part') || {},
	    name_range = store.name_range || '',
	    participant = !document.body.classList.contains('part') && store.participant || '',
		hash = window.location.hash.substr(6);
	if (hash) {
		var p_id = KonOpas.hash_decode(hash);
		var pa = this.list.filter(function(p) { return p.id == p_id; });
		if (pa.length) {
			participant = 'p' + pa[0].id;
			var n0 = pa[0].sortname[0].toUpperCase();
			if (!n0 || !this.ranges || !this.ranges.some(function(r){
				if (KonOpas.Part.name_in_range(n0, r)) { name_range = r; return true; }
			})) name_range = '';
		} else {
			window.location.hash = '#part';
			return;
		}
	} else if (participant) {
		window.location.hash = '#part/' + participant.substr(1);
		return;
	}
	this.update_view(name_range, participant);
}
<commit_msg>part.js: fix bug where people with surnames beginning with ASCII < A are never listed in People<commit_after>KonOpas.Part = function(list, opt) {
	this.list = list || [];
	this.list.forEach(function(p){
		p.sortname = ((p.name[1] || '') + '  ' + p.name[0]).toLowerCase().replace(/^ +/, '');
		if (!opt.non_ascii_people) p.sortname = p.sortname.make_ascii();
	});
	this.list.sort(opt.non_ascii_people
		? function(a, b) { return a.sortname.localeCompare(b.sortname, opt.lc); }
		: function(a, b) { return a.sortname  b.sortname; });
	this.set_ranges(opt.people_per_screen || 0);
}

KonOpas.Part.prototype.set_ranges = function(bin_size) {
	var	self = this,
		_ranges = function(a, bin_size) {
			var	ends = [], start = ' ',
				n_bins = bin_size ? Math.round(a.length / bin_size) : 0,
				_prev_matches = function(a, i) { return (i > 0) && (a[i - 1] == a[i]); },
				_next_matches = function(a, i) { return (i < a.length - 1) && (a[i + 1] == a[i]); };
			for (var i = 1; i <= n_bins; ++i) {
				var e = Math.round(i * a.length / n_bins), n_up = 0, n_down = 0;
				if (e < 0) e = 0;
				if (e >= a.length) e = a.length - 1;
				while (_next_matches(a, e + n_up)) ++n_up;
				if (n_up) while (_prev_matches(a, e - n_down)) ++n_down;
				if (n_up <= n_down) e += n_up;
				else if (e > n_down) e -= n_down + 1;
				if (!ends.length || (ends[ends.length - 1] != a[e])) ends.push(a[e]);
			}
			ends.forEach(function(e, i){
				if (e > start) ends[i] = start + e;
				if (e >= start) start = String.fromCharCode(e.charCodeAt(0) + 1);
			});
			return ends;
		},
		_range_set = function(div, nr) {
			var ul = _new_elem('ul', 'name-list');
			nr.forEach(function(n){
				var startChar = n.charAt(0);
				if (startChar == ' ') startChar = 'A';
				var li = _new_elem('li', '', startChar);
				if (n.length > 1) li.textContent += ' - ' + n.charAt(1);
				li.setAttribute('data-range', n);
				ul.appendChild(li);
			});
			div.appendChild(ul);
			div.onclick = (function(ev) {
				var name_range = (ev || window.event).target.getAttribute('data-range');
				if (name_range) {
					konopas.store.set('part', { 'name_range': name_range, 'participant': '' });
					window.location.hash = '#part';
					this.update_view(name_range, '');
				}
			}).bind(self);
		};

	var fn = [], ln = [];
	this.list.forEach(function(p){
		if (p.name && p.name.length) {
			fn.push(p.name[0].trim().charAt(0).toUpperCase());
			if (p.name.length >= 2) ln.push(p.name[1].trim().charAt(0).toUpperCase());
		}
	});

	this.ranges = _ranges(ln.length ? ln : fn, bin_size);
	_range_set(_el('part-sidebar'), this.ranges);
	_range_set(_el('part-narrow'), this.ranges);

	var nl_type =  fn.length &&  ln.length ? ''
	            :  fn.length && !ln.length ? 'fn-only'
	            : !fn.length &&  ln.length ? 'ln-only'
	            : 'error';
	if (nl_type) _el('part_names').classList.add(nl_type);
}

KonOpas.Part.name_in_range = function(n0, range) {
	switch (range.length) {
		case 1:  return (n0 == range[0]);
		case 2:  return konopas.non_ascii_people
			? (n0.localeCompare(range[0], konopas.lc) >= 0) && (n0.localeCompare(range[1], konopas.lc) <= 0)
			: ((n0 >= range[0]) && (n0 <= range[1]));
		default: return (range.indexOf(n0) >= 0);
	}
}

KonOpas.Part.prototype.show_one = function(i) {
	var p = this.list[i],
	    p_name = KonOpas.clean_name(p, false),
	    links = '',
	    img = '',
	    pl = KonOpas.clean_links(p);
	if (pl) {
		links += '
';
		for (var type in pl) if (type != 'img') {
			var tgt = pl[type].tgt, txt = pl[type].txt || tgt;
			links += '
' + type + ':
' + '' + txt + '';
		}
		links += '
';
		if (pl.img && navigator.onLine) {
			img = '';
		}
	}
	_el("part_names").innerHTML = '';
	_el("part_info").innerHTML =
		  '
' + p_name + '
'
		+ ((p.bio || img) ? ('
' + img + p.bio) : '')
		+ links;
	KonOpas.Item.show_list(konopas.program.list.filter(function(it) { return p.prog.indexOf(it.id) >= 0; }));
	_el("top").scrollIntoView();
}

KonOpas.Part.prototype.show_list = function(name_range) {
	var lp = !name_range ? this.list : this.list.filter(function(p) {
		var n0 = p.sortname[0].toUpperCase();
		return KonOpas.Part.name_in_range(n0, name_range);
	});
	_el('part_names').innerHTML = lp.map(function(p) {
		return '
' + KonOpas.clean_name(p, true) + '
';
	}).join('');
	_el('part_info').innerHTML = '';
	_el('prog_ls').innerHTML = '';
}

KonOpas.Part.prototype.update_view = function(name_range, participant) {
	var	p_id = participant.substr(1),
		ll = document.querySelectorAll('.name-list > li');
	if (!name_range) name_range = this.ranges && this.ranges[0] || '';
	for (var i = 0; i < ll.length; ++i) {
		var cmd = (ll[i].getAttribute('data-range') == name_range) ? 'add' : 'remove';
		ll[i].classList[cmd]('selected');
	}
	if (!p_id || !this.list.some(function(p,i){
		if (p.id == p_id) { this.show_one(i); return true; }
	}, this)) {
		participant = '';
		this.show_list(name_range);
	}
	konopas.store.set('part', { 'name_range': name_range, 'participant': participant });
}

KonOpas.Part.prototype.show = function(hash) {
	if (!this.list.length) { window.location.hash = ''; return; }
	var store = konopas.store.get('part') || {},
	    name_range = store.name_range || '',
	    participant = !document.body.classList.contains('part') && store.participant || '',
		hash = window.location.hash.substr(6);
	if (hash) {
		var p_id = KonOpas.hash_decode(hash);
		var pa = this.list.filter(function(p) { return p.id == p_id; });
		if (pa.length) {
			participant = 'p' + pa[0].id;
			var n0 = pa[0].sortname[0].toUpperCase();
			if (!n0 || !this.ranges || !this.ranges.some(function(r){
				if (KonOpas.Part.name_in_range(n0, r)) { name_range = r; return true; }
			})) name_range = '';
		} else {
			window.location.hash = '#part';
			return;
		}
	} else if (participant) {
		window.location.hash = '#part/' + participant.substr(1);
		return;
	}
	this.update_view(name_range, participant);
}
<|endoftext|>


<commit_before>import _ from 'lodash'

import * as ttypes from './constants/task-type'
import * as ctypes from './constants/connection-type'

import util from './util'
import flexio from './flexio'

var toBase64 = function(str) {
  try { return btoa(unescape(encodeURIComponent(str))) } catch(e) { return e }
}

var fromBase64 = function(str) {
  try { return decodeURIComponent(escape(atob(str))) } catch(e) { return e }
}

export default (auth_token) => {
  return _.assign({}, {
    // -- state --

    pipe: {
      name: 'Javascript SDK Pipe',
      description: 'This pipe was created using the Flex.io Javascript SDK',
      task: []
    },
    processes: [],
    loading: false,
    saving: false,
    running: false,

    // -- methods --

    getJSON() {
      return _.assign({}, this.pipe)
    },

    getProcesses() {
      return [].concat(this.processes)
    },

    getLastProcess() {
      return _.last(this.processes)
    },

    addTask(task) {
      this.pipe.task.push(task)
      return this
    },

    clearTasks() {
      this.pipe.task = []
      return this
    },

    load() {
      var args = Array.from(arguments)
      var identifier = _.get(args, '[0]')
      var callback = _.get(args, '[1]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.load.apply(this, arguments) }, 50)
        return this
      }

      if (_.isNil(identifier))
        return util.debug.call(this, "The `identifier` parameter is required. Either the pipe's eid or pipe's alias may be used.")

      this.loading = true
      util.debug.call(this, 'Loading Pipe `' + identifier + '`...')

      flexio.http().get('/pipes/' + identifier)
        .then(response => {
          var obj = _.get(response, 'data', {})
          this.pipe = _.assign({}, obj)
          this.loading = false
          util.debug.call(this, 'Pipe Loaded.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.loading = false
          util.debug.call(this, 'Pipe Load Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    save() {
      var args = Array.from(arguments)
      var params = _.get(args, '[0]')
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.save.apply(this, arguments) }, 50)
        return this
      }

      if (_.isObject(params))
      {
        _.assign(this.pipe, _.pick(params, ['name', 'description', 'ename']))
        callback = _.get(args, '[1]')
      }

      this.saving = true
      util.debug.call(this, 'Saving Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      flexio.http().post('/pipes', this.pipe)
        .then(response => {
          var pipe = _.get(response, 'data', {})
          this.pipe = _.assign({}, pipe)
          this.saving = false
          util.debug.call(this, 'Pipe Saved.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.saving = false
          util.debug.call(this, 'Pipe Save Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    run() {
      var args = Array.from(arguments)
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.run.apply(this, arguments) }, 50)
        return this
      }

      this.running = true
      util.debug.call(this, 'Running Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      var run_params = _.assign({}, this.pipe)

      // if we have saved this pipe, use the pipe's eid as the parent eid
      var parent_eid = _.get(this.pipe, 'eid', '')
      if (parent_eid.length > 0)
        run_params = { parent_eid }

      // set the process to run mode
      _.assign(run_params, {
        process_mode: 'R'
      })

      flexio.http().post('/processes', run_params)
        .then(response => {
          var obj = _.get(response, 'data', {})
          var process_eid = _.get(obj, 'eid', '')
          this.processes.push(obj)
          util.debug.call(this, 'Created Process.')

          flexio.http().post('/processes/'+process_eid+'/run')
            .then(response => {
              var obj2 = _.get(response, 'data', {})
              util.debug.call(this, 'Process Complete.')
              this.running = false

              if (typeof callback == 'function')
                callback.call(this, null, obj2)
            })
        })
        .catch(error => {
          util.debug.call(this, 'Process Create Failed.')
          this.running = false

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    // -- tasks --

    input() {
      var type = ttypes.TASK_TYPE_INPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var items = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The input task requires at least 1 parameter')

      switch (connection_type)
      {
        default:
          connection_type = ctypes.CONNECTION_TYPE_HTTP
          items = [].concat(args)
          break

        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_HTTP:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          items = _.get(args, '[2]', [])
          break

        case ctypes.CONNECTION_TYPE_RSS:
          connection = connection_type
          items = _.get(args, '[1]', [])
          break
      }

      items = _.map(items, (item) => {
        return {
          path: item
        }
      })

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          items
        }
      })
    },

    output() {
      var type = ttypes.TASK_TYPE_OUTPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var location = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The output task requires at least 1 parameter')

      switch (connection_type)
      {
        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
          connection = _.get(args, '[1]', '')
          break

        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          location = _.get(args, '[2]', '/')
          break
      }

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          location
        }
      })
    },

    convert(input, output) {
      var type = ttypes.TASK_TYPE_CONVERT

      var task = {
        type,
        params: {}
      }

      var csv_format = {
        format: 'delimited',
        delimiter: '{comma}',
        header: true,
        qualifier: '{double-quote}'
      }

      var tsv_format = {
        format: 'delimited',
        delimiter: '{tab}',
        header: true,
        qualifier: '{none}'
      }

      // convert input

      if (_.isString(input))
      {
        if (input == 'csv')
          _.set(task, 'params.input', csv_format)
           else if (input == 'tsv')
          _.set(task, 'params.input', tsv_format)
           else
          _.set(task, 'params.input.format', input)
      }
       else if (_.isObject(input))
      {
        _.set(task, 'params.input', input)
      }

      // convert output

      if (_.isString(output))
      {
        if (output == 'csv')
          _.set(task, 'params.output', csv_format)
           else if (output == 'tsv')
          _.set(task, 'params.output', tsv_format)
           else
          _.set(task, 'params.output.format', output)
      }
       else if (_.isObject(output))
      {
        _.set(task, 'params.output', output)
      }

      return this.addTask(task)
    },

    email(to, subject, body_text, body_html, data) {
      var type = ttypes.TASK_TYPE_EMAIL_SEND

      if (_.isNil(to))
        return util.debug.call(this, 'The `to` parameter is required')

      if (_.isNil(subject))
        return util.debug.call(this, 'The `subject` parameter is required')

      if (_.isNil(body_text))
        return util.debug.call(this, 'The `body_text` parameter is required')

      // `to` parameter must be an array
      if (!_.isArray(to))
        to = [to]

      if (_.isNil(body_html))
        body_html = body_text

      if (data != 'body' && data != 'attachment')
        data = 'none'

      return this.addTask({
        type,
        params: {
          to,
          subject,
          body_text,
          body_html,
          data
        }
      })
    },

    execute() {
      var type = ttypes.TASK_TYPE_EXECUTE
      var args = Array.from(arguments)
      var lang = _.get(args, '[0]', '')
      var code = undefined

      var task = {
        type,
        params: {}
      }

      // allow for flexible parameters
      if (lang == 'python' || lang == 'javascript')
      {
        code = _.get(args, '[1]', '')
      }
       else
      {
        // default to javascript
        lang = 'javascript'
        code = _.get(args, '[0]', '')
      }

      if (_.isFunction(code))
      {
        // first argument is a function; we're using javascript
        if (_.isNil(lang))
          lang = 'javascript'

        try {
          // stringify the function
          code = code.toString()

          // remove the wrapper function and just return its body
          code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))

          // trim outer whitespace
          code = code.trim()
        } catch (e) {
          code = ''
        }
      }

      // set the job's language
      _.set(task, 'params.lang', lang)

      // handle files or code snippets
      var http_regex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/
      if (code.match(http_regex))
        _.set(task, 'params.file', code)
         else
        _.set(task, 'params.code', toBase64(code))

      return this.addTask(task)
    },

    filter(where) {
      var type = ttypes.TASK_TYPE_FILTER

      if (_.isNil(where))
        return util.debug.call(this, 'The `filter` parameter is required')

      return this.addTask({
        type,
        params: {
          where
        }
      })
    },

    // shorthand for .execute('javascript', ...)
    javascript() {
      var args = Array.from(arguments)
      return this.execute('javascript', _.get(args, '[0]', ''))
    },

    limit(value) {
      var type = ttypes.TASK_TYPE_LIMIT
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    },

    // shorthand for .execute('python', ...)
    python() {
      var args = Array.from(arguments)
      return this.execute('python', _.get(args, '[0]', ''))
    },

    request() {
      var type = ttypes.TASK_TYPE_REQUEST
      var args = Array.from(arguments)
      var params = _.get(args, '[0]', {})

      // defaults
      params = _.assign({
        method: 'GET'
      }, params)

      return this.addTask({
        type,
        params
      })
    },

    select() {
      var type = ttypes.TASK_TYPE_SELECT
      var columns = Array.from(arguments)

      return this.addTask({
        type,
        params: {
          columns
        }
      })
    },

    sleep(value) {
      var type = ttypes.TASK_TYPE_SLEEP
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    }
  })
}
<commit_msg>Allow an array to be passed as the first param of the .select() command.<commit_after>import _ from 'lodash'

import * as ttypes from './constants/task-type'
import * as ctypes from './constants/connection-type'

import util from './util'
import flexio from './flexio'

var toBase64 = function(str) {
  try { return btoa(unescape(encodeURIComponent(str))) } catch(e) { return e }
}

var fromBase64 = function(str) {
  try { return decodeURIComponent(escape(atob(str))) } catch(e) { return e }
}

export default (auth_token) => {
  return _.assign({}, {
    // -- state --

    pipe: {
      name: 'Javascript SDK Pipe',
      description: 'This pipe was created using the Flex.io Javascript SDK',
      task: []
    },
    processes: [],
    loading: false,
    saving: false,
    running: false,

    // -- methods --

    getJSON() {
      return _.assign({}, this.pipe)
    },

    getProcesses() {
      return [].concat(this.processes)
    },

    getLastProcess() {
      return _.last(this.processes)
    },

    addTask(task) {
      this.pipe.task.push(task)
      return this
    },

    clearTasks() {
      this.pipe.task = []
      return this
    },

    load() {
      var args = Array.from(arguments)
      var identifier = _.get(args, '[0]')
      var callback = _.get(args, '[1]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.load.apply(this, arguments) }, 50)
        return this
      }

      if (_.isNil(identifier))
        return util.debug.call(this, "The `identifier` parameter is required. Either the pipe's eid or pipe's alias may be used.")

      this.loading = true
      util.debug.call(this, 'Loading Pipe `' + identifier + '`...')

      flexio.http().get('/pipes/' + identifier)
        .then(response => {
          var obj = _.get(response, 'data', {})
          this.pipe = _.assign({}, obj)
          this.loading = false
          util.debug.call(this, 'Pipe Loaded.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.loading = false
          util.debug.call(this, 'Pipe Load Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    save() {
      var args = Array.from(arguments)
      var params = _.get(args, '[0]')
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.save.apply(this, arguments) }, 50)
        return this
      }

      if (_.isObject(params))
      {
        _.assign(this.pipe, _.pick(params, ['name', 'description', 'ename']))
        callback = _.get(args, '[1]')
      }

      this.saving = true
      util.debug.call(this, 'Saving Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      flexio.http().post('/pipes', this.pipe)
        .then(response => {
          var pipe = _.get(response, 'data', {})
          this.pipe = _.assign({}, pipe)
          this.saving = false
          util.debug.call(this, 'Pipe Saved.')

          if (typeof callback == 'function')
            callback.call(this, null, obj)
        })
        .catch(error => {
          this.saving = false
          util.debug.call(this, 'Pipe Save Failed.')

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    run() {
      var args = Array.from(arguments)
      var callback = _.get(args, '[0]')

      if (this.loading === true || this.saving === true || this.running === true)
      {
        setTimeout(() => { this.run.apply(this, arguments) }, 50)
        return this
      }

      this.running = true
      util.debug.call(this, 'Running Pipe `' + _.get(this.pipe, 'name', 'Untitled Pipe') + '`...')

      var run_params = _.assign({}, this.pipe)

      // if we have saved this pipe, use the pipe's eid as the parent eid
      var parent_eid = _.get(this.pipe, 'eid', '')
      if (parent_eid.length > 0)
        run_params = { parent_eid }

      // set the process to run mode
      _.assign(run_params, {
        process_mode: 'R'
      })

      flexio.http().post('/processes', run_params)
        .then(response => {
          var obj = _.get(response, 'data', {})
          var process_eid = _.get(obj, 'eid', '')
          this.processes.push(obj)
          util.debug.call(this, 'Created Process.')

          flexio.http().post('/processes/'+process_eid+'/run')
            .then(response => {
              var obj2 = _.get(response, 'data', {})
              util.debug.call(this, 'Process Complete.')
              this.running = false

              if (typeof callback == 'function')
                callback.call(this, null, obj2)
            })
        })
        .catch(error => {
          util.debug.call(this, 'Process Create Failed.')
          this.running = false

          if (typeof callback == 'function')
            callback.call(this, error, null)
        })

      return this
    },

    // -- tasks --

    input() {
      var type = ttypes.TASK_TYPE_INPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var items = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The input task requires at least 1 parameter')

      switch (connection_type)
      {
        default:
          connection_type = ctypes.CONNECTION_TYPE_HTTP
          items = [].concat(args)
          break

        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_HTTP:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          items = _.get(args, '[2]', [])
          break

        case ctypes.CONNECTION_TYPE_RSS:
          connection = connection_type
          items = _.get(args, '[1]', [])
          break
      }

      items = _.map(items, (item) => {
        return {
          path: item
        }
      })

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          items
        }
      })
    },

    output() {
      var type = ttypes.TASK_TYPE_OUTPUT
      var args = Array.from(arguments)
      var connection_type = _.get(args, '[0]', '')
      var connection = undefined
      var location = undefined

      if (args.length == 0)
        return util.debug.call(this, 'The output task requires at least 1 parameter')

      switch (connection_type)
      {
        case ctypes.CONNECTION_TYPE_AMAZONS3:
        case ctypes.CONNECTION_TYPE_ELASTICSEARCH:
        case ctypes.CONNECTION_TYPE_GOOGLESHEETS:
        case ctypes.CONNECTION_TYPE_MYSQL:
        case ctypes.CONNECTION_TYPE_POSTGRES:
          connection = _.get(args, '[1]', '')
          break

        case ctypes.CONNECTION_TYPE_DROPBOX:
        case ctypes.CONNECTION_TYPE_GOOGLEDRIVE:
        case ctypes.CONNECTION_TYPE_SFTP:
          connection = _.get(args, '[1]', '')
          location = _.get(args, '[2]', '/')
          break
      }

      return this.addTask({
        type,
        metadata: { connection_type },
        params: {
          connection,
          location
        }
      })
    },

    convert(input, output) {
      var type = ttypes.TASK_TYPE_CONVERT

      var task = {
        type,
        params: {}
      }

      var csv_format = {
        format: 'delimited',
        delimiter: '{comma}',
        header: true,
        qualifier: '{double-quote}'
      }

      var tsv_format = {
        format: 'delimited',
        delimiter: '{tab}',
        header: true,
        qualifier: '{none}'
      }

      // convert input

      if (_.isString(input))
      {
        if (input == 'csv')
          _.set(task, 'params.input', csv_format)
           else if (input == 'tsv')
          _.set(task, 'params.input', tsv_format)
           else
          _.set(task, 'params.input.format', input)
      }
       else if (_.isObject(input))
      {
        _.set(task, 'params.input', input)
      }

      // convert output

      if (_.isString(output))
      {
        if (output == 'csv')
          _.set(task, 'params.output', csv_format)
           else if (output == 'tsv')
          _.set(task, 'params.output', tsv_format)
           else
          _.set(task, 'params.output.format', output)
      }
       else if (_.isObject(output))
      {
        _.set(task, 'params.output', output)
      }

      return this.addTask(task)
    },

    email(to, subject, body_text, body_html, data) {
      var type = ttypes.TASK_TYPE_EMAIL_SEND

      if (_.isNil(to))
        return util.debug.call(this, 'The `to` parameter is required')

      if (_.isNil(subject))
        return util.debug.call(this, 'The `subject` parameter is required')

      if (_.isNil(body_text))
        return util.debug.call(this, 'The `body_text` parameter is required')

      // `to` parameter must be an array
      if (!_.isArray(to))
        to = [to]

      if (_.isNil(body_html))
        body_html = body_text

      if (data != 'body' && data != 'attachment')
        data = 'none'

      return this.addTask({
        type,
        params: {
          to,
          subject,
          body_text,
          body_html,
          data
        }
      })
    },

    execute() {
      var type = ttypes.TASK_TYPE_EXECUTE
      var args = Array.from(arguments)
      var lang = _.get(args, '[0]', '')
      var code = undefined

      var task = {
        type,
        params: {}
      }

      // allow for flexible parameters
      if (lang == 'python' || lang == 'javascript')
      {
        code = _.get(args, '[1]', '')
      }
       else
      {
        // default to javascript
        lang = 'javascript'
        code = _.get(args, '[0]', '')
      }

      if (_.isFunction(code))
      {
        // first argument is a function; we're using javascript
        if (_.isNil(lang))
          lang = 'javascript'

        try {
          // stringify the function
          code = code.toString()

          // remove the wrapper function and just return its body
          code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'))

          // trim outer whitespace ???
          //code = code.trim()
        } catch (e) {
          code = ''
        }
      }

      // set the job's language
      _.set(task, 'params.lang', lang)

      // handle files or code snippets
      var http_regex = /^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/
      if (code.match(http_regex))
        _.set(task, 'params.file', code)
         else
        _.set(task, 'params.code', toBase64(code))

      return this.addTask(task)
    },

    filter(where) {
      var type = ttypes.TASK_TYPE_FILTER

      if (_.isNil(where))
        return util.debug.call(this, 'The `filter` parameter is required')

      return this.addTask({
        type,
        params: {
          where
        }
      })
    },

    // shorthand for .execute('javascript', ...)
    javascript() {
      var args = Array.from(arguments)
      return this.execute('javascript', _.get(args, '[0]', ''))
    },

    limit(value) {
      var type = ttypes.TASK_TYPE_LIMIT
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    },

    // shorthand for .execute('python', ...)
    python() {
      var args = Array.from(arguments)
      return this.execute('python', _.get(args, '[0]', ''))
    },

    request() {
      var type = ttypes.TASK_TYPE_REQUEST
      var args = Array.from(arguments)
      var params = _.get(args, '[0]', {})

      // defaults
      params = _.assign({
        method: 'GET'
      }, params)

      return this.addTask({
        type,
        params
      })
    },

    select() {
      var type = ttypes.TASK_TYPE_SELECT
      var columns = Array.from(arguments)

      // handle the case where the user passed an array of items
      // instead of just passing them as arguments
      if (columns.length == 1 && _.isArray(_.get(columns, '[0]')))
        columns = _.get(columns, '[0]', [])

      return this.addTask({
        type,
        params: {
          columns
        }
      })
    },

    sleep(value) {
      var type = ttypes.TASK_TYPE_SLEEP
      value = _.defaultTo(value, 10)

      return this.addTask({
        type,
        params: {
          value
        }
      })
    }
  })
}
<|endoftext|>


<commit_before>/*
 * grunt-plum
 *
 * Copyright (c) 2015 Jason Bellamy
 * Licensed under the MIT license.
 */
import fixture from 'plum-fixture';
import regression from 'plum-regression';

let plum = (grunt) => {

  grunt.registerMultiTask('plum', 'Grunt plugin to build and run visual regression tests against plum stylesheets.', function () {
    let done     = this.async();
    let options  = this.options();
    let fixtures = `${options.results}/fixtures`;
    let failures = `${options.results}/failures`;
    let results  = `${options.results}/results`;
    let base     = options.base;
    let tests    = (() => {
      if (grunt.option('tests')) {
        return grunt.option('tests').split(',');
      }
      return options.tests;
    })().map(test => `${base}/${test}`);
    let stylesheets = grunt.file.expand(options.stylesheets).map(function(path) {
      return { url: path };
    });

    if (grunt.file.exists(options.results)) {
      grunt.file.delete(options.results);
    }

    if (!grunt.file.exists(fixtures)) {
      grunt.file.mkdir(fixtures);
    }

    fixture({
      stylesheets: stylesheets,
      files: tests,
      destination: fixtures
    }, (err, response) => {
      if (err) {
        return grunt.fail.error(err);
      }

      regression({
        stylesheets: base,
        tests: tests,
        fixtures: fixtures,
        results: results,
        failures: failures
        }, (err, resp) => {
          if (err) {
            return grunt.fail.error(err);
          }

          grunt.file.delete(fixtures);
          grunt.file.delete(results);
          grunt.log.ok(resp);
          done();
      });
    });

  });
};

export default plum;
<commit_msg>only pass the stylesheet folders/files into the fixture method<commit_after>/*
 * grunt-plum
 *
 * Copyright (c) 2015 Jason Bellamy
 * Licensed under the MIT license.
 */
import fixture from 'plum-fixture';
import regression from 'plum-regression';

let plum = (grunt) => {

  grunt.registerMultiTask('plum', 'Grunt plugin to build and run visual regression tests against plum stylesheets.', function () {
    let done        = this.async();
    let options     = this.options();
    let stylesheets = grunt.file.expand(options.stylesheets);
    let fixtures    = `${options.results}/fixtures`;
    let failures    = `${options.results}/failures`;
    let results     = `${options.results}/results`;
    let base        = options.base;
    let tests       = (() => {
      if (grunt.option('tests')) {
        return grunt.option('tests').split(',');
      }
      return options.tests;
    })().map(test => `${base}/${test}`);

    if (grunt.file.exists(options.results)) {
      grunt.file.delete(options.results);
    }

    if (!grunt.file.exists(fixtures)) {
      grunt.file.mkdir(fixtures);
    }

    fixture({
      stylesheets: stylesheets,
      files: tests,
      destination: fixtures
    }, (err, response) => {
      if (err) {
        return grunt.fail.error(err);
      }

      regression({
        stylesheets: base,
        tests: tests,
        fixtures: fixtures,
        results: results,
        failures: failures
        }, (err, resp) => {
          if (err) {
            return grunt.fail.error(err);
          }

          grunt.file.delete(fixtures);
          grunt.file.delete(results);
          grunt.log.ok(resp);
          done();
      });
    });

  });
};

export default plum;
<|endoftext|>