<commit_before>// TODO: horizontal scrolling, file i/o
'use strict';
var c=document.getElementById('c').getContext('2d'),
    KEYQ=[{mods:[false,false,false,false],k:''}],// lightens duties for key event handler
    buf=Buffer(),
    cur=Cursor(buf),

    Settings=()=>({
        font_name:'Sans-Serif',
        font_size:'24px',
        init(c){
            c.font=this.font_size+' '+this.font_name;
            c.fillStyle='#dacaba';
        },
    }),

    ScreenOffsets=(c)=>({// c: the target Canvas
        bw:20,// border width
        // defaults: bad enough to clue you in
        line_height:10, baseline:5, v:{x:0,y:0,w:0,h:0},// viewport position and size
        ln_top(n){return this.bw+this.line_height*(n+1);},// top pixel of line n
        num_visible_lines(){return Math.floor((c.canvas.height-2*this.bw)/this.line_height);},
        scroll(soff=4){// Scroll to cursor. Optional scroll offset `soff` lines from top/bottom of screen.
            // TODO: horizontal scroll

            var prev_y=this.v.y;
            // NOTE: while loops make the math easier.
            while(this.ln_top(cur.cl+soff)>this.v.y+this.v.h){this.v.y+=this.ln_top(cur.cl)-this.ln_top(cur.cl-1);}
            while(this.ln_top(cur.cl-soff)<this.v.y){this.v.y-=this.ln_top(cur.cl)-this.ln_top(cur.cl-1);}
            if(this.v.y<0){this.v.y=0;}// bounds check
            if(prev_y!=this.v.y){c.setTransform(1,0,0,1,0,-this.v.y);}// move canvas opposite of viewport
        },
        init(ctx){// must be called before using other ScreenOffsets methods
            var fm=FontMetric(settings.font_name,settings.font_size);
            //fm[0] is baseline (bottom edge of letters such as abcde)
            this.line_height=fm[1];// total line height
            this.baseline=fm[2];// lower bound of text such as: jgpq|
            this.v={x:0,y:0,w:c.canvas.width,h:c.canvas.height};
            this.scroll();
        },
    }),
    settings=Settings(),
    offs=ScreenOffsets(c);

var render_text=()=>{
    if(buf.changed){// If the buffer changed, render everything
        buf.changed=false;
        c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);
        buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));
    }
    // TODO: only render lines inside of viewport?
    // if soff==0, there could be up to 1 screen's worth of lines on either side
    // of the cursor.  So always render that many lines before and after.
    else{
        var from_line=cur.cl-offs.num_visible_lines(),
            to_line=cur.cl+offs.num_visible_lines();
        if(from_line<0){from_line=0;}
        if(to_line>buf.lines.length-1){to_line=buf.lines.length-1;}
        console.log('from: '+from_line+', to: '+to_line);
        c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);
        for(var i=from_line;i<to_line+1;++i){
            c.fillText(buf.getline(i),offs.bw,offs.ln_top(i));
        }
    }
};

var render_cursor=()=>{// {Buffer, Cursor, Canvas}=>Rectangle
    // 1. clear where cursor was previously (currently handled by render_text)
    // 2. rewrite text at old cursor position (same)
    // 3. draw the cursor at the new position
    c.save();
    var l=buf.getline(cur.cl),// current line
        //ltop=offs.ln_top(cur.cl),// top edge of current line
        cur_left_edge=c.measureText(l.slice(0,cur.co)).width,
        wid=cur.mode==='insert'?1:c.measureText(l.slice(0,cur.co+1)).width-cur_left_edge||10;

    // statusbar background
    var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);
    c.fillStyle='rgba(20,20,20,0.9)';
    c.fillRect(0,status_line_y-offs.line_height-offs.baseline,c.canvas.width,2*offs.line_height);

    // statusbar
    c.fillStyle='orange';
    c.globalCompositeOperation='difference';
    c.fillText(cur.status(),offs.bw,status_line_y);// debug status line

    // cursor
    c.fillRect(offs.bw+cur_left_edge,offs.ln_top(cur.cl)-offs.line_height+offs.baseline,wid,offs.line_height);
    c.restore();
};

var gameloop=now=>{
    update(KEYQ,now);
    offs.scroll();// must happen before anything gets drawn, or scrolling is one frame behind
    render_text();
    // render_minimap();
    // render_statusline();
    render_cursor();
    // render_popups();

};

var rsz=()=>{
    requestAnimationFrame(gameloop);
    c.canvas.width=c.canvas.clientWidth;
    c.canvas.height=c.canvas.clientHeight;
    settings.init(c);
    offs.init(c);
};

window.onload=rsz;
window.onresize=rsz;
window.onkeydown=(k)=>{
    requestAnimationFrame(gameloop);
    if(k.type==='keydown'){// push incoming events to a queue as they occur
        if(!k.metaKey){k.preventDefault();}
        KEYQ.push({mods:[k.altKey,k.ctrlKey,k.metaKey,k.shiftKey], k:k.code});
    }
};

var example_code=
    'if(!wid){wid=10;}\n'+
    '// bottom of screen\n'+
    'var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);\n'+
    'c.clearRect(0,status_line_y-offs.line_height,c.canvas.width,2*offs.line_height);\n'+
    'c.fillText(cur.status(),offs.bw,status_line_y);// debug status line\n'+
    '\n'+
    'if(cur.mode==="insert"){wid=1;}\n'+
    'c.fillRect(offs.bw+cur_left_edge,ltop-offs.line_height+offs.baseline,wid,offs.line_height);\n'+
    'c.restore();\n'+
    '};\n'+
    '\n'+
    'var render_text=()=>{\n'+
    'c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);\n'+
    '// render ALL THE LINES\n'+
    'buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));\n'+
    'if(!wid){wid=10;}\n'+
    '// bottom of screen\n'+
    'var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);\n'+
    'c.clearRect(0,status_line_y-offs.line_height,c.canvas.width,2*offs.line_height);\n'+
    'c.fillText(cur.status(),offs.bw,status_line_y);// debug status line\n'+
    '\n'+
    'if(cur.mode==="insert"){wid=1;}\n'+
    'c.fillRect(offs.bw+cur_left_edge,ltop-offs.line_height+offs.baseline,wid,offs.line_height);\n'+
    'c.restore();\n'+
    '};\n'+
    '\n'+
    'var render_text=()=>{\n'+
    'c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);\n'+
    '// render ALL THE LINES\n'+
    'buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));\n'+
    '};';
buf.ins(example_code);
cur.rowcol();
<commit_msg>scroll and redraw only visible bits<commit_after>// TODO: horizontal scrolling, file i/o
'use strict';
var c=document.getElementById('c').getContext('2d'),
    KEYQ=[{mods:[false,false,false,false],k:''}],// lightens duties for key event handler
    buf=Buffer(),
    cur=Cursor(buf),

    Settings=()=>({
        font_name:'Sans-Serif',
        font_size:'24px',
        init(c){
            c.font=this.font_size+' '+this.font_name;
            c.fillStyle='#dacaba';
        },
    }),

    ScreenOffsets=(c)=>({// c: the target Canvas
        bw:20,// border width
        // defaults: bad enough to clue you in
        line_height:10, baseline:5, v:{x:0,y:0,w:0,h:0},// viewport position and size
        ln_top(n){return this.bw+this.line_height*(n+1);},// top pixel of line n
        num_visible_lines(){return Math.floor((c.canvas.height-2*this.bw)/this.line_height);},
        scroll(soff=4){// Scroll to cursor. Optional scroll offset `soff` lines from top/bottom of screen.
            // TODO: horizontal scroll

            var prev_y=this.v.y;
            // NOTE: while loops make the math easier.
            while(this.ln_top(cur.cl+soff)>this.v.y+this.v.h){this.v.y+=this.ln_top(cur.cl)-this.ln_top(cur.cl-1);}
            while(this.ln_top(cur.cl-soff)<this.v.y){this.v.y-=this.ln_top(cur.cl)-this.ln_top(cur.cl-1);}
            if(this.v.y<0){this.v.y=0;}// bounds check
            if(prev_y!=this.v.y){c.setTransform(1,0,0,1,0,-this.v.y);}// move canvas opposite of viewport
        },
        init(ctx){// must be called before using other ScreenOffsets methods
            var fm=FontMetric(settings.font_name,settings.font_size);
            //fm[0] is baseline (bottom edge of letters such as abcde)
            this.line_height=fm[1];// total line height
            this.baseline=fm[2];// lower bound of text such as: jgpq|
            this.v={x:0,y:0,w:c.canvas.width,h:c.canvas.height};
            this.scroll();
        },
    }),
    settings=Settings(),
    offs=ScreenOffsets(c);

var render_text=()=>{
    c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);
    if(buf.changed){// If the buffer changed, render everything
        buf.changed=false;
        buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));
    }
    else{
        var from_line=cur.cl-offs.num_visible_lines(),
            to_line=cur.cl+offs.num_visible_lines();
        if(from_line<0){from_line=0;}
        if(to_line>buf.lines.length-1){to_line=buf.lines.length-1;}
        for(var i=from_line;i<to_line+1;++i){
            c.fillText(buf.getline(i),offs.bw,offs.ln_top(i));
        }
    }
};

var render_cursor=()=>{// {Buffer, Cursor, Canvas}=>Rectangle
    // 1. clear where cursor was previously (currently handled by render_text)
    // 2. rewrite text at old cursor position (same)
    // 3. draw the cursor at the new position
    c.save();
    var l=buf.getline(cur.cl),// current line
        //ltop=offs.ln_top(cur.cl),// top edge of current line
        cur_left_edge=c.measureText(l.slice(0,cur.co)).width,
        wid=cur.mode==='insert'?1:c.measureText(l.slice(0,cur.co+1)).width-cur_left_edge||10;

    // statusbar background
    var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);
    c.fillStyle='rgba(20,20,20,0.9)';
    c.fillRect(0,status_line_y-offs.line_height-offs.baseline,c.canvas.width,2*offs.line_height);

    // statusbar
    c.fillStyle='orange';
    c.globalCompositeOperation='difference';
    c.fillText(cur.status(),offs.bw,status_line_y);// debug status line

    // cursor
    c.fillRect(offs.bw+cur_left_edge,offs.ln_top(cur.cl)-offs.line_height+offs.baseline,wid,offs.line_height);
    c.restore();
};

var gameloop=now=>{
    update(KEYQ,now);
    offs.scroll();
    render_text();
    // other ideas:
    // render_minimap();
    // render_statusline();
    render_cursor();
    // render_popups();

};

var rsz=()=>{
    requestAnimationFrame(gameloop);
    c.canvas.width=c.canvas.clientWidth;
    c.canvas.height=c.canvas.clientHeight;
    settings.init(c);
    offs.init(c);
};

window.onload=rsz;
window.onresize=rsz;
window.onkeydown=(k)=>{
    requestAnimationFrame(gameloop);
    if(k.type==='keydown'){// push incoming events to a queue as they occur
        if(!k.metaKey){k.preventDefault();}
        KEYQ.push({mods:[k.altKey,k.ctrlKey,k.metaKey,k.shiftKey], k:k.code});
    }
};

var example_code=
    'if(!wid){wid=10;}\n'+
    '// bottom of screen\n'+
    'var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);\n'+
    'c.clearRect(0,status_line_y-offs.line_height,c.canvas.width,2*offs.line_height);\n'+
    'c.fillText(cur.status(),offs.bw,status_line_y);// debug status line\n'+
    '\n'+
    'if(cur.mode==="insert"){wid=1;}\n'+
    'c.fillRect(offs.bw+cur_left_edge,ltop-offs.line_height+offs.baseline,wid,offs.line_height);\n'+
    'c.restore();\n'+
    '};\n'+
    '\n'+
    'var render_text=()=>{\n'+
    'c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);\n'+
    '// render ALL THE LINES\n'+
    'buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));\n'+
    'if(!wid){wid=10;}\n'+
    '// bottom of screen\n'+
    'var status_line_y=offs.v.y+offs.v.h-offs.ln_top(-1);\n'+
    'c.clearRect(0,status_line_y-offs.line_height,c.canvas.width,2*offs.line_height);\n'+
    'c.fillText(cur.status(),offs.bw,status_line_y);// debug status line\n'+
    '\n'+
    'if(cur.mode==="insert"){wid=1;}\n'+
    'c.fillRect(offs.bw+cur_left_edge,ltop-offs.line_height+offs.baseline,wid,offs.line_height);\n'+
    'c.restore();\n'+
    '};\n'+
    '\n'+
    'var render_text=()=>{\n'+
    'c.clearRect(0,0,c.canvas.width,offs.v.y+offs.v.h);\n'+
    '// render ALL THE LINES\n'+
    'buf.lines.forEach((ln,i)=>c.fillText(buf.getline(i),offs.bw,offs.ln_top(i)));\n'+
    '};';
buf.ins(example_code);
cur.rowcol();
<|endoftext|>


<commit_before>
<commit_msg>Update standalone.js<commit_after>
if(("standalone" in window.navigator) && window.navigator.standalone) {

  var noddy, remotes = false;

  document.addEventListener("click", function(event) {

    noddy = event.target;

    while(noddy.nodeName !== "A" && noddy.nodeName !== "HTML") {

      noddy = noddy.parentNode;

    }

    if("href" in noddy && noddy.href.indexOf("http") !== -1 && (noddy.href.indexOf(document.location.host) !== -1 || remotes)) {

      event.preventDefault();

      document.location.href = noddy.href;

    }

  }, false);
<|endoftext|>


<commit_before>import React from 'react'

const Page = ({ children, header }) => {
  return (

        {header}

        {children}

  )
}
export { Page as default }
<commit_msg>chore(page): use number for plain numeric styles<commit_after>import React from 'react'

const Page = ({ children, header }) => {
  return (

        {header}

        {children}

  )
}
export { Page as default }
<|endoftext|>


<commit_before>/**
 * @license
 * Vissense <http://vissense.com/>
 * Copyright 2014 tbk <theborakompanioni+vissense@gmail.com>
 * Available under MIT license <http://opensource.org/licenses/MIT>
 */

 /**
 * detects visibility changes of an element.
 *
 * example:
 * var elem = document.getElementById('myElement');
 * var visobj = VisSense(eleme):
 * var vismon = visobj.monitor();
 *
 * vismon.onVisibilityChange(function() { ... });
 * vismon.onVisibilityPercentageChange(function() { ... });
 * vismon.onVisible(function() { ... });
 * vismon.onFullyVisible(function() { ... });
 * vismon.onHidden(function() { ... });
 *
 *
 * hasVisibilityChanged() // => true
 * hasVisibilityPercentageChanged // => true
 *
 * fireIfVisibilityChanged(function() { ... });
 * fireIfVisibilityPercentageChanged(function() { ... });
 *
 */
;(function(window, Math, VisSense, VisSenseUtils, undefined) {
  'use strict';

    var states =  {
        HIDDEN: 0,
        VISIBLE: 1,
        FULLY_VISIBLE: 2
    };

    /*--------------------------------------------------------------------------*/

    var VisState = (function() {
        var STATES = {
            HIDDEN: 0,
            VISIBLE: 1,
            FULLY_VISIBLE: 2
        };

        function VisState(state) {
            this.isVisible = function() {
                return state ===  STATES.VISIBLE || this.isFullyVisible();
            };
            this.isFullyVisible = function() {
                return state ===  STATES.FULLY_VISIBLE;
            };
            this.isHidden = function() {
                return state ===  STATES.HIDDEN;
            };
            this.state = function() {
                return state;
            };
        }

        function withPrevious(visstate, prev) {
            if(!!prev) {
                // disable getting previous state from prev
                prev.prev = VisSenseUtils.noop;
            }

            visstate.wasVisible = function() {
                return !!prev && prev.isVisible();
            };

            visstate.wasFullyVisible = function() {
                return !!prev && prev.isFullyVisible();
            };

            visstate.wasHidden = function() {
                return !!prev && prev.isHidden();
            };

            visstate.hasVisibilityChanged = function() {
                return !prev || visstate.state() !== prev.state();
            };

            visstate.prev = function() {
                return prev;
            };

            return visstate;
        }

        function withPercentage(visstate, percentage) {
            visstate.hasVisibilityPercentageChanged = function() {
                return !this.prev() || percentage !== this.prev().percentage();
            };
            visstate.percentage = function() {
                return percentage;
            };
            return visstate;
        }

        function state(status, percentage, prev) {
            return withPrevious(withPercentage(new VisState(status), percentage), prev);
        }

        var exports = {};

        exports.hidden = function(percentage, prev) {
            return state(STATES.HIDDEN, percentage, prev || null);
        };

        exports.visible = function(percentage, prev) {
            return state(STATES.VISIBLE, percentage, prev || null);
        };
        exports.fullyvisible = function(percentage, prev) {
            return state(STATES.FULLY_VISIBLE, percentage, prev || null);
        };

        return exports;
    }());

    function nextState(visobj, visstate) {
        var percentage = visobj.getVisibilityPercentage();

        if(visobj.isHidden()) {
            return VisState.hidden(percentage, visstate);
        }
        else if (visobj.isFullyVisible()) {
             return VisState.fullyvisible(percentage, visstate);
        }
        else if (visobj.isVisible()) {
          return VisState.visible(percentage, visstate);
        }

        throw new Error('IllegalState');
    };

    /*--------------------------------------------------------------------------*/

    function fireListeners(listeners, context) {
        for(var i in listeners) {
            listeners[i].call(context || window);
        }
    }
    /*--------------------------------------------------------------------------*/

    function VisMon(visobj, config) {
        var me = this;

        var lastListenerId = -1;
        var _private = {
          status: null,
          listeners: []
        };

        // read-only access to VisSense instance
        me.visobj = function() {
            return visobj;
        };

        /**
        * read-only access to status
        */
        me.status = function(prop) {
            return _private.status;
        };

        me.getVisibilityPercentage = function() {
            return me.status().percentage();
        }
        /**
        * read-only access to status
        */
        me.prev = function(prop) {
            return me.status().prev();
        };

        // Adds a listener.
        //
        // var id = visobj.monitor().register(function() {
        //   doSomething();
        // });
        me.register = function(callback) {
            _private.listeners[++lastListenerId] = callback;
            return lastListenerId;
        };

        /**
        * expose update method
        */
        me.update = function() {
            _private.status = nextState(visobj, _private.status);

            // notify listeners
            fireListeners(_private.listeners, me);
        };
    }

    VisSense.monitor = function monitor(visobj, config) {
        return new VisMon(visobj, config);
    };

    VisSense.prototype.monitor = function(config) {
        return VisSense.monitor(this, config);
    };

    /**
    * Returns a function that invokes callback only
    * if the visibility state changes.
    *
    * shorthand for
    * if(visobj.hasVisibilityChanged()) {
    *   callback();
    * }
    * visobj.fireIfVisibilityChanged(callback)
    */
    VisMon.prototype.fireIfVisibilityChanged = function(callback) {
        var me = this;
        return VisSenseUtils.fireIf(function() {
            return me.status().hasVisibilityChanged();
        }, callback);
    };

    /**
    * Returns a function that invokes callback only
    * if visibility rate changes.
    * This does not occur when element is hidden but may
    * be called multiple times if element is in state
    * `VISIBLE` and (depending on the config) `FULLY_VISIBLE`
    */
    VisMon.prototype.fireIfVisibilityPercentageChanged = function(callback) {
        var me = this;
        return VisSenseUtils.fireIf(function() {
            return me.status().hasVisibilityPercentageChanged();
        }, callback);
    };

    /**
    * Fires when visibility state changes
    */
    VisMon.prototype.onVisibilityChange = function (callback) {
        return this.register(this.fireIfVisibilityChanged(callback));
    };

    /**
    * Fires when visibility percentage changes
    */
    VisMon.prototype.onVisibilityPercentageChange = function (callback) {
        return this.register(this.fireIfVisibilityPercentageChanged(callback));
    };

    /**
    * Fires when visibility changes and and state transits from:
    * HIDDEN => VISIBLE
    * HIDDEN => FULLY_VISIBLE
    * Fires NOT when state transits from:
    * VISIBLE => FULLY_VISIBLE or
    * FULLY_VISIBLE => VISIBLE
    *
    * VisSense(document.getElementById('example1')).monitor().onVisible(function() {
    *   Animations.startAnimation();
    * });
    */
    VisMon.prototype.onVisible = function (callback) {
        var me = this;

        var fireIfVisible =  VisSenseUtils.fireIf(function() {
            return me.status().isVisible();
        }, callback);

        // only fire when coming from state hidden or no previous state is present
        var handler = this.fireIfVisibilityChanged(VisSenseUtils.fireIf(function() {
            return !me.status().prev() || me.status().wasHidden();
        }, fireIfVisible));
        return this.register(handler);
    };

    /**
    * Fires when visibility changes and element becomes fully visible
    */
    VisMon.prototype.onFullyVisible = function (callback) {
        var me = this;
        var fireIfFullyVisible =  VisSenseUtils.fireIf(function() {
            return me.status().isFullyVisible();
        }, callback);

        var handler = this.fireIfVisibilityChanged(fireIfFullyVisible);
        return this.register(handler);
    };

    /**
    * Fires when visibility changes and element becomes hidden
    */
    VisMon.prototype.onHidden = function (callback) {
        var me = this;

        var fireIfHidden =  VisSenseUtils.fireIf(function() {
            return me.status().isHidden();
        }, callback);

        var handler = this.fireIfVisibilityChanged(fireIfHidden);
        return this.register(handler);
    };

    VisMon.prototype.on = function(eventName, handler) {
        var emitEvents = {
            'hidden' : this.onHidden,
            'visible' : this.onVisible,
            'fullyvisible' : this.onFullyVisible,
            'percentagechange' : this.onVisibilityPercentageChange,
            'visibilitychange' : this.onVisibilityChange
        };

        if(!emitEvents[eventName]) {
            throw new Error("VisMon: Event '"+ eventName +"' is not supported");
        }

        return emitEvents[eventName](handler);
    };

}.call(this, this, this.Math, this.VisSense, this.VisSenseUtils));<commit_msg>remove unused variable<commit_after>/**
 * @license
 * Vissense <http://vissense.com/>
 * Copyright 2014 tbk <theborakompanioni+vissense@gmail.com>
 * Available under MIT license <http://opensource.org/licenses/MIT>
 */

 /**
 * detects visibility changes of an element.
 *
 * example:
 * var elem = document.getElementById('myElement');
 * var visobj = VisSense(eleme):
 * var vismon = visobj.monitor();
 *
 * vismon.onVisibilityChange(function() { ... });
 * vismon.onVisibilityPercentageChange(function() { ... });
 * vismon.onVisible(function() { ... });
 * vismon.onFullyVisible(function() { ... });
 * vismon.onHidden(function() { ... });
 *
 *
 * hasVisibilityChanged() // => true
 * hasVisibilityPercentageChanged // => true
 *
 * fireIfVisibilityChanged(function() { ... });
 * fireIfVisibilityPercentageChanged(function() { ... });
 *
 */
;(function(window, Math, VisSense, VisSenseUtils, undefined) {
  'use strict';
    /*--------------------------------------------------------------------------*/

    var VisState = (function() {
        var STATES = {
            HIDDEN: 0,
            VISIBLE: 1,
            FULLY_VISIBLE: 2
        };

        function VisState(state) {
            this.isVisible = function() {
                return state ===  STATES.VISIBLE || this.isFullyVisible();
            };
            this.isFullyVisible = function() {
                return state ===  STATES.FULLY_VISIBLE;
            };
            this.isHidden = function() {
                return state ===  STATES.HIDDEN;
            };
            this.state = function() {
                return state;
            };
        }

        function withPrevious(visstate, prev) {
            if(!!prev) {
                // disable getting previous state from prev
                prev.prev = VisSenseUtils.noop;
            }

            visstate.wasVisible = function() {
                return !!prev && prev.isVisible();
            };

            visstate.wasFullyVisible = function() {
                return !!prev && prev.isFullyVisible();
            };

            visstate.wasHidden = function() {
                return !!prev && prev.isHidden();
            };

            visstate.hasVisibilityChanged = function() {
                return !prev || visstate.state() !== prev.state();
            };

            visstate.prev = function() {
                return prev;
            };

            return visstate;
        }

        function withPercentage(visstate, percentage) {
            visstate.hasVisibilityPercentageChanged = function() {
                return !this.prev() || percentage !== this.prev().percentage();
            };
            visstate.percentage = function() {
                return percentage;
            };
            return visstate;
        }

        function state(status, percentage, prev) {
            return withPrevious(withPercentage(new VisState(status), percentage), prev);
        }

        var exports = {};

        exports.hidden = function(percentage, prev) {
            return state(STATES.HIDDEN, percentage, prev || null);
        };

        exports.visible = function(percentage, prev) {
            return state(STATES.VISIBLE, percentage, prev || null);
        };
        exports.fullyvisible = function(percentage, prev) {
            return state(STATES.FULLY_VISIBLE, percentage, prev || null);
        };

        return exports;
    }());

    function nextState(visobj, visstate) {
        var percentage = visobj.getVisibilityPercentage();

        if(visobj.isHidden()) {
            return VisState.hidden(percentage, visstate);
        }
        else if (visobj.isFullyVisible()) {
             return VisState.fullyvisible(percentage, visstate);
        }
        else if (visobj.isVisible()) {
          return VisState.visible(percentage, visstate);
        }

        throw new Error('IllegalState');
    }

    /*--------------------------------------------------------------------------*/

    function fireListeners(listeners, context) {
        for(var i in listeners) {
            listeners[i].call(context || window);
        }
    }
    /*--------------------------------------------------------------------------*/

    function VisMon(visobj, config) {
        var me = this;

        var lastListenerId = -1;
        var _private = {
          status: null,
          listeners: []
        };

        // read-only access to VisSense instance
        me.visobj = function() {
            return visobj;
        };

        /**
        * read-only access to status
        */
        me.status = function(prop) {
            return _private.status;
        };

        me.getVisibilityPercentage = function() {
            return me.status().percentage();
        }
        /**
        * read-only access to status
        */
        me.prev = function(prop) {
            return me.status().prev();
        };

        // Adds a listener.
        //
        // var id = visobj.monitor().register(function() {
        //   doSomething();
        // });
        me.register = function(callback) {
            _private.listeners[++lastListenerId] = callback;
            return lastListenerId;
        };

        /**
        * expose update method
        */
        me.update = function() {
            _private.status = nextState(visobj, _private.status);

            // notify listeners
            fireListeners(_private.listeners, me);
        };
    }

    VisSense.monitor = function monitor(visobj, config) {
        return new VisMon(visobj, config);
    };

    VisSense.prototype.monitor = function(config) {
        return VisSense.monitor(this, config);
    };

    /**
    * Returns a function that invokes callback only
    * if the visibility state changes.
    *
    * shorthand for
    * if(visobj.hasVisibilityChanged()) {
    *   callback();
    * }
    * visobj.fireIfVisibilityChanged(callback)
    */
    VisMon.prototype.fireIfVisibilityChanged = function(callback) {
        var me = this;
        return VisSenseUtils.fireIf(function() {
            return me.status().hasVisibilityChanged();
        }, callback);
    };

    /**
    * Returns a function that invokes callback only
    * if visibility rate changes.
    * This does not occur when element is hidden but may
    * be called multiple times if element is in state
    * `VISIBLE` and (depending on the config) `FULLY_VISIBLE`
    */
    VisMon.prototype.fireIfVisibilityPercentageChanged = function(callback) {
        var me = this;
        return VisSenseUtils.fireIf(function() {
            return me.status().hasVisibilityPercentageChanged();
        }, callback);
    };

    /**
    * Fires when visibility state changes
    */
    VisMon.prototype.onVisibilityChange = function (callback) {
        return this.register(this.fireIfVisibilityChanged(callback));
    };

    /**
    * Fires when visibility percentage changes
    */
    VisMon.prototype.onVisibilityPercentageChange = function (callback) {
        return this.register(this.fireIfVisibilityPercentageChanged(callback));
    };

    /**
    * Fires when visibility changes and and state transits from:
    * HIDDEN => VISIBLE
    * HIDDEN => FULLY_VISIBLE
    * Fires NOT when state transits from:
    * VISIBLE => FULLY_VISIBLE or
    * FULLY_VISIBLE => VISIBLE
    *
    * VisSense(document.getElementById('example1')).monitor().onVisible(function() {
    *   Animations.startAnimation();
    * });
    */
    VisMon.prototype.onVisible = function (callback) {
        var me = this;

        var fireIfVisible =  VisSenseUtils.fireIf(function() {
            return me.status().isVisible();
        }, callback);

        // only fire when coming from state hidden or no previous state is present
        var handler = this.fireIfVisibilityChanged(VisSenseUtils.fireIf(function() {
            return !me.status().prev() || me.status().wasHidden();
        }, fireIfVisible));
        return this.register(handler);
    };

    /**
    * Fires when visibility changes and element becomes fully visible
    */
    VisMon.prototype.onFullyVisible = function (callback) {
        var me = this;
        var fireIfFullyVisible =  VisSenseUtils.fireIf(function() {
            return me.status().isFullyVisible();
        }, callback);

        var handler = this.fireIfVisibilityChanged(fireIfFullyVisible);
        return this.register(handler);
    };

    /**
    * Fires when visibility changes and element becomes hidden
    */
    VisMon.prototype.onHidden = function (callback) {
        var me = this;

        var fireIfHidden =  VisSenseUtils.fireIf(function() {
            return me.status().isHidden();
        }, callback);

        var handler = this.fireIfVisibilityChanged(fireIfHidden);
        return this.register(handler);
    };

    VisMon.prototype.on = function(eventName, handler) {
        var emitEvents = {
            'hidden' : this.onHidden,
            'visible' : this.onVisible,
            'fullyvisible' : this.onFullyVisible,
            'percentagechange' : this.onVisibilityPercentageChange,
            'visibilitychange' : this.onVisibilityChange
        };

        if(!emitEvents[eventName]) {
            throw new Error("VisMon: Event '"+ eventName +"' is not supported");
        }

        return emitEvents[eventName](handler);
    };

}.call(this, this, this.Math, this.VisSense, this.VisSenseUtils));<|endoftext|>


<commit_before>'use strict';

function CodeCtrl($scope, $http, $location, $timeout) {
  $scope.mirror = CodeMirror.fromTextArea($('#codetext')[0], {
    mode: 'python',
    lineNumbers: true,
    theme: 'neat',
    indentUnit: 2,
    extraKeys: {
      'Tab': 'indentMore',
      'Shift-Enter': false,
    },
  });

  $(document).keydown(function(event) {
    var e = window.event || event;
    if (e.keyCode == 13 && e.shiftKey) {  // shift-enter
      $scope.runCode($scope.code());
    } else if (e.keyCode == 33) {  // page up
      $scope.location.path("/" + $scope.prevChapter());
      $scope.$apply();
      return false;
    } else if (e.keyCode == 34) {  // page down
      $scope.location.path("/" + $scope.nextChapter());
      $scope.$apply();
      return false;
    }
  });

  $scope.showDiffIfDirty = function() {
    if ($scope.dirty()) {
      $scope.showDiff();
    }
  };

  $scope.doDiff = function(left, right) {
    var target = $('#diffview')[0];
    target.innerHTML = '';
    var mv = CodeMirror.MergeView(target, {
      orig: right,
      value: left,
      mode: 'python',
      lineNumbers: true,
      highlightDifferences: true,
      connect: null,
    });
  };

  $scope.showDiff = function() {
    var cw = $('#workspace_top')[0];
    // Set the width to be the min of twice the code window width and the screen width:
    var w = Math.min(cw.clientWidth * 2, screen.width);
    var h = cw.clientHeight;

    var w = window.open('#/diff/' + $scope.chapter, "DiffWin-" + $scope.chapter, "width=" + w + ",height=" + h);
    w.focus();
  };

  $scope.code = function(code) {
    if (code === undefined) {
      return $scope.mirror.getValue();
    }
    $scope.mirror.setValue(code);
  };

  $scope.location = $location;

  // This naively assumes that the dirty state is "sticky" unless you force a
  // full recomputation. Works for most purposes in the UI.
  $scope._dirty = false;
  $scope.dirty = function(force_recompute) {
    if (force_recompute || !$scope._dirty) {
      $scope._dirty = ($scope.tutorial != undefined &&
                       $scope.code() != $scope.tutorial.code);
    }
    return $scope._dirty;
  };

  $scope.storageKey = function() {
    return "pytut-" + $scope.tutorial.name;
  };

  $scope.saveCode = function() {
    if ($scope.tutorial === undefined) {
      return;
    }
    if ($scope.inDiff) {
      return;
    }
    var dirty = $scope.dirty(true);

    // Save to the internal JS cache first.
    $scope.tutorial.userCode = (dirty) ? $scope.code() : undefined;

    // Also save to HTML5 local storage if possible.
    // This protects users against refresh and browser crashes.
    if (typeof(Storage) != "undefined") {
      if ($scope.tutorial.userCode === undefined) {
        localStorage.removeItem($scope.storageKey());
      } else {
        localStorage[$scope.storageKey()] = JSON.stringify({
          time_ms: +new Date(),
          user_code: $scope.tutorial.userCode,
        });
      }
    }
  };

  $scope.loadCode = function() {
    if ($scope.tutorial === undefined) {
      return;
    }

    // Get from localStorage if possible.
    if (typeof(Storage) != "undefined") {
      var val = localStorage[$scope.storageKey()];
      if (val !== undefined) {
        // TODO: Age out old things?
        $scope.tutorial.userCode = JSON.parse(val).user_code;
      }
    }
    // If there is data in userCode now, then we display that. Otherwise we get
    // the tutorial code and display that.
    if ($scope.tutorial.userCode !== undefined) {
      $scope.code($scope.tutorial.userCode);
    } else {
      $scope.code($scope.tutorial.code);
    }
    $scope.dirty(true);  // Force dirty bit recomputation.
  };
  // TOC should be off when we start.
  $scope.tocShowing = false;

  function parseTutorial(text) {
    var lines = text.split(/\r\n|\n|\r/);
    // If the file starts with comments right at the beginning, strip them off
    // (typically directives to editors).
    var cur = 0;
    for (; cur<lines.length; cur++) {
      if (lines[cur].trim() && lines[cur].search(/^\s*#.*$/) < 0) {
        break;
      }
    }
    // Remaining text is treated as a whole.
    text = lines.slice(cur).join('\n');
    var groups = text.match(/\s*"""([^]*?)\n\s*([^]*?)"""\s+([^]*)$/m)
    if (!groups) {
      return {title: "", description: "", code: ""};
    }

    return {
      title: groups[1].replace(/\\(["'])/g, '$1'),
      description: groups[2].replace(/\\(["'])/g, '$1'),
      code: groups[3].replace(/^(\s*)__doc__\s*=\s*/, '$1'),
    };
  }

  (function() {
    $scope.tutorials = [];
    $scope._preamble = '';
    var divs = $('#chapter-contents div');
    var index = 0;
    for (var i=0, l=divs.length; i<l; i++) {
      var d = $(divs[i]);
      var name = d.attr('name');
      // Discard empty starting lines (one belongs to the end of the tag).
      var text = d.text().replace(/^\s*\n/m, '');
      // Find the indentation of the first line.
      var indent = text.match(/^(\s*)/)[1];
      // Remove indentation from all lines.
      var indent_re = new RegExp('^' + indent, 'mg');
      text = text.replace(indent_re, '');

      if (name == '__preamble__') {
        $scope._preamble = text;
      } else {
        var parsed = parseTutorial(text);
        $scope.tutorials.push({
          name: name,
          index: index++,
          title: parsed.title,
          description: parsed.description,
          code: parsed.code,
        });
      }
    }
  }());

  (function() {
    function loadTutorial(index) {
      return $scope.tutorials[index];
    }
    $scope.tutorial = loadTutorial($scope.chapter-1);
    $scope.loadCode();

    // Redirect to the first page if none is specified.
    if (!$location.path()) {
      $location.path('/1').replace();
    }

    function goToChapter(chapter) {
      if (chapter == 0) {
        // Special value - don't go to chapter 0.
        chapter = $scope.chapter;
        $scope.location.path("/" + $scope.chapter).replace();
      }
      $scope.tocShowing = false;
      if (chapter != $scope.chapter) {
        $scope.saveCode();
        $scope.chapter = chapter;
        $scope.tutorial = loadTutorial(chapter - 1);
        $scope.loadCode();
        $scope.clearOutput();
        $(document.body).scrollTop(0);
      }
    }

    // Notice when the path changes and use that to
    // navigate, but only after we actually have
    // data.
    $scope.$watch('location.path()', function(path) {
      var pieces = path.split(/[/]/).slice(1);
      var chapter = +pieces[pieces.length-1];
      // This must come before goToChapter, to avoid the diff saving code.
      $scope.inDiff = pieces[0] == 'diff';
      goToChapter(chapter);
      if ($scope.inDiff) {
        $scope.doDiff($scope.tutorial.code, $scope.code());
      }
    });
  }());

  // Set up a handy timer that watches when _time changes and starts a new
  // timeout to change it. It's a bit more elegant than creating a function
  // and creating a timeout from a timeout.
  $scope._time = new Date();
  $scope.$watch('_time', function() {
    $scope.saveCode();  // Also forces dirty bit recomputation.
    $timeout(function(){
      $scope._time = new Date();
    }, 5000);
  });

  $scope.addOutputText = function(text) {
    $scope._addText(text, "stdout");
  };

  $scope.addErrorText = function(text) {
    $scope._addText(text, "stderr");
  };

  $scope._addText = function(text, elementClass) {
    var output = $('#output');
    var scrolled = output.scrollHeight - output.clientHeight - output.scrollTop;
    var scrollDown = scrolled < 12;
    $('#output').append($('<pre>')
                        .addClass(elementClass)
                        .addClass('output-entry')
                        .text(text));
    if (scrollDown) {
      output.scrollTop = output.scrollHeight - output.clientHeight;
    }
  };

  $scope.clearOutput = function() {
    $('#output .output-entry').remove();
  };

  $scope.firstRun = true;

  // Creates a function that runs code on PyPy.js.
  // onLoaded is called when the vm has finished initializing.
  function makeRunOnPyPyJS() {
    function started() {
      $scope.$apply(function() {
        console.log("running code");
        $scope.codeRunning = true;
      });
    }
    function done() {
      $scope.$apply(function() {
        $scope.codeRunning = false;
        $scope.firstRun = false;
        console.log("done");
      });
    }
    function loading() {
      $scope.vmLoaded = false;
    }
    function loaded() {
      $scope.vmLoaded = true;
      console.log("Python loaded");
    }

    function makePythonLines(code) {
      var lines = code.split(/\n/);
      for (var i=0,l=lines.length; i<l; i++) {
        var line = lines[i];
        lines[i] = '"' + line.replace(/["\\]/g, '\\$&') + '"';
      }
      return "__lines__ = [" + lines.join(',\n') + "\n]";
    }

    loading();

    // Initialize PyPy.js
    var vm = new PyPyJS({
      stdout: $scope.addOutputText,
      stderr: $scope.addErrorText,
      autoLoadModules: false,
    });
    function run() {
      if (!$scope.vmLoaded) {
        console.log("Python not ready - ignoring request to run code")
        return;
      }
      $scope.clearOutput();
      started();
      window.setTimeout(function() {
        try {
          // Clean up the global namespace, get the help function, and create a doctest.
          vm.exec($scope._preamble + '\n' +
                  makePythonLines($scope.code()) + '\n')
          .then(function() {
            return vm.exec($scope.code())
            .catch(function(err) {
              $scope.addErrorText(err.trace);
            })
            .then(done);
          })
          .catch(function(err) {
            $scope.addErrorText(err.trace);
          })
          .then(done);
        } catch (err) {
          console.log(err);
          $scope.addErrorText("Internal Error: " + $scope.prettyError(err));
          done();
        }
      }, 100);
    }
    vm.ready.then(function() {
      loaded();
      // Prime the VM by running something useful.
      vm.loadModuleData("contextlib", "argparse");
    });
    return run;
  }

  // prettyError is used to make javascript exceptions easier on the eyes.
  $scope.prettyError = function(err) {
    // If we are dealing with Firefox, just output the message.
    if (err.stack[0] == '@') {
      return err.name + ": " + err.message;
    }
    var lines = err.stack.split(/\r\n|\r|\n/);
    var output = [lines[0]];
    // Now only keep lines that have <anonymous> as the file name, and filter
    // out irrelevant text from those.
    for (var i = 1; i < lines.length; i++) {
      var line = lines[i];
      if (line.match(/^\s*at .*runCode/)) {
        break;
      }
      line = line.replace(/\([^()]*\)/, '');
      line = line.replace(/\s*\(.*:(\d+):(\d+)\)$/, ":$1:$2");
      line = line.replace("Object.eval", "_Main_");
      output.push(line);
    }
    return output.join("\n");
  };

  // If PyPyJS loaded, we can use that. Otherwise, run this thing on the server.
  (function(document, window, undefined) {
    function hasDiffPath() {
      return $scope.location.path().split(/[/]/)[1] == 'diff';
    };

    $scope.runCode = function() {
      console.log("Can't run code yet - not wired up.");
    };
    $scope.vmLoaded = false;
    $scope.coderunning = false;
    if (hasDiffPath()) {
      // Note that we don't just check inDiff here, because it might not be set yet.
      return;
    }

    if (window.PyPyJS !== undefined) {
      console.log("Using client-side PyPy.js implementation");
      $scope.runCode = makeRunOnPyPyJS();
    } else {
      console.log("Using server-side Python implementation");
      $scope.runCode = function() {
        $scope.clearOutput();
        $http.post("/runcode", $scope.code()).success(function(data) {
          $scope.addOutputText(data.stdout);
          $scope.addErrorText(data.stderr);
        });
      };
      $scope.vmLoaded = true;
    }
  }(document, window))

  // This is useful for binding keys in the code window to do
  // nothing at all. We can't, for whatever reason, define this
  // function in-line.
  //
  // Note that we use this for Shift-Enter because it is a
  // document-wide keystroke that should run the code, but if
  // you're in the code window it causes a newline to be
  // inserted, too.
  $scope.doNothing = function(e) {}

  $scope.clearCode = function() {
    $scope.code('');
  }

  $scope.revertCode = function() {
    $scope.code($scope.tutorial.code);
    // Force dirty bit recomputation:
    $scope.dirty(true);
  };

  $scope.revertAll = function() {
    // Remove all local storage if we have it.
    if (typeof(Storage) != "undefined") {
      localStorage.clear();
    }
    $scope.revertCode();
  };

  $scope.prevChapter = function() {
    if ($scope.tutorial == undefined || $scope.tutorial.index <= 0) {
      return 0;  // special value meaning don't go there.
    }
    return $scope.tutorial.index;  // chapter - 1
  }

  $scope.nextChapter = function() {
    if ($scope.tutorial == undefined ||
        $scope.tutorial.index >= $scope.tutorials.length - 1) {
      return 0;  // special value - don't go there.
    }
    return $scope.tutorial.index + 2;  // chapter + 1
  }
}
<commit_msg>Added initial implementations of setStorage and getStorage.<commit_after>'use strict';

function setStorage(keys, val) {
  if (typeof Storage === undefined) {
    return;
  }
  var curr = localStorage;
  for (var i=0, len=keys.length; i<len-1; i++) {
    var key = keys[i];
    if (curr[key] == null) {
      curr[key] = {};
    }
    curr = curr[key];
  }
  curr[keys[keys.length-1]] = val;
}

function getStorage(keys) {
  if (typeof Storage === undefined) {
    return undefined;
  }
  var curr = localStorage;
  for (var i=0, len=keys.length; i<len; i++) {
    curr = curr[keys[i]];
  }
  return curr;
}

function CodeCtrl($scope, $http, $location, $timeout) {
  $scope.mirror = CodeMirror.fromTextArea($('#codetext')[0], {
    mode: 'python',
    lineNumbers: true,
    theme: 'neat',
    indentUnit: 2,
    extraKeys: {
      'Tab': 'indentMore',
      'Shift-Enter': false,
    },
  });

  $(document).keydown(function(event) {
    var e = window.event || event;
    if (e.keyCode == 13 && e.shiftKey) {  // shift-enter
      $scope.runCode($scope.code());
    } else if (e.keyCode == 33) {  // page up
      $scope.location.path("/" + $scope.prevChapter());
      $scope.$apply();
      return false;
    } else if (e.keyCode == 34) {  // page down
      $scope.location.path("/" + $scope.nextChapter());
      $scope.$apply();
      return false;
    }
  });

  $scope.showDiffIfDirty = function() {
    if ($scope.dirty()) {
      $scope.showDiff();
    }
  };

  $scope.doDiff = function(left, right) {
    var target = $('#diffview')[0];
    target.innerHTML = '';
    var mv = CodeMirror.MergeView(target, {
      orig: right,
      value: left,
      mode: 'python',
      lineNumbers: true,
      highlightDifferences: true,
      connect: null,
    });
  };

  $scope.showDiff = function() {
    var cw = $('#workspace_top')[0];
    // Set the width to be the min of twice the code window width and the screen width:
    var w = Math.min(cw.clientWidth * 2, screen.width);
    var h = cw.clientHeight;

    var w = window.open('#/diff/' + $scope.chapter, "DiffWin-" + $scope.chapter, "width=" + w + ",height=" + h);
    w.focus();
  };

  $scope.code = function(code) {
    if (code === undefined) {
      return $scope.mirror.getValue();
    }
    $scope.mirror.setValue(code);
  };

  $scope.location = $location;

  // This naively assumes that the dirty state is "sticky" unless you force a
  // full recomputation. Works for most purposes in the UI.
  $scope._dirty = false;
  $scope.dirty = function(force_recompute) {
    if (force_recompute || !$scope._dirty) {
      $scope._dirty = ($scope.tutorial != undefined &&
                       $scope.code() != $scope.tutorial.code);
    }
    return $scope._dirty;
  };

  $scope.storageKey = function() {
    return "pytut-" + $scope.tutorial.name;
  };

  $scope.saveCode = function() {
    if ($scope.tutorial === undefined) {
      return;
    }
    if ($scope.inDiff) {
      return;
    }
    var dirty = $scope.dirty(true);

    // Save to the internal JS cache first.
    $scope.tutorial.userCode = (dirty) ? $scope.code() : undefined;

    // Also save to HTML5 local storage if possible.
    // This protects users against refresh and browser crashes.
    if (typeof(Storage) != "undefined") {
      if ($scope.tutorial.userCode === undefined) {
        localStorage.removeItem($scope.storageKey());
      } else {
        localStorage[$scope.storageKey()] = JSON.stringify({
          time_ms: +new Date(),
          user_code: $scope.tutorial.userCode,
        });
      }
    }
  };

  $scope.loadCode = function() {
    if ($scope.tutorial === undefined) {
      return;
    }

    // Get from localStorage if possible.
    if (typeof(Storage) != "undefined") {
      var val = localStorage[$scope.storageKey()];
      if (val !== undefined) {
        // TODO: Age out old things?
        $scope.tutorial.userCode = JSON.parse(val).user_code;
      }
    }
    // If there is data in userCode now, then we display that. Otherwise we get
    // the tutorial code and display that.
    if ($scope.tutorial.userCode !== undefined) {
      $scope.code($scope.tutorial.userCode);
    } else {
      $scope.code($scope.tutorial.code);
    }
    $scope.dirty(true);  // Force dirty bit recomputation.
  };
  // TOC should be off when we start.
  $scope.tocShowing = false;

  function parseTutorial(text) {
    var lines = text.split(/\r\n|\n|\r/);
    // If the file starts with comments right at the beginning, strip them off
    // (typically directives to editors).
    var cur = 0;
    for (; cur<lines.length; cur++) {
      if (lines[cur].trim() && lines[cur].search(/^\s*#.*$/) < 0) {
        break;
      }
    }
    // Remaining text is treated as a whole.
    text = lines.slice(cur).join('\n');
    var groups = text.match(/\s*"""([^]*?)\n\s*([^]*?)"""\s+([^]*)$/m)
    if (!groups) {
      return {title: "", description: "", code: ""};
    }

    return {
      title: groups[1].replace(/\\(["'])/g, '$1'),
      description: groups[2].replace(/\\(["'])/g, '$1'),
      code: groups[3].replace(/^(\s*)__doc__\s*=\s*/, '$1'),
    };
  }

  (function() {
    $scope.tutorials = [];
    $scope._preamble = '';
    var divs = $('#chapter-contents div');
    var index = 0;
    for (var i=0, l=divs.length; i<l; i++) {
      var d = $(divs[i]);
      var name = d.attr('name');
      // Discard empty starting lines (one belongs to the end of the tag).
      var text = d.text().replace(/^\s*\n/m, '');
      // Find the indentation of the first line.
      var indent = text.match(/^(\s*)/)[1];
      // Remove indentation from all lines.
      var indent_re = new RegExp('^' + indent, 'mg');
      text = text.replace(indent_re, '');

      if (name == '__preamble__') {
        $scope._preamble = text;
      } else {
        var parsed = parseTutorial(text);
        $scope.tutorials.push({
          name: name,
          index: index++,
          title: parsed.title,
          description: parsed.description,
          code: parsed.code,
        });
      }
    }
  }());

  (function() {
    function loadTutorial(index) {
      return $scope.tutorials[index];
    }
    $scope.tutorial = loadTutorial($scope.chapter-1);
    $scope.loadCode();

    // Redirect to the first page if none is specified.
    if (!$location.path()) {
      $location.path('/1').replace();
    }

    function goToChapter(chapter) {
      if (chapter == 0) {
        // Special value - don't go to chapter 0.
        chapter = $scope.chapter;
        $scope.location.path("/" + $scope.chapter).replace();
      }
      $scope.tocShowing = false;
      if (chapter != $scope.chapter) {
        $scope.saveCode();
        $scope.chapter = chapter;
        $scope.tutorial = loadTutorial(chapter - 1);
        $scope.loadCode();
        $scope.clearOutput();
        $(document.body).scrollTop(0);
      }
    }

    // Notice when the path changes and use that to
    // navigate, but only after we actually have
    // data.
    $scope.$watch('location.path()', function(path) {
      var pieces = path.split(/[/]/).slice(1);
      var chapter = +pieces[pieces.length-1];
      // This must come before goToChapter, to avoid the diff saving code.
      $scope.inDiff = pieces[0] == 'diff';
      goToChapter(chapter);
      if ($scope.inDiff) {
        $scope.doDiff($scope.tutorial.code, $scope.code());
      }
    });
  }());

  // Set up a handy timer that watches when _time changes and starts a new
  // timeout to change it. It's a bit more elegant than creating a function
  // and creating a timeout from a timeout.
  $scope._time = new Date();
  $scope.$watch('_time', function() {
    $scope.saveCode();  // Also forces dirty bit recomputation.
    $timeout(function(){
      $scope._time = new Date();
    }, 5000);
  });

  $scope.addOutputText = function(text) {
    $scope._addText(text, "stdout");
  };

  $scope.addErrorText = function(text) {
    $scope._addText(text, "stderr");
  };

  $scope._addText = function(text, elementClass) {
    var output = $('#output');
    var scrolled = output.scrollHeight - output.clientHeight - output.scrollTop;
    var scrollDown = scrolled < 12;
    $('#output').append($('<pre>')
                        .addClass(elementClass)
                        .addClass('output-entry')
                        .text(text));
    if (scrollDown) {
      output.scrollTop = output.scrollHeight - output.clientHeight;
    }
  };

  $scope.clearOutput = function() {
    $('#output .output-entry').remove();
  };

  $scope.firstRun = true;

  // Creates a function that runs code on PyPy.js.
  // onLoaded is called when the vm has finished initializing.
  function makeRunOnPyPyJS() {
    function started() {
      $scope.$apply(function() {
        console.log("running code");
        $scope.codeRunning = true;
      });
    }
    function done() {
      $scope.$apply(function() {
        $scope.codeRunning = false;
        $scope.firstRun = false;
        console.log("done");
      });
    }
    function loading() {
      $scope.vmLoaded = false;
    }
    function loaded() {
      $scope.vmLoaded = true;
      console.log("Python loaded");
    }

    function makePythonLines(code) {
      var lines = code.split(/\n/);
      for (var i=0,l=lines.length; i<l; i++) {
        var line = lines[i];
        lines[i] = '"' + line.replace(/["\\]/g, '\\$&') + '"';
      }
      return "__lines__ = [" + lines.join(',\n') + "\n]";
    }

    loading();

    // Initialize PyPy.js
    var vm = new PyPyJS({
      stdout: $scope.addOutputText,
      stderr: $scope.addErrorText,
      autoLoadModules: false,
    });
    function run() {
      if (!$scope.vmLoaded) {
        console.log("Python not ready - ignoring request to run code")
        return;
      }
      $scope.clearOutput();
      started();
      window.setTimeout(function() {
        try {
          // Clean up the global namespace, get the help function, and create a doctest.
          vm.exec($scope._preamble + '\n' +
                  makePythonLines($scope.code()) + '\n')
          .then(function() {
            return vm.exec($scope.code())
            .catch(function(err) {
              $scope.addErrorText(err.trace);
            })
            .then(done);
          })
          .catch(function(err) {
            $scope.addErrorText(err.trace);
          })
          .then(done);
        } catch (err) {
          console.log(err);
          $scope.addErrorText("Internal Error: " + $scope.prettyError(err));
          done();
        }
      }, 100);
    }
    vm.ready.then(function() {
      loaded();
      // Prime the VM by running something useful.
      vm.loadModuleData("contextlib", "argparse");
    });
    return run;
  }

  // prettyError is used to make javascript exceptions easier on the eyes.
  $scope.prettyError = function(err) {
    // If we are dealing with Firefox, just output the message.
    if (err.stack[0] == '@') {
      return err.name + ": " + err.message;
    }
    var lines = err.stack.split(/\r\n|\r|\n/);
    var output = [lines[0]];
    // Now only keep lines that have <anonymous> as the file name, and filter
    // out irrelevant text from those.
    for (var i = 1; i < lines.length; i++) {
      var line = lines[i];
      if (line.match(/^\s*at .*runCode/)) {
        break;
      }
      line = line.replace(/\([^()]*\)/, '');
      line = line.replace(/\s*\(.*:(\d+):(\d+)\)$/, ":$1:$2");
      line = line.replace("Object.eval", "_Main_");
      output.push(line);
    }
    return output.join("\n");
  };

  // If PyPyJS loaded, we can use that. Otherwise, run this thing on the server.
  (function(document, window, undefined) {
    function hasDiffPath() {
      return $scope.location.path().split(/[/]/)[1] == 'diff';
    };

    $scope.runCode = function() {
      console.log("Can't run code yet - not wired up.");
    };
    $scope.vmLoaded = false;
    $scope.coderunning = false;
    if (hasDiffPath()) {
      // Note that we don't just check inDiff here, because it might not be set yet.
      return;
    }

    if (window.PyPyJS !== undefined) {
      console.log("Using client-side PyPy.js implementation");
      $scope.runCode = makeRunOnPyPyJS();
    } else {
      console.log("Using server-side Python implementation");
      $scope.runCode = function() {
        $scope.clearOutput();
        $http.post("/runcode", $scope.code()).success(function(data) {
          $scope.addOutputText(data.stdout);
          $scope.addErrorText(data.stderr);
        });
      };
      $scope.vmLoaded = true;
    }
  }(document, window))

  // This is useful for binding keys in the code window to do
  // nothing at all. We can't, for whatever reason, define this
  // function in-line.
  //
  // Note that we use this for Shift-Enter because it is a
  // document-wide keystroke that should run the code, but if
  // you're in the code window it causes a newline to be
  // inserted, too.
  $scope.doNothing = function(e) {}

  $scope.clearCode = function() {
    $scope.code('');
  }

  $scope.revertCode = function() {
    $scope.code($scope.tutorial.code);
    // Force dirty bit recomputation:
    $scope.dirty(true);
  };

  $scope.revertAll = function() {
    // Remove all local storage if we have it.
    if (typeof(Storage) != "undefined") {
      localStorage.clear();
    }
    $scope.revertCode();
  };

  $scope.prevChapter = function() {
    if ($scope.tutorial == undefined || $scope.tutorial.index <= 0) {
      return 0;  // special value meaning don't go there.
    }
    return $scope.tutorial.index;  // chapter - 1
  }

  $scope.nextChapter = function() {
    if ($scope.tutorial == undefined ||
        $scope.tutorial.index >= $scope.tutorials.length - 1) {
      return 0;  // special value - don't go there.
    }
    return $scope.tutorial.index + 2;  // chapter + 1
  }
}
<|endoftext|>


<commit_before>'use strict';

(function() {
	var WHEREABOUTS = [
		'working on his personal brand',
		'working on his personal gastric band',
		'at home writing medium posts',
		'fuck knows!',
		'project managing',
		'having a cigarette',
		'having a coffee and quinoa in a startup restaurant',
        'watching an art house film in a rooftop cinema',
        'in Dalston seeing a band he read about in Pitchfork Media',
        'writing Ruby',
        'in Brooklyn eating a ramen burger',
        'at a pop-up restaurant in a bathroom in Walthamstow',
        'at Lidl buying ironically unethical Brussels sprouts',
        'in Peckham, like everybody else',
        'wearing a suit and tie in the office',
        'campaigning for the Labour party in an Armani suit',
        'preening his beard',
        'applying for Alan\'s job',
        'in an artisan tailors purchasing a foppish yet sophisticated new suit'
	];

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

    function getRandomLocation() {
        return WHEREABOUTS[getRandomInt(0, WHEREABOUTS.length)];
    }

    document.querySelector('.js-whereabouts').innerHTML = getRandomLocation();
})();
<commit_msg>Whitespace<commit_after>'use strict';

(function() {
	var WHEREABOUTS = [
		'working on his personal brand',
		'working on his personal gastric band',
		'at home writing medium posts',
		'fuck knows!',
		'project managing',
		'having a cigarette',
		'having a coffee and quinoa in a startup restaurant',
        	'watching an art house film in a rooftop cinema',
        	'in Dalston seeing a band he read about in Pitchfork Media',
        	'writing Ruby',
        	'in Brooklyn eating a ramen burger',
        	'at a pop-up restaurant in a bathroom in Walthamstow',
        	'at Lidl buying ironically unethical Brussels sprouts',
        	'in Peckham, like everybody else',
        	'wearing a suit and tie in the office',
        	'campaigning for the Labour party in an Armani suit',
        	'preening his beard',
        	'applying for Alan\'s job',
        	'in an artisan tailors purchasing a foppish yet sophisticated new suit'
	];

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

    function getRandomLocation() {
        return WHEREABOUTS[getRandomInt(0, WHEREABOUTS.length)];
    }

    document.querySelector('.js-whereabouts').innerHTML = getRandomLocation();
})();
<|endoftext|>


<commit_before>'use strict';

if (!waitlist) {
	var waitlist = {};
}

waitlist.sse = (function() {
	var getMetaData = waitlist.base.getMetaData;
	var displayMessage = waitlist.base.displayMessage;

	var loadWaitlist = waitlist.listdom.loadWaitlist;
	var addFitToDom = waitlist.listdom.addFitToDom;
	var addNewEntry = waitlist.listdom.addNewEntry;
	var removeFitFromDom = waitlist.listdom.removeFitFromDom;
	var removeEntryFromDom = waitlist.listdom.removeEntryFromDom;
	var updateMissedInvite = waitlist.listdom.updateMissedInvite;

	var eventListeners = [];

	var settings = {};

	var eventSource;
	var errorCount = 0;
	function handleSSEError(event) {
		event.target.close();
		errorCount++;
		if (errorCount < 2) { // our first error reconnect this instant
			connectSSE();
		} else if (errorCount >= 2 && errorCount <= 5) {  // 2-5 errors, try
															// reconnect after
															// 1s
			setTimeout(connectSSE, 1000);
		} else { // > 5 errors try reconnect after 10s
			setTimeout(connectSSE, 10000);
		}
		event.target.close();
	}

	function handleSSEOpen(event) {
		if (errorCount > 1) {
			// refresh the page using json, to pull ALL the date, we might have
			// missed sth
			loadWaitlist();
		}
		errorCount = 0; // reset error counter
	}

	function connectSSE() {
		var wlgroup = getMetaData('wl-group-id');
		if(typeof wlgroup !== "undefined") {
			eventSource = getSSE();
		}
	}

	function fitAddedListener(event) {
		var data = JSON.parse(event.data);
		addFitToDom(data.listId, data.entryId, data.fit, data.isQueue, data.userId);
	}

	function entryAddedListener(event) {
		var data = JSON.parse(event.data);
		addNewEntry(data.listId, data.entry, data.groupId, data.isQueue);
		if (data.isQueue) {
			sendNotificationForEntry(data);
		}
	}

	function fitRemovedListener(event) {
		var data = JSON.parse(event.data);
		removeFitFromDom(data.listId, data.entryId, data.fitId);
	}

	function entryRemovedListener(event) {
		var data = JSON.parse(event.data);
		removeEntryFromDom(data.listId, data.entryId);
	}

	function missedInviteListener(event) {
		var data = JSON.parse(event.data);
		updateMissedInvite(data.userId);
	}

	function noSSE() {
		displayMessage('We have had to disable features please consider upgrading your browser', 'danger');
	}

	function getSSE() {
		var sse = new EventSource(getMetaData('api-sse')+"?events="+encodeURIComponent("waitlistUpdates,gong")+"&groupId="+encodeURIComponent(getMetaData("wl-group-id")));
		sse.onerror = handleSSEError;
		sse.onopen = handleSSEOpen;

		sse.addEventListener("fit-added", fitAddedListener);
		sse.addEventListener("fit-removed", fitRemovedListener);

		sse.addEventListener("entry-added", entryAddedListener);
		sse.addEventListener("entry-removed", entryRemovedListener);

		sse.addEventListener("invite-missed", missedInviteListener);

		for (let events of eventListeners){
		    sse.addEventListener(events.event, events.listener);
		}

		return sse;
	}

	function sendNotificationForEntry(data) {
		if (!("Notification" in window)) {
			return;
		}
		var title = "New X-UP";
		var options = {
			'body': `New X-UP from ${data.entry.character.name}`
		};
		// if we have permission
		if (Notification.permission === "granted") {
			var notification = new Notification(title, options);
		// if we are not denied (user didn't select yet
		} else if (Notification.permission !== 'denied') {
			Notification.requestPermission(function (permission) {
				// If the user accepts, let's create a notification
				if (permission === "granted") {
					var notification = new Notification(title, options);
				}
			});
		}
	}

    function addEventListener(event, listener) {
        eventListeners.push({event: event, listener: listener});
        if (typeof eventSource !== 'undefined') {
            eventSource.addEventListener(event, listener);
        }
    }

	function init() {
		settings.can_manage = (getMetaData('can-fleetcomp') === "True");
		if (!!window.EventSource) {
			connectSSE();
			loadWaitlist();
		} else {
			noSSE();
			loadWaitlist();
		}
	}

    $(document).ready(init);
	return {
	addEventListener: addEventListener
	};
})();

<commit_msg>NoSSE function update.<commit_after>'use strict';

if (!waitlist) {
	var waitlist = {};
}

waitlist.sse = (function() {
	var getMetaData = waitlist.base.getMetaData;
	var displayMessage = waitlist.base.displayMessage;

	var loadWaitlist = waitlist.listdom.loadWaitlist;
	var addFitToDom = waitlist.listdom.addFitToDom;
	var addNewEntry = waitlist.listdom.addNewEntry;
	var removeFitFromDom = waitlist.listdom.removeFitFromDom;
	var removeEntryFromDom = waitlist.listdom.removeEntryFromDom;
	var updateMissedInvite = waitlist.listdom.updateMissedInvite;

	var eventListeners = [];

	var settings = {};

	var eventSource;
	var errorCount = 0;
	function handleSSEError(event) {
		event.target.close();
		errorCount++;
		if (errorCount < 2) { // our first error reconnect this instant
			connectSSE();
		} else if (errorCount >= 2 && errorCount <= 5) {  // 2-5 errors, try
															// reconnect after
															// 1s
			setTimeout(connectSSE, 1000);
		} else { // > 5 errors try reconnect after 10s
			setTimeout(connectSSE, 10000);
		}
		event.target.close();
	}

	function handleSSEOpen(event) {
		if (errorCount > 1) {
			// refresh the page using json, to pull ALL the date, we might have
			// missed sth
			loadWaitlist();
		}
		errorCount = 0; // reset error counter
	}

	function connectSSE() {
		var wlgroup = getMetaData('wl-group-id');
		if(typeof wlgroup !== "undefined") {
			eventSource = getSSE();
		}
	}

	function fitAddedListener(event) {
		var data = JSON.parse(event.data);
		addFitToDom(data.listId, data.entryId, data.fit, data.isQueue, data.userId);
	}

	function entryAddedListener(event) {
		var data = JSON.parse(event.data);
		addNewEntry(data.listId, data.entry, data.groupId, data.isQueue);
		if (data.isQueue) {
			sendNotificationForEntry(data);
		}
	}

	function fitRemovedListener(event) {
		var data = JSON.parse(event.data);
		removeFitFromDom(data.listId, data.entryId, data.fitId);
	}

	function entryRemovedListener(event) {
		var data = JSON.parse(event.data);
		removeEntryFromDom(data.listId, data.entryId);
	}

	function missedInviteListener(event) {
		var data = JSON.parse(event.data);
		updateMissedInvite(data.userId);
	}

	function noSSE() {
		displayMessage('We have had to disable features please consider upgrading your browser', 'danger', true);
	}

	function getSSE() {
		var sse = new EventSource(getMetaData('api-sse')+"?events="+encodeURIComponent("waitlistUpdates,gong")+"&groupId="+encodeURIComponent(getMetaData("wl-group-id")));
		sse.onerror = handleSSEError;
		sse.onopen = handleSSEOpen;

		sse.addEventListener("fit-added", fitAddedListener);
		sse.addEventListener("fit-removed", fitRemovedListener);

		sse.addEventListener("entry-added", entryAddedListener);
		sse.addEventListener("entry-removed", entryRemovedListener);

		sse.addEventListener("invite-missed", missedInviteListener);

		for (let events of eventListeners){
		    sse.addEventListener(events.event, events.listener);
		}

		return sse;
	}

	function sendNotificationForEntry(data) {
		if (!("Notification" in window)) {
			return;
		}
		var title = "New X-UP";
		var options = {
			'body': `New X-UP from ${data.entry.character.name}`
		};
		// if we have permission
		if (Notification.permission === "granted") {
			var notification = new Notification(title, options);
		// if we are not denied (user didn't select yet
		} else if (Notification.permission !== 'denied') {
			Notification.requestPermission(function (permission) {
				// If the user accepts, let's create a notification
				if (permission === "granted") {
					var notification = new Notification(title, options);
				}
			});
		}
	}

    function addEventListener(event, listener) {
        eventListeners.push({event: event, listener: listener});
        if (typeof eventSource !== 'undefined') {
            eventSource.addEventListener(event, listener);
        }
    }

	function init() {
		settings.can_manage = (getMetaData('can-fleetcomp') === "True");
		if (!!window.EventSource) {
			connectSSE();
			loadWaitlist();
		} else {
			noSSE();
			loadWaitlist();
		}
	}

    $(document).ready(init);
	return {
	addEventListener: addEventListener
	};
})();

<|endoftext|>


<commit_before>const webpack = require("webpack");
const path = require("path");

module.exports = {
    entry: {
        index: "./static/dist/front/index",
    },
    output: {
        path: path.join(__dirname, "static/scripts/"),
        filename: "[name].js"
    },
    plugins: [
        new webpack.DefinePlugin({
            "process.env": {
                "NODE_ENV": JSON.stringify("production")
            }
        }),
        new webpack.NoEmitOnErrorsPlugin(),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false,
            },
            output: {
                comments: false,
            },
        }),
    ],
    resolve: {
        alias: {
            "vue$": "vue/dist/vue.min.js"
        }
    }
};
<commit_msg>fix config<commit_after>const webpack = require("webpack");
const path = require("path");

module.exports = {
    entry: {
        index: "./static/dist/front/index",
    },
    output: {
        path: path.join(__dirname, "scripts/"),
        filename: "[name].js"
    },
    plugins: [
        new webpack.DefinePlugin({
            "process.env": {
                "NODE_ENV": JSON.stringify("production")
            }
        }),
        new webpack.NoEmitOnErrorsPlugin(),
        new webpack.optimize.UglifyJsPlugin({
            compress: {
                warnings: false,
            },
            output: {
                comments: false,
            },
        }),
    ],
    resolve: {
        alias: {
            "vue$": "vue/dist/vue.min.js"
        }
    }
};
<|endoftext|>


<commit_before>var config = require('./config.js');
var knex = require('knex')({
  dialect: 'mysql',
  connection: config.mysql,
  debug: false
});

Data = {}

FIELDS_KEEP = {
  user: ['id', 'name', 'screen_name', 'location', 'url', 'description', 'protected', 'verified', 'followers_count',
  'listed_count', 'favourites_count', 'statuses_count', 'utc_offset', 'time_zone', 'geo_enabled', 'lang',
  'contributors_enabled', 'is_translator', 'default_profile', 'friends_count'],
  tweet: ['id', 'text', 'source', 'truncated', 'coordinates', 'contributors', 'retweet_count',
  'favorite_count', 'entities', 'created_at', 'favorited', 'retweeted', 'filter_level', 'possibly_sensitive', 'lang',
  'userId', 'flag']
}

var flags = ['angular','bootstrap','famous','famous_angular','ionic','meteor','backbone','bmarrionette',
'd3','ember','enyo','react','hammer','jquerymobile','jquery','kendo','knockout','polymer','sencha','threejs','titanium'];

Data.saveUser = function (tweet){
  return knex('users').insert(_pickFields(tweet.user, FIELDS_KEEP.user))
  .then(function(id){
    //object safely stored
  }, function(error) {
    //error occured, duplicate entry?
    console.log('SAVE USER ERROR', tweet);
  });
}

Data.saveTweet = function (tweet){
  tweet.userId = tweet.user.id;
  //convert string to timestamps
  tweet.created_at = Date.parse(tweet.created_at) * 0.001;
  tweet.entities = tweet.entities.toString();

  var new_tweet = _pickFields(tweet, FIELDS_KEEP.tweet);
  return knex('tweets').insert(new_tweet).then(function(data){
  }, function(error){
    console.log('SAVE TWEET ERROR', tweet);
  });
}

Data.getAllTweets = function () {
  //retrieve tweets for showing.
  return knex('tweets').select().then(function(data){
    return _formatStream(data, flags);
  });
}

_pickFields = function (object, whitelist) {
  var newObj = {};
  for(var key in whitelist) {
    var fieldName = whitelist[key];
    newObj[fieldName] = object[fieldName];
  }
  return newObj;
}

_formatStream = function (data, stream_names) {

  var data_streams = {};
  for(var i=0; i<data.length; i++){
    var record = data[i];
    if(data_streams[record.flag] == undefined){
      data_streams[record.flag] = [];
    }
    data_streams[record.flag].push(record.created_at);
  }

  return data_streams
}

module.exports = Data;
<commit_msg>using timestamp_ms instead, created_at is nonuniform<commit_after>var config = require('./config.js');
var knex = require('knex')({
  dialect: 'mysql',
  connection: config.mysql,
  debug: false
});

Data = {}

FIELDS_KEEP = {
  user: ['id', 'name', 'screen_name', 'location', 'url', 'description', 'protected', 'verified', 'followers_count',
  'listed_count', 'favourites_count', 'statuses_count', 'utc_offset', 'time_zone', 'geo_enabled', 'lang',
  'contributors_enabled', 'is_translator', 'default_profile', 'friends_count'],
  tweet: ['id', 'text', 'source', 'truncated', 'coordinates', 'contributors', 'retweet_count',
  'favorite_count', 'entities', 'created_at', 'favorited', 'retweeted', 'filter_level', 'possibly_sensitive', 'lang',
  'userId', 'flag']
}

var flags = ['angular','bootstrap','famous','famous_angular','ionic','meteor','backbone','bmarrionette',
'd3','ember','enyo','react','hammer','jquerymobile','jquery','kendo','knockout','polymer','sencha','threejs','titanium'];

Data.saveUser = function (tweet){
  debugger;
  var picked = _pickFields(tweet.user, FIELDS_KEEP.user);
  return knex('users').insert(picked)
  .then(function(id){
    //object safely stored
    debugger;
  }, function(error) {
    debugger;
    //error occured, duplicate entry?
    console.log('SAVE USER ERROR', tweet);
  });
}

Data.saveTweet = function (tweet){
  debugger;
  tweet.userId = tweet.user.id;
  //convert string to timestamps
  tweet.created_at = Math.floor(tweet.timestamp_ms * 0.001);
  tweet.entities = tweet.entities.toString();

  var new_tweet = _pickFields(tweet, FIELDS_KEEP.tweet);
  debugger;
  return knex('tweets').insert(new_tweet).then(function(data){
  }, function(error){
    debugger;
    console.log('SAVE TWEET ERROR', tweet);
  });
}

Data.getAllTweets = function () {
  //retrieve tweets for showing.
  return knex('tweets').select().then(function(data){
    return _formatStream(data, flags);
  });
}

_pickFields = function (object, whitelist) {
  var newObj = {};
  for(var key in whitelist) {
    var fieldName = whitelist[key];
    newObj[fieldName] = object[fieldName];
  }
  return newObj;
}

_formatStream = function (data, stream_names) {

  var data_streams = {};
  for(var i=0; i<data.length; i++){
    var record = data[i];
    if(data_streams[record.flag] == undefined){
      data_streams[record.flag] = [];
    }
    data_streams[record.flag].push(record.created_at);
  }

  return data_streams
}

module.exports = Data;
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ValueView',
  extends: 'foam.u2.View',

  documentation: 'Just shows the value of data as a string.',

  properties: [
    {
      name: 'prop'
    }
  ],

  methods: [
    function fromProperty(prop) {
      this.SUPER(prop);
      this.prop = prop;
    },

    function initE() {
      this.SUPER();
      var self = this;
      var prop = this.prop;

      if ( prop && prop.unitPropValueToString ) {
        this.add(this.data$.map(data => {
          return prop.unitPropValueToString(self.__subContext__, data, self.__context__.objData[prop.unitPropName]);
        }));
      } else {
        this.add(this.data$);
      }
    }
  ]
});
<commit_msg>update ValueView (#4858)<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.u2.view',
  name: 'ValueView',
  extends: 'foam.u2.View',

  documentation: 'Just shows the value of data as a string.',

  properties: [
    {
      name: 'prop'
    }
  ],

  methods: [
    function fromProperty(prop) {
      this.SUPER(prop);
      this.prop = prop;
    },

    function initE() {
      this.SUPER();
      var self = this;
      var prop = this.prop;

      if ( prop && prop.unitPropValueToString ) {
        this.add(this.data$.map(data => {
          return prop.unitPropValueToString.call(self.__subContext__.objData, self.__subContext__, data, self.__context__.objData[prop.unitPropName]);
        }));
      } else {
        this.add(this.data$);
      }
    }
  ]
});
<|endoftext|>


<commit_before>var _ = require("lodash");
var jslint = require("jslint").load('latest');
var doLint = require("jslint/lib/linter").doLint;
var through = require("through2");

var extractDirectiveLines = function (lines) {
    var directive_lines = [];
    var cur_line;
    if (/^\/\*/.test(lines[0])) {
        while (true) {
            cur_line = lines[directive_lines.length];
            directive_lines.push(cur_line);
            if (/\*\/$/.test(cur_line)) {
                break;
            }
        }
    }
    return directive_lines;
};

function supplant(string, object) {//copy pasted this from jslint
    return string.replace(/\{([^{}]*)\}/g, function (found, filling) {
        var replacement = object[filling];
        return replacement !== undefined
            ? replacement
            : found;
    });
}

var lintMe = function (js_code) {
    if (/^\/\/IM_NOT_JSLINT_WORTHY_YET\n/.test(js_code)) { //temporarally provide an escape hatch for code that we have not yet migrated to pass jslint
        return [];
    }

    var lines = js_code.split(/\n/);
    var directive_lines = extractDirectiveLines(lines);
    lines = lines.slice(directive_lines.length);

    //Wrap in a function with 'use strict';
    //This is what babelify will do, so let's teach jslint that that's what's happening
    js_code = directive_lines.join("\n") + "\n" + "(function () {\n    'use strict';\n" + _.map(lines, function (line) {
        return line.length === 0 ? "" : "    " + line;
    }).join("\n") + "\n}());";
    js_code = js_code.trim();

    var r = doLint(jslint, js_code, {
        es6: true,
        node: true, // b/c we use browserify
        browser: false //only very few files need browser globals, those files should declare that dependancy using the jslint /*global ...*/ at the top of their file
    });
    return _.filter(_.map(r.warnings, function (warning) {
        if (warning.code === 'bad_property_a' && /^__/.test(warning.a)) {
            //ignore these b/c we use dunder for private methods
            return undefined;
        }
        //fixing line and col numbers
        warning.line = warning.line - 2 + 1;
        warning.column = warning.column - 4 + 1;

        if (warning.code === 'expected_a_at_b_c') {
            warning.b = warning.b - 4 + 1;
            warning.c = warning.c - 4 + 1;
            warning.message = supplant("Expected '{a}' at column {b}, not column {c}.", warning);
        } else if(warning.code=== 'expected_a_b_from_c_d') {
            warning.c = warning.c - 4 + 1;
            warning.message = supplant("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", warning);
        }

        return warning;
    }));
};

var jslintWarningToHuman = function (file, warning) {
    var loc = file.replace(/^.*\/src\//, '') + ':' + warning.line + ',' + warning.column;
    return 'jslint: ' + warning.message + ' @ ' + loc;
};

module.exports = function (file) {
    if (!/\.js$/i.test(file)) {
        return through();
    }
    var js_code = '';
    return through(function (data, enc, done) {
        js_code += data;
        this.push(data);
        done();
    }, function (done) {
        var warnings = lintMe(js_code);
        if (_.size(warnings) === 0) {
            return done();
        }
        done(jslintWarningToHuman(file, _.first(warnings)));
    });
};
<commit_msg>some new lint rules for working with React<commit_after>var _ = require("lodash");
var jslint = require("jslint").load('latest');
var doLint = require("jslint/lib/linter").doLint;
var through = require("through2");

var extractDirectiveLines = function (lines) {
    var directive_lines = [];
    var cur_line;
    if (/^\/\*/.test(lines[0])) {
        while (true) {
            cur_line = lines[directive_lines.length];
            directive_lines.push(cur_line);
            if (/\*\/$/.test(cur_line)) {
                break;
            }
        }
    }
    return directive_lines;
};

function supplant(string, object) {//copy pasted this from jslint
    return string.replace(/\{([^{}]*)\}/g, function (found, filling) {
        var replacement = object[filling];
        return replacement !== undefined
            ? replacement
            : found;
    });
}

var lintMe = function (js_code) {
    if (/^\/\/IM_NOT_JSLINT_WORTHY_YET\n/.test(js_code)) { //temporarally provide an escape hatch for code that we have not yet migrated to pass jslint
        return [];
    }

    var lines = js_code.split(/\n/);
    var directive_lines = extractDirectiveLines(lines);
    lines = lines.slice(directive_lines.length);

    //Wrap in a function with 'use strict';
    //This is what babelify will do, so let's teach jslint that that's what's happening
    js_code = directive_lines.join("\n") + "\n" + "(function () {\n    'use strict';\n" + _.map(lines, function (line) {
        return line.length === 0 ? "" : "    " + line;
    }).join("\n") + "\n}());";
    js_code = js_code.trim();

    var r = doLint(jslint, js_code, {
        es6: true,
        node: true, // b/c we use browserify
        "this": true, // this is only allowed for working with React
        browser: false //only very few files need browser globals, those files should declare that dependancy using the jslint /*global ...*/ at the top of their file
    });
    return _.filter(_.map(r.warnings, function (warning) {
        if (warning.code === 'bad_property_a' && /^__/.test(warning.a)) {
            //ignore these b/c we use dunder for private methods
            return;
        }
        if (warning.code === 'expected_a_before_b' && warning.a === 'new' && /^[A-Z]/.test(warning.b)) {
            //ignore these b/c the React convention is to name components uppercase and not use "new"
            return;
        }
        //fixing line and col numbers
        warning.line = warning.line - 2 + 1;
        warning.column = warning.column - 4 + 1;

        if (warning.code === 'expected_a_at_b_c') {
            warning.b = warning.b - 4 + 1;
            warning.c = warning.c - 4 + 1;
            warning.message = supplant("Expected '{a}' at column {b}, not column {c}.", warning);
        } else if(warning.code=== 'expected_a_b_from_c_d') {
            warning.c = warning.c - 4 + 1;
            warning.message = supplant("Expected '{a}' to match '{b}' from line {c} and instead saw '{d}'.", warning);
        }

        return warning;
    }));
};

var jslintWarningToHuman = function (file, warning) {
    var loc = file.replace(/^.*\/src\//, '') + ':' + warning.line + ',' + warning.column;
    return 'jslint: ' + warning.message + ' @ ' + loc;
};

module.exports = function (file) {
    if (!/\.js$/i.test(file)) {
        return through();
    }
    var js_code = '';
    return through(function (data, enc, done) {
        js_code += data;
        this.push(data);
        done();
    }, function (done) {
        var warnings = lintMe(js_code);
        if (_.size(warnings) === 0) {
            return done();
        }
        done(jslintWarningToHuman(file, _.first(warnings)));
    });
};
<|endoftext|>


<commit_before>// ELMO.App
//
// handles general client side stuff that appears in the template (e.g. language chooser form)
(function(ns, klass) {

  // constructor
  ns.App = klass = function(params) { var self = this;
    self.params = params;

    // setup the language change form and link
    $("a#locale_form_link").on("click", function(){ $("#locale_form").css("display", "inline-block"); $(this).hide(); return false; });
    $("#locale_form select").on("change", function(){ self.change_locale($(this).val()); return false; });

    // set session countdown
    self.reset_session_countdown();

    // listen for any ajax calls so we can update the session countdown
    $(document).bind("ajaxComplete", function(){ self.reset_session_countdown(); });
  }

  // sets a countdown to session timeout
  klass.prototype.reset_session_countdown = function() { var self = this;
    if (self.params.logged_in) {
      // clear the old one
      if (self.session_countdown) clearTimeout(self.session_countdown);

      // set the new one (subtract 5s to account for transit times)
      self.session_countdown = setTimeout(function(){ self.redirect_to_login(); }, self.params.session_timeout * 1000 - 5000);
    }
  }

  // redirects the user to the login page
  klass.prototype.redirect_to_login = function() { var self = this;
    window.location.href = self.params.login_path;
  }

  // changes the current locale by rewriting the url to use the new locale
  klass.prototype.change_locale = function(new_locale) { var self = this;
    // get the new path with the locale stripped
    var path = self.strip_locale(window.location.pathname).replace();

    // build a new url and go there
    window.location.href = Utils.build_url(path, {locale: new_locale});
  }

  // strips any locale information from the given path
  // "/en/foo" => "/foo"; "/foo" => "/foo"; "/en/" => "/"; "/en" => "/"; "/" => "/", "" => "/";
  klass.prototype.strip_locale = function(path) {
    // replace the "/en/", "/en", "/" variants with ""
    if (path == "" || path.match(/^\/([a-z]{2}(\/)?)?$/))
      return "/";
    // else fix the "/en/foo" variants
    else
      return path.replace(/^\/[a-z]{2}\/(.+)/, function(m, $1){ return "/" + $1; });
  }

  // sets the title in h1#title and <title>
  klass.prototype.set_title = function(title) { var self = this;
    $("title").text(self.params.site_name + ": " + title)
    $("h1.title").text(title);
  }
})(ELMO);
<commit_msg>fixed session timeout<commit_after>// ELMO.App
//
// handles general client side stuff that appears in the template (e.g. language chooser form)
(function(ns, klass) {

  // constructor
  ns.App = klass = function(params) { var self = this;
    self.params = params;

    // setup the language change form and link
    $("a#locale_form_link").on("click", function(){ $("#locale_form").css("display", "inline-block"); $(this).hide(); return false; });
    $("#locale_form select").on("change", function(){ self.change_locale($(this).val()); return false; });

    // set session countdown
    self.reset_session_countdown();

    // listen for any ajax calls so we can update the session countdown
    // but don't update the countdown if the auto param is set, because those don't count
    $(document).bind("ajaxComplete", function(event, xhr, ajaxopts){
      if (!ajaxopts.url.match(/\bauto=1\b/))
        self.reset_session_countdown();
    });
  }

  // sets a countdown to session timeout
  klass.prototype.reset_session_countdown = function() { var self = this;
    if (self.params.logged_in) {
      // clear the old one
      if (self.session_countdown) clearTimeout(self.session_countdown);

      // set the new one (subtract 5s to account for transit times)
      self.session_countdown = setTimeout(function(){ self.redirect_to_login(); }, self.params.session_timeout * 1000 - 5000);
    }
  }

  // redirects the user to the login page
  klass.prototype.redirect_to_login = function() { var self = this;
    window.location.href = self.params.login_path;
  }

  // changes the current locale by rewriting the url to use the new locale
  klass.prototype.change_locale = function(new_locale) { var self = this;
    // get the new path with the locale stripped
    var path = self.strip_locale(window.location.pathname).replace();

    // build a new url and go there
    window.location.href = Utils.build_url(path, {locale: new_locale});
  }

  // strips any locale information from the given path
  // "/en/foo" => "/foo"; "/foo" => "/foo"; "/en/" => "/"; "/en" => "/"; "/" => "/", "" => "/";
  klass.prototype.strip_locale = function(path) {
    // replace the "/en/", "/en", "/" variants with ""
    if (path == "" || path.match(/^\/([a-z]{2}(\/)?)?$/))
      return "/";
    // else fix the "/en/foo" variants
    else
      return path.replace(/^\/[a-z]{2}\/(.+)/, function(m, $1){ return "/" + $1; });
  }

  // sets the title in h1#title and <title>
  klass.prototype.set_title = function(title) { var self = this;
    $("title").text(self.params.site_name + ": " + title)
    $("h1.title").text(title);
  }
})(ELMO);
<|endoftext|>