<commit_before>define(['./util', './vars', './common'], function(util, vars, common) {

    var AUX_RADIUS = 9;
    var LABEL_PAD = 7;
    var STROKE = 1;
    var CLOUD_PATH = 'M 25.731189,3.8741489 C 21.525742,3.8741489 18.07553,7.4486396 17.497605,12.06118 C 16.385384,10.910965 14.996889,10.217536 13.45908,10.217535 C 9.8781481,10.217535 6.9473481,13.959873 6.9473482,18.560807 C 6.9473482,19.228828 7.0507906,19.875499 7.166493,20.498196 C 3.850265,21.890233 1.5000346,25.3185 1.5000346,29.310191 C 1.5000346,34.243794 5.1009986,38.27659 9.6710049,38.715902 C 9.6186538,39.029349 9.6083922,39.33212 9.6083922,39.653348 C 9.6083922,45.134228 17.378069,49.59028 26.983444,49.590279 C 36.58882,49.590279 44.389805,45.134229 44.389803,39.653348 C 44.389803,39.35324 44.341646,39.071755 44.295883,38.778399 C 44.369863,38.780301 44.440617,38.778399 44.515029,38.778399 C 49.470875,38.778399 53.499966,34.536825 53.499965,29.310191 C 53.499965,24.377592 49.928977,20.313927 45.360301,19.873232 C 45.432415,19.39158 45.485527,18.91118 45.485527,18.404567 C 45.485527,13.821862 42.394553,10.092543 38.598118,10.092543 C 36.825927,10.092543 35.215888,10.918252 33.996078,12.248669 C 33.491655,7.5434856 29.994502,3.8741489 25.731189,3.8741489 z';
    var CLOUD_WIDTH = 55;
    var ARROWHEAD_RADIUS = 8;

    var errors = common.errors;

    /// d_name converts a string into the format the user expects to
    /// see on a diagram.
    var d_name = function(s) {
        return s.replace('\\n', ' ').replace('_', ' '); // FIXME(bp) re-add newline
    }

    /// e_name converts a string into the format used internally in
    /// the engine and drawing code with underscores.
    var e_name = function(s) {
        return s.replace('\\n', '_').replace(' ', '_');
    }

    var hasKey = function(o, k) {
        return o.hasOwnProperty(k);
    }

    var isZero = function(n) {
        return Math.abs(n) < .0000001;
    }

    var sq = function(n) {
        return Math.pow(n, 2);
    }

    var pt = function(x, y) {
        return {'x': x, 'y': y};
    }

    var path_for_circle = function(x, y, r) {
        return 'M' + x + ',' + (y-r) + 'A' + r + ',' + r + ' 0 1,1,' + (x-0.1) + ',' + (y-r) + 'z';
    }

    var cloud_at = function(paper, x, y) {
        const scale = (AUX_RADIUS*2/CLOUD_WIDTH);
        const t = 'T' + (x - AUX_RADIUS/scale) + ',' + (y - AUX_RADIUS/scale) + 'S' + scale + ',' + scale;
        return paper.path(CLOUD_PATH).attr({
            'fill': '#ffffff',
            'stroke':'#6388dc',
            'stroke-width': 2,
            'stroke-linejoin': 'round',
            'stroke-miterlimit': 4,
        }).transform('matrix(' + scale + ', 0, 0, ' + scale + ', ' + (x - AUX_RADIUS) + ', ' + (y - AUX_RADIUS) + ')');
    }

    var circle_from_points = function(p1, p2, p3) {
        var off = sq(p2.x) + sq(p2.y);
        var bc = (sq(p1.x) + sq(p1.y) - off)/2;
        var cd = (off - sq(p3.x) - sq(p3.y))/2;
        var det = (p1.x - p2.x)*(p2.y - p3.y) - (p2.x - p3.x)*(p1.y - p2.y);
        if (is_zero(det)) {
            console.log('blerg');
            return;
        }
        var idet = 1/det;
        var cx = (bc*(p2.y - p3.y) - cd*(p1.y - p2.y))*idet;
        var cy = (cd*(p1.x - p2.x) - bc*(p2.x - p3.x))*idet;
        return {
            'x': cx,
            'y': cy,
            'r': Math.sqrt(Math.pow(p2.x - cx, 2) + Math.pow(p2.y - cy, 2)),
        };
    }

    var label_bottom = function label_bottom(paper, x, y, text) {
        text = d_name(text);
        const label = paper.text(x, y, text.split('\n')).attr({
            'font-size': '12px',
            'font-family': 'Source Sans Pro',
            'font-weight': '300',
            'text-anchor': 'middle',
            'white-space': 'nowrap',
        });
        const spans = $(label.node).children('tspan');
        var i;
        for (i = 0; i < spans.length; i++) {
            // TODO(bp) newlines
        }
        label.attr({y: y + .5*label.getBBox().height});
        return label;
    }

    var DStock = function (drawing, element) {
        this.drawing = drawing;
        this.e = element;
        this.je = $(element);
        this.name = this.je.attr('name');
    }
    DStock.prototype.init = function() {
        // we are a stock, and need to inform all the flows into and
        // out of us that they, in fact, flow in and out of us.

        var mEnt = this.drawing.model.vars[this.name];

        var i, n, dEnt;
        for (i=0; i < mEnt.inflows.length; i++) {
            n = mEnt.inflows[i];
            dEnt = this.drawing.named_ents[n];
            if (!dEnt) {
                console.log('failed connecting ' + this.name + ' .to ' + n);
                continue;
            }
            dEnt.to = this.name;
        }
        for (i=0; i < mEnt.outflows.length; i++) {
            n = mEnt.outflows[i];
            dEnt = this.drawing.named_ents[n];
            if (!dEnt) {
                console.log('failed connecting ' + this.name + ' .from ' + n);
                continue;
            }
            this.drawing.named_ents[n].from = this.name;
        }
    }
    DStock.prototype.draw = function() {
        var paper = this.drawing.paper;
        var je = this.je;
        var cx = parseFloat(je.attr('x'));
        var cy = parseFloat(je.attr('y'));
        var w = 45;
        var h = 35;

        // FIXME: the ceil calls are for Stella Modeler compatability.
        this.set = paper.group([
            paper.rect(Math.ceil(cx - w/2), Math.ceil(cy - h/2), w, h).attr({
                'fill': 'white',
                'stroke-width': STROKE,
                'stroke': 'black',
            }),
            label_bottom(paper, cx, cy + h/2 + LABEL_PAD, je.attr('name')),
        ]);
    }

    var DTypes = {
        'STOCK': DStock,
    };

    /**
       Drawing represents a stock-and-flow diagram of a SD model.
    */
    var Drawing = function Drawing(model, svgElementID) {
        common.err = null;

        var view = $(model.xmile.find('view')[0]);

        this.model = model;
        this.paper = Snap(svgElementID);

        var zoom = parseFloat(view.attr('zoom'))/100.;
        $(svgElementID).get()[0].setAttribute('preserveAspectRatio', 'xMinYMin');
        $(svgElementID).get()[0].setAttribute('viewBox',
                                              [view.attr('scroll_x')/zoom,
                                               view.attr('scroll_y')/zoom,
                                               1024/zoom,
                                               768/zoom].join(' '));

        var elems = $(view.children());

        this.d_ents = [];
        this.named_ents = {};

        var i, j, e, de;

        // create a drawing entity for each known tag in the display
        for (i = 0; i < elems.length; i++) {
            e = elems[i];
            if (!hasKey(DTypes, e.tagName)) {
                // FIXME(bp) log unknown ents
                continue;
            }
            de = new DTypes[e.tagName](this, e);
            this.d_ents.push(de);
            if (de.name)
                this.named_ents[de.name] = de;
        }

        // all draw ents need to be constructed and read in before we
        // can initialize them
        for (i = 0; i < this.d_ents.length; i++)
            this.d_ents[i].init();

        // TODO(bp) sort by draw order
        for (i = 0; i < this.d_ents.length; i++)
            this.d_ents[i].draw();

        return;
    }

    return {
        'Drawing': Drawing,
    };
});
<commit_msg>draw: use eName and dName for stock<commit_after>define(['./util', './vars', './common'], function(util, vars, common) {

    var AUX_RADIUS = 9;
    var LABEL_PAD = 7;
    var STROKE = 1;
    var CLOUD_PATH = 'M 25.731189,3.8741489 C 21.525742,3.8741489 18.07553,7.4486396 17.497605,12.06118 C 16.385384,10.910965 14.996889,10.217536 13.45908,10.217535 C 9.8781481,10.217535 6.9473481,13.959873 6.9473482,18.560807 C 6.9473482,19.228828 7.0507906,19.875499 7.166493,20.498196 C 3.850265,21.890233 1.5000346,25.3185 1.5000346,29.310191 C 1.5000346,34.243794 5.1009986,38.27659 9.6710049,38.715902 C 9.6186538,39.029349 9.6083922,39.33212 9.6083922,39.653348 C 9.6083922,45.134228 17.378069,49.59028 26.983444,49.590279 C 36.58882,49.590279 44.389805,45.134229 44.389803,39.653348 C 44.389803,39.35324 44.341646,39.071755 44.295883,38.778399 C 44.369863,38.780301 44.440617,38.778399 44.515029,38.778399 C 49.470875,38.778399 53.499966,34.536825 53.499965,29.310191 C 53.499965,24.377592 49.928977,20.313927 45.360301,19.873232 C 45.432415,19.39158 45.485527,18.91118 45.485527,18.404567 C 45.485527,13.821862 42.394553,10.092543 38.598118,10.092543 C 36.825927,10.092543 35.215888,10.918252 33.996078,12.248669 C 33.491655,7.5434856 29.994502,3.8741489 25.731189,3.8741489 z';
    var CLOUD_WIDTH = 55;
    var ARROWHEAD_RADIUS = 8;

    var errors = common.errors;

    /// dName converts a string into the format the user expects to
    /// see on a diagram.
    var dName = function(s) {
        return s.replace('\\n', ' ').replace('_', ' '); // FIXME(bp) re-add newline
    }

    /// eName converts a string into the format used internally in
    /// the engine and drawing code with underscores.
    var eName = function(s) {
        return s.replace('\\n', '_').replace(' ', '_').toLowerCase();
    }

    var hasKey = function(o, k) {
        return o.hasOwnProperty(k);
    }

    var isZero = function(n) {
        return Math.abs(n) < .0000001;
    }

    var sq = function(n) {
        return Math.pow(n, 2);
    }

    var pt = function(x, y) {
        return {'x': x, 'y': y};
    }

    var path_for_circle = function(x, y, r) {
        return 'M' + x + ',' + (y-r) + 'A' + r + ',' + r + ' 0 1,1,' + (x-0.1) + ',' + (y-r) + 'z';
    }

    var cloud_at = function(paper, x, y) {
        const scale = (AUX_RADIUS*2/CLOUD_WIDTH);
        const t = 'T' + (x - AUX_RADIUS/scale) + ',' + (y - AUX_RADIUS/scale) + 'S' + scale + ',' + scale;
        return paper.path(CLOUD_PATH).attr({
            'fill': '#ffffff',
            'stroke':'#6388dc',
            'stroke-width': 2,
            'stroke-linejoin': 'round',
            'stroke-miterlimit': 4,
        }).transform('matrix(' + scale + ', 0, 0, ' + scale + ', ' + (x - AUX_RADIUS) + ', ' + (y - AUX_RADIUS) + ')');
    }

    var circle_from_points = function(p1, p2, p3) {
        var off = sq(p2.x) + sq(p2.y);
        var bc = (sq(p1.x) + sq(p1.y) - off)/2;
        var cd = (off - sq(p3.x) - sq(p3.y))/2;
        var det = (p1.x - p2.x)*(p2.y - p3.y) - (p2.x - p3.x)*(p1.y - p2.y);
        if (is_zero(det)) {
            console.log('blerg');
            return;
        }
        var idet = 1/det;
        var cx = (bc*(p2.y - p3.y) - cd*(p1.y - p2.y))*idet;
        var cy = (cd*(p1.x - p2.x) - bc*(p2.x - p3.x))*idet;
        return {
            'x': cx,
            'y': cy,
            'r': Math.sqrt(Math.pow(p2.x - cx, 2) + Math.pow(p2.y - cy, 2)),
        };
    }

    var label_bottom = function label_bottom(paper, x, y, text) {
        const label = paper.text(x, y, text.split('\n')).attr({
            'font-size': '12px',
            'font-family': 'Source Sans Pro',
            'font-weight': '300',
            'text-anchor': 'middle',
            'white-space': 'nowrap',
        });
        const spans = $(label.node).children('tspan');
        var i;
        for (i = 0; i < spans.length; i++) {
            // TODO(bp) newlines
        }
        label.attr({y: y + .5*label.getBBox().height});
        return label;
    }

    var DStock = function (drawing, element) {
        this.drawing = drawing;
        this.e = element;
        this.je = $(element);
        this.name = eName(this.je.attr('name'));
        this.dName = dName(this.je.attr('name'));
    }
    DStock.prototype.init = function() {
        // we are a stock, and need to inform all the flows into and
        // out of us that they, in fact, flow in and out of us.

        var mEnt = this.drawing.model.vars[this.name];

        var i, n, dEnt;
        for (i=0; i < mEnt.inflows.length; i++) {
            n = mEnt.inflows[i];
            dEnt = this.drawing.named_ents[n];
            if (!dEnt) {
                console.log('failed connecting ' + this.name + ' .to ' + n);
                continue;
            }
            dEnt.to = this.name;
        }
        for (i=0; i < mEnt.outflows.length; i++) {
            n = mEnt.outflows[i];
            dEnt = this.drawing.named_ents[n];
            if (!dEnt) {
                console.log('failed connecting ' + this.name + ' .from ' + n);
                continue;
            }
            this.drawing.named_ents[n].from = this.name;
        }
    }
    DStock.prototype.draw = function() {
        var paper = this.drawing.paper;
        var je = this.je;
        var cx = parseFloat(je.attr('x'));
        var cy = parseFloat(je.attr('y'));
        var w = 45;
        var h = 35;

        // FIXME: the ceil calls are for Stella Modeler compatability.
        this.set = paper.group([
            paper.rect(Math.ceil(cx - w/2), Math.ceil(cy - h/2), w, h).attr({
                'fill': 'white',
                'stroke-width': STROKE,
                'stroke': 'black',
            }),
            label_bottom(paper, cx, cy + h/2 + LABEL_PAD, this.dName),
        ]);
    }

    var DTypes = {
        'STOCK': DStock,
    };

    /**
       Drawing represents a stock-and-flow diagram of a SD model.
    */
    var Drawing = function Drawing(model, svgElementID) {
        common.err = null;

        var view = $(model.xmile.find('view')[0]);

        this.model = model;
        this.paper = Snap(svgElementID);

        var zoom = parseFloat(view.attr('zoom'))/100.;
        $(svgElementID).get()[0].setAttribute('preserveAspectRatio', 'xMinYMin');
        $(svgElementID).get()[0].setAttribute('viewBox',
                                              [view.attr('scroll_x')/zoom,
                                               view.attr('scroll_y')/zoom,
                                               1024/zoom,
                                               768/zoom].join(' '));

        var elems = $(view.children());

        this.d_ents = [];
        this.named_ents = {};

        var i, j, e, de;

        // create a drawing entity for each known tag in the display
        for (i = 0; i < elems.length; i++) {
            e = elems[i];
            if (!hasKey(DTypes, e.tagName)) {
                // FIXME(bp) log unknown ents
                continue;
            }
            de = new DTypes[e.tagName](this, e);
            this.d_ents.push(de);
            if (de.name)
                this.named_ents[de.name] = de;
        }

        // all draw ents need to be constructed and read in before we
        // can initialize them
        for (i = 0; i < this.d_ents.length; i++)
            this.d_ents[i].init();

        // TODO(bp) sort by draw order
        for (i = 0; i < this.d_ents.length; i++)
            this.d_ents[i].draw();

        return;
    }

    return {
        'Drawing': Drawing,
    };
});
<|endoftext|>


<commit_before>$(function () {

  var duration = $('#duration');
  var animationDuration = initialDuration = duration.val();

  // The code in these are deliberately using some weird formatting.  The code
  // within gets used as a string.  Like magic!
  Tweenable.prototype.formula.customEase1 =
      function (x) {return Math.pow(x, 4)};

  Tweenable.prototype.formula.customEase2 =
      function (x) {return Math.pow(x, 0.25)};

  function getFormulaFromEasingFunc (fn) {
    var fnString = fn.toString(); // An f'n string
    var indexOfReturn = fnString.indexOf('return');
    var deprefixed = fnString.slice(indexOfReturn + 7);
    var desuffixed = deprefixed.replace(/\}$/, '');
    return desuffixed;
  }

  var PRERENDER_GRANULARITY = 100;
  function generatePathPoints (x1, y1, x2, y2, easeX, easeY) {
    var points = [];
    var from = {
        'x': x1
        ,'y': y1
      };
    var to = {
        'x': x2
        ,'y': y2
      };
    var easing = {
      'x': easeX
      ,'y': easeY
    };
    var i, point;
    for (i = 0; i <= PRERENDER_GRANULARITY; i++) {
      point = Tweenable.util.interpolate(
          from, to, (1 / PRERENDER_GRANULARITY) * i, easing);
      points.push(point);
    }

    return points;
  }

  var prerenderedPath;
  function generatePathPrerender (x1, y1, x2, y2, easeX, easeY) {
    prerenderedPath = document.createElement('canvas');
    prerenderedPath.width = kapi.canvas_width();
    prerenderedPath.height = kapi.canvas_height();
    var ctx = prerenderedPath.ctx = prerenderedPath.getContext('2d');
    var points = generatePathPoints.apply(this, arguments);

    var previousPoint;
    ctx.beginPath();
    _.each(points, function (point) {
      if (previousPoint) {
        ctx.lineTo(point.x, point.y);
      } else {
        ctx.moveTo(point.x, point.y);
      }

      previousPoint = point;
    });
    ctx.lineWidth = 1;
    ctx.strokeStyle = '#fa0';
    ctx.stroke();
    ctx.closePath();
  }

  var ease = $('.ease');
  ease.on('keyup', function (evt) {
    var el = $(evt.target);
    var val = el.val();
    var easename = el.data('easename');
    var lastValid = el.data('lastvalidfn');

    try {
      eval('Tweenable.prototype.formula.' + easename
          + ' = function (x) {return ' + val + '}');
      el.data('lastvalidfn', val);
      el.removeClass('error');
      kapi.canvas_clear();
      updatePath();
    } catch (ex) {
      eval('Tweenable.prototype.formula.' + easename
          + ' = function (x) {return ' + lastValid + '}');
      el.addClass('error');
    }
  });

  ease.each(function (i, el) {
    el = $(el);
    var easename = el.data('easename');
    var fnString = Tweenable.prototype.formula[easename];
    el.val(getFormulaFromEasingFunc(fnString));
    el.data('lastvalidfn', fnString);
  });

  function getCrosshairCoords (crosshair) {
    var pos = crosshair.position();
    return {
      x: pos.left + crosshair.width()/2
      ,y: pos.top + crosshair.height()/2
    };
  }

  function  moveLastKeyframe (actor, toMillisecond) {
    var trackNames = actor.getTrackNames();
    var lastFrameIndex = actor.getTrackLength(trackNames[0]) - 1;

    _.each(trackNames, function (trackName) {
      actor.modifyKeyframeProperty(trackName, lastFrameIndex, {
            'millisecond': toMillisecond
          });
    });

    actor.kapi.updateInternalState();
    animationDuration = toMillisecond;
  }

  var canvas = $('canvas')[0];
  var kapi = new Kapi(canvas, {
      'fps': 60
      ,'height': 400
      ,'width': 500
    })
    ,circle = new Kapi.Actor({
      'draw': function (canvas_context, state) {

        if (isPathShowing && prerenderedPath) {
          canvas_context.drawImage(prerenderedPath, 0, 0);
        }

        canvas_context.beginPath();
          canvas_context.arc(
            state.x || 0,
            state.y || 0,
            state.radius || 50,
            0,
            Math.PI*2,
            true);
          canvas_context.fillStyle = state.color || '#444';
          canvas_context.fill();
          canvas_context.closePath();
          return this;
        }
      });
  kapi.canvas_style('background', '#eee');

  var crosshairs = {
    'from': $('.crosshair.from')
    ,'to': $('.crosshair.to')
  };

  crosshairs.from.add(crosshairs.to).draggable({
    'containment': 'parent'
    ,'drag': handleDrag
    ,'stop': handleDragStop
  });

  function updatePath () {
    var fromCoords = getCrosshairCoords(crosshairs.from);
    var toCoords = getCrosshairCoords(crosshairs.to);
    generatePathPrerender(fromCoords.x, fromCoords.y, toCoords.x, toCoords.y,
        selects._from.val(), selects._to.val());
  }

  function handleDrag (evt, ui) {
    var target = $(evt.target);
    var pos = target.data('pos');
    var timeToModify = pos === 'from' ? 0 : animationDuration;
    circle.modifyKeyframe(timeToModify, getCrosshairCoords(crosshairs[pos]));
    kapi
      .canvas_clear()
      .redraw();
    updatePath();
  }

  function handleDragStop (evt, ui) {
    handleDrag.apply(this, arguments);
  }

  function initSelect (select) {
    _.each(Tweenable.prototype.formula, function (formula, name) {
      var option = $(document.createElement('option'), {
          'value': name
        });

      option.html(name);
      select.append(option);
    });
  }

  var selects = $('#tween-controls select');

  // TODO: This is reeeeeeeally sloppy, just attaching the selects to $ instance
  // itself. Clean this silliness up.
  selects._from = selects.filter('#x-easing');
  selects._to = selects.filter('#y-easing');

  selects.each(function (i, el) {
    initSelect($(el));
  });

  selects.on('change', function (evt) {
    var target = $(evt.target);
    var easingObj = {};
    easingObj[target.data('axis')] = target.val();
    circle.modifyKeyframe(animationDuration, {}, easingObj)
    updatePath();
    kapi
      .canvas_clear()
      .redraw();
  });

  var showPath = $('#show-path');
  var isPathShowing = true;
  showPath.on('change', function (evt) {
    var checked = showPath.attr('checked');
    isPathShowing = !!checked;
  });

  duration.on('keyup', function (evt) {
    var val = duration.val();
    var validVal = Math.abs(val);
    if (!isNaN(val)) {
      moveLastKeyframe(circle, validVal);
    }
  });

  duration.on('keydown', function (evt) {
    var augmentBy;
    var which = evt.which;

    if (which != 38 && which != 40) {
      return;
    }

    if (which == 38) { // up
      augmentBy = 10;
    } else if (which == 40) { // down
      augmentBy = -10;
    }

    duration.val(parseInt(animationDuration, 10) + augmentBy);
    duration.trigger('keyup');
  });

  kapi.addActor(circle);
  circle.keyframe(0, _.extend(getCrosshairCoords(crosshairs.from), {
      'color': '#777'
      ,'radius': 15
    }))
    .keyframe(initialDuration, _.extend(getCrosshairCoords(crosshairs.to), {
      'color': '#333'
    }));

  var controls = new RekapiScrubber(kapi);
  updatePath();
  kapi.play();

});
<commit_msg>Fixes a redraw bug on the easing app.<commit_after>$(function () {

  var duration = $('#duration');
  var animationDuration = initialDuration = duration.val();

  // The code in these are deliberately using some weird formatting.  The code
  // within gets used as a string.  Like magic!
  Tweenable.prototype.formula.customEase1 =
      function (x) {return Math.pow(x, 4)};

  Tweenable.prototype.formula.customEase2 =
      function (x) {return Math.pow(x, 0.25)};

  function getFormulaFromEasingFunc (fn) {
    var fnString = fn.toString(); // An f'n string
    var indexOfReturn = fnString.indexOf('return');
    var deprefixed = fnString.slice(indexOfReturn + 7);
    var desuffixed = deprefixed.replace(/\}$/, '');
    return desuffixed;
  }

  var PRERENDER_GRANULARITY = 100;
  function generatePathPoints (x1, y1, x2, y2, easeX, easeY) {
    var points = [];
    var from = {
        'x': x1
        ,'y': y1
      };
    var to = {
        'x': x2
        ,'y': y2
      };
    var easing = {
      'x': easeX
      ,'y': easeY
    };
    var i, point;
    for (i = 0; i <= PRERENDER_GRANULARITY; i++) {
      point = Tweenable.util.interpolate(
          from, to, (1 / PRERENDER_GRANULARITY) * i, easing);
      points.push(point);
    }

    return points;
  }

  var prerenderedPath;
  function generatePathPrerender (x1, y1, x2, y2, easeX, easeY) {
    prerenderedPath = document.createElement('canvas');
    prerenderedPath.width = kapi.canvas_width();
    prerenderedPath.height = kapi.canvas_height();
    var ctx = prerenderedPath.ctx = prerenderedPath.getContext('2d');
    var points = generatePathPoints.apply(this, arguments);

    var previousPoint;
    ctx.beginPath();
    _.each(points, function (point) {
      if (previousPoint) {
        ctx.lineTo(point.x, point.y);
      } else {
        ctx.moveTo(point.x, point.y);
      }

      previousPoint = point;
    });
    ctx.lineWidth = 1;
    ctx.strokeStyle = '#fa0';
    ctx.stroke();
    ctx.closePath();
  }

  var ease = $('.ease');
  ease.on('keyup', function (evt) {
    var el = $(evt.target);
    var val = el.val();
    var easename = el.data('easename');
    var lastValid = el.data('lastvalidfn');

    try {
      eval('Tweenable.prototype.formula.' + easename
          + ' = function (x) {return ' + val + '}');
      el.data('lastvalidfn', val);
      el.removeClass('error');
      kapi.canvas_clear();
      updatePath();
    } catch (ex) {
      eval('Tweenable.prototype.formula.' + easename
          + ' = function (x) {return ' + lastValid + '}');
      el.addClass('error');
    }
  });

  ease.each(function (i, el) {
    el = $(el);
    var easename = el.data('easename');
    var fnString = Tweenable.prototype.formula[easename];
    el.val(getFormulaFromEasingFunc(fnString));
    el.data('lastvalidfn', fnString);
  });

  function getCrosshairCoords (crosshair) {
    var pos = crosshair.position();
    return {
      x: pos.left + crosshair.width()/2
      ,y: pos.top + crosshair.height()/2
    };
  }

  function  moveLastKeyframe (actor, toMillisecond) {
    var trackNames = actor.getTrackNames();
    var lastFrameIndex = actor.getTrackLength(trackNames[0]) - 1;

    _.each(trackNames, function (trackName) {
      actor.modifyKeyframeProperty(trackName, lastFrameIndex, {
            'millisecond': toMillisecond
          });
    });

    actor.kapi.updateInternalState();
    animationDuration = toMillisecond;
  }

  var canvas = $('canvas')[0];
  var kapi = new Kapi(canvas, {
      'fps': 60
      ,'height': 400
      ,'width': 500
    })
    ,circle = new Kapi.Actor({
      'draw': function (canvas_context, state) {

        if (isPathShowing && prerenderedPath) {
          canvas_context.drawImage(prerenderedPath, 0, 0);
        }

        canvas_context.beginPath();
          canvas_context.arc(
            state.x || 0,
            state.y || 0,
            state.radius || 50,
            0,
            Math.PI*2,
            true);
          canvas_context.fillStyle = state.color || '#444';
          canvas_context.fill();
          canvas_context.closePath();
          return this;
        }
      });
  kapi.canvas_style('background', '#eee');

  var crosshairs = {
    'from': $('.crosshair.from')
    ,'to': $('.crosshair.to')
  };

  crosshairs.from.add(crosshairs.to).draggable({
    'containment': 'parent'
    ,'drag': handleDrag
    ,'stop': handleDragStop
  });

  function updatePath () {
    var fromCoords = getCrosshairCoords(crosshairs.from);
    var toCoords = getCrosshairCoords(crosshairs.to);
    generatePathPrerender(fromCoords.x, fromCoords.y, toCoords.x, toCoords.y,
        selects._from.val(), selects._to.val());
  }

  function handleDrag (evt, ui) {
    var target = $(evt.target);
    var pos = target.data('pos');
    var timeToModify = pos === 'from' ? 0 : animationDuration;
    circle.modifyKeyframe(timeToModify, getCrosshairCoords(crosshairs[pos]));
    kapi
      .canvas_clear()
      .redraw();
    updatePath();
  }

  function handleDragStop (evt, ui) {
    handleDrag.apply(this, arguments);
  }

  function initSelect (select) {
    _.each(Tweenable.prototype.formula, function (formula, name) {
      var option = $(document.createElement('option'), {
          'value': name
        });

      option.html(name);
      select.append(option);
    });
  }

  var selects = $('#tween-controls select');

  // TODO: This is reeeeeeeally sloppy, just attaching the selects to $ instance
  // itself. Clean this silliness up.
  selects._from = selects.filter('#x-easing');
  selects._to = selects.filter('#y-easing');

  selects.each(function (i, el) {
    initSelect($(el));
  });

  selects.on('change', function (evt) {
    var target = $(evt.target);
    var easingObj = {};
    easingObj[target.data('axis')] = target.val();
    circle.modifyKeyframe(animationDuration, {}, easingObj)
    updatePath();
    kapi
      .canvas_clear()
      .redraw();
  });

  var showPath = $('#show-path');
  var isPathShowing = true;
  showPath.on('change', function (evt) {
    var checked = showPath.attr('checked');
    isPathShowing = !!checked;
    kapi.redraw();
  });

  duration.on('keyup', function (evt) {
    var val = duration.val();
    var validVal = Math.abs(val);
    if (!isNaN(val)) {
      moveLastKeyframe(circle, validVal);
    }
  });

  duration.on('keydown', function (evt) {
    var augmentBy;
    var which = evt.which;

    if (which != 38 && which != 40) {
      return;
    }

    if (which == 38) { // up
      augmentBy = 10;
    } else if (which == 40) { // down
      augmentBy = -10;
    }

    duration.val(parseInt(animationDuration, 10) + augmentBy);
    duration.trigger('keyup');
  });

  kapi.addActor(circle);
  circle.keyframe(0, _.extend(getCrosshairCoords(crosshairs.from), {
      'color': '#777'
      ,'radius': 15
    }))
    .keyframe(initialDuration, _.extend(getCrosshairCoords(crosshairs.to), {
      'color': '#333'
    }));

  var controls = new RekapiScrubber(kapi);
  updatePath();
  kapi.play();

});
<|endoftext|>


<commit_before>//
// Eyes.js - a customizable value inspector for Node.js
//
//   usage:
//
//       var inspect = require('eyes').inspector({styles: {all: 'magenta'}});
//
//       inspect(something); // inspect with the settings passed to `inspector`
//
//     or
//
//       var eyes = require('eyes');
//
//       eyes.inspect(something); // inspect with the default settings
//
var eyes = {
    defaults: {
        styles: {                // Styles applied to stdout
            all:    'cyan',      // Overall style applied to everything
            label:  'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]`
            other:  'inverted',  // Objects which don't have a literal representation, such as functions
            key:    'bold',      // The keys in object literals, like 'a' in `{a: 1}`
            special: null,       // null, undefined...
            string:  null,
            number:  null,
            bool:    null
        },
        writer: process.stdio.write,
        maxLength: 2048          // Truncate output if longer
    },

    // Return a curried inspect() function, with the `options` argument filled in.
    inspector: function (options) {
        var that = this;
        return function (obj, label, opts) {
            return that.inspect(obj, label,
                process.mixin(true, {}, options || {}, opts || {}));
        };
    },

    // Calls print() to output the inspected object
    inspect: function (obj, label, options) {
        options = process.mixin(true, {}, this.defaults, options || {})
        return this.print(this.stringify(obj, options.styles) + "\n", label, options);
    },

    // Output using the 'writer', and an optional label
    print: function (str, label, options) {
        if (str.length > options.maxLength) str = str.slice(0, options.maxLength) + '…';
        return options.writer((label ? this.stylize(label, options.styles.label, options.styles) + ': ' : '') +
                                       this.stylize(str,   options.styles.all, options.styles));
    },

    // Convert any object to a string, ready for output.
    // When an 'array' or an 'object' are encountered, they are
    // passed to specialized functions, which can then recursively call
    // stringify().
    stringify: function (obj, styles) {
        var that = this, stylize = function (str, style) {
            return that.stylize(str, style, styles)
        };

        switch (typeOf(obj)) {
            case "string":
                obj = (obj.length === 1 ? "'" + obj + "'" : '"' + obj + '"')
                      .replace(/\n/g, '\\n');
                return stylize(obj, styles.string);
            case "regexp"   : return stylize('/' + obj.source + '/', styles.regexp);
            case "number"   : return stylize(obj + '', styles.number);
            case "function" : return stylize("Function",  styles.other);
            case "null"     : return stylize("null",      styles.special);
            case "undefined": return stylize("undefined", styles.special);
            case "boolean"  : return stylize(obj + '',    styles.bool);
            case "array"    : return this.stringifyArray(obj,  styles);
            case "object"   : return this.stringifyObject(obj, styles);
        }
    },

    // Convert an array to a string, such as [1, 2, 3].
    // This function calls stringify() for each of the elements
    // in the array.
    stringifyArray: function (ary, styles) {
        var out = [];

        for (var i = 0; i < ary.length; i++) {
            out.push(this.stringify(ary[i], styles));
        }
        return '[' + out.join(', ') + ']';
    },

    // Convert an object to a string, such as {a: 1}.
    // This function calls stringify() for each of its values,
    // and does not output functions or prototype values.
    stringifyObject: function (obj, styles) {
        var out = [];

        for (var k in obj) {
            if (obj.hasOwnProperty(k) && !(obj[k] instanceof Function)) {
                out.push(this.stylize(k, styles.key, styles) + ': ' + this.stringify(obj[k], styles));
            }
        }
        return "{" + out.join(', ') + "}";
    },

    // Apply a style to a string, eventually,
    // I'd like this to support passing multiple
    // styles.
    stylize: function (str, style, styles) {
        var codes = {
            'bold'      : [1,  22],
            'underline' : [4,  24],
            'inverse'   : [7,  27],
            'cyan'      : [36, 39],
            'magenta'   : [35, 39],
            'yellow'    : [33, 39],
            'green'     : [32, 39],
            'red'       : [31, 39]
        }, endCode;

        if (style && codes[style]) {
            endCode = (codes[style][1] === 39 && styles.all) ? codes[styles.all][0]
                                                             : codes[style][1];
            return '\033[' + codes[style][0] + 'm' + str +
                   '\033[' + endCode + 'm';
        } else { return str }
    }
};

// CommonJS module support
process.mixin(exports, eyes);

// A better `typeof`
function typeOf(value) {
    var s = typeof value;

    if (s === 'object' || s === 'function') {
        if (value) {
            if (value instanceof Array) { s = 'array' }
            else if (value instanceof RegExp) { s = 'regexp' }
        } else { s = 'null' }
    }
    return s;
}

<commit_msg>actually compute the string length accurately. A little on the ugly side, but beats tokenization for now.<commit_after>//
// Eyes.js - a customizable value inspector for Node.js
//
//   usage:
//
//       var inspect = require('eyes').inspector({styles: {all: 'magenta'}});
//
//       inspect(something); // inspect with the settings passed to `inspector`
//
//     or
//
//       var eyes = require('eyes');
//
//       eyes.inspect(something); // inspect with the default settings
//
var eyes = {
    defaults: {
        styles: {                // Styles applied to stdout
            all:    'cyan',      // Overall style applied to everything
            label:  'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]`
            other:  'inverted',  // Objects which don't have a literal representation, such as functions
            key:    'bold',      // The keys in object literals, like 'a' in `{a: 1}`
            special: null,       // null, undefined...
            string:  null,
            number:  null,
            bool:    null
        },
        writer: process.stdio.write,
        maxLength: 2048          // Truncate output if longer
    },

    // Return a curried inspect() function, with the `options` argument filled in.
    inspector: function (options) {
        var that = this;
        return function (obj, label, opts) {
            return that.inspect(obj, label,
                process.mixin(true, {}, options || {}, opts || {}));
        };
    },

    // Calls print() to output the inspected object
    inspect: function (obj, label, options) {
        options = process.mixin(true, {}, this.defaults, options || {})
        return this.print(this.stringify(obj, options.styles) + "\n", label, options);
    },

    // Output using the 'writer', and an optional label
    // Loop through `str`, and truncate it after `options.maxLength` has been reached.
    // Because escape sequences are, at this point embeded within
    // the output string, we can't measure the length of the string
    // in a useful way, without separating what is an escape sequence,
    // versus a printable character (`c`). So we resort to counting the
    // length manually.
    print: function (str, label, options) {
        for (var c = 0, i = 0; i < str.length; i++) {
            if (str.charAt(i) === '\033') { i += 4 } // `4` because '\033[25m'.length + 1 == 5
            else if (c === options.maxLength) {
               str = str.slice(0, i - 1) + '…';
               break;
            } else { c++ }
        }
        return options.writer((label ? this.stylize(label, options.styles.label, options.styles) + ': ' : '') +
                                       this.stylize(str,   options.styles.all, options.styles));
    },

    // Convert any object to a string, ready for output.
    // When an 'array' or an 'object' are encountered, they are
    // passed to specialized functions, which can then recursively call
    // stringify().
    stringify: function (obj, styles) {
        var that = this, stylize = function (str, style) {
            return that.stylize(str, style, styles)
        };

        switch (typeOf(obj)) {
            case "string":
                obj = (obj.length === 1 ? "'" + obj + "'" : '"' + obj + '"')
                      .replace(/\n/g, '\\n');
                return stylize(obj, styles.string);
            case "regexp"   : return stylize('/' + obj.source + '/', styles.regexp);
            case "number"   : return stylize(obj + '', styles.number);
            case "function" : return stylize("Function",  styles.other);
            case "null"     : return stylize("null",      styles.special);
            case "undefined": return stylize("undefined", styles.special);
            case "boolean"  : return stylize(obj + '',    styles.bool);
            case "array"    : return this.stringifyArray(obj,  styles);
            case "object"   : return this.stringifyObject(obj, styles);
        }
    },

    // Convert an array to a string, such as [1, 2, 3].
    // This function calls stringify() for each of the elements
    // in the array.
    stringifyArray: function (ary, styles) {
        var out = [];

        for (var i = 0; i < ary.length; i++) {
            out.push(this.stringify(ary[i], styles));
        }
        return '[' + out.join(', ') + ']';
    },

    // Convert an object to a string, such as {a: 1}.
    // This function calls stringify() for each of its values,
    // and does not output functions or prototype values.
    stringifyObject: function (obj, styles) {
        var out = [];

        for (var k in obj) {
            if (obj.hasOwnProperty(k) && !(obj[k] instanceof Function)) {
                out.push(this.stylize(k, styles.key, styles) + ': ' + this.stringify(obj[k], styles));
            }
        }
        return "{" + out.join(', ') + "}";
    },

    // Apply a style to a string, eventually,
    // I'd like this to support passing multiple
    // styles.
    stylize: function (str, style, styles) {
        var codes = {
            'bold'      : [1,  22],
            'underline' : [4,  24],
            'inverse'   : [7,  27],
            'cyan'      : [36, 39],
            'magenta'   : [35, 39],
            'yellow'    : [33, 39],
            'green'     : [32, 39],
            'red'       : [31, 39]
        }, endCode;

        if (style && codes[style]) {
            endCode = (codes[style][1] === 39 && styles.all) ? codes[styles.all][0]
                                                             : codes[style][1];
            return '\033[' + codes[style][0] + 'm' + str +
                   '\033[' + endCode + 'm';
        } else { return str }
    }
};

// CommonJS module support
process.mixin(exports, eyes);

// A better `typeof`
function typeOf(value) {
    var s = typeof value;

    if (s === 'object' || s === 'function') {
        if (value) {
            if (value instanceof Array) { s = 'array' }
            else if (value instanceof RegExp) { s = 'regexp' }
        } else { s = 'null' }
    }
    return s;
}

<|endoftext|>


<commit_before>var xml = require('xml');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.title       = options.title;
    this.description = options.description;
    this.link        = options.link;
    this.image       = options.image;
    this.copyright   = options.copyright;

    this.author      = options.author;

    this.items       = [];
    this.categories  = [];

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            image:          options.image,
            author:         options.author
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
        console.warn('DEPRECATED: use addItem() instead of item()');
        return this.addItem(options);
    }

    this.category = function (category) {
        console.warn('DEPRECATED: use addCategory() instead of category()');
        return this.addCategory(category);
    }
};

var urlPattern = new RegExp('(http|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?');

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    feed.push({ title:      options.title });
    feed.push({ id:         options.link });
    feed.push({ updated:    RFC3339(d) });      // Auto-magic :)

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/
    if(author)
    {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (relative link to feed) - will be added in a future version

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/
    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    // contributor

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i=0; i<entries.length; i++) {
        //
        // entry: required elements
        //
        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        // entry author(s)
        if(entries[i].author) {
            for(var y=0; y<entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor

        // published

        // source

        // rights

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var author = options.author;
    var d = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    /**
     * Required Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements
     */
    if(options.title == undefined)
        throw new Error('channel title is mandatory');
    else
        channel.push({ title: options.title });

    if(options.description == undefined)
        throw new Error('channel description is mandatory');
    else
        channel.push({ description: options.description });

    if(options.link == undefined)
        throw new Error('channel link is mandatory');
    else
        if(urlPattern.test(options.link))
            throw new Error('invalid channel link');
        else
            channel.push({ link: options.link });

    /**
     * Optional Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(author != undefined) {
        if(author.email && author.name)
            channel.push({ author: author.email + ' (' + author.name + ')' });

        else if(author.email && !author.name)
            channel.push({ author: author.email });

        else if(!author.email && author.name)
            channel.push({ author: author.name });
    }

    channel.push({ lastBuildDate: d.toUTCString() });
    channel.push({ docs: 'http://blogs.law.harvard.edu/tech/rss'});

    /**
     * Channel Image
     * http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
     */
    if(options.image)
        channel.push({ image: options.image });

    /**
     * Channel Copyright
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(options.copyright)
        channel.push({ copyright: options.copyright });

    channel.push({ generator: generatorName });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#comments
     */
    for(var i = 0; i < categories.length; i++)
        channel.push({ category: categories[i] });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
     */
    for(var i = 0; i < items.length; i++) {

        // You need to have at least a title OR a description
        if(items[i].title && items[i].description)
            throw new Error('item must have at least a title or a description');

        var item = [];

        if(items[i].title)
            item.push({ title: { _cdata: items[i].title }});

        if(items[i].link)
            item.push({ link: items[i].link });

        if(items[i].date)
            item.push({ pubDate: items[i].date.toUTCString() });

        if(items[i].description)
            item.push({ description: { _cdata: items[i].description }});

        /**
         * Item Author
         * http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
         */
        if(items[i].author) {
            for(var y = 0; y < items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

module.exports = Feed;<commit_msg>Fixed a couple of reverse logic issues.<commit_after>var xml = require('xml');
var generatorName = 'Feed for Node.js';

// ============================================================================
// Feed Main Module
// ============================================================================
function Feed(options) {
    this.title       = options.title;
    this.description = options.description;
    this.link        = options.link;
    this.image       = options.image;
    this.copyright   = options.copyright;

    this.author      = options.author;

    this.items       = [];
    this.categories  = [];

    this.addItem = function (options) {
        options = options || {};
        var item = {
            title:          options.title,
            link:           options.link,
            description:    options.description,
            date:           options.date,
            image:          options.image,
            author:         options.author
        };

        this.items.push(item);
        return this;
    }

    this.addCategory = function (category) {
        this.categories.push(category);
        return this;
    }

    this.render = function (format) {
        switch(format) {
            case 'rss-2.0':
                return rss_2_0(this);
            break;

            case 'atom-1.0':
                return atom_1_0(this);
            break;

            default:
                return rss_2_0(this);
        }
    };

    /**
     * DEPRECATED METHODS
     */
    this.item = function (options)  {
        console.warn('DEPRECATED: use addItem() instead of item()');
        return this.addItem(options);
    }

    this.category = function (category) {
        console.warn('DEPRECATED: use addCategory() instead of category()');
        return this.addCategory(category);
    }
};

var urlPattern = new RegExp('(http|https)://[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?');

// ============================================================================
// Atom 1.0 [application/atom+xml]
//
// - Reference: http://www.atomenabled.org/developers/syndication/
// ============================================================================

function atom_1_0(options) {
    var items       = [],
        container   = [],
        feed        = [],

        entries     = options.items,
        categories  = options.categories,
        author      = options.author,
        d           = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ feed: feed });
    feed.push({ _attr: { xmlns: 'http://www.w3.org/2005/Atom' }});

    /**************************************************************************
     * "feed" node: REQUIRED elements
     *************************************************************************/
    feed.push({ title:      options.title });
    feed.push({ id:         options.link });
    feed.push({ updated:    RFC3339(d) });      // Auto-magic :)

    /**************************************************************************
     * "feed" node: recommended elements
     *************************************************************************/
    if(author)
    {
        var authorDetails = [];

        if(author.name)
            authorDetails.push({ name:     author.name });

        if(author.email)
            authorDetails.push({ email:     author.email });

        if(author.link)
            authorDetails.push({ uri:     author.link });

        feed.push({ author: authorDetails });
    }

    // link (relative link to feed) - will be added in a future version

    /**************************************************************************
     * "feed" node: optional elements
     *************************************************************************/
    if(options.description)
        feed.push({ subtitle:   options.description });

    if(options.image)
        feed.push({ logo:       options.image });

    if(options.copyright)
        feed.push({ rights:     options.copyright });

    feed.push({ generator:      generatorName });

    // Looping through categories (if any set!)
    for(var i=0; i<categories.length; i++)
        feed.push({ category: [{ _attr: { term: categories[i] } }] });

    // contributor

    // icon

    /**************************************************************************
     * "entry" nodes
     *************************************************************************/
    for(var i=0; i<entries.length; i++) {
        //
        // entry: required elements
        //
        var entry = [
            { title:        { _attr: { type: 'html' }, _cdata: entries[i].title }},
            { id:           entries[i].link },
            { link:         [{ _attr: { href: entries[i].link } }]},
            { updated:      RFC3339(entries[i].date) }
        ];

        //
        // entry: recommended elements
        //
        if(entries[i].description)
            entry.push({ summary: { _attr: { type: 'html' }, _cdata: entries[i].description }});

        // entry author(s)
        if(entries[i].author) {
            for(var y=0; y<entries[i].author.length; y++) {
                var entryAuthor = [];

                if(entries[i].author[y].name)
                    entryAuthor.push({ name: entries[i].author[y].name });

                if(entries[i].author[y].email)
                    entryAuthor.push({ email: entries[i].author[y].email });

                if(entries[i].author[y].link)
                    entryAuthor.push({ uri: entries[i].author[y].link });

                entry.push({ author: entryAuthor });
            }
        }

        // content

        // link - relative link to article

        //
        // entry: optional elements
        //

        // category

        // contributor

        // published

        // source

        // rights

        feed.push({ entry: entry });
    }

    return doctype + xml(container, true);
}

// ============================================================================
// Real Simple Syndication 2.0 (RSS 2.0)
//
// - Reference: http://feed2.w3.org/docs/rss2.html
// ============================================================================
function rss_2_0(options) {
    var items       = [],
        channel     = [],
        container   = [];

    var items = options.items;
    var categories = options.categories;
    var author = options.author;
    var d = new Date();

    var doctype = '<?xml version="1.0" encoding="utf-8"?>\n';

    container.push({ rss: [] });
    container[0].rss.push({ _attr: { version: '2.0' } });
    container[0].rss.push({ channel: channel });

    /**
     * Required Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#requiredChannelElements
     */
    if(options.title == undefined)
        throw new Error('channel title is mandatory');
    else
        channel.push({ title: options.title });

    if(options.description == undefined)
        throw new Error('channel description is mandatory');
    else
        channel.push({ description: options.description });

    if(options.link == undefined)
        throw new Error('channel link is mandatory');
    else
        if(!urlPattern.test(options.link))
            throw new Error('invalid channel link');
        else
            channel.push({ link: options.link });

    /**
     * Optional Channel Elements
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(author != undefined) {
        if(author.email && author.name)
            channel.push({ author: author.email + ' (' + author.name + ')' });

        else if(author.email && !author.name)
            channel.push({ author: author.email });

        else if(!author.email && author.name)
            channel.push({ author: author.name });
    }

    channel.push({ lastBuildDate: d.toUTCString() });
    channel.push({ docs: 'http://blogs.law.harvard.edu/tech/rss'});

    /**
     * Channel Image
     * http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
     */
    if(options.image)
        channel.push({ image: options.image });

    /**
     * Channel Copyright
     * http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
     */
    if(options.copyright)
        channel.push({ copyright: options.copyright });

    channel.push({ generator: generatorName });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#comments
     */
    for(var i = 0; i < categories.length; i++)
        channel.push({ category: categories[i] });

    /**
     * Channel Categories
     * http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
     */
    for(var i = 0; i < items.length; i++) {

        // You need to have at least a title OR a description
        if(!items[i].title && !items[i].description)
            throw new Error('item must have at least a title or a description');

        var item = [];

        if(items[i].title)
            item.push({ title: { _cdata: items[i].title }});

        if(items[i].link)
            item.push({ link: items[i].link });

        if(items[i].date)
            item.push({ pubDate: items[i].date.toUTCString() });

        if(items[i].description)
            item.push({ description: { _cdata: items[i].description }});

        /**
         * Item Author
         * http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
         */
        if(items[i].author) {
            for(var y = 0; y < items[i].author.length; y++) {
                var itemAuthor = [];

                if(items[i].author[y].name)
                    itemAuthor.push({ name: items[i].author[y].name });

                if(items[i].author[y].email)
                    itemAuthor.push({ email: items[i].author[y].email });

                if(items[i].author[y].link)
                    itemAuthor.push({ link: items[i].author[y].link });

                item.push({ author: itemAuthor });
            }
        }

        if(items[i].image)
            item.push({ enclosure: [{ _attr: { url: items[i].image } }] });

        channel.push({ item: item });
    }

    return doctype + xml(container, true);
}

function RFC3339(d) {
    function pad(n) { return n < 10 ? '0' + n : n }

    return d.getUTCFullYear()+'-'
        + pad(d.getUTCMonth()+1)+'-'
        + pad(d.getUTCDate())+'T'
        + pad(d.getUTCHours())+':'
        + pad(d.getUTCMinutes())+':'
        + pad(d.getUTCSeconds())+'Z'
}

module.exports = Feed;<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');
var path = require('path');
var debug = require('debug')('file:file');

var Struct = require('./lib/struct');

function File(filepath) {
	if (!(this instanceof File))
		return new File(filepath);

	this.filepath = filepath;

	this.fd = null;
	this._flags = undefined;
	this._mode = undefined;
}
module.exports = File;

function makeCallback(args, context) {
	var callback = args[args.length - 1];

	return function(err) {
		if (err) throw err;
		if (typeof callback == 'function') {
			callback.apply(context, arguments);
		}
	}
}

File.open = function(filepath, flags, mode, cb) {
	var file = new File(filepath);

	cb = makeCallback(arguments, file);
	if (typeof mode === 'function')
		mode = undefined;
	if (typeof flags === 'function')
		flags = undefined;

	file.open(flags, mode, cb);
	return file;
};

File.prototype.open = function(flags, mode, cb) {
	cb = makeCallback(arguments, this);
	if (typeof mode === 'function')
		mode = undefined;
	if (typeof flags === 'function')
		flags = undefined;

	flags = flags || this._flags || 'r+';
	mode = mode || this._mode;

	debug('open: %s (%s)', this.filepath, flags);

	fs.open(this.filepath, flags, mode, (err, fd) => {
		if (err) return cb(err);

		this.fd = fd;
		this._flags = flags;
		this._mode = mode;

		cb(null, this);
	});
};

File.prototype.close = function(cb) {
	cb = makeCallback(arguments, this);
	if (this.fd) {
		fs.close(this.fd, cb);
	}
};

File.prototype.isOpen = function() {
	return !!this.fd;
};

File.prototype.sync = function(cb) {
	cb = makeCallback(arguments, this);
	fs.fsync(this.fd, cb);
};

File.prototype.stat = function(cb) {
	cb = makeCallback(arguments, this);
	fs.fstat(this.fd, cb);
};

File.prototype.utimes = function(atime, mtime, cb) {
	cb = makeCallback(arguments, this);
	fs.futimes(this.fd, atime, mtime, cb);
};

File.prototype.truncate = function(len, cb) {
	cb = makeCallback(arguments, this);
	fs.ftruncate(this.fd, len, cb);
};

File.prototype.rename = function(dest, cb) {
	cb = makeCallback(arguments, this);

	this.close();
	fs.rename(this.filepath, dest, (err) => {
		if (err) return cb(err);

		this.filepath = dest;
		this.open(cb);
	})
};

//
// Read methods
//

File.prototype.read = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read: %d bytes at %d', len, pos);
	var buffer = new Buffer(len);
	fs.read(this.fd, buffer, 0, len, pos, (err, bytesRead, data) => {
		cb(err, data, bytesRead);
	});
};

File.prototype.readString = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read string: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		cb(null, data.toString(), bytesRead);
	});
};

File.prototype.readUIntBE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read uintBE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readUIntBE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readUIntLE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read uintLE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readUIntLE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readIntBE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read intBE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readIntBE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readIntLE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read intLE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readIntLE(0, len);
		cb(null, value, bytesRead);
	});
};

File.prototype.readStringLenBE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read string with len prefix %d bytes at %d', len_size, pos);
	this.readUIntBE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.readString(pos + lenBytesRead, len, (err, str, strBytesRead) => {
			cb(err, str, lenBytesRead + strBytesRead);
		});
	});
};

File.prototype.readStringLenLE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read string with len prefix %d bytes at %d', len_size, pos);
	this.readUIntLE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.readString(pos + lenBytesRead, len, (err, str, strBytesRead) => {
			cb(err, str, lenBytesRead + strBytesRead);
		});
	});
};

File.prototype.readBufferLenBE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read buffer with len prefix %d bytes at %d', len_size, pos);
	this.readUIntBE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.read(pos + lenBytesRead, len, (err, data, dataBytesRead) => {
			cb(err, str, lenBytesRead + dataBytesRead);
		});
	});
};

File.prototype.readBufferLenLE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read buffer with len prefix %d bytes at %d', len_size, pos);
	this.readUIntLE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.read(pos + lenBytesRead, len, (err, data, dataBytesRead) => {
			cb(err, str, lenBytesRead + dataBytesRead);
		});
	});
};

File.prototype.createReadStruct = function(pos) {
	return new Struct.Read(this, pos);
};

// Read aliases
File.prototype.readUInt = File.prototype.readUIntBE;
File.prototype.readInt = File.prototype.readIntBE;
File.prototype.readStringLen = File.prototype.readStringLenBE;
File.prototype.readBufferLen = File.prototype.readBufferLenBE;

//
// Write methods
//

File.prototype.write = function(data, pos, cb) {
	cb = makeCallback(arguments, this);
	fs.write(this.fd, data, 0, data.length, pos, cb);
};

File.prototype.writeString = function(str, pos, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(str);
	this.write(buffer, pos, cb);
};

File.prototype.writeUIntBE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeUIntBE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeUIntLE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeUIntLE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeIntBE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeIntBE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeIntLE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeIntLE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeStringLenBE = function(str, pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	var str_len = Buffer.byteLength(str);
	var buffer = new Buffer(len_size + str_len);
	buffer.writeUIntBE(str_len, 0, len_size);
	buffer.write(str, len_size);
	this.write(buffer, pos, cb);
};

File.prototype.writeStringLenLE = function(str, pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	var str_len = Buffer.byteLength(str);
	var buffer = new Buffer(len_size + str_len);
	buffer.writeUIntLE(str_len, 0, len_size);
	buffer.write(str, len_size);
	this.write(buffer, pos, cb);
};

File.prototype.writeBufferLenBE = function(data, pos, len_size, cb) {
	cb = makeCallback(arguments, this);
	var len_buffer = new Buffer(len_size);
	len_buffer.writeUIntBE(data.length, 0, len_size);
	this.write(Buffer.concat([len_buffer, data]), pos, cb);
};

File.prototype.writeBufferLenLE = function(data, pos, len_size, cb) {
	cb = makeCallback(arguments, this);
	var len_buffer = new Buffer(len_size);
	len_buffer.writeUIntLE(data.length, 0, len_size);
	this.write(Buffer.concat([len_buffer, data]), pos, cb);
};

File.prototype.createWriteStruct = function(pos) {
	return new Struct.Write(this, pos);
};

// Write aliases
File.prototype.writeUInt = File.prototype.writeUIntBE;
File.prototype.writeInt = File.prototype.writeIntBE;
File.prototype.writeStringLen = File.prototype.writeStringLenBE;
File.prototype.writeBufferLen = File.prototype.writeBufferLenBE;
<commit_msg>Use zero as default position for create structs<commit_after>"use strict";

var fs = require('fs');
var path = require('path');
var debug = require('debug')('file:file');

var Struct = require('./lib/struct');

function File(filepath) {
	if (!(this instanceof File))
		return new File(filepath);

	this.filepath = filepath;

	this.fd = null;
	this._flags = undefined;
	this._mode = undefined;
}
module.exports = File;

function makeCallback(args, context) {
	var callback = args[args.length - 1];

	return function(err) {
		if (err) throw err;
		if (typeof callback == 'function') {
			callback.apply(context, arguments);
		}
	}
}

File.open = function(filepath, flags, mode, cb) {
	var file = new File(filepath);

	cb = makeCallback(arguments, file);
	if (typeof mode === 'function')
		mode = undefined;
	if (typeof flags === 'function')
		flags = undefined;

	file.open(flags, mode, cb);
	return file;
};

File.prototype.open = function(flags, mode, cb) {
	cb = makeCallback(arguments, this);
	if (typeof mode === 'function')
		mode = undefined;
	if (typeof flags === 'function')
		flags = undefined;

	flags = flags || this._flags || 'r+';
	mode = mode || this._mode;

	debug('open: %s (%s)', this.filepath, flags);

	fs.open(this.filepath, flags, mode, (err, fd) => {
		if (err) return cb(err);

		this.fd = fd;
		this._flags = flags;
		this._mode = mode;

		cb(null, this);
	});
};

File.prototype.close = function(cb) {
	cb = makeCallback(arguments, this);
	if (this.fd) {
		fs.close(this.fd, cb);
	}
};

File.prototype.isOpen = function() {
	return !!this.fd;
};

File.prototype.sync = function(cb) {
	cb = makeCallback(arguments, this);
	fs.fsync(this.fd, cb);
};

File.prototype.stat = function(cb) {
	cb = makeCallback(arguments, this);
	fs.fstat(this.fd, cb);
};

File.prototype.utimes = function(atime, mtime, cb) {
	cb = makeCallback(arguments, this);
	fs.futimes(this.fd, atime, mtime, cb);
};

File.prototype.truncate = function(len, cb) {
	cb = makeCallback(arguments, this);
	fs.ftruncate(this.fd, len, cb);
};

File.prototype.rename = function(dest, cb) {
	cb = makeCallback(arguments, this);

	this.close();
	fs.rename(this.filepath, dest, (err) => {
		if (err) return cb(err);

		this.filepath = dest;
		this.open(cb);
	})
};

//
// Read methods
//

File.prototype.read = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read: %d bytes at %d', len, pos);
	var buffer = new Buffer(len);
	fs.read(this.fd, buffer, 0, len, pos, (err, bytesRead, data) => {
		cb(err, data, bytesRead);
	});
};

File.prototype.readString = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read string: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		cb(null, data.toString(), bytesRead);
	});
};

File.prototype.readUIntBE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read uintBE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readUIntBE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readUIntLE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read uintLE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readUIntLE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readIntBE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read intBE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readIntBE(0, len);
		cb(null, value, bytesRead);
	});
};
File.prototype.readIntLE = function(pos, len, cb) {
	cb = makeCallback(arguments, this);

	debug('read intLE: %d bytes at %d', len, pos);
	this.read(pos, len, (err, data, bytesRead) => {
		if (err) return cb(err);
		var value = data.readIntLE(0, len);
		cb(null, value, bytesRead);
	});
};

File.prototype.readStringLenBE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read string with len prefix %d bytes at %d', len_size, pos);
	this.readUIntBE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.readString(pos + lenBytesRead, len, (err, str, strBytesRead) => {
			cb(err, str, lenBytesRead + strBytesRead);
		});
	});
};

File.prototype.readStringLenLE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read string with len prefix %d bytes at %d', len_size, pos);
	this.readUIntLE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.readString(pos + lenBytesRead, len, (err, str, strBytesRead) => {
			cb(err, str, lenBytesRead + strBytesRead);
		});
	});
};

File.prototype.readBufferLenBE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read buffer with len prefix %d bytes at %d', len_size, pos);
	this.readUIntBE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.read(pos + lenBytesRead, len, (err, data, dataBytesRead) => {
			cb(err, str, lenBytesRead + dataBytesRead);
		});
	});
};

File.prototype.readBufferLenLE = function(pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	debug('read buffer with len prefix %d bytes at %d', len_size, pos);
	this.readUIntLE(pos, len_size, (err, len, lenBytesRead) => {
		if (err) return cb(err);

		this.read(pos + lenBytesRead, len, (err, data, dataBytesRead) => {
			cb(err, str, lenBytesRead + dataBytesRead);
		});
	});
};

File.prototype.createReadStruct = function(pos) {
	return new Struct.Read(this, pos || 0);
};

// Read aliases
File.prototype.readUInt = File.prototype.readUIntBE;
File.prototype.readInt = File.prototype.readIntBE;
File.prototype.readStringLen = File.prototype.readStringLenBE;
File.prototype.readBufferLen = File.prototype.readBufferLenBE;

//
// Write methods
//

File.prototype.write = function(data, pos, cb) {
	cb = makeCallback(arguments, this);
	fs.write(this.fd, data, 0, data.length, pos, cb);
};

File.prototype.writeString = function(str, pos, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(str);
	this.write(buffer, pos, cb);
};

File.prototype.writeUIntBE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeUIntBE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeUIntLE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeUIntLE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeIntBE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeIntBE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeIntLE = function(value, pos, len, cb) {
	cb = makeCallback(arguments, this);
	var buffer = new Buffer(len);
	buffer.writeIntLE(value, 0, len);
	this.write(buffer, pos, cb);
};

File.prototype.writeStringLenBE = function(str, pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	var str_len = Buffer.byteLength(str);
	var buffer = new Buffer(len_size + str_len);
	buffer.writeUIntBE(str_len, 0, len_size);
	buffer.write(str, len_size);
	this.write(buffer, pos, cb);
};

File.prototype.writeStringLenLE = function(str, pos, len_size, cb) {
	cb = makeCallback(arguments, this);

	var str_len = Buffer.byteLength(str);
	var buffer = new Buffer(len_size + str_len);
	buffer.writeUIntLE(str_len, 0, len_size);
	buffer.write(str, len_size);
	this.write(buffer, pos, cb);
};

File.prototype.writeBufferLenBE = function(data, pos, len_size, cb) {
	cb = makeCallback(arguments, this);
	var len_buffer = new Buffer(len_size);
	len_buffer.writeUIntBE(data.length, 0, len_size);
	this.write(Buffer.concat([len_buffer, data]), pos, cb);
};

File.prototype.writeBufferLenLE = function(data, pos, len_size, cb) {
	cb = makeCallback(arguments, this);
	var len_buffer = new Buffer(len_size);
	len_buffer.writeUIntLE(data.length, 0, len_size);
	this.write(Buffer.concat([len_buffer, data]), pos, cb);
};

File.prototype.createWriteStruct = function(pos) {
	return new Struct.Write(this, pos || 0);
};

// Write aliases
File.prototype.writeUInt = File.prototype.writeUIntBE;
File.prototype.writeInt = File.prototype.writeIntBE;
File.prototype.writeStringLen = File.prototype.writeStringLenBE;
File.prototype.writeBufferLen = File.prototype.writeBufferLenBE;
<|endoftext|>


<commit_before>'use strict';

angular.module('sgApp')
  .controller('MainCtrl', function ($scope, Socket) {
    // Emit ready event.
    Socket.emit('ready');

    // Listen for the talk event.
    Socket.on('update', function(data) {
      $scope.sections = data;
    });
  });<commit_msg>Add front end css parser<commit_after>'use strict';

angular.module('sgApp')
  .controller('MainCtrl', function ($scope, Socket) {
    // Emit ready event.
    Socket.emit('ready');

    // Listen for the talk event.
    Socket.on('update', function(data) {
      $scope.sections = data;
      generateCss();
    });

    // TODO: The following is copied from kss-node, should make a better one

    function generateCss() {
      var KssStateGenerator;

      KssStateGenerator = (function() {
        var pseudo_selectors;

        pseudo_selectors = ['hover', 'enabled', 'disabled', 'active', 'visited', 'focus', 'target', 'checked', 'empty', 'first-of-type', 'last-of-type', 'first-child', 'last-child'];

        function KssStateGenerator() {
          var idx, idxs, pseudos, replaceRule, rule, stylesheet, _i, _len, _len2, _ref, _ref2;
          pseudos = new RegExp("(\\:" + (pseudo_selectors.join('|\\:')) + ")", "g");
          try {
            _ref = document.styleSheets;
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              stylesheet = _ref[_i];
              if (stylesheet.href && stylesheet.href.indexOf(document.domain) >= 0) {
                idxs = [];
                _ref2 = stylesheet.cssRules;
                for (idx = 0, _len2 = _ref2.length; idx < _len2; idx++) {
                  rule = _ref2[idx];
                  if ((rule.type === CSSRule.STYLE_RULE) && pseudos.test(rule.selectorText)) {
                    replaceRule = function(matched, stuff) {
                      return matched.replace(/\:/g, '.pseudo-class-');
                    };
                    this.insertRule(rule.cssText.replace(pseudos, replaceRule));
                  }
                  pseudos.lastIndex = 0;
                }
              }
            }
          } catch (_error) {}
        }

        KssStateGenerator.prototype.insertRule = function(rule) {
          var headEl, styleEl;
          headEl = document.getElementsByTagName('head')[0];
          styleEl = document.createElement('style');
          styleEl.type = 'text/css';
          if (styleEl.styleSheet) {
            styleEl.styleSheet.cssText = rule;
          } else {
            styleEl.appendChild(document.createTextNode(rule));
          }
          return headEl.appendChild(styleEl);
        };

        return KssStateGenerator;

      })();

      new KssStateGenerator;
    }
  });<|endoftext|>


<commit_before>import FS from './fs';

const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

export default class File {
  constructor(path) {
    this.path = FS.translatePath(path);
    path = this.path.split('/');
    this.filename = path.pop();
    this.dirname = path.join('/') || '/';
    this.dir = FS.open(this.dirname);
  }

  static open(path) {
    return new File(path);
  }

  static getTimestamp () {
    return new Date().toISOString();
  }

  parentExists() {
    return this.dir !== undefined;
  }

  isValid() {
    return typeof this.dir === 'object' && this.dir.type === 'd';
  }

  exists() {
    return this.isValid() && (!this.filename || typeof this.dir.content[this.filename] !== 'undefined');
  }

  isFile() {
    return this.exists() && this.filename &&
      this.dir.content[this.filename].type === 'f';
  }

  isDir() {
    return this.exists() &&
      (!this.filename || this.dir.content[this.filename].type === 'd');
  }

  delete() {
    if (this.exists()) {
      delete this.dir.content[this.filename];
      FS.writeFS();
    }
  }

  clear() {
    this.write('', false, true);
  }

  write(content, append, force) {
    var time = File.getTimestamp();

    if (!this.exists()) {
      if (force && this.isValid()) {
        this.createFile(time);
      } else {
        throw new Error('Invalid file: ' + this.path);
      }
    } else if (!this.isFile()) {
      throw new Error('Cannot write to directory: %s', this.path);
    } else {
      var _content = '';
      if (append) {
        _content += this.read();
      }

      this.dir.mtime = time;
      this.dir.content[this.filename].mtime = time;
      this.dir.content[this.filename].content = _content + content;
      FS.writeFS();
    }
  }

  read() {
    return this.filename ? this.dir.content[this.filename].content : this.dir.content;
  }

  _create(type, content, timestamp) {
    if (this.exists()) {
      throw new Error('File %s already exists', this.path);
    }

    if (!timestamp) {
      timestamp = File.getTimestamp();
    }

    this.dir.content[this.filename] = {
      ctime: timestamp,
      mtime: timestamp,
      content: content,
      type: type
    };

    FS.writeFS();
  }

  createFolder(timestamp) {
    this._create('d', {}, timestamp);
  }

  createFile(timestamp) {
    this._create('f', '', timestamp);
  }

  self() {
    return this.filename ? this.dir : this.dir.content[this.filename];
  }

  open(file) {
    return File.open(this.path + '/' + file);
  }

  length() {
    var content = this.read();

    if (this.isFile()) {
      return content.length;
    } else if (this.isDir()) {
      return Object.keys(content).length;
    } else {
      return 0;
    }
  }

  mtime() {
    var t = new Date(this.self().mtime);

    var dayAndMonth = MONTHS[t.getMonth()] + ' ' + t.getDay();
    if (Date.now() - t.getTime() > 6 * 30 * 24 * 60 * 60 * 1000) {
      return dayAndMonth + ' ' + t.getFullYear();
    } else {
      return dayAndMonth + ' ' + t.getHours() + ':' + t.getMinutes();
    }
  };
}
<commit_msg>Fix minor bugs on File abstraction<commit_after>import FS from './fs';

const MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];

export default class File {
  constructor(path) {
    this.path = FS.translatePath(path);
    path = this.path.split('/');
    this.filename = path.pop();
    this.dirname = path.join('/') || '/';
    this.dir = FS.open(this.dirname);
  }

  static open(path) {
    return new File(path);
  }

  static getTimestamp () {
    return new Date().toISOString();
  }

  parentExists() {
    return this.dir !== undefined;
  }

  isValid() {
    return typeof this.dir === 'object' && this.dir.type === 'd';
  }

  exists() {
    return this.isValid() && (!this.filename || typeof this.dir.content[this.filename] !== 'undefined');
  }

  isFile() {
    return this.exists() && this.filename &&
      this.dir.content[this.filename].type === 'f';
  }

  isDir() {
    return this.exists() &&
      (!this.filename || this.dir.content[this.filename].type === 'd');
  }

  delete() {
    if (this.exists()) {
      delete this.dir.content[this.filename];
      FS.writeFS();
    }
  }

  clear() {
    this.write('', false, true);
  }

  write(content, append, force) {
    var time = File.getTimestamp();

    if (!this.exists()) {
      if (force && this.isValid()) {
        this.createFile(time);
      } else {
        throw new Error('Invalid file: ' + this.path);
      }
    } else if (!this.isFile()) {
      throw new Error('Cannot write to directory: %s', this.path);
    }

    var _content = '';
    if (append) {
      _content += this.read();
    }

    this.dir.mtime = time;
    this.dir.content[this.filename].mtime = time;
    this.dir.content[this.filename].content = _content + content;
    FS.writeFS();
  }

  read() {
    if (!this.exists()) {
      throw new Error('File %s doesn\'t exist', this.path);
    }
    return this.filename ? this.dir.content[this.filename].content : this.dir.content;
  }

  _create(type, content, timestamp) {
    if (this.exists()) {
      throw new Error('File %s already exists', this.path);
    }

    if (!timestamp) {
      timestamp = File.getTimestamp();
    }

    this.dir.content[this.filename] = {
      ctime: timestamp,
      mtime: timestamp,
      content: content,
      type: type
    };

    FS.writeFS();
  }

  createFolder(timestamp) {
    this._create('d', {}, timestamp);
  }

  createFile(timestamp) {
    this._create('f', '', timestamp);
  }

  self() {
    return this.filename ? this.dir.content[this.filename] : this.dir;
  }

  open(file) {
    return File.open(this.path + '/' + file);
  }

  length() {
    var content = this.read();

    if (this.isFile()) {
      return content.length;
    } else if (this.isDir()) {
      return Object.keys(content).length;
    } else {
      return 0;
    }
  }

  mtime() {
    var t = new Date(this.self().mtime);

    var dayAndMonth = MONTHS[t.getMonth()] + ' ' + t.getDay();
    if (Date.now() - t.getTime() > 6 * 30 * 24 * 60 * 60 * 1000) {
      return dayAndMonth + ' ' + t.getFullYear();
    } else {
      return dayAndMonth + ' ' + t.getHours() + ':' + t.getMinutes();
    }
  };
}
<|endoftext|>