<commit_before>(function () {
    /**
     * vanilla JS implementation
     * of jQuery.extend()
     */
    d3._extend = function (defaults, options) {
        var extended = {},
            prop;
        for (prop in defaults) {
            if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                extended[prop] = defaults[prop];
            }
        }
        for (prop in options) {
            if (Object.prototype.hasOwnProperty.call(options, prop)) {
                extended[prop] = options[prop];
            }
        }
        return extended;
    };

    d3._pxToNumber = function (val) {
        return parseFloat(val.replace('px'));
    };

    d3._windowHeight = function () {
        return window.innerHeight || document.documentElement.clientHeight || 600;
    };

    d3.netJsonGraph = function(url, opts) {
        if (d3._pxToNumber(d3.select("body").style("height")) < 60) {
            d3.select("body").style("height", d3._windowHeight() + "px");
        }

        // default options, overridable
        opts = d3._extend({
            el: "body",
            zoomExtent: [0.25, 5],
            charge: -130,
            linkDistance: 40,
            linkStrength: 0.2,
            friction: 0.9,  // d3 default
            chargeDistance: Infinity,  // d3 default
            theta: 0.8,  // d3 default
            gravity: 0.1,
            // by default, high density areas have longer links
            linkDistanceFunc: function(d){
                var val = opts.linkDistance;
                if (d.source.linkCount >= 4 && d.target.linkCount >= 4) {
                    return val * 2;
                }
                return val;
            },
            // called on zoom and pan
            redraw: function () {
                panner.attr("transform", "translate(" + d3.event.translate + ") " +
                                         "scale(" + d3.event.scale + ")");
                tooltip.style("visibility", "hidden");
            },
            /**
             * converts NetJSON NetworkGraph
             * to the data structure consumed by d3
             */
            prepareData: function(graph) {
                var nodesMap = {},
                    nodes = graph.nodes.slice(), // copy
                    links = graph.links.slice(), // copy
                    nodes_length = graph.nodes.length;
                links_length = graph.links.length;
                for (var i = 0; i < nodes_length; i++) {
                    // count how many links every node has
                    nodes[i].linkCount = 0;
                    nodesMap[nodes[i].id] = i;
                }
                for (var c = 0; c < links_length; c++) {
                    var sourceIndex = nodesMap[links[c].source],
                        targetIndex = nodesMap[links[c].target];
                    links[c].source = sourceIndex;
                    links[c].target = targetIndex;
                    // add link count to both ends
                    nodes[sourceIndex].linkCount++;
                    nodes[targetIndex].linkCount++;
                }
                return { "nodes": nodes, "links": links };
            }
        }, opts);

        var el = d3.select(opts.el)
                   .style("position", "relative"),
            width = d3._pxToNumber(el.style('width')),
            height = d3._pxToNumber(el.style('height')),
            force = d3.layout.force()
                      .charge(opts.charge)
                      .linkStrength(opts.linkStrength)
                      .linkDistance(opts.linkDistanceFunc)
                      .friction(opts.friction)
                      .chargeDistance(opts.chargeDistance)
                      .theta(opts.theta)
                      .gravity(opts.gravity)
                      // width is easy to get, if height is 0 take the height of the body
                      .size([width, height]),
            zoom = d3.behavior.zoom().scaleExtent(opts.zoomExtent),
            // panner is the element that allows zooming and panning
            panner = el.append("svg")
                       .attr("width", width)
                       .attr("height", height)
                       .call(zoom.on("zoom", opts.redraw))
                       .append("g")
                       .style("position", "relative"),
            svg = d3.select(opts.el+' svg'),
            drag = force.drag(),
            // create tooltip div
            tooltip = d3.select(opts.el)
                        .append("div")
                        .attr("class", "tooltip")
                        .style("position", "absolute")
                        .style("z-index", "10")
                        .style("visibility", "hidden"),
            nodeMouseOver = function(n) {
                tooltip.text(n.label || n.id);
                // position of current element relative to svg container
                var pos = getPosition(d3.select(this), svg),
                // find horizontal and vertical offsets
                    xOffset = (tooltip.node().getBoundingClientRect().width/2) - pos.width/2,
                    yOffset = 1 + zoom.scale() / 5;
                // position tooltip accordingly
                return tooltip.style("left", pos.left - xOffset + "px")
                              .style("top", pos.top - 25 * yOffset + "px")
                              .style("visibility", "visible");
            },
            nodeMouseOut = function(){
                tooltip.style("visibility", "hidden");
            },
            /**
             * get position of `element` relative to `container`
             */
            getPosition = function(element, container) {
                var n = element.node(),
                    nPos = n.getBoundingClientRect();
                    cPos = container.node().getBoundingClientRect();
                return {
                    top: nPos.top - cPos.top,
                    left: nPos.left - cPos.left,
                    width: nPos.width,
                    bottom: nPos.bottom - cPos.top,
                    height: nPos.height,
                    right: nPos.right - cPos.left
                };
            };

        d3.json(url, function(error, graph) {
            if (error) { throw error; }

            var data = opts.prepareData(graph),
                links = data.links,
                nodes = data.nodes;

            // disable some transitions while dragging
            drag.on('dragstart', function(n){
                d3.event.sourceEvent.stopPropagation();
                d3.select(this).on("mouseover", null);
                zoom.on('zoom', null);
            })
            // re-enable transitions when dragging stops
            .on('dragend', function(n){
                d3.select(this).on("mouseover", nodeMouseOver);
                zoom.on('zoom', opts.redraw);
            })
            .on("drag", function(d) {
                // avoid pan & drag conflict
                d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
            });

            force.nodes(nodes)
                 .links(links)
                 .start();

            var link = panner.selectAll(".link")
                             .data(links)
                             .enter().append("line")
                             .attr("class", "link"),
                node = panner.selectAll(".node")
                             .data(nodes)
                             .enter().append("circle")
                             .attr("class", "node")
                             .attr("r", 7)
                             .on("mouseover", nodeMouseOver)
                             .on("mouseout", nodeMouseOut)
                             .call(drag);

            force.on("tick", function() {
                link.attr("x1", function(d) {
                        return d.source.x;
                    })
                    .attr("y1", function(d) {
                        return d.source.y;
                    })
                    .attr("x2", function(d) {
                        return d.target.x;
                    })
                    .attr("y2", function(d) {
                        return d.target.y;
                    });

                node.attr("cx", function(d) {
                        return d.x;
                    })
                    .attr("cy", function(d) {
                        return d.y;
                    });
            })
            .on("end", function(){
                force.stop();
            });
        });

        return force;
    };
})();
<commit_msg>Rename zoomExtent option to scaleExtent<commit_after>(function () {
    /**
     * vanilla JS implementation
     * of jQuery.extend()
     */
    d3._extend = function (defaults, options) {
        var extended = {},
            prop;
        for (prop in defaults) {
            if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
                extended[prop] = defaults[prop];
            }
        }
        for (prop in options) {
            if (Object.prototype.hasOwnProperty.call(options, prop)) {
                extended[prop] = options[prop];
            }
        }
        return extended;
    };

    d3._pxToNumber = function (val) {
        return parseFloat(val.replace('px'));
    };

    d3._windowHeight = function () {
        return window.innerHeight || document.documentElement.clientHeight || 600;
    };

    d3.netJsonGraph = function(url, opts) {
        if (d3._pxToNumber(d3.select("body").style("height")) < 60) {
            d3.select("body").style("height", d3._windowHeight() + "px");
        }

        // default options, overridable
        opts = d3._extend({
            el: "body",
            scaleExtent: [0.25, 5],
            charge: -130,
            linkDistance: 40,
            linkStrength: 0.2,
            friction: 0.9,  // d3 default
            chargeDistance: Infinity,  // d3 default
            theta: 0.8,  // d3 default
            gravity: 0.1,
            // by default, high density areas have longer links
            linkDistanceFunc: function(d){
                var val = opts.linkDistance;
                if (d.source.linkCount >= 4 && d.target.linkCount >= 4) {
                    return val * 2;
                }
                return val;
            },
            // called on zoom and pan
            redraw: function () {
                panner.attr("transform", "translate(" + d3.event.translate + ") " +
                                         "scale(" + d3.event.scale + ")");
                tooltip.style("visibility", "hidden");
            },
            /**
             * converts NetJSON NetworkGraph
             * to the data structure consumed by d3
             */
            prepareData: function(graph) {
                var nodesMap = {},
                    nodes = graph.nodes.slice(), // copy
                    links = graph.links.slice(), // copy
                    nodes_length = graph.nodes.length;
                links_length = graph.links.length;
                for (var i = 0; i < nodes_length; i++) {
                    // count how many links every node has
                    nodes[i].linkCount = 0;
                    nodesMap[nodes[i].id] = i;
                }
                for (var c = 0; c < links_length; c++) {
                    var sourceIndex = nodesMap[links[c].source],
                        targetIndex = nodesMap[links[c].target];
                    links[c].source = sourceIndex;
                    links[c].target = targetIndex;
                    // add link count to both ends
                    nodes[sourceIndex].linkCount++;
                    nodes[targetIndex].linkCount++;
                }
                return { "nodes": nodes, "links": links };
            }
        }, opts);

        var el = d3.select(opts.el)
                   .style("position", "relative"),
            width = d3._pxToNumber(el.style('width')),
            height = d3._pxToNumber(el.style('height')),
            force = d3.layout.force()
                      .charge(opts.charge)
                      .linkStrength(opts.linkStrength)
                      .linkDistance(opts.linkDistanceFunc)
                      .friction(opts.friction)
                      .chargeDistance(opts.chargeDistance)
                      .theta(opts.theta)
                      .gravity(opts.gravity)
                      // width is easy to get, if height is 0 take the height of the body
                      .size([width, height]),
            zoom = d3.behavior.zoom().scaleExtent(opts.scaleExtent),
            // panner is the element that allows zooming and panning
            panner = el.append("svg")
                       .attr("width", width)
                       .attr("height", height)
                       .call(zoom.on("zoom", opts.redraw))
                       .append("g")
                       .style("position", "relative"),
            svg = d3.select(opts.el+' svg'),
            drag = force.drag(),
            // create tooltip div
            tooltip = d3.select(opts.el)
                        .append("div")
                        .attr("class", "tooltip")
                        .style("position", "absolute")
                        .style("z-index", "10")
                        .style("visibility", "hidden"),
            nodeMouseOver = function(n) {
                tooltip.text(n.label || n.id);
                // position of current element relative to svg container
                var pos = getPosition(d3.select(this), svg),
                // find horizontal and vertical offsets
                    xOffset = (tooltip.node().getBoundingClientRect().width/2) - pos.width/2,
                    yOffset = 1 + zoom.scale() / 5;
                // position tooltip accordingly
                return tooltip.style("left", pos.left - xOffset + "px")
                              .style("top", pos.top - 25 * yOffset + "px")
                              .style("visibility", "visible");
            },
            nodeMouseOut = function(){
                tooltip.style("visibility", "hidden");
            },
            /**
             * get position of `element` relative to `container`
             */
            getPosition = function(element, container) {
                var n = element.node(),
                    nPos = n.getBoundingClientRect();
                    cPos = container.node().getBoundingClientRect();
                return {
                    top: nPos.top - cPos.top,
                    left: nPos.left - cPos.left,
                    width: nPos.width,
                    bottom: nPos.bottom - cPos.top,
                    height: nPos.height,
                    right: nPos.right - cPos.left
                };
            };

        d3.json(url, function(error, graph) {
            if (error) { throw error; }

            var data = opts.prepareData(graph),
                links = data.links,
                nodes = data.nodes;

            // disable some transitions while dragging
            drag.on('dragstart', function(n){
                d3.event.sourceEvent.stopPropagation();
                d3.select(this).on("mouseover", null);
                zoom.on('zoom', null);
            })
            // re-enable transitions when dragging stops
            .on('dragend', function(n){
                d3.select(this).on("mouseover", nodeMouseOver);
                zoom.on('zoom', opts.redraw);
            })
            .on("drag", function(d) {
                // avoid pan & drag conflict
                d3.select(this).attr("x", d.x = d3.event.x).attr("y", d.y = d3.event.y);
            });

            force.nodes(nodes)
                 .links(links)
                 .start();

            var link = panner.selectAll(".link")
                             .data(links)
                             .enter().append("line")
                             .attr("class", "link"),
                node = panner.selectAll(".node")
                             .data(nodes)
                             .enter().append("circle")
                             .attr("class", "node")
                             .attr("r", 7)
                             .on("mouseover", nodeMouseOver)
                             .on("mouseout", nodeMouseOut)
                             .call(drag);

            force.on("tick", function() {
                link.attr("x1", function(d) {
                        return d.source.x;
                    })
                    .attr("y1", function(d) {
                        return d.source.y;
                    })
                    .attr("x2", function(d) {
                        return d.target.x;
                    })
                    .attr("y2", function(d) {
                        return d.target.y;
                    });

                node.attr("cx", function(d) {
                        return d.x;
                    })
                    .attr("cy", function(d) {
                        return d.y;
                    });
            })
            .on("end", function(){
                force.stop();
            });
        });

        return force;
    };
})();
<|endoftext|>


<commit_before>var fs = require('fs'),
    readline = require('readline'),
    Subscription = require('./subscription')

module.exports = NewsManager

// Manager for news updates.
// Registers Subscription objects and compares
// their results to updates stored in file.
// Input: filePath (path to file containing API tokens).
// If no filePath is supplied, it will search for a file in
// the parent directory named 'news'.
// Input: subscriptions (map of game names to Subscription services for those games).
function NewsManager(filePath, subscriptions) {

  this.filePath = '../news'
  this.updates = {}
  this.updatesReceived = {}
  this.subscriptions = subscriptions
  this.checked = 0
  this.onNewsReadFromFileHandler = this.DEFAULT_ON_NEWS_READ_FROM_FILE_HANDLER
  this.onFinishSubscribingHandler = this.DEFAULT_ON_FINISH_SUBSCRIBING_HANDLER
  this.onFinishCheckingHandler = this.DEFAULT_ON_FINISH_CHECKING_HANDLER
  this.onFinishCheckingForUpdatesHandler = null
  this.onNewerContentFoundHandler = this.DEFAULT_ON_NEWER_CONTENT_FOUND_HANDLER

  if(filePath)
    this.filePath = filePath

  this.readNewsFromFile()

  return this
}

NewsManager.prototype.readNewsFromFile = function () {
  // trusting this to throw necessary errors rn...
  var rl = readline.createInterface({
    input: fs.createReadStream(this.filePath),
    terminal: false
  })
  rl.on('line', function (line) {
    var words = line.split(' ')
    if(words.length !== 2)
      throw new Error('Improper formatting. (Name-timestamp pairs are space-delimited, and separate from the next pair by newline.)')
    this.updates[words[0]] = words[1]
  }.bind(this)).on('close', function () {
    return this.onNewsReadFromFileHandler()
  }.bind(this)).on('error', function (err) {
    throw err
  })
}
NewsManager.prototype.subscribe = function (subscriptions) {
  if(!subscriptions && !this.subscriptions)
    throw new Error('Subscribing requires a list of subscriptions.')
  else if(!this.subscriptions)
    this.subscriptions = subscriptions

  if(typeof this.subscriptions !== 'object')
    throw new Error('The map of subscriptions must be a generic Javascript object.')
  if(Object.keys(this.subscriptions).length < 1)
    throw new Error('The map of subscriptions must contain subscriptions.')
  for(var s in this.subscriptions) {
    if(typeof this.subscriptions[s] !== 'function' || !(new this.subscriptions[s]() instanceof Subscription))
      throw new Error('Every subscription handler must implement the Subscription interface.')

    // we trust that we have already read the relevant information from file.
    // if there is no available data, we send null, but never undefined.
    this.subscriptions[s] = new this.subscriptions[s](
      (this.updates[s] !== undefined)?
      this.updates[s]
    : null)
      .onNewerContentFound(this.onNewerContentFoundHandler.bind(this))
      .onFinishChecking(this.onFinishCheckingHandler.bind(this))
  }
  this.onFinishSubscribingHandler()
}
NewsManager.prototype.checkForUpdates = function () {
  for(var s in this.subscriptions) {
    this.subscriptions[s].check()
  }
}
NewsManager.prototype.writeUpdatesToFile = function () {

  if(!(Array.isArray(this.updatesReceived) && this.updatesReceived.length > 0)) return;

  // this will be what we write to the file, and will house 100% of the news file's contents
  var news = {}

  var rl = readline.createInterface({
    input: fs.createReadStream(this.filePath),
    terminal: false
  })
  rl.on('line', function (line) {

    var pair = line.split(' '),
        k = pair[0],
        v = pair[1]

    if(pair.length !== 2)
      return false

    // if we got an update for the key on this line,
    // then we'll take the new content instead of the old.
    if(typeof (this.updatesReceived[k]) !== 'undefined') {
      news[k] = this.updatesReceived[k]
      delete this.updatesReceived[k]
    }
    // otherwise, we'll take the old content.
    else
      news[k] = v
  }.bind(this)).on('close', function () {

    // 'w' flag truncates, so we're completely rewriting the file
    var ws = fs.createWriteStream('news', { flag: 'w' })
    ws.on('error', function (err) {
      console.log(err)
    })

    // combine the remaining updates into the news object
    // (this should be all updates we didn't already have keys for)
    for(var u in this.updatesReceived)
      if(this.updatesReceived.hasOwnProperty(u))
        news[u] = this.updatesReceived[u]

    // write to file
    console.log('Writing news...')
    console.log(news)
    for(var n in news)
      ws.write(n + ' ' + news[n])

    ws.end()
  })
}

NewsManager.prototype.onNewsReadFromFile = function (callback) {
  this.onNewsReadFromFileHandler = callback
  return this
}
NewsManager.prototype.onFinishSubscribing = function (callback) {
  this.onFinishSubscribingHandler = callback
  return this
}
NewsManager.prototype.onFinishCheckingForUpdates = function (callback) {
  this.onFinishCheckingForUpdatesHandler = callback
  return this
}
NewsManager.prototype.onNewerContentFound = function (callback) {
  if(typeof callback !== 'function')
    throw new TypeError('Only functions can be registered as callbacks. (Received: ' + typeof callback + ')')
  this.onNewerContentFoundHandler = callback
  return this
}

NewsManager.prototype.DEFAULT_ON_FINISH_CHECKING_HANDLER = function (update) {
  this.checked++
  console.log(update)
  if(update) this.onNewerContentFoundHandler(update)
  this.onFinishCheckingForUpdatesHandler()
}
NewsManager.prototype.DEFAULT_ON_NEWS_READ_FROM_FILE_HANDLER = function () {
  return this.subscribe()
}
NewsManager.prototype.DEFAULT_ON_FINISH_SUBSCRIBING_HANDLER = function () {
  return this.checkForUpdates()
}
NewsManager.prototype.DEFAULT_ON_NEWER_CONTENT_FOUND_HANDLER = function (update) {
  this.updatesReceived[update.name] = update.data
}

<commit_msg>Debugging qq this is awkward<commit_after>var fs = require('fs'),
    readline = require('readline'),
    Subscription = require('./subscription')

module.exports = NewsManager

// Manager for news updates.
// Registers Subscription objects and compares
// their results to updates stored in file.
// Input: filePath (path to file containing API tokens).
// If no filePath is supplied, it will search for a file in
// the parent directory named 'news'.
// Input: subscriptions (map of game names to Subscription services for those games).
function NewsManager(filePath, subscriptions) {

  this.filePath = '../news'
  this.updates = {}
  this.updatesReceived = {}
  this.subscriptions = subscriptions
  this.checked = 0
  this.onNewsReadFromFileHandler = this.DEFAULT_ON_NEWS_READ_FROM_FILE_HANDLER
  this.onFinishSubscribingHandler = this.DEFAULT_ON_FINISH_SUBSCRIBING_HANDLER
  this.onFinishCheckingHandler = this.DEFAULT_ON_FINISH_CHECKING_HANDLER
  this.onFinishCheckingForUpdatesHandler = null
  this.onNewerContentFoundHandler = this.DEFAULT_ON_NEWER_CONTENT_FOUND_HANDLER

  if(filePath)
    this.filePath = filePath

  this.readNewsFromFile()

  return this
}

NewsManager.prototype.readNewsFromFile = function () {
  // trusting this to throw necessary errors rn...
  var rl = readline.createInterface({
    input: fs.createReadStream(this.filePath),
    terminal: false
  })
  rl.on('line', function (line) {
    var words = line.split(' ')
    if(words.length !== 2)
      throw new Error('Improper formatting. (Name-timestamp pairs are space-delimited, and separate from the next pair by newline.)')
    this.updates[words[0]] = words[1]
  }.bind(this)).on('close', function () {
    return this.onNewsReadFromFileHandler()
  }.bind(this)).on('error', function (err) {
    throw err
  })
}
NewsManager.prototype.subscribe = function (subscriptions) {
  if(!subscriptions && !this.subscriptions)
    throw new Error('Subscribing requires a list of subscriptions.')
  else if(!this.subscriptions)
    this.subscriptions = subscriptions

  if(typeof this.subscriptions !== 'object')
    throw new Error('The map of subscriptions must be a generic Javascript object.')
  if(Object.keys(this.subscriptions).length < 1)
    throw new Error('The map of subscriptions must contain subscriptions.')
  for(var s in this.subscriptions) {
    if(typeof this.subscriptions[s] !== 'function' || !(new this.subscriptions[s]() instanceof Subscription))
      throw new Error('Every subscription handler must implement the Subscription interface.')

    // we trust that we have already read the relevant information from file.
    // if there is no available data, we send null, but never undefined.
    this.subscriptions[s] = new this.subscriptions[s](
      (this.updates[s] !== undefined)?
      this.updates[s]
    : null)
      .onNewerContentFound(this.onNewerContentFoundHandler.bind(this))
      .onFinishChecking(this.onFinishCheckingHandler.bind(this))
  }
  this.onFinishSubscribingHandler()
}
NewsManager.prototype.checkForUpdates = function () {
  for(var s in this.subscriptions) {
    this.subscriptions[s].check()
  }
}
NewsManager.prototype.writeUpdatesToFile = function () {

  console.log('1000')
  if(!(Array.isArray(this.updatesReceived) && this.updatesReceived.length > 0)) return;

  console.log('1010')
  // this will be what we write to the file, and will house 100% of the news file's contents
  var news = {}

  console.log('1020')
  var rl = readline.createInterface({
    input: fs.createReadStream(this.filePath),
    terminal: false
  })
  console.log('1030')
  rl.on('line', function (line) {

    console.log('2000')
    var pair = line.split(' '),
        k = pair[0],
        v = pair[1]
    console.log('2010')
    if(pair.length !== 2)
      return false
    console.log('2020')
    // if we got an update for the key on this line,
    // then we'll take the new content instead of the old.
    if(typeof (this.updatesReceived[k]) !== 'undefined') {
      news[k] = this.updatesReceived[k]
      delete this.updatesReceived[k]
    }
    // otherwise, we'll take the old content.
    else
      news[k] = v
    console.log('2030')
  }.bind(this)).on('close', function () {

    // 'w' flag truncates, so we're completely rewriting the file
    console.log('3000')
    var ws = fs.createWriteStream('news', { flag: 'w' })
    console.log('3010')
    ws.on('error', function (err) {
      console.log(err)
    })
    console.log('3020')

    // combine the remaining updates into the news object
    // (this should be all updates we didn't already have keys for)
    for(var u in this.updatesReceived)
      if(this.updatesReceived.hasOwnProperty(u))
        news[u] = this.updatesReceived[u]

    console.log('3030')
    // write to file
    console.log('Writing news...')
    console.log(news)
    for(var n in news)
      ws.write(n + ' ' + news[n])

    console.log('3040')
    ws.end()
    console.log('3050')
  })
  console.log('1040')
}

NewsManager.prototype.onNewsReadFromFile = function (callback) {
  this.onNewsReadFromFileHandler = callback
  return this
}
NewsManager.prototype.onFinishSubscribing = function (callback) {
  this.onFinishSubscribingHandler = callback
  return this
}
NewsManager.prototype.onFinishCheckingForUpdates = function (callback) {
  this.onFinishCheckingForUpdatesHandler = callback
  return this
}
NewsManager.prototype.onNewerContentFound = function (callback) {
  if(typeof callback !== 'function')
    throw new TypeError('Only functions can be registered as callbacks. (Received: ' + typeof callback + ')')
  this.onNewerContentFoundHandler = callback
  return this
}

NewsManager.prototype.DEFAULT_ON_FINISH_CHECKING_HANDLER = function (update) {
  this.checked++
  console.log(update)
  if(update) this.onNewerContentFoundHandler(update)
  this.onFinishCheckingForUpdatesHandler()
}
NewsManager.prototype.DEFAULT_ON_NEWS_READ_FROM_FILE_HANDLER = function () {
  return this.subscribe()
}
NewsManager.prototype.DEFAULT_ON_FINISH_SUBSCRIBING_HANDLER = function () {
  return this.checkForUpdates()
}
NewsManager.prototype.DEFAULT_ON_NEWER_CONTENT_FOUND_HANDLER = function (update) {
  this.updatesReceived[update.name] = update.data
}

<|endoftext|>


<commit_before>var fragmentState = function(transitions, index) {
  return {
    transitions: transitions === null ? [] : transitions,
    index: index || null,
  };
};

var fragmentTransition = function(spec, target) {
  return {
    spec: spec,
    target: target,
  };
};

var fragment = function(head, tails) {
  return {
    head: head,
    tails: tails,
  }
};

var namedFragment = function(name, head, tails) {
  var f = fragment(head, tails);
  f.name = name;
  return f;
}

function patch (tails, state) {
  tails.forEach(function(tail) {
    tail.target = state;
  });
};

function epsilonState () {
  return {
    isEpsilon: true,
  };
}

function namedEpsilonState (dir, name, op, group) {
  var s = epsilonState();
  s.name = name;
  s.op = op;
  s.group = group;
  s.dir = dir;
  return s;
}

function frontWithState(state, f) {
  var transIn = fragmentTransition(
    state, f.head);
  var newHead = fragmentState([transIn]);
  var newF = fragment(newHead, f.tails);
  return newF;
}

function rearWithState(state, f) {
  var newTrans = fragmentTransition(state, null);
  var newS = fragmentState([newTrans], null)
  patch(f.tails, newS);
  var r = fragment(f.head, [newTrans]);
  return r;
}

var build = {};

build.PRED = function(spec) {
  var trans = fragmentTransition(spec, null);
  var head = fragmentState([trans], null);
  var tails = [trans];
  var f = fragment(head, tails);
  return f;
};

build.CAT = function(frags) {
  // var i;
  // for (i = 0; i < frags.length; i++) {
  //   var curr = frags[i];
  //   var next = frags[i + 1];
  //   var trans = fragmentTransition(epsilonState(), f.head);
  //   trans.outName = curr.name;
  //
  // }
  var binaryConcat = function(frag1, currFrag) {

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(frag1.tails, false, null));
    // console.log('--------------------------------');
    patch(frag1.tails, currFrag.head);
    var head = frag1.head;
    var tails = currFrag.tails;
    var newF = fragment(head, tails);
    return newF;
  };

  frags = frags.map(function addEpsilonState (f) {
    var originalTails = f.tails;
    var trans = fragmentTransition(
      namedEpsilonState('out', f.name, 'CAT', true), null);
    var nameOutState = fragmentState([trans]);
    patch(f.tails, nameOutState);

    var nameInTranstions = f.head.transitions.map(function (t) {
      var s = fragmentState([t]);
      var namedInTrans = fragmentTransition(
        namedEpsilonState('in', f.name, 'CAT', true), s);
      return namedInTrans;
    });
    var newHead = fragmentState(nameInTranstions);

    var newF = namedFragment(f.name, newHead, [trans]);

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(newHead, false, null));
    // console.log('--------------------------------');
    return newF;
  });
  var r = frags.reduce(binaryConcat);
  r = frontWithState(
    namedEpsilonState('enter', null, 'CAT'),
    r
  );

  r = rearWithState(
    namedEpsilonState('exit', null, 'CAT'), r);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(r, false, null));
  // console.log('--------------------------------');

  return r;
};

build.OR = function(frags) {
  frags = frags.map(function(f) {
    var originalTails = f.tails;
    var outState = namedEpsilonState('out', f.name, 'OR', true);
    var trans = fragmentTransition(outState, null);
    var nameOutState = fragmentState([trans]);
    patch(f.tails, nameOutState);

    // var nameInTranstions = f.head.transitions.map(function (t) {
    //   var s = fragmentState([t]);
    //   var namedInTrans = fragmentTransition(nameInEpsilonState(f.name), s);
    //   return namedInTrans;
    // });
    // var newHead = fragmentState(nameInTranstions);

    var transIn = fragmentTransition(namedEpsilonState('in', f.name, 'OR', true), f.head);
    var newHead = fragmentState([transIn]);
    var newF = namedFragment(f.name, newHead, [trans]);

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(newF, false, null));
    // console.log('--------------------------------');

    return newF;
  });
  var binaryAlt = function(frag1, frag2) {
    var combinedTransitions = frag1.head.transitions.concat(frag2.head.transitions);
    var head = fragmentState(combinedTransitions);
    var tails = frag1.tails.concat(frag2.tails);
    var acc = namedFragment(frag1.name, head, tails);
    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(frag1, false, null));
    // console.log('--------------------------------');
    return acc;
  };

  var newF = frags.reduce(binaryAlt);
  newF = frontWithState(namedEpsilonState('enter', null, 'OR'), newF);
  newF = rearWithState(namedEpsilonState('exit', null, 'OR'), newF);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newF, false, null));
  // console.log('--------------------------------');

  return newF;
};

build.ZERO_OR_MORE = function(frag) {

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newHead, false, null));
  // console.log('--------------------------------');

  var loopTrans = fragmentTransition(namedEpsilonState('loop', null, 'ZERO_OR_MORE', null), frag.head);
  var breakTrans = fragmentTransition(epsilonState(), null);
  var head = fragmentState([loopTrans, breakTrans]);
  patch(frag.tails, head);
  var newF = fragment(head, [breakTrans]);

  var nameInTranstions = newF.head.transitions.map(function (t) {
    var s = fragmentState([t]);
    var namedInTrans = fragmentTransition(epsilonState(), s);
    return namedInTrans;
  });
  var newHead = fragmentState(nameInTranstions);
  newF = fragment(newHead, newF.tails);

  newF = frontWithState(
    namedEpsilonState('maybe_enter', null, 'ZERO_OR_MORE'),
    newF);
  newF = rearWithState(
    namedEpsilonState('maybe_exit', null, 'ZERO_OR_MORE'), newF);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newF, false, null));
  // console.log('--------------------------------');
  return newF;
};

build.ONE_OR_MORE = function(frag) {
  var loopTrans = fragmentTransition(epsilonState(), frag.head);
  var breakTrans = fragmentTransition(epsilonState(), null);
  var state = fragmentState([loopTrans, breakTrans]);
  patch(frag.tails, state);
  return fragment(frag.head, [breakTrans]);
};

build.ZERO_OR_ONE = function(frag) {
  var matchTrans = fragmentTransition(epsilonState(), frag.head);
  var skipTrans = fragmentTransition(epsilonState(), null);
  var head = fragmentState([matchTrans, skipTrans]);
  var tails = frag.tails.concat([skipTrans]);
  return fragment(head, tails);
};

build.ROOT = function(frag) {
  var finalState = fragmentState(null, null);
  patch(frag.tails, finalState);
  return fragment(frag.head, []);
};

module.exports = build;
<commit_msg>fix oneOrMore, zeroOrOne<commit_after>var fragmentState = function(transitions, index) {
  return {
    transitions: transitions === null ? [] : transitions,
    index: index || null,
  };
};

var fragmentTransition = function(spec, target) {
  return {
    spec: spec,
    target: target,
  };
};

var fragment = function(head, tails) {
  return {
    head: head,
    tails: tails,
  }
};

var namedFragment = function(name, head, tails) {
  var f = fragment(head, tails);
  f.name = name;
  return f;
}

function patch (tails, state) {
  tails.forEach(function(tail) {
    tail.target = state;
  });
};

function epsilonState () {
  return {
    isEpsilon: true,
  };
}

function namedEpsilonState (dir, name, op, group) {
  var s = epsilonState();
  s.name = name;
  s.op = op;
  s.group = group;
  s.dir = dir;
  return s;
}

function frontWithState(state, f) {
  var transIn = fragmentTransition(
    state, f.head);
  var newHead = fragmentState([transIn]);
  var newF = fragment(newHead, f.tails);
  return newF;
}

function rearWithState(state, f) {
  var newTrans = fragmentTransition(state, null);
  var newS = fragmentState([newTrans], null)
  patch(f.tails, newS);
  var r = fragment(f.head, [newTrans]);
  return r;
}

var build = {};

build.PRED = function(spec) {
  var trans = fragmentTransition(spec, null);
  var head = fragmentState([trans], null);
  var tails = [trans];
  var f = fragment(head, tails);
  return f;
};

build.CAT = function(frags) {
  // var i;
  // for (i = 0; i < frags.length; i++) {
  //   var curr = frags[i];
  //   var next = frags[i + 1];
  //   var trans = fragmentTransition(epsilonState(), f.head);
  //   trans.outName = curr.name;
  //
  // }
  var binaryConcat = function(frag1, currFrag) {

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(frag1.tails, false, null));
    // console.log('--------------------------------');
    patch(frag1.tails, currFrag.head);
    var head = frag1.head;
    var tails = currFrag.tails;
    var newF = fragment(head, tails);
    return newF;
  };

  frags = frags.map(function addEpsilonState (f) {
    var originalTails = f.tails;
    var trans = fragmentTransition(
      namedEpsilonState('out', f.name, 'CAT', true), null);
    var nameOutState = fragmentState([trans]);
    patch(f.tails, nameOutState);

    var nameInTranstions = f.head.transitions.map(function (t) {
      var s = fragmentState([t]);
      var namedInTrans = fragmentTransition(
        namedEpsilonState('in', f.name, 'CAT', true), s);
      return namedInTrans;
    });
    var newHead = fragmentState(nameInTranstions);

    var newF = namedFragment(f.name, newHead, [trans]);

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(newHead, false, null));
    // console.log('--------------------------------');
    return newF;
  });
  var r = frags.reduce(binaryConcat);
  r = frontWithState(
    namedEpsilonState('enter', null, 'CAT'),
    r
  );

  r = rearWithState(
    namedEpsilonState('exit', null, 'CAT'), r);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(r, false, null));
  // console.log('--------------------------------');

  return r;
};

build.OR = function(frags) {
  frags = frags.map(function(f) {
    var originalTails = f.tails;
    var outState = namedEpsilonState('out', f.name, 'OR', true);
    var trans = fragmentTransition(outState, null);
    var nameOutState = fragmentState([trans]);
    patch(f.tails, nameOutState);

    // var nameInTranstions = f.head.transitions.map(function (t) {
    //   var s = fragmentState([t]);
    //   var namedInTrans = fragmentTransition(nameInEpsilonState(f.name), s);
    //   return namedInTrans;
    // });
    // var newHead = fragmentState(nameInTranstions);

    var transIn = fragmentTransition(namedEpsilonState('in', f.name, 'OR', true), f.head);
    var newHead = fragmentState([transIn]);
    var newF = namedFragment(f.name, newHead, [trans]);

    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(newF, false, null));
    // console.log('--------------------------------');

    return newF;
  });
  var binaryAlt = function(frag1, frag2) {
    var combinedTransitions = frag1.head.transitions.concat(frag2.head.transitions);
    var head = fragmentState(combinedTransitions);
    var tails = frag1.tails.concat(frag2.tails);
    var acc = namedFragment(frag1.name, head, tails);
    // var util = require('util');
    // console.log('--------------------------------');
    // console.log(util.inspect(frag1, false, null));
    // console.log('--------------------------------');
    return acc;
  };

  var newF = frags.reduce(binaryAlt);
  newF = frontWithState(namedEpsilonState('enter', null, 'OR'), newF);
  newF = rearWithState(namedEpsilonState('exit', null, 'OR'), newF);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newF, false, null));
  // console.log('--------------------------------');

  return newF;
};

build.ZERO_OR_MORE = function(frag) {

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newHead, false, null));
  // console.log('--------------------------------');

  var l = 'ZERO_OR_MORE';
  var loopTrans = fragmentTransition(namedEpsilonState('loop', null, l, null), frag.head);
  var breakTrans = fragmentTransition(epsilonState(), null);
  var head = fragmentState([loopTrans, breakTrans]);
  patch(frag.tails, head);
  var newF = fragment(head, [breakTrans]);

  var nameInTranstions = newF.head.transitions.map(function (t) {
    var s = fragmentState([t]);
    var namedInTrans = fragmentTransition(epsilonState(), s);
    return namedInTrans;
  });
  var newHead = fragmentState(nameInTranstions);

  newF = fragment(newHead, newF.tails);
  newF = frontWithState(namedEpsilonState('maybe_enter', null, l), newF);
  newF = rearWithState(namedEpsilonState('maybe_exit', null, l), newF);

  // var util = require('util');
  // console.log('--------------------------------');
  // console.log(util.inspect(newF, false, null));
  // console.log('--------------------------------');
  return newF;
};

build.ONE_OR_MORE = function(frag) {
  var l = 'ONE_OR_MORE';
  var loopTrans = fragmentTransition(namedEpsilonState('loop', null, l, null), frag.head);
  var breakTrans = fragmentTransition(epsilonState(), null);
  var state = fragmentState([loopTrans, breakTrans]);
  patch(frag.tails, state);

  var newF = fragment(frag.head, [breakTrans]);
  newF = frontWithState(namedEpsilonState('maybe_enter', null, l), newF);
  newF = rearWithState(namedEpsilonState('maybe_exit', null, l), newF);

  return newF;
};

build.ZERO_OR_ONE = function(frag) {
  var l = 'ZERO_OR_ONE';
  var matchTrans = fragmentTransition(namedEpsilonState('loop', null, l, null), frag.head);
  var skipTrans = fragmentTransition(epsilonState(), null);
  var head = fragmentState([matchTrans, skipTrans]);
  var tails = frag.tails.concat([skipTrans]);

  var newF = fragment(head, tails);
  newF = frontWithState(namedEpsilonState('maybe_enter', null, l), newF);
  newF = rearWithState(namedEpsilonState('maybe_exit', null, l), newF);

  return newF;
};

build.ROOT = function(frag) {
  var finalState = fragmentState(null, null);
  patch(frag.tails, finalState);
  return fragment(frag.head, []);
};

module.exports = build;
<|endoftext|>


<commit_before>var Timer = require('../util/timer');

function Sequencer (runtime) {
    /**
     * A utility timer for timing thread sequencing.
     * @type {!Timer}
     */
    this.timer = new Timer();

    /**
     * Reference to the runtime owning this sequencer.
     * @type {!Runtime}
     */
    this.runtime = runtime;
}

/**
 * The sequencer does as much work as it can within WORK_TIME milliseconds,
 * then yields. This is essentially a rate-limiter for blocks.
 * In Scratch 2.0, this is set to 75% of the target stage frame-rate (30fps).
 * @const {!number}
 */
Sequencer.WORK_TIME = 1000 / 60;

/**
 * Step through all threads in `this.threads`, running them in order.
 * @return {Array.<Thread>} All threads which have finished in this iteration.
 */
Sequencer.prototype.stepThreads = function (threads) {
    // Start counting toward WORK_TIME
    this.timer.start();
    // List of threads which have been killed by this step.
    var inactiveThreads = [];
    // While there are still threads to run and we are within WORK_TIME,
    // continue executing threads.
    while (threads.length > 0 &&
           this.timer.timeElapsed() < Sequencer.WORK_TIME) {
        // New threads at the end of the iteration.
        var newThreads = [];
        // Attempt to run each thread one time
        for (var i = 0; i < threads.length; i++) {
            var activeThread = threads[i];
            this.stepThread(activeThread);
            if (activeThread.nextBlock !== null) {
                newThreads.push(activeThread);
            } else {
                inactiveThreads.push(activeThread);
            }
        }
        // Effectively filters out threads that have stopped.
        threads = newThreads;
    }
    return inactiveThreads;
};

/**
 * Step the requested thread
 * @param {!Thread} thread Thread object to step
 */
Sequencer.prototype.stepThread = function (thread) {
    var opcode = this.runtime._getOpcode(thread.nextBlock);

    if (!opcode) {
        console.warn('Could not get opcode for block: ' + thread.nextBlock);
    }
    else {
        var blockFunction = this.runtime.getOpcodeFunction(opcode);
        if (!blockFunction) {
            console.warn('Could not get implementation for opcode: ' + opcode);
        }
        else {
            try {
                blockFunction();
            }
            catch(e) {
                console.error('Exception calling block function',
                    {opcode: opcode, exception: e});
            }
        }
    }

    thread.nextBlock = this.runtime._getNextBlock(thread.nextBlock);
};

module.exports = Sequencer;
<commit_msg>Update nextBlock to be set before primitive call<commit_after>var Timer = require('../util/timer');

function Sequencer (runtime) {
    /**
     * A utility timer for timing thread sequencing.
     * @type {!Timer}
     */
    this.timer = new Timer();

    /**
     * Reference to the runtime owning this sequencer.
     * @type {!Runtime}
     */
    this.runtime = runtime;
}

/**
 * The sequencer does as much work as it can within WORK_TIME milliseconds,
 * then yields. This is essentially a rate-limiter for blocks.
 * In Scratch 2.0, this is set to 75% of the target stage frame-rate (30fps).
 * @const {!number}
 */
Sequencer.WORK_TIME = 1000 / 60;

/**
 * Step through all threads in `this.threads`, running them in order.
 * @return {Array.<Thread>} All threads which have finished in this iteration.
 */
Sequencer.prototype.stepThreads = function (threads) {
    // Start counting toward WORK_TIME
    this.timer.start();
    // List of threads which have been killed by this step.
    var inactiveThreads = [];
    // While there are still threads to run and we are within WORK_TIME,
    // continue executing threads.
    while (threads.length > 0 &&
           this.timer.timeElapsed() < Sequencer.WORK_TIME) {
        // New threads at the end of the iteration.
        var newThreads = [];
        // Attempt to run each thread one time
        for (var i = 0; i < threads.length; i++) {
            var activeThread = threads[i];
            this.stepThread(activeThread);
            if (activeThread.nextBlock !== null) {
                newThreads.push(activeThread);
            } else {
                inactiveThreads.push(activeThread);
            }
        }
        // Effectively filters out threads that have stopped.
        threads = newThreads;
    }
    return inactiveThreads;
};

/**
 * Step the requested thread
 * @param {!Thread} thread Thread object to step
 */
Sequencer.prototype.stepThread = function (thread) {
    // Save the current block and set the nextBlock.
    // If the primitive would like to do control flow,
    // it can overwrite nextBlock.
    var currentBlock = thread.nextBlock;
    thread.nextBlock = this.runtime._getNextBlock(thread.nextBlock);

    var opcode = this.runtime._getOpcode(currentBlock);

    if (!opcode) {
        console.warn('Could not get opcode for block: ' + currentBlock);
    }
    else {
        var blockFunction = this.runtime.getOpcodeFunction(opcode);
        if (!blockFunction) {
            console.warn('Could not get implementation for opcode: ' + opcode);
        }
        else {
            try {
                blockFunction();
            }
            catch(e) {
                console.error('Exception calling block function',
                    {opcode: opcode, exception: e});
            }
        }
    }

};

module.exports = Sequencer;
<|endoftext|>


<commit_before>angular.module('controllers', [])
  .controller('TestCases', function($scope, $http) {
    $scope.parseRules = function(string, targetEl, caseOptions) {
      var rules = string.split(/,?\s/);
      var obj = {};
      var id = _.uniqueId('E-');

      targetEl.attr('id', id);

      _.each(rules, function(rule) {
        var parts = rule.split(':');

        var keys  = _.initial(parts);
        var key   = keys[0];
        var group = keys[1] || null;
        var mod   = _.last(parts);

        var option = caseOptions[key] = caseOptions[key] || {};
        var instruction = { modifier: mod, id: id };

        if(group) {
          option.group = option.group || {};
          option = option.group[group] = option.group[group] || [];
          option.label = key + ' ' + (+group + 1);
        } else {
          option.label = key;
        }

        option.instructions = option.instructions || [];
        option.instructions.push(instruction);
      });

      return caseOptions;
    };
  });
<commit_msg>Demo: Removed unused variable<commit_after>angular.module('controllers', [])
  .controller('TestCases', function($scope, $http) {
    $scope.parseRules = function(string, targetEl, caseOptions) {
      var rules = string.split(/,?\s/);
      var id = _.uniqueId('E-');

      targetEl.attr('id', id);

      _.each(rules, function(rule) {
        var parts = rule.split(':');

        var keys  = _.initial(parts);
        var key   = keys[0];
        var group = keys[1] || null;
        var mod   = _.last(parts);

        var option = caseOptions[key] = caseOptions[key] || {};
        var instruction = { modifier: mod, id: id };

        if(group) {
          option.group = option.group || {};
          option = option.group[group] = option.group[group] || [];
          option.label = key + ' ' + (+group + 1);
        } else {
          option.label = key;
        }

        option.instructions = option.instructions || [];
        option.instructions.push(instruction);
      });

      return caseOptions;
    };
  });
<|endoftext|>


<commit_before>import JSCookie from 'js-cookie';
import rison from 'rison';

const BrowserCookies = JSCookie.withConverter({
	read: (value, name) => value,
	write: (value, name) =>
		encodeURIComponent(value)
			.replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`),
});

/**
 * A module for middleware that would like to make external calls through `fetch`
 * @module fetchUtils
 */

export const CSRF_HEADER = 'x-csrf-jwt';
export const CSRF_HEADER_COOKIE = 'x-csrf-jwt-header';

export const parseQueryResponse = queries => ({ responses, error, message }) => {
	if (error) {
		throw new Error(JSON.stringify({ error, message }));  // treat like an API error
	}
	return {
		queries,
		responses: responses || [],
	};
};

/**
 * that the request will have the required OAuth and CSRF credentials and constructs
 * the `fetch` call arguments based on the request method. It also records the
 * CSRF header value in a cookie for use as a CSRF header in future fetches.
 *
 * @param {String} apiUrl the general-purpose endpoint for API calls to the
 *   application server
 * @param {Object} options {
 *     method: "get", "post", "delete", or "patch",
 *   }
 * @param {Array} queries the queries to send - must all use the same `method`
 * @param {Object} meta additional characteristics of the request, e.g. logout,
 *   click tracking data
 * @return {Object} { url, config } arguments for a fetch call
 */
export const getFetchArgs = (apiUrl, options, queries, meta) => {
	const {
		headers={},
	} = options;

	const method = (
		(queries[0].meta || {}).method ||
			options.method ||  // fallback to options
			'get'  // fallback to 'get'
	).toLowerCase();

	const isPost = method === 'post';
	const isDelete = method === 'delete';

	const fetchUrl = new URL(apiUrl);
	fetchUrl.searchParams.append('queries', rison.encode_array(queries));

	if (meta) {
		const {
			clickTracking,
			logout,
			...metadata
		} = meta;

		BrowserCookies.set(
			'click-track',
			JSON.stringify(clickTracking),
			{ domain: '.meetup.com' }
		);

		// special logout param
		if (logout) {
			fetchUrl.searchParams.append('logout', true);
		}

		// send other metadata in searchParams
		fetchUrl.searchParams.append('metadata', rison.encode_object(metadata));

	}
	const config = {
		method,
		headers: {
			...headers,
			'content-type': isPost ? 'application/x-www-form-urlencoded' : 'text/plain',
			[CSRF_HEADER]: (isPost || isDelete) ? BrowserCookies.get(CSRF_HEADER_COOKIE) : '',
		},
		credentials: 'same-origin'  // allow response to set-cookies
	};
	if (isPost) {
		config.body = fetchUrl.searchParams.toString();
	}

	const url = isPost ? apiUrl : fetchUrl.toString();
	return {
		url,
		config,
	};
};

/**
 * Wrapper around `fetch` to send an array of queries to the server and organize
 * the responses.
*
 * **IMPORTANT**: This function should _only_ be called from the browser. The
 * server should never need to call itself over HTTP
 *
 * @param {String} apiUrl the general-purpose endpoint for API calls to the
 *   application server
 * @param {Object} options {
 *     method: "get", "post", "delete", or "patch",
 *   }
 * @param {Array} queries the queries to send - must all use the same `method`
 * @param {Object} meta additional characteristics of the request, e.g. logout,
 *   click tracking data
 * @return {Promise} resolves with a `{queries, responses}` object
 */
export const fetchQueries = (apiUrl, options={}) => (queries, meta) => {
	if (
		typeof window === 'undefined' &&  // not in browser
		typeof test === 'undefined'  // not in testing env (global set by Jest)
	) {
		throw new Error('fetchQueries was called on server - cannot continue');
	}

	const {
		url,
		config,
	} = getFetchArgs(apiUrl, options, queries, meta);

	return fetch(url, config)
		.then(queryResponse => queryResponse.json())
		.then(queryJSON => ({
			...parseQueryResponse(queries)(queryJSON),
		}))
		.catch(err => {
			console.error(JSON.stringify({
				err: err.stack,
				message: 'App server API fetch error',
				context: config,
			}));
			throw err;  // handle the error upstream
		});
};

/**
 * Attempt to JSON parse a Response object from a fetch call
 *
 * @param {String} reqUrl the URL that was requested
 * @param {Response} response the fetch Response object
 * @return {Promise} a Promise that resolves with the JSON-parsed text
 */
export const tryJSON = reqUrl => response => {
	const { status, statusText } = response;
	if (status >= 400) {  // status always 200: bugzilla #52128
		return Promise.reject(
			new Error(`Request to ${reqUrl} responded with error code ${status}: ${statusText}`)
		);
	}
	return response.text().then(text => JSON.parse(text));
};

<commit_msg>don't send metadata if empty<commit_after>import JSCookie from 'js-cookie';
import rison from 'rison';

const BrowserCookies = JSCookie.withConverter({
	read: (value, name) => value,
	write: (value, name) =>
		encodeURIComponent(value)
			.replace(/[!'()*]/g, c => `%${c.charCodeAt(0).toString(16)}`),
});

/**
 * A module for middleware that would like to make external calls through `fetch`
 * @module fetchUtils
 */

export const CSRF_HEADER = 'x-csrf-jwt';
export const CSRF_HEADER_COOKIE = 'x-csrf-jwt-header';

export const parseQueryResponse = queries => ({ responses, error, message }) => {
	if (error) {
		throw new Error(JSON.stringify({ error, message }));  // treat like an API error
	}
	return {
		queries,
		responses: responses || [],
	};
};

/**
 * that the request will have the required OAuth and CSRF credentials and constructs
 * the `fetch` call arguments based on the request method. It also records the
 * CSRF header value in a cookie for use as a CSRF header in future fetches.
 *
 * @param {String} apiUrl the general-purpose endpoint for API calls to the
 *   application server
 * @param {Object} options {
 *     method: "get", "post", "delete", or "patch",
 *   }
 * @param {Array} queries the queries to send - must all use the same `method`
 * @param {Object} meta additional characteristics of the request, e.g. logout,
 *   click tracking data
 * @return {Object} { url, config } arguments for a fetch call
 */
export const getFetchArgs = (apiUrl, options, queries, meta) => {
	const {
		headers={},
	} = options;

	const method = (
		(queries[0].meta || {}).method ||
			options.method ||  // fallback to options
			'get'  // fallback to 'get'
	).toLowerCase();

	const isPost = method === 'post';
	const isDelete = method === 'delete';

	const fetchUrl = new URL(apiUrl);
	fetchUrl.searchParams.append('queries', rison.encode_array(queries));

	if (meta) {
		const {
			clickTracking,
			logout,
			...metadata
		} = meta;

		BrowserCookies.set(
			'click-track',
			JSON.stringify(clickTracking),
			{ domain: '.meetup.com' }
		);

		// special logout param
		if (logout) {
			fetchUrl.searchParams.append('logout', true);
		}

		if (Object.keys(metadata).length) {
			// send other metadata in searchParams
			fetchUrl.searchParams.append('metadata', rison.encode_object(metadata));
		}
	}
	const config = {
		method,
		headers: {
			...headers,
			'content-type': isPost ? 'application/x-www-form-urlencoded' : 'text/plain',
			[CSRF_HEADER]: (isPost || isDelete) ? BrowserCookies.get(CSRF_HEADER_COOKIE) : '',
		},
		credentials: 'same-origin'  // allow response to set-cookies
	};
	if (isPost) {
		config.body = fetchUrl.searchParams.toString();
	}

	const url = isPost ? apiUrl : fetchUrl.toString();
	return {
		url,
		config,
	};
};

/**
 * Wrapper around `fetch` to send an array of queries to the server and organize
 * the responses.
*
 * **IMPORTANT**: This function should _only_ be called from the browser. The
 * server should never need to call itself over HTTP
 *
 * @param {String} apiUrl the general-purpose endpoint for API calls to the
 *   application server
 * @param {Object} options {
 *     method: "get", "post", "delete", or "patch",
 *   }
 * @param {Array} queries the queries to send - must all use the same `method`
 * @param {Object} meta additional characteristics of the request, e.g. logout,
 *   click tracking data
 * @return {Promise} resolves with a `{queries, responses}` object
 */
export const fetchQueries = (apiUrl, options={}) => (queries, meta) => {
	if (
		typeof window === 'undefined' &&  // not in browser
		typeof test === 'undefined'  // not in testing env (global set by Jest)
	) {
		throw new Error('fetchQueries was called on server - cannot continue');
	}

	const {
		url,
		config,
	} = getFetchArgs(apiUrl, options, queries, meta);

	return fetch(url, config)
		.then(queryResponse => queryResponse.json())
		.then(queryJSON => ({
			...parseQueryResponse(queries)(queryJSON),
		}))
		.catch(err => {
			console.error(JSON.stringify({
				err: err.stack,
				message: 'App server API fetch error',
				context: config,
			}));
			throw err;  // handle the error upstream
		});
};

/**
 * Attempt to JSON parse a Response object from a fetch call
 *
 * @param {String} reqUrl the URL that was requested
 * @param {Response} response the fetch Response object
 * @return {Promise} a Promise that resolves with the JSON-parsed text
 */
export const tryJSON = reqUrl => response => {
	const { status, statusText } = response;
	if (status >= 400) {  // status always 200: bugzilla #52128
		return Promise.reject(
			new Error(`Request to ${reqUrl} responded with error code ${status}: ${statusText}`)
		);
	}
	return response.text().then(text => JSON.parse(text));
};

<|endoftext|>


<commit_before>#!/usr/bin/env node

var app = require('commander')
var pckg = require('./package.json')

app
  .version(pckg.version)
  .command('ls', 'List all of your tasks')
  .command('add [task]', 'Add a task to your inbox')
  .command('open', 'Open Wunderlist')
  .command('whoami', 'Display effective user')
  .command('flush', 'Flush the application cache')
  .parse(process.argv)
<commit_msg>renames variable<commit_after>#!/usr/bin/env node

var app = require('commander')
var pkg = require('./package.json')

app
  .version(pkg.version)
  .command('ls', 'List all of your tasks')
  .command('add [task]', 'Add a task to your inbox')
  .command('open', 'Open Wunderlist')
  .command('whoami', 'Display effective user')
  .command('flush', 'Flush the application cache')
  .parse(process.argv)
<|endoftext|>


<commit_before>'use strict';
var WebApp = null;
var WebAppClass = function() {

	// Platform settings:
	var isLogEnabled = true;
	// Platform API:
	this.isLogEnabled = function() {
		return isLogEnabled;
	};
	this.setLogEnabled = function(booleanState) {
		isLogEnabled = booleanState;
	};

	// Application settings:
	var isRunning = false;
	// Application API:
	this.isRunning = function() {
		return isRunning;
	};
	this.setRunning = function(booleanState) {
		if (booleanState) resume();
		else pause();
	};

	// Page settings:
	var pageList = null;
	var pageStack = null;
	var currentPage = null;
	var defaultPageId = null;
	// Page API:
	this.getDefaultPageId = function() {
		return defaultPageId;
	};
	this.setDefaultPageId = function(pageId) {
		defaultPageId = pageId;
	};

	// Animation/Transition settings:
	var styleRules = '\
		/* Animation Effects (based on jquery.mobile-1.4.5). */\
		@keyframes fadein {from {opacity: 0;} to {opacity: 1;}}\
		@keyframes fadeout {from {opacity: 1;} to {opacity: 0;}}\
		.fadein {animation-name: fadein; animation-duration: 225ms; opacity: 1;}\
		.fadeout {animation-name: fadeout; animation-duration: 125ms; opacity: 0;}\
		@keyframes popin {from {transform: scale(.8); opacity: 0;} to {transform: scale(1); opacity: 1;}}\
		@keyframes popout {from {transform: scale(1); opacity: 1;} to {transform: scale(.8); opacity: 0;}}\
		.popin {animation-name: popin; animation-duration: 225ms; animation-timing-function: ease-out; opacity: 1;}\
		.popout {animation-name: popout; animation-duration: 125ms; animation-timing-function: ease-in; opacity: 0;}\
		@keyframes flipin {from {transform: matrix(0.2,0.2,0,1,0,0); opacity: 0;} to {transform: matrix(1,0,0,1,0,0); opacity: 1;}}\
		@keyframes flipout {from {transform: matrix(1,0,0,1,0,0); opacity: 1;} to {transform: matrix(0.2,-0.2,0,1,0,0); opacity: 0;}}\
		@keyframes fliprevin {from {transform: matrix(0.2,-0.2,0,1,0,0); opacity: 0;} to {transform: matrix(1,0,0,1,0,0); opacity: 1;}}\
		@keyframes fliprevout {from {transform: matrix(1,0,0,1,0,0); opacity: 1;} to {transform: matrix(0.2,0.2,0,1,0,0); opacity: 0;}}\
		.flipin, .fliprevin {animation-duration: 225ms; animation-timing-function: ease-out;}\
		.flipout, .fliprevout {animation-duration: 125ms; animation-timing-function: ease-in;}\
		.flipin {animation-name: flipin; opacity: 1;}\
		.flipout {animation-name: flipout; opacity: 0;}\
		.fliprevin {animation-name: fliprevin; opacity: 1;}\
		.fliprevout {animation-name: fliprevout; opacity: 0;}\
		@keyframes slideinfromright {from {transform: translateX(100%);} to {transform: translateX(0);}}\
		@keyframes slideinfromleft {from {transform: translateX(-100%);} to {transform: translateX(0);}}\
		@keyframes slideouttoright {from {transform: translateX(0);} to {transform: translateX(100%);}}\
		@keyframes slideouttoleft {from {transform: translateX(0);} to {transform: translateX(-100%);}}\
		.slidein, .sliderevin {animation-duration: 225ms; animation-timing-function: ease-out;}\
		.slideout, .sliderevout {animation-duration: 125ms; animation-timing-function: ease-in;}\
		.slidein {animation-name: slideinfromright; transform: translateX(0);}\
		.slideout {animation-name: slideouttoleft; transform: translateX(-100%);}\
		.sliderevin {animation-name: slideinfromleft; transform: translateX(0);}\
		.sliderevout {animation-name: slideouttoright; transform: translateX(100%);}\
	';
	var animationTypes = ['fadein', 'fadeout', 'popin', 'popout', 'flipin', 'flipout', 'fliprevin', 'fliprevout', 'slidein', 'slideout', 'sliderevin', 'sliderevout'];
	var transitionTypes = ['none', 'fade', 'pop', 'flip', 'fliprev', 'fliporder', 'slide', 'sliderev', 'slideorder'];
	var defaultTransition = transitionTypes[1];
	var nextTransition = null;
	// Animation/Transition API:
	this.animateElement = function(element, animation, callback) {
		if (element && element.nodeType === 1 && animation && animationTypes.indexOf(animation) >= 0) animateElement(element, animation, callback);
	};
	this.getAnimationTypes = function() {
		return animationTypes;
	};
	this.getTransitionTypes = function() {
		return transitionTypes;
	};
	this.getDefaultTransition = function() {
		return defaultTransition;
	};
	this.setDefaultTransition = function(transitionType) {
		if (transitionType && transitionTypes.indexOf(transitionType) >= 0) defaultTransition = transitionType;
	};
	this.getNextTransition = function() {
		return nextTransition;
	};
	this.setNextTransition = function(transitionType) {
		if (transitionType && transitionTypes.indexOf(transitionType) >= 0) nextTransition = transitionType;
	};

	// Functions related to application life cycle: {

	this.load = function() {
		if (isLogEnabled) console.log('WebApp.js: load()');
		if (typeof WebApp['onLoad'] === 'function') WebApp.onLoad();

		// Setup page elements:
		if (!pageStack) {
			pageStack = {};
			Array.prototype.forEach.call(document.body.children, function (node) {
				if (node.classList.contains('page') && node.id) {
					node.styleDisplay = node.style.display;
					node.style.display = 'none';
					pageStack[node.id] = node;
					if (typeof node['onLoad'] === 'function') node.onLoad();
				}
			});
			pageList = Object.keys(pageStack);
			if (!defaultPageId || (pageList.indexOf(defaultPageId) < 0)) defaultPageId = pageList[0];

			// Setup CSS style effects:
			var style = document.createElement('style');
			style.type = 'text/css';
			style.innerHTML = styleRules;
			document.getElementsByTagName('head')[0].appendChild(style);

			// Setup application life cycle callbacks:
			window.onblur = function() {pause();};
			window.onfocus = function() {resume();};
			window.onkeydown = function(arg) {keyDown(arg);};
			window.onresize = function() {resize();};
			window.onunload = function() {unload();};
			resume(); // Required to dispatch initial onResume event.

			// Setup page change listener:
			window.addEventListener('hashchange', updatePage);
			updatePage({'type': 'hashchange', 'newURL': null, 'oldURL': null}); // Required to set initial page.
		}
	};

	function unload() {
		if (isLogEnabled) console.log('WebApp.js: unload()');
		if (typeof WebApp['onUnload'] === 'function') WebApp.onUnload();
		if (isRunning) pause();
		currentPage = null;
	}

	function pause() {
		if (isLogEnabled) console.log('WebApp.js: pause()');
		if (isRunning) {
			isRunning = false;
			if (typeof WebApp['onPause'] === 'function') WebApp.onPause();
		}
	}

	function resume() {
		if (isLogEnabled) console.log('WebApp.js: resume()');
		if (!isRunning) {
			isRunning = true;
			if (typeof WebApp['onResume'] === 'function') WebApp.onResume();
		}
	}

	// }
	// Functions related to application settings: {

	function resize() {
		if (isLogEnabled) console.log('WebApp.js: resize()');
		if (typeof WebApp['onResize'] === 'function') WebApp.onResize();
	}

	// }
	// Functions related to internal actions: {

	function updatePage(hashChangeEvent) {
		if (isLogEnabled) console.log('WebApp.js: updatePage(hashChangeEvent)... newURL = ' + hashChangeEvent.newURL + ', oldURL = ' + hashChangeEvent.oldURL);

		// Parse URL data:
		var nextSearch = (window.location.hash.indexOf('?') >= 0)? window.location.hash.substring(window.location.hash.indexOf('?') + 1): null;
		var nextHash = (window.location.hash.length > 1)? window.location.hash.substring(1, (nextSearch? window.location.hash.indexOf('?'): window.location.hash.length)): null;
		var nextPage = (nextHash && pageStack[nextHash])? pageStack[nextHash]: null;

		// Parse URL parameters:
		var nextParams = {};
		if (nextSearch) {
			var indexOfEqual = null;
			nextSearch = nextSearch.split('&');
			nextSearch.forEach(function(param) {
				indexOfEqual = param.indexOf('=');
				if (indexOfEqual > 0) {
					nextParams[param.slice(0, indexOfEqual)] = param.slice(indexOfEqual + 1, param.length);
				}
			});
		}

		// Execute update action:
		if (nextPage) {
			if (nextPage === currentPage) {
				// TODO: implement URL parameters updated engine.
			} else {
				switchPage(currentPage, nextPage);
				currentPage = nextPage;
			}
		} else if (currentPage) window.history.back();
		else window.location.replace(window.location.protocol + '//' + window.location.pathname + '#' + defaultPageId);
	}

	function switchPage(current, next) {
		if (!nextTransition) nextTransition = defaultTransition;
		if (nextTransition === 'fliporder') {
			nextTransition = (current && pageList.indexOf(current.id) > pageList.indexOf(next.id))? 'fliprev': 'flip';
		} else if (nextTransition === 'slideorder') {
			nextTransition = (current && pageList.indexOf(current.id) > pageList.indexOf(next.id))? 'sliderev': 'slide';
		}

		if (nextTransition === 'none') {
			if (current) hidePage(current);
			showPage(next);
			nextTransition = null;
		} else {
			var showNext = function() {
				animateElement(next, nextTransition + 'in', null);
				showPage(next);
				nextTransition = null;
			};
			if (current) {
				animateElement(current, nextTransition + 'out', function() {
					hidePage(current);
					showNext();
				});
			} else showNext();
		}
	}

	function animateElement(element, animation, callback) {
		var animationRunning = true;
		var animationEnded = function() {
			animationRunning = false;
			element.removeEventListener('animationend', animationEnded);
			element.className = element.className.replace(new RegExp('(?:^|\\s)' + animation + '(?!\\S)', 'g'), '');
			if (typeof callback === 'function') callback();
		};
		setTimeout(function() {if (animationRunning) animationEnded();}, 1000);
		element.addEventListener('animationend', animationEnded);
		element.className += ' ' + animation;
	}

	function showPage(page) {
		page.style.display = page['styleDisplay'] || 'block';
		if (typeof page['onShow'] === 'function') page.onShow();
	}

	function hidePage(page) {
		if (typeof page['onHide'] === 'function') page.onHide();
		page.style.display = 'none';
	}

	// }
	// Functions related to user interaction: {

	function keyDown(keyEvent) {
		if (isLogEnabled) console.log('WebApp.js: keyDown(' + keyEvent.keyCode + ')');
		if (keyEvent.keyCode === 13) {
			if (keyEvent.target['onclick']) keyEvent.target.onclick();
		} else {
			if (typeof currentPage['onKeyDown'] === 'function') currentPage.onKeyDown(keyEvent);
		}
	}

	// }
};
WebApp = new WebAppClass();
document.addEventListener('DOMContentLoaded', function() {WebApp.load();});
<commit_msg>Code review: rename slideinfromright, slideinfromleft, slideouttoright, and slideouttoleft.<commit_after>'use strict';
var WebApp = null;
var WebAppClass = function() {

	// Platform settings:
	var isLogEnabled = true;
	// Platform API:
	this.isLogEnabled = function() {
		return isLogEnabled;
	};
	this.setLogEnabled = function(booleanState) {
		isLogEnabled = booleanState;
	};

	// Application settings:
	var isRunning = false;
	// Application API:
	this.isRunning = function() {
		return isRunning;
	};
	this.setRunning = function(booleanState) {
		if (booleanState) resume();
		else pause();
	};

	// Page settings:
	var pageList = null;
	var pageStack = null;
	var currentPage = null;
	var defaultPageId = null;
	// Page API:
	this.getDefaultPageId = function() {
		return defaultPageId;
	};
	this.setDefaultPageId = function(pageId) {
		defaultPageId = pageId;
	};

	// Animation/Transition settings:
	var styleRules = '\
		/* Animation Effects (based on jquery.mobile-1.4.5). */\
		@keyframes fadein {from {opacity: 0;} to {opacity: 1;}}\
		@keyframes fadeout {from {opacity: 1;} to {opacity: 0;}}\
		.fadein {animation-name: fadein; animation-duration: 225ms; opacity: 1;}\
		.fadeout {animation-name: fadeout; animation-duration: 125ms; opacity: 0;}\
		@keyframes popin {from {transform: scale(.8); opacity: 0;} to {transform: scale(1); opacity: 1;}}\
		@keyframes popout {from {transform: scale(1); opacity: 1;} to {transform: scale(.8); opacity: 0;}}\
		.popin {animation-name: popin; animation-duration: 225ms; animation-timing-function: ease-out; opacity: 1;}\
		.popout {animation-name: popout; animation-duration: 125ms; animation-timing-function: ease-in; opacity: 0;}\
		@keyframes flipin {from {transform: matrix(0.2,0.2,0,1,0,0); opacity: 0;} to {transform: matrix(1,0,0,1,0,0); opacity: 1;}}\
		@keyframes flipout {from {transform: matrix(1,0,0,1,0,0); opacity: 1;} to {transform: matrix(0.2,-0.2,0,1,0,0); opacity: 0;}}\
		@keyframes fliprevin {from {transform: matrix(0.2,-0.2,0,1,0,0); opacity: 0;} to {transform: matrix(1,0,0,1,0,0); opacity: 1;}}\
		@keyframes fliprevout {from {transform: matrix(1,0,0,1,0,0); opacity: 1;} to {transform: matrix(0.2,0.2,0,1,0,0); opacity: 0;}}\
		.flipin, .fliprevin {animation-duration: 225ms; animation-timing-function: ease-out;}\
		.flipout, .fliprevout {animation-duration: 125ms; animation-timing-function: ease-in;}\
		.flipin {animation-name: flipin; opacity: 1;}\
		.flipout {animation-name: flipout; opacity: 0;}\
		.fliprevin {animation-name: fliprevin; opacity: 1;}\
		.fliprevout {animation-name: fliprevout; opacity: 0;}\
		@keyframes slidein {from {transform: translateX(100%);} to {transform: translateX(0);}}\
		@keyframes sliderevin {from {transform: translateX(-100%);} to {transform: translateX(0);}}\
		@keyframes sliderevout {from {transform: translateX(0);} to {transform: translateX(100%);}}\
		@keyframes slideout {from {transform: translateX(0);} to {transform: translateX(-100%);}}\
		.slidein, .sliderevin {animation-duration: 225ms; animation-timing-function: ease-out;}\
		.slideout, .sliderevout {animation-duration: 125ms; animation-timing-function: ease-in;}\
		.slidein {animation-name: slidein; transform: translateX(0);}\
		.slideout {animation-name: slideout; transform: translateX(-100%);}\
		.sliderevin {animation-name: sliderevin; transform: translateX(0);}\
		.sliderevout {animation-name: sliderevout; transform: translateX(100%);}\
	';
	var animationTypes = ['fadein', 'fadeout', 'popin', 'popout', 'flipin', 'flipout', 'fliprevin', 'fliprevout', 'slidein', 'slideout', 'sliderevin', 'sliderevout'];
	var transitionTypes = ['none', 'fade', 'pop', 'flip', 'fliprev', 'fliporder', 'slide', 'sliderev', 'slideorder'];
	var defaultTransition = transitionTypes[1];
	var nextTransition = null;
	// Animation/Transition API:
	this.animateElement = function(element, animation, callback) {
		if (element && element.nodeType === 1 && animation && animationTypes.indexOf(animation) >= 0) animateElement(element, animation, callback);
	};
	this.getAnimationTypes = function() {
		return animationTypes;
	};
	this.getTransitionTypes = function() {
		return transitionTypes;
	};
	this.getDefaultTransition = function() {
		return defaultTransition;
	};
	this.setDefaultTransition = function(transitionType) {
		if (transitionType && transitionTypes.indexOf(transitionType) >= 0) defaultTransition = transitionType;
	};
	this.getNextTransition = function() {
		return nextTransition;
	};
	this.setNextTransition = function(transitionType) {
		if (transitionType && transitionTypes.indexOf(transitionType) >= 0) nextTransition = transitionType;
	};

	// Functions related to application life cycle: {

	this.load = function() {
		if (isLogEnabled) console.log('WebApp.js: load()');
		if (typeof WebApp['onLoad'] === 'function') WebApp.onLoad();

		// Setup page elements:
		if (!pageStack) {
			pageStack = {};
			Array.prototype.forEach.call(document.body.children, function (node) {
				if (node.classList.contains('page') && node.id) {
					node.styleDisplay = node.style.display;
					node.style.display = 'none';
					pageStack[node.id] = node;
					if (typeof node['onLoad'] === 'function') node.onLoad();
				}
			});
			pageList = Object.keys(pageStack);
			if (!defaultPageId || (pageList.indexOf(defaultPageId) < 0)) defaultPageId = pageList[0];

			// Setup CSS style effects:
			var style = document.createElement('style');
			style.type = 'text/css';
			style.innerHTML = styleRules;
			document.getElementsByTagName('head')[0].appendChild(style);

			// Setup application life cycle callbacks:
			window.onblur = function() {pause();};
			window.onfocus = function() {resume();};
			window.onkeydown = function(arg) {keyDown(arg);};
			window.onresize = function() {resize();};
			window.onunload = function() {unload();};
			resume(); // Required to dispatch initial onResume event.

			// Setup page change listener:
			window.addEventListener('hashchange', updatePage);
			updatePage({'type': 'hashchange', 'newURL': null, 'oldURL': null}); // Required to set initial page.
		}
	};

	function unload() {
		if (isLogEnabled) console.log('WebApp.js: unload()');
		if (typeof WebApp['onUnload'] === 'function') WebApp.onUnload();
		if (isRunning) pause();
		currentPage = null;
	}

	function pause() {
		if (isLogEnabled) console.log('WebApp.js: pause()');
		if (isRunning) {
			isRunning = false;
			if (typeof WebApp['onPause'] === 'function') WebApp.onPause();
		}
	}

	function resume() {
		if (isLogEnabled) console.log('WebApp.js: resume()');
		if (!isRunning) {
			isRunning = true;
			if (typeof WebApp['onResume'] === 'function') WebApp.onResume();
		}
	}

	// }
	// Functions related to application settings: {

	function resize() {
		if (isLogEnabled) console.log('WebApp.js: resize()');
		if (typeof WebApp['onResize'] === 'function') WebApp.onResize();
	}

	// }
	// Functions related to internal actions: {

	function updatePage(hashChangeEvent) {
		if (isLogEnabled) console.log('WebApp.js: updatePage(hashChangeEvent)... newURL = ' + hashChangeEvent.newURL + ', oldURL = ' + hashChangeEvent.oldURL);

		// Parse URL data:
		var nextSearch = (window.location.hash.indexOf('?') >= 0)? window.location.hash.substring(window.location.hash.indexOf('?') + 1): null;
		var nextHash = (window.location.hash.length > 1)? window.location.hash.substring(1, (nextSearch? window.location.hash.indexOf('?'): window.location.hash.length)): null;
		var nextPage = (nextHash && pageStack[nextHash])? pageStack[nextHash]: null;

		// Parse URL parameters:
		var nextParams = {};
		if (nextSearch) {
			var indexOfEqual = null;
			nextSearch = nextSearch.split('&');
			nextSearch.forEach(function(param) {
				indexOfEqual = param.indexOf('=');
				if (indexOfEqual > 0) {
					nextParams[param.slice(0, indexOfEqual)] = param.slice(indexOfEqual + 1, param.length);
				}
			});
		}

		// Execute update action:
		if (nextPage) {
			if (nextPage === currentPage) {
				// TODO: implement URL parameters updated engine.
			} else {
				switchPage(currentPage, nextPage);
				currentPage = nextPage;
			}
		} else if (currentPage) window.history.back();
		else window.location.replace(window.location.protocol + '//' + window.location.pathname + '#' + defaultPageId);
	}

	function switchPage(current, next) {
		if (!nextTransition) nextTransition = defaultTransition;
		if (nextTransition === 'fliporder') {
			nextTransition = (current && pageList.indexOf(current.id) > pageList.indexOf(next.id))? 'fliprev': 'flip';
		} else if (nextTransition === 'slideorder') {
			nextTransition = (current && pageList.indexOf(current.id) > pageList.indexOf(next.id))? 'sliderev': 'slide';
		}

		if (nextTransition === 'none') {
			if (current) hidePage(current);
			showPage(next);
			nextTransition = null;
		} else {
			var showNext = function() {
				animateElement(next, nextTransition + 'in', null);
				showPage(next);
				nextTransition = null;
			};
			if (current) {
				animateElement(current, nextTransition + 'out', function() {
					hidePage(current);
					showNext();
				});
			} else showNext();
		}
	}

	function animateElement(element, animation, callback) {
		var animationRunning = true;
		var animationEnded = function() {
			animationRunning = false;
			element.removeEventListener('animationend', animationEnded);
			element.className = element.className.replace(new RegExp('(?:^|\\s)' + animation + '(?!\\S)', 'g'), '');
			if (typeof callback === 'function') callback();
		};
		setTimeout(function() {if (animationRunning) animationEnded();}, 1000);
		element.addEventListener('animationend', animationEnded);
		element.className += ' ' + animation;
	}

	function showPage(page) {
		page.style.display = page['styleDisplay'] || 'block';
		if (typeof page['onShow'] === 'function') page.onShow();
	}

	function hidePage(page) {
		if (typeof page['onHide'] === 'function') page.onHide();
		page.style.display = 'none';
	}

	// }
	// Functions related to user interaction: {

	function keyDown(keyEvent) {
		if (isLogEnabled) console.log('WebApp.js: keyDown(' + keyEvent.keyCode + ')');
		if (keyEvent.keyCode === 13) {
			if (keyEvent.target['onclick']) keyEvent.target.onclick();
		} else {
			if (typeof currentPage['onKeyDown'] === 'function') currentPage.onKeyDown(keyEvent);
		}
	}

	// }
};
WebApp = new WebAppClass();
document.addEventListener('DOMContentLoaded', function() {WebApp.load();});
<|endoftext|>


<commit_before>/* @flow */

import { inBrowser } from './dom'
import { saveScrollPosition } from './scroll'
import { genStateKey, setStateKey, getStateKey } from './state-key'

export const supportsPushState =
  inBrowser &&
  (function () {
    const ua = window.navigator.userAgent

    if (
      (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
      ua.indexOf('Mobile Safari') !== -1 &&
      ua.indexOf('Chrome') === -1 &&
      ua.indexOf('Windows Phone') === -1
    ) {
      return false
    }

    return window.history && 'pushState' in window.history
  })()

export function pushState (url?: string, replace?: boolean) {
  saveScrollPosition()
  // try...catch the pushState call to get around Safari
  // DOM Exception 18 where it limits to 100 pushState calls
  const history = window.history
  try {
    if (replace) {
      history.replaceState({ key: getStateKey() }, '', url)
    } else {
      history.pushState({ key: setStateKey(genStateKey()) }, '', url)
    }
  } catch (e) {
    window.location[replace ? 'replace' : 'assign'](url)
  }
}

export function replaceState (url?: string) {
  pushState(url, true)
}
<commit_msg>feat(history): preserve existing history.state<commit_after>/* @flow */

import { inBrowser } from './dom'
import { saveScrollPosition } from './scroll'
import { genStateKey, setStateKey, getStateKey } from './state-key'
import { extend } from './misc'

export const supportsPushState =
  inBrowser &&
  (function () {
    const ua = window.navigator.userAgent

    if (
      (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
      ua.indexOf('Mobile Safari') !== -1 &&
      ua.indexOf('Chrome') === -1 &&
      ua.indexOf('Windows Phone') === -1
    ) {
      return false
    }

    return window.history && 'pushState' in window.history
  })()

export function pushState (url?: string, replace?: boolean) {
  saveScrollPosition()
  // try...catch the pushState call to get around Safari
  // DOM Exception 18 where it limits to 100 pushState calls
  const history = window.history
  try {
    if (replace) {
      // preserve existing history state as it could be overriden by the user
      const stateCopy = extend({}, history.state)
      stateCopy.key = getStateKey()
      history.replaceState(stateCopy, '', url)
    } else {
      history.pushState({ key: setStateKey(genStateKey()) }, '', url)
    }
  } catch (e) {
    window.location[replace ? 'replace' : 'assign'](url)
  }
}

export function replaceState (url?: string) {
  pushState(url, true)
}
<|endoftext|>


<commit_before>'use strict';

(function(){
  var svgDirectives = {};

  angular.forEach([
      'clipPath',
      'colorProfile',
      'src',
      'cursor',
      'fill',
      'filter',
      'marker',
      'markerStart',
      'markerMid',
      'markerEnd',
      'mask',
      'stroke'
    ],
    function(attr) {
      svgDirectives[attr] = [
          '$rootScope',
          '$location',
          '$interpolate',
          '$sniffer',
          'urlResolve',
          'computeSVGAttrValue',
          'svgAttrExpressions',
          function(
              $rootScope,
              $location,
              $interpolate,
              $sniffer,
              urlResolve,
              computeSVGAttrValue,
              svgAttrExpressions) {
            return {
              restrict: 'A',
              link: function(scope, element, attrs) {
                var initialUrl;

                //Only apply to svg elements to avoid unnecessary observing
                //Check that is in html5Mode and that history is supported
                if ((!svgAttrExpressions.SVG_ELEMENT.test(element[0] &&
                    element[0].toString())) ||
                  !$location.$$html5 ||
                  !$sniffer.history) return;

                //Assumes no expressions, since svg is unforgiving of xml violations
                initialUrl = attrs[attr];
                attrs.$observe(attr, updateValue);
                $rootScope.$on('$locationChangeSuccess', updateValue);

                function updateValue () {
                  var newVal = computeSVGAttrValue(initialUrl);
                  //Prevent recursive updating
                  if (newVal && attrs[attr] !== newVal) attrs.$set(attr, newVal);
                }
              }
            };
          }];
  });

  angular.module('ngSVGAttributes', []).
    factory('urlResolve', [function() {
      //Duplicate of urlResolve & urlParsingNode in angular core
      var urlParsingNode = document.createElement('a');
      return function urlResolve(url) {
        urlParsingNode.setAttribute('href', url);
        return urlParsingNode;
      };
    }]).
    value('svgAttrExpressions', {
      FUNC_URI: /^url\((.*)\)$/,
      SVG_ELEMENT: /SVG[a-zA-Z]*Element/,
      HASH_PART: /#.*/
    }).
    factory('computeSVGAttrValue', [
                '$location', '$sniffer', 'svgAttrExpressions', 'urlResolve',
        function($location,   $sniffer,   svgAttrExpressions,   urlResolve) {
          return function computeSVGAttrValue(url) {
            var match, fullUrl;
            if (match = svgAttrExpressions.FUNC_URI.exec(url)) {
              //hash in html5Mode, forces to be relative to current url instead of base
              if (match[1].indexOf('#') === 0) {
                fullUrl = $location.absUrl().
                  replace(svgAttrExpressions.HASH_PART, '') +
                  match[1];
              }
              //Presumably links to external SVG document
              else {
                fullUrl = urlResolve(match[1]);
              }
            }
            return fullUrl ? 'url(' + fullUrl + ')' : null;
          };
        }
      ]
    ).
    directive(svgDirectives);
}());
<commit_msg>Improved FUNC_URI regex so that it works in IE<commit_after>'use strict';

(function(){
  var svgDirectives = {};

  angular.forEach([
      'clipPath',
      'colorProfile',
      'src',
      'cursor',
      'fill',
      'filter',
      'marker',
      'markerStart',
      'markerMid',
      'markerEnd',
      'mask',
      'stroke'
    ],
    function(attr) {
      svgDirectives[attr] = [
          '$rootScope',
          '$location',
          '$interpolate',
          '$sniffer',
          'urlResolve',
          'computeSVGAttrValue',
          'svgAttrExpressions',
          function(
              $rootScope,
              $location,
              $interpolate,
              $sniffer,
              urlResolve,
              computeSVGAttrValue,
              svgAttrExpressions) {
            return {
              restrict: 'A',
              link: function(scope, element, attrs) {
                var initialUrl;

                //Only apply to svg elements to avoid unnecessary observing
                //Check that is in html5Mode and that history is supported
                if ((!svgAttrExpressions.SVG_ELEMENT.test(element[0] &&
                    element[0].toString())) ||
                  !$location.$$html5 ||
                  !$sniffer.history) return;

                //Assumes no expressions, since svg is unforgiving of xml violations
                initialUrl = attrs[attr];
                attrs.$observe(attr, updateValue);
                $rootScope.$on('$locationChangeSuccess', updateValue);

                function updateValue () {
                  var newVal = computeSVGAttrValue(initialUrl);
                  //Prevent recursive updating
                  if (newVal && attrs[attr] !== newVal) attrs.$set(attr, newVal);
                }
              }
            };
          }];
  });

  angular.module('ngSVGAttributes', []).
    factory('urlResolve', [function() {
      //Duplicate of urlResolve & urlParsingNode in angular core
      var urlParsingNode = document.createElement('a');
      return function urlResolve(url) {
        urlParsingNode.setAttribute('href', url);
        return urlParsingNode;
      };
    }]).
    value('svgAttrExpressions', {
      FUNC_URI: /^url\(["']*(.*?)["']*\)$/,
      SVG_ELEMENT: /SVG[a-zA-Z]*Element/,
      HASH_PART: /#.*/
    }).
    factory('computeSVGAttrValue', [
                '$location', '$sniffer', 'svgAttrExpressions', 'urlResolve',
        function($location,   $sniffer,   svgAttrExpressions,   urlResolve) {
          return function computeSVGAttrValue(url) {
            var match, fullUrl;
            if (match = svgAttrExpressions.FUNC_URI.exec(url)) {
              //hash in html5Mode, forces to be relative to current url instead of base
              if (match[1].indexOf('#') === 0) {
                fullUrl = $location.absUrl().
                  replace(svgAttrExpressions.HASH_PART, '') +
                  match[1];
              }
              //Presumably links to external SVG document
              else {
                fullUrl = urlResolve(match[1]);
              }
            }
            return fullUrl ? 'url(' + fullUrl + ')' : null;
          };
        }
      ]
    ).
    directive(svgDirectives);
}());
<|endoftext|>


<commit_before>export default [
  require('src/extensions/custom_extension/index.js').default,
  require('src/extensions/payment-cash-on-delivery/index.js').default,
  require('src/extensions/payment-backend-methods/index.js').default,
  require('src/extensions/mailchimp-subscribe/index.js').default,
  require('src/extensions/google-analytics/index.js').default,

  require('vue-storefront-stripe/index.js').default
]
<commit_msg>Simplified import paths of extensions <commit_after>export default [
  require('src/extensions/custom_extension'),
  require('src/extensions/payment-cash-on-delivery'),
  require('src/extensions/payment-backend-methods'),
  require('src/extensions/mailchimp-subscribe'),
  require('src/extensions/google-analytics'),
  require('vue-storefront-stripe')
]
<|endoftext|>