<commit_before>function hd(arr){
  return arr[0];
}

function tl(arr){
  return arr.slice(1);
}

function tlTest(){
  var arr = [1,2,3];
  var tail = tl(arr);
  return arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         arr.length == 3 &&
         tail[0] == 2 &&
         tail[1] == 3 &&
         tail.length == 2;
}

function cons(arr, elem){
  copy = arr.slice(0);
  copy.unshift(elem);
  return copy;
}

function consTest(){
  var arr = [1,2,3];
  var arr2 = cons(arr, 0);
  return arr.length == 3 &&
         arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         arr2.length == 4 &&
         arr2[0] == 0 &&
         arr2[1] == 1 &&
         arr2[2] == 2 &&
         arr2[3] == 3;
}

function reverse(arr){
  copy = arr.slice(0);
  copy.reverse();
  return copy;
}

function reverseTest(){
  var arr = [1,2,3];
  var rev = reverse(arr);
  return arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         rev[0] == 3 &&
         rev[1] == 2 &&
         rev[2] == 1;
}

function concat(arr1, arr2){
  return arr1.concat(arr2);
}

function map(fun, arr, maybe_results){
  var results = (maybe_results == undefined ? [] : maybe_results.slice(0));
  var next;
  if(arr.length == 0){
    return reverse(results);
  }else{
    return map(fun, tl(arr), cons(results, fun(hd(arr))));
  }
}

function mapTest(){
  var result = map(function(x){ return x + 1 }, seq(3));
  return result[0] == 2 &&
         result[1] == 3 &&
         result[2] == 4;
}

function seq(maybe_next, maybe_end, maybe_sequence){
  var next;
  var end;
  var sequence;
  if(maybe_end == undefined){
    end = maybe_next;
    next = 1;
  }else{
    next = maybe_next;
    end = maybe_end;
  }
  if(maybe_sequence == undefined){
    sequence = []
  }else{
    sequence = maybe_sequence;
  }
  if(next > end){
    return sequence;
  }else{
    sequence.push(next);
    return seq(next + 1, end, sequence);
  }
}

function seqTest(){
  var seq1 = seq(3);
  var seq2 = seq(4,6);
  return seq1[0] == 1 &&
         seq1[1] == 2 &&
         seq1[2] == 3 &&
         seq1.length == 3 &&
         seq2[0] == 4 &&
         seq2[1] == 5 &&
         seq2[2] == 6 &&
         seq2.length == 3;
}

function repeat(elem, count){
  var sequence = seq(count);
  return map(function(x){ return elem }, sequence);
}

function repeatTest(){
  var arr = repeat('x', 4);
  return arr[0] == 'x' &&
         arr[1] == 'x' &&
         arr[2] == 'x' &&
         arr[3] == 'x';
}

function filter(fun, arr){
  return filter_(fun, arr, []);
}

function filter_(fun, arr, results){
  if(arr.length == 0){
    return reverse(results);
  }

  if(fun(hd(arr))){
    return filter_(fun, tl(arr), cons(results, hd(arr)));
  } else {
    return filter_(fun, tl(arr), results.slice(0));
  }
}

function filterTest(){
  var even = function(x){return x % 2 == 0};
  var results = filter(even, [1,2,3,4]);
  return results[0] == 2 &&
         results[1] == 4;
}

function foldl(fun, arr, acc){
  if(arr.length == 0){
    return acc;
  }
  acc_copy = acc.slice(0);
  return foldl(fun, tl(arr), cons(acc, fun(hd(arr), acc_copy)));
}

function zip(arr1, arr2){
  return zip_(arr1, arr2, []);
}

function zip_(arr1, arr2, zips){
  if(arr1.length == 0 ||
     arr2.length == 0){
    return reverse(zips);
  }

  return zip_(tl(arr1),
              tl(arr2),
              cons(zips, [hd(arr1), hd(arr2)]));

}

function zip3(arr1, arr2, arr3){
  return zip3_(arr1, arr2, arr3, []);
}

function zip3_(arr1, arr2, arr3, zips){
  if(arr1.length == 0 ||
     arr2.length == 0 ||
     arr3.length == 0){
    return reverse(zips);
  }

  return zip3_(tl(arr1),
              tl(arr2),
              tl(arr3),
              cons(zips, [hd(arr1), hd(arr2), hd(arr3)]));
}

function zip3Test(){
  var arr1 = [1,2,3];
  var arr2 = [4,5,6];
  var arr3 = [7,8,9];

  var zipped = zip3(arr1, arr2, arr3);
  return zipped[0][0] == 1 &&
         zipped[0][1] == 4 &&
         zipped[0][2] == 7 &&
         zipped[1][0] == 2 &&
         zipped[1][1] == 5 &&
         zipped[1][2] == 8 &&
         zipped[2][0] == 3 &&
         zipped[2][1] == 6 &&
         zipped[2][2] == 9 &&
         zipped.length == 3 &&
         zipped[0].length == 3 &&
         zipped[1].length == 3 &&
         zipped[2].length == 3;
}
<commit_msg>Fix foldl: don't expect array acc<commit_after>function hd(arr){
  return arr[0];
}

function tl(arr){
  return arr.slice(1);
}

function tlTest(){
  var arr = [1,2,3];
  var tail = tl(arr);
  return arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         arr.length == 3 &&
         tail[0] == 2 &&
         tail[1] == 3 &&
         tail.length == 2;
}

function cons(arr, elem){
  copy = arr.slice(0);
  copy.unshift(elem);
  return copy;
}

function consTest(){
  var arr = [1,2,3];
  var arr2 = cons(arr, 0);
  return arr.length == 3 &&
         arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         arr2.length == 4 &&
         arr2[0] == 0 &&
         arr2[1] == 1 &&
         arr2[2] == 2 &&
         arr2[3] == 3;
}

function reverse(arr){
  copy = arr.slice(0);
  copy.reverse();
  return copy;
}

function reverseTest(){
  var arr = [1,2,3];
  var rev = reverse(arr);
  return arr[0] == 1 &&
         arr[1] == 2 &&
         arr[2] == 3 &&
         rev[0] == 3 &&
         rev[1] == 2 &&
         rev[2] == 1;
}

function concat(arr1, arr2){
  return arr1.concat(arr2);
}

function map(fun, arr, maybe_results){
  var results = (maybe_results == undefined ? [] : maybe_results.slice(0));
  var next;
  if(arr.length == 0){
    return reverse(results);
  }else{
    return map(fun, tl(arr), cons(results, fun(hd(arr))));
  }
}

function mapTest(){
  var result = map(function(x){ return x + 1 }, seq(3));
  return result[0] == 2 &&
         result[1] == 3 &&
         result[2] == 4;
}

function seq(maybe_next, maybe_end, maybe_sequence){
  var next;
  var end;
  var sequence;
  if(maybe_end == undefined){
    end = maybe_next;
    next = 1;
  }else{
    next = maybe_next;
    end = maybe_end;
  }
  if(maybe_sequence == undefined){
    sequence = []
  }else{
    sequence = maybe_sequence;
  }
  if(next > end){
    return sequence;
  }else{
    sequence.push(next);
    return seq(next + 1, end, sequence);
  }
}

function seqTest(){
  var seq1 = seq(3);
  var seq2 = seq(4,6);
  return seq1[0] == 1 &&
         seq1[1] == 2 &&
         seq1[2] == 3 &&
         seq1.length == 3 &&
         seq2[0] == 4 &&
         seq2[1] == 5 &&
         seq2[2] == 6 &&
         seq2.length == 3;
}

function repeat(elem, count){
  var sequence = seq(count);
  return map(function(x){ return elem }, sequence);
}

function repeatTest(){
  var arr = repeat('x', 4);
  return arr[0] == 'x' &&
         arr[1] == 'x' &&
         arr[2] == 'x' &&
         arr[3] == 'x';
}

function filter(fun, arr){
  return filter_(fun, arr, []);
}

function filter_(fun, arr, results){
  if(arr.length == 0){
    return reverse(results);
  }

  if(fun(hd(arr))){
    return filter_(fun, tl(arr), cons(results, hd(arr)));
  } else {
    return filter_(fun, tl(arr), results.slice(0));
  }
}

function filterTest(){
  var even = function(x){return x % 2 == 0};
  var results = filter(even, [1,2,3,4]);
  return results[0] == 2 &&
         results[1] == 4;
}

function foldl(fun, arr, acc){
  if(arr.length == 0){
    return acc;
  }
  return foldl(fun, tl(arr), fun(hd(arr), acc));
}

function zip(arr1, arr2){
  return zip_(arr1, arr2, []);
}

function zip_(arr1, arr2, zips){
  if(arr1.length == 0 ||
     arr2.length == 0){
    return reverse(zips);
  }

  return zip_(tl(arr1),
              tl(arr2),
              cons(zips, [hd(arr1), hd(arr2)]));

}

function zip3(arr1, arr2, arr3){
  return zip3_(arr1, arr2, arr3, []);
}

function zip3_(arr1, arr2, arr3, zips){
  if(arr1.length == 0 ||
     arr2.length == 0 ||
     arr3.length == 0){
    return reverse(zips);
  }

  return zip3_(tl(arr1),
              tl(arr2),
              tl(arr3),
              cons(zips, [hd(arr1), hd(arr2), hd(arr3)]));
}

function zip3Test(){
  var arr1 = [1,2,3];
  var arr2 = [4,5,6];
  var arr3 = [7,8,9];

  var zipped = zip3(arr1, arr2, arr3);
  return zipped[0][0] == 1 &&
         zipped[0][1] == 4 &&
         zipped[0][2] == 7 &&
         zipped[1][0] == 2 &&
         zipped[1][1] == 5 &&
         zipped[1][2] == 8 &&
         zipped[2][0] == 3 &&
         zipped[2][1] == 6 &&
         zipped[2][2] == 9 &&
         zipped.length == 3 &&
         zipped[0].length == 3 &&
         zipped[1].length == 3 &&
         zipped[2].length == 3;
}
<|endoftext|>


<commit_before>
// setup
Elm.Native = Elm.Native || {};
Elm.Native.Graphics = Elm.Native.Graphics || {};
Elm.Native.Graphics.Element = Elm.Native.Graphics.Element || {};

// definition
Elm.Native.Graphics.Element.make = function(localRuntime) {
    'use strict';

    // attempt to short-circuit
    if ('values' in Elm.Native.Graphics.Element) {
        return Elm.Native.Graphics.Element.values;
    }

    var Color = Elm.Native.Color.make(localRuntime);
    var List = Elm.Native.List.make(localRuntime);
    var Utils = Elm.Native.Utils.make(localRuntime);

    function createNode(elementType) {
        var node = document.createElement(elementType);
        node.style.padding = "0";
        node.style.margin = "0";
        return node;
    }

    function setProps(elem, node) {
        var props = elem.props;

        var element = elem.element;
        var width = props.width - (element.adjustWidth || 0);
        var height = props.height - (element.adjustHeight || 0);
        node.style.width  = (width |0) + 'px';
        node.style.height = (height|0) + 'px';

        if (props.opacity !== 1) {
            node.style.opacity = props.opacity;
        }

        if (props.color.ctor === 'Just') {
            node.style.backgroundColor = Color.toCss(props.color._0);
        }

        if (props.tag !== '') {
            node.id = props.tag;
        }

        if (props.hover.ctor !== '_Tuple0') {
            addHover(node, props.hover);
        }

        if (props.click.ctor !== '_Tuple0') {
            addClick(node, props.click);
        }

        if (props.href !== '') {
            var anchor = createNode('a');
            anchor.href = props.href;
            anchor.style.display = 'block';
            anchor.style.pointerEvents = 'auto';
            anchor.appendChild(node);
            node = anchor;
        }

        return node;
    }

    function addClick(e, handler) {
        e.style.pointerEvents = 'auto';
        e.elm_click_handler = handler;
        function trigger(ev) {
            e.elm_click_handler(Utils.Tuple0);
            ev.stopPropagation();
        }
        e.elm_click_trigger = trigger;
        e.addEventListener('click', trigger);
    }

    function removeClick(e, handler) {
        if (e.elm_click_trigger) {
            e.removeEventListener('click', e.elm_click_trigger);
            e.elm_click_trigger = null;
            e.elm_click_handler = null;
        }
    }

    function addHover(e, handler) {
        e.style.pointerEvents = 'auto';
        e.elm_hover_handler = handler;
        e.elm_hover_count = 0;

        function over(evt) {
            if (e.elm_hover_count++ > 0) return;
            e.elm_hover_handler(true);
            evt.stopPropagation();
        }
        function out(evt) {
            if (e.contains(evt.toElement || evt.relatedTarget)) return;
            e.elm_hover_count = 0;
            e.elm_hover_handler(false);
            evt.stopPropagation();
        }
        e.elm_hover_over = over;
        e.elm_hover_out = out;
        e.addEventListener('mouseover', over);
        e.addEventListener('mouseout', out);
    }

    function removeHover(e) {
        e.elm_hover_handler = null;
        if (e.elm_hover_over) {
            e.removeEventListener('mouseover', e.elm_hover_over);
            e.elm_hover_over = null;
        }
        if (e.elm_hover_out) {
            e.removeEventListener('mouseout', e.elm_hover_out);
            e.elm_hover_out = null;
        }
    }

    function image(props, img) {
        switch (img._0.ctor) {
        case 'Plain':   return plainImage(img._3);
        case 'Fitted':  return fittedImage(props.width, props.height, img._3);
        case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
        case 'Tiled':   return tiledImage(img._3);
        }
    }

    function plainImage(src) {
        var img = createNode('img');
        img.src = src;
        img.name = src;
        img.style.display = "block";
        return img;
    }

    function tiledImage(src) {
        var div = createNode('div');
        div.style.backgroundImage = 'url(' + src + ')';
        return div;
    }

    function fittedImage(w, h, src) {
        var div = createNode('div');
        div.style.background = 'url(' + src + ') no-repeat center';
        div.style.webkitBackgroundSize = 'cover';
        div.style.MozBackgroundSize = 'cover';
        div.style.OBackgroundSize = 'cover';
        div.style.backgroundSize = 'cover';
        return div;
    }

    function croppedImage(elem, w, h, src) {
        var pos = elem._0._0;
        var e = createNode('div');
        e.style.overflow = "hidden";

        var img = createNode('img');
        img.onload = function() {
            var sw = w / elem._1, sh = h / elem._2;
            img.style.width = ((this.width * sw)|0) + 'px';
            img.style.height = ((this.height * sh)|0) + 'px';
            img.style.marginLeft = ((- pos._0 * sw)|0) + 'px';
            img.style.marginTop = ((- pos._1 * sh)|0) + 'px';
        };
        img.src = src;
        img.name = src;
        e.appendChild(img);
        return e;
    }

    function goOut(e) { e.style.position = 'absolute'; return e; }
    function goDown(e) { return e }
    function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }

    var directionTable = {
        DUp    : goDown,
        DDown  : goDown,
        DLeft  : goRight,
        DRight : goRight,
        DIn    : goOut,
        DOut   : goOut
    };
    function needsReversal(dir) {
        return dir == 'DUp' || dir == 'DLeft' || dir == 'DIn';
    }

    function flow(dir,elist) {
        var array = List.toArray(elist);
        var container = createNode('div');
        var goDir = directionTable[dir];
        if (goDir == goOut) {
            container.style.pointerEvents = 'none';
        }
        if (needsReversal(dir)) {
            array.reverse();
        }
        var len = array.length;
        for (var i = 0; i < len; ++i) {
            container.appendChild(goDir(render(array[i])));
        }
        return container;
    }

    function toPos(pos) {
        switch(pos.ctor) {
        case "Absolute": return  pos._0 + "px";
        case "Relative": return (pos._0 * 100) + "%";
        }
    }

    // must clear right, left, top, bottom, and transform
    // before calling this function
    function setPos(pos,elem,e) {
        var element = elem.element;
        var props = elem.props;
        var w = props.width + (element.adjustWidth ? element.adjustWidth : 0);
        var h = props.height + (element.adjustHeight ? element.adjustHeight : 0);

        e.style.position = 'absolute';
        e.style.margin = 'auto';
        var transform = '';
        switch(pos.horizontal.ctor) {
        case 'P': e.style.right = toPos(pos.x); e.style.removeProperty('left'); break;
        case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
        case 'N': e.style.left = toPos(pos.x); e.style.removeProperty('right'); break;
        }
        switch(pos.vertical.ctor) {
        case 'N': e.style.bottom = toPos(pos.y); e.style.removeProperty('top'); break;
        case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
        case 'P': e.style.top = toPos(pos.y); e.style.removeProperty('bottom'); break;
        }
        if (transform !== '') {
            addTransform(e.style, transform);
        }
        return e;
    }

    function addTransform(style, transform) {
        style.transform       = transform;
        style.msTransform     = transform;
        style.MozTransform    = transform;
        style.webkitTransform = transform;
        style.OTransform      = transform;
    }

    function container(pos,elem) {
        var e = render(elem);
        setPos(pos, elem, e);
        var div = createNode('div');
        div.style.position = 'relative';
        div.style.overflow = 'hidden';
        div.appendChild(e);
        return div;
    }

    function rawHtml(elem) {
        var html = elem.html;
        var guid = elem.guid;
        var align = elem.align;

        var div = createNode('div');
        div.innerHTML = html;
        div.style.visibility = "hidden";
        if (align) {
            div.style.textAlign = align;
        }
        div.style.visibility = 'visible';
        div.style.pointerEvents = 'auto';
        return div;
    }

    function render(elem) {
        return setProps(elem, makeElement(elem));
    }
    function makeElement(e) {
        var elem = e.element;
        switch(elem.ctor) {
        case 'Image':     return image(e.props, elem);
        case 'Flow':      return flow(elem._0.ctor, elem._1);
        case 'Container': return container(elem._0, elem._1);
        case 'Spacer':    return createNode('div');
        case 'RawHtml':   return rawHtml(elem);
        case 'Custom':    return elem.render(elem.model);
        }
    }

    function update(node, curr, next) {
        if (node.tagName === 'A') {
            node = node.firstChild;
        }
        if (curr.props.id === next.props.id) {
            return updateProps(node, curr, next);
        }
        if (curr.element.ctor !== next.element.ctor) {
            node.parentNode.replaceChild(render(next),node);
            return true;
        }
        var nextE = next.element, currE = curr.element;
        switch(nextE.ctor) {
        case "Spacer": break;
        case "RawHtml":
            // only markdown blocks have guids, so this must be a text block
            if (nextE.guid === null) {
                if(currE.html.valueOf() !== nextE.html.valueOf()) {
                    node.innerHTML = nextE.html;
                }
                break;
            }
            if (nextE.guid !== currE.guid) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            break;
        case "Image":
            if (nextE._0.ctor === 'Plain') {
                if (nextE._3 !== currE._3) node.src = nextE._3;
            } else if (!Utils.eq(nextE,currE) ||
                       next.props.width !== curr.props.width ||
                       next.props.height !== curr.props.height) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            break;
        case "Flow":
            var arr = List.toArray(nextE._1);
            for (var i = arr.length; i--; ) { arr[i] = arr[i].element.ctor; }
            if (nextE._0.ctor !== currE._0.ctor) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            var nexts = List.toArray(nextE._1);
            var kids = node.childNodes;
            if (nexts.length !== kids.length) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            var currs = List.toArray(currE._1);
            var dir = nextE._0.ctor;
            var goDir = directionTable[dir];
            var toReverse = needsReversal(dir);
            var len = kids.length;
            for (var i = len; i-- ;) {
                update(kids[toReverse ? len - i - 1 : i],currs[i],nexts[i]);
                goDir(kids[i]);
            }
            break;
        case "Container":
            update(node.firstChild, currE._1, nextE._1);
            setPos(nextE._0, nextE._1, node.firstChild);
            break;
        case "Custom":
            if (currE.type === nextE.type) {
                var done = nextE.update(node, currE.model, nextE.model);
                if (done) return;
            } else {
                return node.parentNode.replaceChild(render(next), node);
            }
        }
        updateProps(node, curr, next);
    }

    function updateProps(node, curr, next) {
        var nextProps = next.props;
        var currProps = curr.props;

        var element = next.element;
        var width = nextProps.width - (element.adjustWidth || 0);
        var height = nextProps.height - (element.adjustHeight || 0);
        if (width !== currProps.width) {
            node.style.width = (width|0) + 'px';
        }
        if (height !== currProps.height) {
            node.style.height = (height|0) + 'px';
        }

        if (nextProps.opacity !== currProps.opacity) {
            node.style.opacity = nextProps.opacity;
        }

        var nextColor = nextProps.color.ctor === 'Just'
            ? Color.toCss(nextProps.color._0)
            : '';
        if (node.style.backgroundColor !== nextColor) {
            node.style.backgroundColor = nextColor;
        }

        if (nextProps.tag !== currProps.tag) {
            node.id = nextProps.tag;
        }

        if (nextProps.href !== currProps.href) {
            if (currProps.href === '') {
                // add a surrounding href
                var anchor = createNode('a');
                anchor.href = nextProps.href;
                anchor.style.display = 'block';
                anchor.style.pointerEvents = 'auto';

                node.parentNode.replaceChild(anchor, node);
                anchor.appendChild(node);
            } else if (nextProps.href === '') {
                // remove the surrounding href
                var anchor = node.parentNode;
                anchor.parentNode.replaceChild(node, anchor);
            } else {
                // just update the link
                node.parentNode.href = nextProps.href;
            }
        }

        // update click and hover handlers
        var removed = false;

        // update hover handlers
        if (currProps.hover.ctor === '_Tuple0') {
            if (nextProps.hover.ctor !== '_Tuple0') {
                addHover(node, nextProps.hover);
            }
        }
        else {
            if (nextProps.hover.ctor === '_Tuple0') {
                removed = true;
                removeHover(node);
            }
            else {
                node.elm_hover_handler = nextProps.hover;
            }
        }

        // update click handlers
        if (currProps.click.ctor === '_Tuple0') {
            if (nextProps.click.ctor !== '_Tuple0') {
                addClick(node, nextProps.click);
            }
        }
        else {
            if (nextProps.click.ctor === '_Tuple0') {
                removed = true;
                removeClick(node);
            } else {
                node.elm_click_handler = nextProps.click;
            }
        }

        // stop capturing clicks if
        if (removed
            && nextProps.hover.ctor === '_Tuple0'
            && nextProps.click.ctor === '_Tuple0')
        {
            node.style.pointerEvents = 'none';
        }
    }

    function htmlHeight(width, rawHtml) {
        // create dummy node
        var html = rawHtml.html;
        var t = document.createElement('div');
        t.innerHTML = html;
        if (width > 0) { t.style.width = width + "px"; }
        t.style.visibility = "hidden";
        t.style.styleFloat = "left";
        t.style.cssFloat   = "left";

        document.body.appendChild(t);

        // get dimensions
        var style = window.getComputedStyle(t, null);
        var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
        var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
        document.body.removeChild(t);
        return Utils.Tuple2(w,h);
    }

    return Elm.Native.Graphics.Element.values = {
        render: render,
        update: update,

        createNode: createNode,
        addTransform: addTransform,
        htmlHeight: F2(htmlHeight),
        guid: Utils.guid
    };

};
<commit_msg>rename variable t to temp<commit_after>
// setup
Elm.Native = Elm.Native || {};
Elm.Native.Graphics = Elm.Native.Graphics || {};
Elm.Native.Graphics.Element = Elm.Native.Graphics.Element || {};

// definition
Elm.Native.Graphics.Element.make = function(localRuntime) {
    'use strict';

    // attempt to short-circuit
    if ('values' in Elm.Native.Graphics.Element) {
        return Elm.Native.Graphics.Element.values;
    }

    var Color = Elm.Native.Color.make(localRuntime);
    var List = Elm.Native.List.make(localRuntime);
    var Utils = Elm.Native.Utils.make(localRuntime);

    function createNode(elementType) {
        var node = document.createElement(elementType);
        node.style.padding = "0";
        node.style.margin = "0";
        return node;
    }

    function setProps(elem, node) {
        var props = elem.props;

        var element = elem.element;
        var width = props.width - (element.adjustWidth || 0);
        var height = props.height - (element.adjustHeight || 0);
        node.style.width  = (width |0) + 'px';
        node.style.height = (height|0) + 'px';

        if (props.opacity !== 1) {
            node.style.opacity = props.opacity;
        }

        if (props.color.ctor === 'Just') {
            node.style.backgroundColor = Color.toCss(props.color._0);
        }

        if (props.tag !== '') {
            node.id = props.tag;
        }

        if (props.hover.ctor !== '_Tuple0') {
            addHover(node, props.hover);
        }

        if (props.click.ctor !== '_Tuple0') {
            addClick(node, props.click);
        }

        if (props.href !== '') {
            var anchor = createNode('a');
            anchor.href = props.href;
            anchor.style.display = 'block';
            anchor.style.pointerEvents = 'auto';
            anchor.appendChild(node);
            node = anchor;
        }

        return node;
    }

    function addClick(e, handler) {
        e.style.pointerEvents = 'auto';
        e.elm_click_handler = handler;
        function trigger(ev) {
            e.elm_click_handler(Utils.Tuple0);
            ev.stopPropagation();
        }
        e.elm_click_trigger = trigger;
        e.addEventListener('click', trigger);
    }

    function removeClick(e, handler) {
        if (e.elm_click_trigger) {
            e.removeEventListener('click', e.elm_click_trigger);
            e.elm_click_trigger = null;
            e.elm_click_handler = null;
        }
    }

    function addHover(e, handler) {
        e.style.pointerEvents = 'auto';
        e.elm_hover_handler = handler;
        e.elm_hover_count = 0;

        function over(evt) {
            if (e.elm_hover_count++ > 0) return;
            e.elm_hover_handler(true);
            evt.stopPropagation();
        }
        function out(evt) {
            if (e.contains(evt.toElement || evt.relatedTarget)) return;
            e.elm_hover_count = 0;
            e.elm_hover_handler(false);
            evt.stopPropagation();
        }
        e.elm_hover_over = over;
        e.elm_hover_out = out;
        e.addEventListener('mouseover', over);
        e.addEventListener('mouseout', out);
    }

    function removeHover(e) {
        e.elm_hover_handler = null;
        if (e.elm_hover_over) {
            e.removeEventListener('mouseover', e.elm_hover_over);
            e.elm_hover_over = null;
        }
        if (e.elm_hover_out) {
            e.removeEventListener('mouseout', e.elm_hover_out);
            e.elm_hover_out = null;
        }
    }

    function image(props, img) {
        switch (img._0.ctor) {
        case 'Plain':   return plainImage(img._3);
        case 'Fitted':  return fittedImage(props.width, props.height, img._3);
        case 'Cropped': return croppedImage(img,props.width,props.height,img._3);
        case 'Tiled':   return tiledImage(img._3);
        }
    }

    function plainImage(src) {
        var img = createNode('img');
        img.src = src;
        img.name = src;
        img.style.display = "block";
        return img;
    }

    function tiledImage(src) {
        var div = createNode('div');
        div.style.backgroundImage = 'url(' + src + ')';
        return div;
    }

    function fittedImage(w, h, src) {
        var div = createNode('div');
        div.style.background = 'url(' + src + ') no-repeat center';
        div.style.webkitBackgroundSize = 'cover';
        div.style.MozBackgroundSize = 'cover';
        div.style.OBackgroundSize = 'cover';
        div.style.backgroundSize = 'cover';
        return div;
    }

    function croppedImage(elem, w, h, src) {
        var pos = elem._0._0;
        var e = createNode('div');
        e.style.overflow = "hidden";

        var img = createNode('img');
        img.onload = function() {
            var sw = w / elem._1, sh = h / elem._2;
            img.style.width = ((this.width * sw)|0) + 'px';
            img.style.height = ((this.height * sh)|0) + 'px';
            img.style.marginLeft = ((- pos._0 * sw)|0) + 'px';
            img.style.marginTop = ((- pos._1 * sh)|0) + 'px';
        };
        img.src = src;
        img.name = src;
        e.appendChild(img);
        return e;
    }

    function goOut(e) { e.style.position = 'absolute'; return e; }
    function goDown(e) { return e }
    function goRight(e) { e.style.styleFloat = e.style.cssFloat = "left"; return e; }

    var directionTable = {
        DUp    : goDown,
        DDown  : goDown,
        DLeft  : goRight,
        DRight : goRight,
        DIn    : goOut,
        DOut   : goOut
    };
    function needsReversal(dir) {
        return dir == 'DUp' || dir == 'DLeft' || dir == 'DIn';
    }

    function flow(dir,elist) {
        var array = List.toArray(elist);
        var container = createNode('div');
        var goDir = directionTable[dir];
        if (goDir == goOut) {
            container.style.pointerEvents = 'none';
        }
        if (needsReversal(dir)) {
            array.reverse();
        }
        var len = array.length;
        for (var i = 0; i < len; ++i) {
            container.appendChild(goDir(render(array[i])));
        }
        return container;
    }

    function toPos(pos) {
        switch(pos.ctor) {
        case "Absolute": return  pos._0 + "px";
        case "Relative": return (pos._0 * 100) + "%";
        }
    }

    // must clear right, left, top, bottom, and transform
    // before calling this function
    function setPos(pos,elem,e) {
        var element = elem.element;
        var props = elem.props;
        var w = props.width + (element.adjustWidth ? element.adjustWidth : 0);
        var h = props.height + (element.adjustHeight ? element.adjustHeight : 0);

        e.style.position = 'absolute';
        e.style.margin = 'auto';
        var transform = '';
        switch(pos.horizontal.ctor) {
        case 'P': e.style.right = toPos(pos.x); e.style.removeProperty('left'); break;
        case 'Z': transform = 'translateX(' + ((-w/2)|0) + 'px) ';
        case 'N': e.style.left = toPos(pos.x); e.style.removeProperty('right'); break;
        }
        switch(pos.vertical.ctor) {
        case 'N': e.style.bottom = toPos(pos.y); e.style.removeProperty('top'); break;
        case 'Z': transform += 'translateY(' + ((-h/2)|0) + 'px)';
        case 'P': e.style.top = toPos(pos.y); e.style.removeProperty('bottom'); break;
        }
        if (transform !== '') {
            addTransform(e.style, transform);
        }
        return e;
    }

    function addTransform(style, transform) {
        style.transform       = transform;
        style.msTransform     = transform;
        style.MozTransform    = transform;
        style.webkitTransform = transform;
        style.OTransform      = transform;
    }

    function container(pos,elem) {
        var e = render(elem);
        setPos(pos, elem, e);
        var div = createNode('div');
        div.style.position = 'relative';
        div.style.overflow = 'hidden';
        div.appendChild(e);
        return div;
    }

    function rawHtml(elem) {
        var html = elem.html;
        var guid = elem.guid;
        var align = elem.align;

        var div = createNode('div');
        div.innerHTML = html;
        div.style.visibility = "hidden";
        if (align) {
            div.style.textAlign = align;
        }
        div.style.visibility = 'visible';
        div.style.pointerEvents = 'auto';
        return div;
    }

    function render(elem) {
        return setProps(elem, makeElement(elem));
    }
    function makeElement(e) {
        var elem = e.element;
        switch(elem.ctor) {
        case 'Image':     return image(e.props, elem);
        case 'Flow':      return flow(elem._0.ctor, elem._1);
        case 'Container': return container(elem._0, elem._1);
        case 'Spacer':    return createNode('div');
        case 'RawHtml':   return rawHtml(elem);
        case 'Custom':    return elem.render(elem.model);
        }
    }

    function update(node, curr, next) {
        if (node.tagName === 'A') {
            node = node.firstChild;
        }
        if (curr.props.id === next.props.id) {
            return updateProps(node, curr, next);
        }
        if (curr.element.ctor !== next.element.ctor) {
            node.parentNode.replaceChild(render(next),node);
            return true;
        }
        var nextE = next.element, currE = curr.element;
        switch(nextE.ctor) {
        case "Spacer": break;
        case "RawHtml":
            // only markdown blocks have guids, so this must be a text block
            if (nextE.guid === null) {
                if(currE.html.valueOf() !== nextE.html.valueOf()) {
                    node.innerHTML = nextE.html;
                }
                break;
            }
            if (nextE.guid !== currE.guid) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            break;
        case "Image":
            if (nextE._0.ctor === 'Plain') {
                if (nextE._3 !== currE._3) node.src = nextE._3;
            } else if (!Utils.eq(nextE,currE) ||
                       next.props.width !== curr.props.width ||
                       next.props.height !== curr.props.height) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            break;
        case "Flow":
            var arr = List.toArray(nextE._1);
            for (var i = arr.length; i--; ) { arr[i] = arr[i].element.ctor; }
            if (nextE._0.ctor !== currE._0.ctor) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            var nexts = List.toArray(nextE._1);
            var kids = node.childNodes;
            if (nexts.length !== kids.length) {
                node.parentNode.replaceChild(render(next),node);
                return true;
            }
            var currs = List.toArray(currE._1);
            var dir = nextE._0.ctor;
            var goDir = directionTable[dir];
            var toReverse = needsReversal(dir);
            var len = kids.length;
            for (var i = len; i-- ;) {
                update(kids[toReverse ? len - i - 1 : i],currs[i],nexts[i]);
                goDir(kids[i]);
            }
            break;
        case "Container":
            update(node.firstChild, currE._1, nextE._1);
            setPos(nextE._0, nextE._1, node.firstChild);
            break;
        case "Custom":
            if (currE.type === nextE.type) {
                var done = nextE.update(node, currE.model, nextE.model);
                if (done) return;
            } else {
                return node.parentNode.replaceChild(render(next), node);
            }
        }
        updateProps(node, curr, next);
    }

    function updateProps(node, curr, next) {
        var nextProps = next.props;
        var currProps = curr.props;

        var element = next.element;
        var width = nextProps.width - (element.adjustWidth || 0);
        var height = nextProps.height - (element.adjustHeight || 0);
        if (width !== currProps.width) {
            node.style.width = (width|0) + 'px';
        }
        if (height !== currProps.height) {
            node.style.height = (height|0) + 'px';
        }

        if (nextProps.opacity !== currProps.opacity) {
            node.style.opacity = nextProps.opacity;
        }

        var nextColor = nextProps.color.ctor === 'Just'
            ? Color.toCss(nextProps.color._0)
            : '';
        if (node.style.backgroundColor !== nextColor) {
            node.style.backgroundColor = nextColor;
        }

        if (nextProps.tag !== currProps.tag) {
            node.id = nextProps.tag;
        }

        if (nextProps.href !== currProps.href) {
            if (currProps.href === '') {
                // add a surrounding href
                var anchor = createNode('a');
                anchor.href = nextProps.href;
                anchor.style.display = 'block';
                anchor.style.pointerEvents = 'auto';

                node.parentNode.replaceChild(anchor, node);
                anchor.appendChild(node);
            } else if (nextProps.href === '') {
                // remove the surrounding href
                var anchor = node.parentNode;
                anchor.parentNode.replaceChild(node, anchor);
            } else {
                // just update the link
                node.parentNode.href = nextProps.href;
            }
        }

        // update click and hover handlers
        var removed = false;

        // update hover handlers
        if (currProps.hover.ctor === '_Tuple0') {
            if (nextProps.hover.ctor !== '_Tuple0') {
                addHover(node, nextProps.hover);
            }
        }
        else {
            if (nextProps.hover.ctor === '_Tuple0') {
                removed = true;
                removeHover(node);
            }
            else {
                node.elm_hover_handler = nextProps.hover;
            }
        }

        // update click handlers
        if (currProps.click.ctor === '_Tuple0') {
            if (nextProps.click.ctor !== '_Tuple0') {
                addClick(node, nextProps.click);
            }
        }
        else {
            if (nextProps.click.ctor === '_Tuple0') {
                removed = true;
                removeClick(node);
            } else {
                node.elm_click_handler = nextProps.click;
            }
        }

        // stop capturing clicks if
        if (removed
            && nextProps.hover.ctor === '_Tuple0'
            && nextProps.click.ctor === '_Tuple0')
        {
            node.style.pointerEvents = 'none';
        }
    }

    function htmlHeight(width, rawHtml) {
        // create dummy node
        var temp = document.createElement('div');
        temp.innerHTML = rawHtml.html;
        if (width > 0) {
            temp.style.width = width + "px";
        }
        temp.style.visibility = "hidden";
        temp.style.styleFloat = "left";
        temp.style.cssFloat   = "left";

        document.body.appendChild(temp);

        // get dimensions
        var style = window.getComputedStyle(temp, null);
        var w = Math.ceil(style.getPropertyValue("width").slice(0,-2) - 0);
        var h = Math.ceil(style.getPropertyValue("height").slice(0,-2) - 0);
        document.body.removeChild(temp);
        return Utils.Tuple2(w,h);
    }

    return Elm.Native.Graphics.Element.values = {
        render: render,
        update: update,

        createNode: createNode,
        addTransform: addTransform,
        htmlHeight: F2(htmlHeight),
        guid: Utils.guid
    };

};
<|endoftext|>


<commit_before>/*
 * Author: Jared Breeden
 * File: chainlang.js
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Jared Breeden
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 */

// chainlang
// ---------

var chainlang = module.exports;

// `chainlang.create` accepts a `lang` parameter and returns
// a [`chain`](#chain) constructor that is used to begin chained
// expressions.
chainlang.create = function(lang){
    if(arguments.length !== 1){
        throw "chainlang.create expects one argument: An object containing the methods of the new chain language";
    }

    // `theChain` is a per-language singleton. That is, each chained
    // expression will access data from the same "chain" object. This
    // means that chains cannot be arbitrarily saved to variables and
    // interleaved with other chain expressions of the same language.
    var theChain = createChainableProxy(lang);

    // `_return` is used to break the chain and return a value
    theChain._return = function(returnValue){
        theChain.__break__ = true;
        theChain.__return__ = returnValue;
    }

    // ### chain expression constructor
    // ---
    // The `chain` function is the constructor for chained expressions
    // of the new `chainlang` type.
    //
    function chain(obj){
        if(arguments.length > 1){
            throw "Chain constructors can only accept one argument";
        }

        // Resetting the properties on `theChain` for the new expression
        theChain._subject = obj;
        theChain._data = {};
        theChain._prev = null;
        theChain.__break__ = false;
        theChain.__return__ = undefined;

        return theChain;
    }

    // Returning the `chain` constructor to the client of `chainlang.create`
    return chain;
}

// Privates
// --------

// This function kicks off the recursive traversal of the language
// object to create an object with chainable methods. (These are
// essentially "proxied" versions of the methods found on the language object.)
function createChainableProxy(language){
    var wrappers = [],
        chain = {};

    createChainableProxyNode(
        chain,
        chain,
        language,
        wrappers);

    return chain;
};

// This is the self-recursive method for construction of the chainable object
function createChainableProxyNode(node, chain, language, wrappers){
    var hasWrapper = hasWrapperMethod(language);

    if(hasWrapper){
        wrappers.push(language['_wrapper']);
    }

    for(var propName in language){
        // Properties named with a leading underscore are reserved for chain-level
        // semi-hidden data to avoid conflicts with language methods. So, for now
        // we'll just ignore any we come across. May want to throw an error later.
        if(propName[0] == "_"){
            continue;
        }

        var prop = language[propName];
        if(prop === undefined || prop === null){
           continue;
        }

        var makeNode = true;
        if(typeof prop == "function"){
            node[propName] = createChainableProxiedMethod(chain, prop, wrappers);
        }
        else if(typeof prop == "object"){
            node[propName] = {};
        }
        else{
            makeNode = false;
            node[propName] = prop;
        }

        if(makeNode){
            createChainableProxyNode(node[propName], chain, prop, wrappers);
        }
    }

    if(hasWrapper){
        wrappers.pop();
    }
}

function hasWrapperMethod(obj){
    return (
        obj['_wrapper'] !== undefined
        && typeof obj['_wrapper'] === "function"
    );
}

function createChainableProxiedMethod(chain, fn, wrappers){
    var i,
        proxiedMethod,
        wrappers = captureArray(wrappers);

    proxiedMethod = function(){
        chain._prev = fn.apply(chain, arguments);
        return returnValue(chain);
    }

    if(wrappers.length > 0){
        for(i = wrappers.length - 1; i >= 0; i -= 1){
            proxiedMethod = createWrappedChainableMethod(chain, wrappers[i], proxiedMethod);
        }
    }

    return proxiedMethod;
}

function createWrappedChainableMethod(chain, wrapperFunction, proxiedMethod){
    return function(){
        var args = [];
        for(var i = 0; i < arguments.length; ++i){
            args.push(arguments[i]);
        }

        chain._prev = wrapperFunction.call(chain, proxiedMethod, args);

        return returnValue(chain);
    }
}

// Returns `chain.__return__` if `chain.__break__` is set,
// or else returns the chain
function returnValue(chain){
    if(chain.__break__){
        return chain.__return__;
    }
    return chain;
}

// "Captures" an array. (i.e. makes a new array with the same references
//  to avoid having the contents of a closed-over array changed externally)
function captureArray(array){
    var capturedArray = [];

    for(i = 0; i < array.length; i += 1){
        capturedArray[i] = array[i];
    }

    return capturedArray;
}
<commit_msg>Removing more garbage from createWrappedChainableMethod<commit_after>/*
 * Author: Jared Breeden
 * File: chainlang.js
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2013 Jared Breeden
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 */

// chainlang
// ---------

var chainlang = module.exports;

// `chainlang.create` accepts a `lang` parameter and returns
// a [`chain`](#chain) constructor that is used to begin chained
// expressions.
chainlang.create = function(lang){
    if(arguments.length !== 1){
        throw "chainlang.create expects one argument: An object containing the methods of the new chain language";
    }

    // `theChain` is a per-language singleton. That is, each chained
    // expression will access data from the same "chain" object. This
    // means that chains cannot be arbitrarily saved to variables and
    // interleaved with other chain expressions of the same language.
    var theChain = createChainableProxy(lang);

    // `_return` is used to break the chain and return a value
    theChain._return = function(returnValue){
        theChain.__break__ = true;
        theChain.__return__ = returnValue;
    }

    // ### chain expression constructor
    // ---
    // The `chain` function is the constructor for chained expressions
    // of the new `chainlang` type.
    //
    function chain(obj){
        if(arguments.length > 1){
            throw "Chain constructors can only accept one argument";
        }

        // Resetting the properties on `theChain` for the new expression
        theChain._subject = obj;
        theChain._data = {};
        theChain._prev = null;
        theChain.__break__ = false;
        theChain.__return__ = undefined;

        return theChain;
    }

    // Returning the `chain` constructor to the client of `chainlang.create`
    return chain;
}

// Privates
// --------

// This function kicks off the recursive traversal of the language
// object to create an object with chainable methods. (These are
// essentially "proxied" versions of the methods found on the language object.)
function createChainableProxy(language){
    var wrappers = [],
        chain = {};

    createChainableProxyNode(
        chain,
        chain,
        language,
        wrappers);

    return chain;
};

// This is the self-recursive method for construction of the chainable object
function createChainableProxyNode(node, chain, language, wrappers){
    var hasWrapper = hasWrapperMethod(language);

    if(hasWrapper){
        wrappers.push(language['_wrapper']);
    }

    for(var propName in language){
        // Properties named with a leading underscore are reserved for chain-level
        // semi-hidden data to avoid conflicts with language methods. So, for now
        // we'll just ignore any we come across. May want to throw an error later.
        if(propName[0] == "_"){
            continue;
        }

        var prop = language[propName];
        if(prop === undefined || prop === null){
           continue;
        }

        var makeNode = true;
        if(typeof prop == "function"){
            node[propName] = createChainableProxiedMethod(chain, prop, wrappers);
        }
        else if(typeof prop == "object"){
            node[propName] = {};
        }
        else{
            makeNode = false;
            node[propName] = prop;
        }

        if(makeNode){
            createChainableProxyNode(node[propName], chain, prop, wrappers);
        }
    }

    if(hasWrapper){
        wrappers.pop();
    }
}

function hasWrapperMethod(obj){
    return (
        obj['_wrapper'] !== undefined
        && typeof obj['_wrapper'] === "function"
    );
}

function createChainableProxiedMethod(chain, fn, wrappers){
    var i,
        proxiedMethod,
        wrappers = captureArray(wrappers);

    proxiedMethod = function(){
        chain._prev = fn.apply(chain, arguments);
        return returnValue(chain);
    }

    if(wrappers.length > 0){
        for(i = wrappers.length - 1; i >= 0; i -= 1){
            proxiedMethod = createWrappedChainableMethod(chain, wrappers[i], proxiedMethod);
        }
    }

    return proxiedMethod;
}

function createWrappedChainableMethod(chain, wrapperFunction, proxiedMethod){
    return function(){
        chain._prev = wrapperFunction.call(chain, proxiedMethod, arguments);
        return returnValue(chain);
    }
}

// Returns `chain.__return__` if `chain.__break__` is set,
// or else returns the chain
function returnValue(chain){
    if(chain.__break__){
        return chain.__return__;
    }
    return chain;
}

// "Captures" an array. (i.e. makes a new array with the same references
//  to avoid having the contents of a closed-over array changed externally)
function captureArray(array){
    var capturedArray = [];

    for(i = 0; i < array.length; i += 1){
        capturedArray[i] = array[i];
    }

    return capturedArray;
}
<|endoftext|>


<commit_before>module.exports = {
  build: "npm start",
  // networks: {
  //   "live": {
  //     network_id: 1 // Ethereum public network
  //   },
  //   "morden": {
  //     network_id: 2   // Official Ethereum test network
  //   },
  //   "development": {
  //     network_id: "default"
  //   }
  // },
  rpc: {
    host: "localhost",
    port: 8545
  }
};
<commit_msg>Add network<commit_after>module.exports = {
  build: "npm start",
  networks: {
    development: {
      host: "localhost",
      port: 8545,
      network_id: "*"
    },
    testnet: {
      host: "localhost",
      port: 8545,
      network_id: 3
    },
    mainnet: {
      host: "localhost",
      port: 8545,
      network_id: 1
    }
  }
};
<|endoftext|>


<commit_before>// @flow

import React from "react";
import wrap from "word-wrap";

type Props = {
  text: string
};

export default class Comment extends React.PureComponent<Props> {
  render() {
    return (

        {wrap(this.props.text || "n/a", { width: 70, indent: "# ", newline: "\n# " })}

    );
  }
}
<commit_msg>Keep multi-paragraph comments intact<commit_after>// @flow

import React from "react";
import wrap from "word-wrap";

type Props = {
  text: string
};

export default class Comment extends React.PureComponent<Props> {
  render() {
    return (

        {wrap(this.props.text || "n/a", { width: 70, indent: '' }).replace(/(^|\n)/g, "$1# ")}

    );
  }
}
<|endoftext|>


<commit_before>'use strict';

const Joi = require('joi');
const Boom = require('boom');
const Queries = require('../../helpers/queries');

exports.addTicket = {
    description: 'Add new support ticket',
    validate: {
        payload: {
            subject: Joi.string()
                .max(500)
                .allow(''),
            email: Joi.string().max(100),
            description: Joi.string()
                .max(5000)
                .allow('')
        },
        failAction: function(request, reply, source, error) {
            // Boom bad request
            return reply(Boom.badRequest(error));
        }
    },
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var ticket = {
            subject: request.payload.subject,
            email: request.payload.email,
            description: request.payload.description
        };

        var freshDesk = request.server.plugins.freshdesk;

        freshDesk.newTicket(ticket, function() {
            return reply({
                statusCode: 200,
                message: 'Successfully created ticket.'
            });
        });
    }
};

exports.submitHelp = {
    description: 'Add new support ticket',
    validate: {
        payload: {
            type: Joi.string()
                .max(500)
                .allow(''),
            contact: Joi.string()
                .max(5000)
                .allow('')
        },
        failAction: function(request, reply, source, error) {
            // Boom bad request
            return reply(Boom.badRequest(error));
        }
    },
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var ticket = {
            subject: 'Employee data screen help request' + request.payload.contact,
            email: 'epodindianrega@gmail.com',
            description:
                'A user has requested assistance updating their employee information. Please contact them at the number provided in the subject line.'
        };

        var freshDesk = request.server.plugins.freshdesk;

        freshDesk.newTicket(ticket, function() {
            return reply({
                statusCode: 200,
                message: 'Successfully created ticket.'
            });
        });
    }
};
exports.askHelp = {
    description: 'Contact number for ask help call',
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var sequelize = request.server.plugins.sequelize.db.sequelize;
        var queryString = Queries.contact();
        sequelize
            .query(queryString, {
                type: sequelize.QueryTypes.SELECT
            })
            .then(function(rows) {
                var data = {
                    contact_no: rows[0].phone
                };

                reply(data);
            });
    }
};
<commit_msg>add type cases to help api<commit_after>'use strict';

const Joi = require('joi');
const Boom = require('boom');
const Queries = require('../../helpers/queries');

exports.addTicket = {
    description: 'Add new support ticket',
    validate: {
        payload: {
            subject: Joi.string()
                .max(500)
                .allow(''),
            email: Joi.string().max(100),
            description: Joi.string()
                .max(5000)
                .allow('')
        },
        failAction: function(request, reply, source, error) {
            // Boom bad request
            return reply(Boom.badRequest(error));
        }
    },
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var ticket = {
            subject: request.payload.subject,
            email: request.payload.email,
            description: request.payload.description
        };

        var freshDesk = request.server.plugins.freshdesk;

        freshDesk.newTicket(ticket, function() {
            return reply({
                statusCode: 200,
                message: 'Successfully created ticket.'
            });
        });
    }
};

exports.submitHelp = {
    description: 'Add new support ticket',
    validate: {
        payload: {
            type: Joi.string()
                .max(500)
                .allow(''),
            data: {
                contact_no: Joi.string()
                    .max(20)
                    .allow(''),
                description: Joi.string()
                    .max(5000)
                    .allow('')
            }
        },
        failAction: function(request, reply, source, error) {
            // Boom bad request
            return reply(Boom.badRequest(error));
        }
    },
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var freshDesk = request.server.plugins.freshdesk;

        if (request.payload.type==='help-employee-info') {
            var ticket = {
                subject: 'Employee data help request [phone: ' + (request.payload.data.contact_no) + ']',
                email: 'epodindianrega@gmail.com',
                description: 'A user has requested assistance updating their employee information. Please contact them at the number provided in the subject line.'
            };

            freshDesk.newTicket(ticket, function() {
                return reply({
                    statusCode: 200,
                    message: 'Successfully created ticket.'
                });
            });
        } else if (request.payload.type==='help-login') {
            var ticket = {
                subject: 'Login screen help request [phone: ' + (request.payload.data.contact_no) + ']',
                email: 'epodindianrega@gmail.com',
                description: request.payload.data.description
            };

            freshDesk.newTicket(ticket, function() {
                return reply({
                    statusCode: 200,
                    message: 'Successfully created ticket.'
                });
            });
        } else {
            return reply(Boom.badRequest('Bad request'));
        }

    }
};
exports.askHelp = {
    description: 'Contact number for ask help call',
    auth: {
        mode: 'try',
        strategy: 'standard'
    },
    plugins: {
        'hapi-auth-cookie': {
            redirectTo: false
        }
    },
    handler: function(request, reply) {
        var sequelize = request.server.plugins.sequelize.db.sequelize;
        var queryString = Queries.contact();
        sequelize
            .query(queryString, {
                type: sequelize.QueryTypes.SELECT
            })
            .then(function(rows) {
                var data = {
                    contact_no: rows[0].phone
                };

                reply(data);
            });
    }
};
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations under
// the License.

var cmd;
var funs = [];        // holds functions used for computation
var map_results = []; // holds temporary emitted values during doc map

var sandbox = null;

emit = function(key, value) {
  map_results.push([key, value]);
}

sum = function(values) {
  var rv = 0;
  for (var i in values) {
    rv += values[i];
  }
  return rv;
}

log = function(message) {
  print(toJSON({log: toJSON(message)}));
}

try {
  // if possible, use evalcx (not always available)
  sandbox = evalcx('');
  sandbox.emit = emit;
  sandbox.sum = sum;
  sandbox.log = log;
} catch (e) {}

// Commands are in the form of json arrays:
// ["commandname",..optional args...]\n
//
// Responses are json values followed by a new line ("\n")

while (cmd = eval(readline())) {
  try {
    switch (cmd[0]) {
      case "reset":
        // clear the globals and run gc
        funs = [];
        gc();
        print("true"); // indicates success
        break;
      case "add_fun":
        // The second arg is a string that will compile to a function.
        // and then we add it to funs array
        funs.push(compileFunction(cmd[1]));
        print("true");
        break;
      case "map_doc":
        // The second arg is a document. We compute all the map functions against
        // it.
        //
        // Each function can output multiple keys value, pairs for each document
        //
        // Example output of map_doc after three functions set by add_fun cmds:
        // [
        //  [["Key","Value"]],                    <- fun 1 returned 1 key value
        //  [],                                   <- fun 2 returned 0 key values
        //  [["Key1","Value1"],["Key2","Value2"]] <- fun 3 returned 2 key values
        // ]
        //
        var doc = cmd[1];
        recursivelySeal(doc); // seal to prevent map functions from changing doc
        var buf = [];
        for (var i = 0; i < funs.length; i++) {
          map_results = [];
          try {
            funs[i](doc);
            buf.push(toJSON(map_results));
          } catch (err) {
            if (err == "fatal_error") {
              // Only if it's a "fatal_error" do we exit. What's a fatal error?
              // That's for the query to decide.
              //
              // This will make it possible for queries to completely error out,
              // by catching their own local exception and rethrowing a
              // fatal_error. But by default if they don't do error handling we
              // just eat the exception and carry on.
              throw {error: "map_runtime_error",
                  reason: "function raised fatal exception"};
            }
            print(toJSON({log: "function raised exception (" + err + ")"}));
            buf.push([]);
          }
        }
        print(toJSON(buf));
        break;

      case "rereduce":
      case "reduce":
        {
        var keys = null;
        var values = null;
        var reduceFuns = cmd[1];
        var rereduce = false;

        if (cmd[0] == "reduce") {
          var kvs = cmd[2];
          keys = new Array(kvs.length);
          values = new Array(kvs.length);
          for(var i = 0; i < kvs.length; i++) {
              keys[i] = kvs[i][0];
              values[i] = kvs[i][1];
          }
        } else {
          values = cmd[2];
          rereduce = true;
        }

        for (var i in reduceFuns) {
          reduceFuns[i] = compileFunction(reduceFuns[i]);
        }

        var reductions = new Array(funs.length);
        for(var i = 0; i < reduceFuns.length; i++) {
          try {
            reductions[i] = reduceFuns[i](keys, values, rereduce);
          } catch (err) {
            if (err == "fatal_error") {
              throw {error: "reduce_runtime_error",
                  reason: "function raised fatal exception"};
            }
            print(toJSON({log: "function raised exception (" + err + ")"}));
            reductions[i] = null;
          }
        }
        print("[true," + toJSON(reductions) + "]");
        }
        break;

      default:
        print(toJSON({error: "query_server_error",
            reason: "unknown command '" + cmd[0] + "'"}));
        quit();
    }
  } catch (exception) {
    print(toJSON(exception));
  }
}

function compileFunction(source) {
  try {
    var functionObject = sandbox ? evalcx(source, sandbox) : eval(source);
  } catch (err) {
    throw {error: "compilation_error",
      reason: err.toString() + " (" + source + ")"};
  }
  if (typeof(functionObject) == "function") {
    return functionObject;
  } else {
    throw {error: "compilation_error",
      reason: "expression does not eval to a function. (" + source + ")"};
  }
}

function recursivelySeal(obj) {
  seal(obj);
  for (var propname in obj) {
    if (typeof doc[propname] == "object") {
      recursivelySeal(doc[propname]);
    }
  }
}

function toJSON(val) {
  if (typeof(val) == "undefined") {
    throw "Cannot encode 'undefined' value as JSON";
  }
  var subs = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f',
              '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
  if (typeof(val) == "xml") { // E4X support
    val = val.toXMLString();
  }
  return {
    "Array": function(v) {
      var buf = [];
      for (var i = 0; i < v.length; i++) {
        buf.push(toJSON(v[i]));
      }
      return "[" + buf.join(",") + "]";
    },
    "Boolean": function(v) {
      return v.toString();
    },
    "Date": function(v) {
      var f = function(n) { return n < 10 ? '0' + n : n }
      return '"' + v.getUTCFullYear()   + '-' +
                 f(v.getUTCMonth() + 1) + '-' +
                 f(v.getUTCDate())      + 'T' +
                 f(v.getUTCHours())     + ':' +
                 f(v.getUTCMinutes())   + ':' +
                 f(v.getUTCSeconds())   + 'Z"';
    },
    "Number": function(v) {
      return isFinite(v) ? v.toString() : "null";
    },
    "Object": function(v) {
      if (v === null) return "null";
      var buf = [];
      for (var k in v) {
        if (!v.hasOwnProperty(k) || typeof(k) !== "string" || v[k] === undefined) {
          continue;
        }
        buf.push(toJSON(k, val) + ": " + toJSON(v[k]));
      }
      return "{" + buf.join(",") + "}";
    },
    "String": function(v) {
      if (/["\\\x00-\x1f]/.test(v)) {
        v = v.replace(/([\x00-\x1f\\"])/g, function(a, b) {
          var c = subs[b];
          if (c) return c;
          c = b.charCodeAt();
          return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
        });
      }
      return '"' + v + '"';
    }
  }[val != null ? val.constructor.name : "Object"](val);
}
<commit_msg>Follow fixes to r670732 for COUCHDB-83.<commit_after>// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations under
// the License.

var cmd;
var funs = [];        // holds functions used for computation
var map_results = []; // holds temporary emitted values during doc map

var sandbox = null;

emit = function(key, value) {
  map_results.push([key, value]);
}

sum = function(values) {
  var rv = 0;
  for (var i in values) {
    rv += values[i];
  }
  return rv;
}

log = function(message) {
  print(toJSON({log: toJSON(message)}));
}

try {
  // if possible, use evalcx (not always available)
  sandbox = evalcx('');
  sandbox.emit = emit;
  sandbox.sum = sum;
  sandbox.log = log;
} catch (e) {}

// Commands are in the form of json arrays:
// ["commandname",..optional args...]\n
//
// Responses are json values followed by a new line ("\n")

while (cmd = eval(readline())) {
  try {
    switch (cmd[0]) {
      case "reset":
        // clear the globals and run gc
        funs = [];
        gc();
        print("true"); // indicates success
        break;
      case "add_fun":
        // The second arg is a string that will compile to a function.
        // and then we add it to funs array
        funs.push(compileFunction(cmd[1]));
        print("true");
        break;
      case "map_doc":
        // The second arg is a document. We compute all the map functions against
        // it.
        //
        // Each function can output multiple keys value, pairs for each document
        //
        // Example output of map_doc after three functions set by add_fun cmds:
        // [
        //  [["Key","Value"]],                    <- fun 1 returned 1 key value
        //  [],                                   <- fun 2 returned 0 key values
        //  [["Key1","Value1"],["Key2","Value2"]] <- fun 3 returned 2 key values
        // ]
        //
        var doc = cmd[1];
        recursivelySeal(doc); // seal to prevent map functions from changing doc
        var buf = [];
        for (var i = 0; i < funs.length; i++) {
          map_results = [];
          try {
            funs[i](doc);
            buf.push(toJSON(map_results));
          } catch (err) {
            if (err == "fatal_error") {
              // Only if it's a "fatal_error" do we exit. What's a fatal error?
              // That's for the query to decide.
              //
              // This will make it possible for queries to completely error out,
              // by catching their own local exception and rethrowing a
              // fatal_error. But by default if they don't do error handling we
              // just eat the exception and carry on.
              throw {error: "map_runtime_error",
                  reason: "function raised fatal exception"};
            }
            print(toJSON({log: "function raised exception (" + err + ")"}));
            buf.push("[]");
          }
        }
        print("[" + buf.join(", ") + "]");
        break;

      case "rereduce":
      case "reduce":
        {
        var keys = null;
        var values = null;
        var reduceFuns = cmd[1];
        var rereduce = false;

        if (cmd[0] == "reduce") {
          var kvs = cmd[2];
          keys = new Array(kvs.length);
          values = new Array(kvs.length);
          for(var i = 0; i < kvs.length; i++) {
              keys[i] = kvs[i][0];
              values[i] = kvs[i][1];
          }
        } else {
          values = cmd[2];
          rereduce = true;
        }

        for (var i in reduceFuns) {
          reduceFuns[i] = compileFunction(reduceFuns[i]);
        }

        var reductions = new Array(funs.length);
        for(var i = 0; i < reduceFuns.length; i++) {
          try {
            reductions[i] = reduceFuns[i](keys, values, rereduce);
          } catch (err) {
            if (err == "fatal_error") {
              throw {error: "reduce_runtime_error",
                  reason: "function raised fatal exception"};
            }
            print(toJSON({log: "function raised exception (" + err + ")"}));
            reductions[i] = null;
          }
        }
        print("[true," + toJSON(reductions) + "]");
        }
        break;

      default:
        print(toJSON({error: "query_server_error",
            reason: "unknown command '" + cmd[0] + "'"}));
        quit();
    }
  } catch (exception) {
    print(toJSON(exception));
  }
}

function compileFunction(source) {
  try {
    var functionObject = sandbox ? evalcx(source, sandbox) : eval(source);
  } catch (err) {
    throw {error: "compilation_error",
      reason: err.toString() + " (" + source + ")"};
  }
  if (typeof(functionObject) == "function") {
    return functionObject;
  } else {
    throw {error: "compilation_error",
      reason: "expression does not eval to a function. (" + source + ")"};
  }
}

function recursivelySeal(obj) {
  seal(obj);
  for (var propname in obj) {
    if (typeof doc[propname] == "object") {
      recursivelySeal(doc[propname]);
    }
  }
}

function toJSON(val) {
  if (typeof(val) == "undefined") {
    throw "Cannot encode 'undefined' value as JSON";
  }
  var subs = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f',
              '\r': '\\r', '"' : '\\"', '\\': '\\\\'};
  if (typeof(val) == "xml") { // E4X support
    val = val.toXMLString();
  }
  return {
    "Array": function(v) {
      var buf = [];
      for (var i = 0; i < v.length; i++) {
        buf.push(toJSON(v[i]));
      }
      return "[" + buf.join(",") + "]";
    },
    "Boolean": function(v) {
      return v.toString();
    },
    "Date": function(v) {
      var f = function(n) { return n < 10 ? '0' + n : n }
      return '"' + v.getUTCFullYear()   + '-' +
                 f(v.getUTCMonth() + 1) + '-' +
                 f(v.getUTCDate())      + 'T' +
                 f(v.getUTCHours())     + ':' +
                 f(v.getUTCMinutes())   + ':' +
                 f(v.getUTCSeconds())   + 'Z"';
    },
    "Number": function(v) {
      return isFinite(v) ? v.toString() : "null";
    },
    "Object": function(v) {
      if (v === null) return "null";
      var buf = [];
      for (var k in v) {
        if (!v.hasOwnProperty(k) || typeof(k) !== "string" || v[k] === undefined) {
          continue;
        }
        buf.push(toJSON(k, val) + ": " + toJSON(v[k]));
      }
      return "{" + buf.join(",") + "}";
    },
    "String": function(v) {
      if (/["\\\x00-\x1f]/.test(v)) {
        v = v.replace(/([\x00-\x1f\\"])/g, function(a, b) {
          var c = subs[b];
          if (c) return c;
          c = b.charCodeAt();
          return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
        });
      }
      return '"' + v + '"';
    }
  }[val != null ? val.constructor.name : "Object"](val);
}
<|endoftext|>


<commit_before>var Stremio = require("stremio-addons");
var http = require("http");
var _ = require("lodash");
var async = require("async");
var sift = require("sift");
var bagpipe = require("bagpipe");
var events = require("events");

module.exports = function(db, utils, cfg) {

// Keeping meta collection up to date; Algo here is
// async.queue with concurrency = 1 / bagpipe(1) ; we push update requests and collectMeta() calls to it
// updateMeta() function, called on idxbuild, debounced at half a second (or 300ms?)
// sample test: 79.1mb / 74.8mb / 75.1mb RAM with 1000 lean meta ; without: 72.0mb, 74.4, 72.3 NO DIFF in memory usage
var LID = cfg.LID || cfg.dbId.slice(0, 10);
var metaQueryProps = ["imdb_id", "type", "name", "year", "genre", "director", "dvdRelease", "imdbRating", "poster", "popularities."+LID];

var CINEMETA_URL = process.env.CINEMETA || cfg.cinemeta || "http://cinemeta.strem.io/stremioget";
var addons = new Stremio.Client();
addons.add(CINEMETA_URL);

var meta = { col: [], updated: 0, have: { } };
var metaPipe = new bagpipe(1);

function updateMeta(ready) {
    db.popularities(function(err, popularities) {
        var popSort = function(x) { return -popularities[x.imdb_id || x] };
        var constructMeta = function(x) {
            x.imdbRating = parseFloat(x.imdbRating);
            x.popularities = { }; // reset that - don't know if it brings any benefits
            x.popularities[LID] = popularities[x.imdb_id] || 0;
            // figure out year? since for series it's like "2011-2015" we can sort by the first field, but we can't replace the value
            meta.have[x.imdb_id] = 1;
        };

        var toGet = _.chain(popularities).omit(_.keys(meta.have)).keys().sortBy(popSort).value();

        if (toGet.length == 0) return process.nextTick(ready);

        addons.meta.find({ query: { imdb_id: { $in: toGet } }, limit: toGet.length }, function(err, res) {
            process.nextTick(ready); // ensure we don't dead-end (deadlock is not a right term, block is not the right term, terms have to figured out for async code)

            if (err) console.error("meta.find from "+CINEMETA_URL, err);
            meta.ready = true;
            meta.col = _.chain(meta.col).concat(res || []).sortBy(popSort).uniq("imdb_id").each(constructMeta).value();
            db.evs.emit("catalogue-update", meta.col, popularities);
        });
    });
};

db.evs.on("idxbuild", _.debounce(function() { if (! metaPipe.queue.length) metaPipe.push(updateMeta) }, 1000));
db.evs.on("idxready", function() { metaPipe.push(updateMeta) });

// Basic validation of args
function validate(args) {
    var meta = args.query;
    if (! (args.query || args.infoHash)) return { code: 0, message: "query/infoHash required" };
    if (meta && !meta.imdb_id) return { code: 1, message: "imdb_id required" };
    if (meta && (meta.type == "series" && !(meta.hasOwnProperty("episode") && meta.hasOwnProperty("season"))))
        return { code: 2, message: "season and episode required for series type" };
    return false;
};

function query(args, callback) {
    var start = Date.now();

    (function(next) {
        if (args.infoHash) return db.get(args.infoHash, function(err, res) { next(err, res && res[0] && res[0].value) });
        if (! args.query) return next(new Error("must specify query or infoHash"));

        //var preferred = _.uniq(PREFERRED.concat(args.preferred || []), function(x) { return x.tag });
        var preferred = args.preferred || [];
        var prio = function(resolution) {
            return preferred.map(function(pref) {
                return utils.getAvailForTorrent(resolution.torrent) >= pref.min_avail && resolution.file.tag.indexOf(pref.tag)!=-1
            }).reduce(function(a,b) { return a+b }, 0);
        };

        var resolution = null;
        db.lookup(args.query, 3, function(err, matches) {
            if (err) return next(err);

            async.whilst(
                function() { return matches.length && (!resolution || prio(resolution) < preferred.length) },
                function(callback) {
                    var hash = matches.shift().id;
                    db.get(hash, function(err, res) {
                        if (err) return callback({ err: err });

                        var tor = res[0] && res[0].value;
                        if (! tor) return callback({ err: "hash not found "+hash });

                        var file = _.find(tor.files, function(f) {
                            return f.imdb_id == args.query.imdb_id &&
                                (args.query.season ? (f.season == args.query.season) : true) &&
                                (args.query.episode ? ((f.episode || []).indexOf(args.query.episode) != -1) : true)
                        });

                        var res = { torrent: tor, file: file };
                        if (!resolution || prio(res) > prio(resolution)) resolution = res;

                        callback();
                    });
                },
                function(err) { resolution ? next(resolution.err, resolution.torrent, resolution.file) : next(err) }
            );
        });
    })(function(err, torrent, file) {
        // Output according to Stremio Addon API for stream.get
        // http://strem.io/addons-api
        callback(err, torrent ? _.extend({
            infoHash: torrent.infoHash.toLowerCase(),
            uploaders: utils.getMaxPopularity(torrent), // optional
            downloaders: Math.max.apply(Math, _.values(torrent.popularity).map(function(x) { return x[1] }).concat(0)), // optional
            //map: torrent.files,
            //pieceLength: torrent.pieceLength,
            availability: utils.getAvailForTorrent(torrent),
            isFree: true,
            sources: utils.getSourcesForTorrent(torrent), // optional but preferred
            runtime: Date.now()-start // optional
        }, file ? {
            mapIdx: file.idx,
            tag: file.tag, filename: file.name,
        } : { }) : null);
    });
};

var manifest = _.merge({
    // this should be always overridable by stremio-manifest
    stremio_LID: LID,
    // set filter so that we intercept meta.find from cinemeta
    // WARNING: this won't work because mpath doesn't support going into keys that contain dots, since it always thinks of it as a delimiter
    filter: _.object([ "sort.popularities."+LID,"query.popularities."+LID ], [{ "$exists": true },{ "$exists": true }])
}, require("./stremio-manifest"), _.pick(require("../package"), "version"), cfg.stremioManifest || {});

var methods;
var service = new Stremio.Server(methods = {
    "stream.get": function(args, callback, user) { // OBSOLETE
        service.events.emit("stream.get", args, callback);

        var error = validate(args);
        if (error) return callback(error);
        query(args, callback);
    },
    "stream.find": function(args, callback, user) {
        service.events.emit("stream.find", args, callback);

        if (args.query) {
            // New format ; same as stream.get, even returns the full result; no point to slim it down, takes same time
            var error = validate(args);
            if (error) return callback(error);
            async.map([ _.extend({ preferred: [{ tag: "hd", min_avail: 2 }] }, args), args ], query, function(err, res) {
                if (err) return callback(err);
                if (! res) return callback(null, res);

                var results = _.chain(res).filter(function(x) { return x }).uniq(function(x) { return x.infoHash }).value();
                service.events.emit("stream.find.res", results);
                callback(null, results);
            });
        } else return callback({code: 10, message: "unsupported arguments"});
    },
    "stream.popularities": function(args, callback, user) { // OBSOLETE
        service.events.emit("stream.popularities", args, callback);
        db.popularities(function(err, popularities) { callback(err, popularities ? { popularities: popularities } : null) });
    },
    "meta.find": function(args, callback, user) {
        service.events.emit("meta.find", args, callback);

        // Call this to wait for meta to be collected
        if (args.projection && ( args.projection=="full" ) ) return callback(new Error("full projection not supported by mp"));

        (meta.ready ? function(n) { n() } : metaPipe.push.bind(metaPipe))(function(ready) {
            if (typeof(ready) == "function") process.nextTick(ready); // ensure we don't lock

            args.query = _.pick.apply(null, [args.query || { }].concat(metaQueryProps));
            args.sort = _.pick.apply(null, [args.sort || { }].concat(metaQueryProps));
            //if (! _.keys(args.sort).length) args.sort["popularities."+LID] = -1; // no need as this is our default sort order

            var proj, projFn;
            if (args.projection && typeof(args.projection) == "object") {
                proj = _.keys(args.projection);
                projFn = _.values(args.projection)[0] ? _.pick : _.omit;
            }

            var res = _.chain(meta.col)
                .filter(args.query ? sift(args.query) : _.constant(true))
                .sortByOrder(_.keys(args.sort), _.values(args.sort).map(function(x) { return x>0 ? "asc" : "desc" }))
                .slice(args.skip || 0, Math.min(400, args.limit))
                .map(function(x) { return projFn ? projFn(x, proj) : x })
                .value();
            service.events.emit("meta.find.res", res);
            callback(null, res);
        });
    },
    "stats.get": function(args, callback, user) {
        service.events.emit("stats.get", args, callback);

        var items = 0, episodes = 0, movies = 0;
        db.forEachMeta(function(n) {
            if (n.key.indexOf(" ") != -1) episodes++; else movies++;
            items++;
        }, function() {
            db.count(function(err, c) {
                callback(null, { statsNum: items+" movies and episodes", stats: [
                    { name: "number of items - "+items, count: items, colour: items > 20 ? "green" : (items > 10 ? "yellow" : "red") },
                    { name: "number of movies - "+movies, count: movies, colour: movies > 20 ? "green" : (movies > 10 ? "yellow" : "red") },
                    { name: "number of episodes - "+episodes, count: episodes, colour: episodes > 20 ? "green" : (episodes > 10 ? "yellow" : "red") },
                    { name: "number of torrents - "+c, count: c, colour: c > 50 ? "green" : (c > 20 ? "yellow" : "red") }
                ] });
            });
        });
    },
}, { stremioget: true, allow: [cfg.stremioCentral,"http://api8.herokuapp.com","http://api9.strem.io","https://api9.strem.io"], secret: cfg.stremioSecret }, manifest);

// Event emitter in case we want to intercept/plug-in to this
service.events = new events.EventEmitter();

function listen(port, ip) {
    var server = http.createServer(function (req, res) {
	req.on("error", function(e) { console.error(e) });
        service.middleware(req, res, function() { res.end() });
    })
    .on("error", function(e) { console.error("mp server", e) })
    .on("listening", function()
    {
        console.log("Multipass Stremio Addon listening on "+server.address().port);
    });
    return server.listen(port, ip);
}

module.exports.service = service;
module.exports.methods = methods;
return listen;

}
<commit_msg>addon: aggressive filter to override cinemeta<commit_after>var Stremio = require("stremio-addons");
var http = require("http");
var _ = require("lodash");
var async = require("async");
var sift = require("sift");
var bagpipe = require("bagpipe");
var events = require("events");

module.exports = function(db, utils, cfg) {

// Keeping meta collection up to date; Algo here is
// async.queue with concurrency = 1 / bagpipe(1) ; we push update requests and collectMeta() calls to it
// updateMeta() function, called on idxbuild, debounced at half a second (or 300ms?)
// sample test: 79.1mb / 74.8mb / 75.1mb RAM with 1000 lean meta ; without: 72.0mb, 74.4, 72.3 NO DIFF in memory usage
var LID = cfg.LID || cfg.dbId.slice(0, 10);
var metaQueryProps = ["imdb_id", "type", "name", "year", "genre", "director", "dvdRelease", "imdbRating", "poster", "popularities."+LID];

var CINEMETA_URL = process.env.CINEMETA || cfg.cinemeta || "http://cinemeta.strem.io/stremioget";
var addons = new Stremio.Client();
addons.add(CINEMETA_URL);

var meta = { col: [], updated: 0, have: { } };
var metaPipe = new bagpipe(1);

function updateMeta(ready) {
    db.popularities(function(err, popularities) {
        var popSort = function(x) { return -popularities[x.imdb_id || x] };
        var constructMeta = function(x) {
            x.imdbRating = parseFloat(x.imdbRating);
            x.popularities = { }; // reset that - don't know if it brings any benefits
            x.popularities[LID] = popularities[x.imdb_id] || 0;
            x.isPeered = !!popularities[x.imdb_id];
            // figure out year? since for series it's like "2011-2015" we can sort by the first field, but we can't replace the value
            meta.have[x.imdb_id] = 1;
        };

        var toGet = _.chain(popularities).omit(_.keys(meta.have)).keys().sortBy(popSort).value();

        if (toGet.length == 0) return process.nextTick(ready);

        addons.meta.find({ query: { imdb_id: { $in: toGet } }, limit: toGet.length }, function(err, res) {
            process.nextTick(ready); // ensure we don't dead-end (deadlock is not a right term, block is not the right term, terms have to figured out for async code)

            if (err) console.error("meta.find from "+CINEMETA_URL, err);
            meta.ready = true;
            meta.col = _.chain(meta.col).concat(res || []).sortBy(popSort).uniq("imdb_id").each(constructMeta).value();
            db.evs.emit("catalogue-update", meta.col, popularities);
        });
    });
};

db.evs.on("idxbuild", _.debounce(function() { if (! metaPipe.queue.length) metaPipe.push(updateMeta) }, 1000));
db.evs.on("idxready", function() { metaPipe.push(updateMeta) });

// Basic validation of args
function validate(args) {
    var meta = args.query;
    if (! (args.query || args.infoHash)) return { code: 0, message: "query/infoHash required" };
    if (meta && !meta.imdb_id) return { code: 1, message: "imdb_id required" };
    if (meta && (meta.type == "series" && !(meta.hasOwnProperty("episode") && meta.hasOwnProperty("season"))))
        return { code: 2, message: "season and episode required for series type" };
    return false;
};

function query(args, callback) {
    var start = Date.now();

    (function(next) {
        if (args.infoHash) return db.get(args.infoHash, function(err, res) { next(err, res && res[0] && res[0].value) });
        if (! args.query) return next(new Error("must specify query or infoHash"));

        //var preferred = _.uniq(PREFERRED.concat(args.preferred || []), function(x) { return x.tag });
        var preferred = args.preferred || [];
        var prio = function(resolution) {
            return preferred.map(function(pref) {
                return utils.getAvailForTorrent(resolution.torrent) >= pref.min_avail && resolution.file.tag.indexOf(pref.tag)!=-1
            }).reduce(function(a,b) { return a+b }, 0);
        };

        var resolution = null;
        db.lookup(args.query, 3, function(err, matches) {
            if (err) return next(err);

            async.whilst(
                function() { return matches.length && (!resolution || prio(resolution) < preferred.length) },
                function(callback) {
                    var hash = matches.shift().id;
                    db.get(hash, function(err, res) {
                        if (err) return callback({ err: err });

                        var tor = res[0] && res[0].value;
                        if (! tor) return callback({ err: "hash not found "+hash });

                        var file = _.find(tor.files, function(f) {
                            return f.imdb_id == args.query.imdb_id &&
                                (args.query.season ? (f.season == args.query.season) : true) &&
                                (args.query.episode ? ((f.episode || []).indexOf(args.query.episode) != -1) : true)
                        });

                        var res = { torrent: tor, file: file };
                        if (!resolution || prio(res) > prio(resolution)) resolution = res;

                        callback();
                    });
                },
                function(err) { resolution ? next(resolution.err, resolution.torrent, resolution.file) : next(err) }
            );
        });
    })(function(err, torrent, file) {
        // Output according to Stremio Addon API for stream.get
        // http://strem.io/addons-api
        callback(err, torrent ? _.extend({
            infoHash: torrent.infoHash.toLowerCase(),
            uploaders: utils.getMaxPopularity(torrent), // optional
            downloaders: Math.max.apply(Math, _.values(torrent.popularity).map(function(x) { return x[1] }).concat(0)), // optional
            //map: torrent.files,
            //pieceLength: torrent.pieceLength,
            availability: utils.getAvailForTorrent(torrent),
            isFree: true,
            sources: utils.getSourcesForTorrent(torrent), // optional but preferred
            runtime: Date.now()-start // optional
        }, file ? {
            mapIdx: file.idx,
            tag: file.tag, filename: file.name,
        } : { }) : null);
    });
};

var manifest = _.merge({
    // this should be always overridable by stremio-manifest
    stremio_LID: LID,
    // set filter so that we intercept meta.find from cinemeta
    filter: _.object([ "sort.popularities."+LID, "query.popularities."+LID, "projection.imdb_id", "popular", "query.type" ], [{ "$exists": true },{ "$exists": true }, {"$exists":true}, {"$exists":true}, { $in: ["movie", "series"] }])
}, require("./stremio-manifest"), _.pick(require("../package"), "version"), cfg.stremioManifest || {});

var methods;
var service = new Stremio.Server(methods = {
    "stream.get": function(args, callback, user) { // OBSOLETE
        service.events.emit("stream.get", args, callback);

        var error = validate(args);
        if (error) return callback(error);
        query(args, callback);
    },
    "stream.find": function(args, callback, user) {
        service.events.emit("stream.find", args, callback);

        if (args.query) {
            // New format ; same as stream.get, even returns the full result; no point to slim it down, takes same time
            var error = validate(args);
            if (error) return callback(error);
            async.map([ _.extend({ preferred: [{ tag: "hd", min_avail: 2 }] }, args), args ], query, function(err, res) {
                if (err) return callback(err);
                if (! res) return callback(null, res);

                var results = _.chain(res).filter(function(x) { return x }).uniq(function(x) { return x.infoHash }).value();
                service.events.emit("stream.find.res", results);
                callback(null, results);
            });
        } else return callback({code: 10, message: "unsupported arguments"});
    },
    "stream.popularities": function(args, callback, user) { // OBSOLETE
        service.events.emit("stream.popularities", args, callback);
        db.popularities(function(err, popularities) { callback(err, popularities ? { popularities: popularities } : null) });
    },
    "meta.find": function(args, callback, user) {
        service.events.emit("meta.find", args, callback);

        var firstSort = Object.keys(args.sort || { })[0];
        if (firstSort !== "popularities."+LID) {
            addons.meta.find(args, function(err, res) {
                //if (res) res.forEach(function(x) { x.isPeered = false });
                callback(err, res);
            });
            return;
        }

        // Call this to wait for meta to be collected
        if (args.projection && ( args.projection=="full" ) ) return callback(new Error("full projection not supported by mp"));

        (meta.ready ? function(n) { n() } : metaPipe.push.bind(metaPipe))(function(ready) {
            if (typeof(ready) == "function") process.nextTick(ready); // ensure we don't lock

            args.query = _.pick.apply(null, [args.query || { }].concat(metaQueryProps));
            args.sort = _.pick.apply(null, [args.sort || { }].concat(metaQueryProps));
            //if (! _.keys(args.sort).length) args.sort["popularities."+LID] = -1; // no need as this is our default sort order

            var proj, projFn;
            if (args.projection && typeof(args.projection) == "object") {
                proj = _.keys(args.projection);
                projFn = _.values(args.projection)[0] ? _.pick : _.omit;
            }

            var res = _.chain(meta.col)
                .filter(args.query ? sift(args.query) : _.constant(true))
                .sortByOrder(_.keys(args.sort), _.values(args.sort).map(function(x) { return x>0 ? "asc" : "desc" }))
                .slice(args.skip || 0, Math.min(400, args.limit))
                .map(function(x) { return projFn ? projFn(x, proj) : x })
                .value();

            service.events.emit("meta.find.res", res);
            callback(null, res);
        });
    },
    "stats.get": function(args, callback, user) {
        service.events.emit("stats.get", args, callback);

        var items = 0, episodes = 0, movies = 0;
        db.forEachMeta(function(n) {
            if (n.key.indexOf(" ") != -1) episodes++; else movies++;
            items++;
        }, function() {
            db.count(function(err, c) {
                callback(null, { statsNum: items+" movies and episodes", stats: [
                    { name: "number of items - "+items, count: items, colour: items > 20 ? "green" : (items > 10 ? "yellow" : "red") },
                    { name: "number of movies - "+movies, count: movies, colour: movies > 20 ? "green" : (movies > 10 ? "yellow" : "red") },
                    { name: "number of episodes - "+episodes, count: episodes, colour: episodes > 20 ? "green" : (episodes > 10 ? "yellow" : "red") },
                    { name: "number of torrents - "+c, count: c, colour: c > 50 ? "green" : (c > 20 ? "yellow" : "red") }
                ] });
            });
        });
    },
}, { stremioget: true, allow: [cfg.stremioCentral,"http://api8.herokuapp.com","http://api9.strem.io","https://api9.strem.io"], secret: cfg.stremioSecret }, manifest);

// Event emitter in case we want to intercept/plug-in to this
service.events = new events.EventEmitter();

function listen(port, ip) {
    var server = http.createServer(function (req, res) {
	req.on("error", function(e) { console.error(e) });
        service.middleware(req, res, function() { res.end() });
    })
    .on("error", function(e) { console.error("mp server", e) })
    .on("listening", function()
    {
        console.log("Multipass Stremio Addon listening on "+server.address().port);
    });
    return server.listen(port, ip);
}

module.exports.service = service;
module.exports.methods = methods;
return listen;

}
<|endoftext|>