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

var _ = require("underscore"),
    wf = require("webfinger"),
    async = require("async"),
    qs = require("querystring"),
    OAuth = require("oauth").OAuth,
    DatabankObject = require("databank").DatabankObject,
    Host = require("./host"),
    Pump2Status = require("./pump2status"),
    RequestToken = require("./requesttoken");

var StatusNet = DatabankObject.subClass("statusnet");

StatusNet.schema = {
    pkey: "hostname",
    fields: ["client_id",
             "client_secret",
             "request_token_endpoint",
             "access_token_endpoint",
             "authorization_endpoint",
             "whoami_endpoint",
             "created",
             "updated"]
};

StatusNet.ensureStatusNet = function(hostname, callback) {
    StatusNet.get(hostname, function(err, statusnet) {
        if (err && err.name == "NoSuchThingError") {
            StatusNet.discover(hostname, callback);
        } else if (err) {
            callback(err, null);
        } else {
            // XXX: update endpoints?
            callback(null, statusnet);
        }
    });
};

StatusNet.discover = function(hostname, callback) {

    var props = {
        hostname: hostname
    };

    async.waterfall([
        function(callback) {
            wf.hostmeta(hostname, callback);
        },
        function(jrd, callback) {
            _.extend(props, {
                request_token_endpoint: "http://"+hostname+"/api/oauth/request_token",
                access_token_endpoint: "http://"+hostname+"/api/oauth/access_token",
                authorization_endpoint: "http://"+hostname+"/api/oauth/authorize",
                whoami_endpoint: "http://"+hostname+"/api/account/verify_credentials.json"
            });
            if (_.has(StatusNet.credentials, hostname)) {
                _.extend(props, StatusNet.credentials[hostname]);
            } else {
                _.extend(props, {
                    client_id: "anonymous",
                    client_secret: "anonymous"
                });
            }
            StatusNet.create(props, callback);
        }
    ], callback);
};

StatusNet.prototype.getRequestToken = function(callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    async.waterfall([
        function(callback) {
            oa.getOAuthRequestToken(callback);
        },
        function(token, secret, other, callback) {
            RequestToken.create({token: token,
                                 secret: secret,
                                 hostname: statusnet.hostname},
                                callback);
        }
    ], callback);
};

StatusNet.prototype.authorizeURL = function(rt, callback) {
    var statusnet = this,
        separator;

    if (_.contains(statusnet.authorization_endpoint, "?")) {
        separator = "&";
    } else {
        separator = "?";
    }

    return statusnet.authorization_endpoint + separator + "oauth_token=" + rt.token;
};

StatusNet.prototype.getAccessToken = function(rt, verifier, callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    oa.getOAuthAccessToken(rt.token, rt.secret, verifier, callback);
};

StatusNet.prototype.whoami = function(token, secret, callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    // XXX: ssl

    async.waterfall([
        function(callback) {
            oa.get(statusnet.whoami_endpoint, token, secret, callback);
        }
    ], function(err, doc, response) {
        var obj;
        if (err) {
            callback(err, null);
        } else {
            try {
                obj = JSON.parse(doc);
                callback(null, obj);
            } catch(e) {
                callback(e, null);
            }
        }
    });
};

StatusNet.prototype.getOAuth = function() {

    var statusnet = this;

    return new OAuth(statusnet.request_token_endpoint,
                     statusnet.access_token_endpoint,
                     statusnet.client_id,
                     statusnet.client_secret,
                     "1.0",
                     Pump2Status.url("/authorized/statusnet/"+statusnet.hostname),
                     "HMAC-SHA1",
                     null, // nonce size; use default
                     {"User-Agent": "pump2status.com/0.1.0"});
};

// Map of hostname => {client_id: ..., client_secret: ...}

StatusNet.credentials = {
};

module.exports = StatusNet;
<commit_msg>Check for HTTPS or HTTP on Oauth endpoints<commit_after>// statusnet.js
//
// data object representing a remote StatusNet host
//
// Copyright 2013, StatusNet Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var _ = require("underscore"),
    wf = require("webfinger"),
    async = require("async"),
    qs = require("querystring"),
    http = require("http"),
    https = require("https"),
    urlfmt = require("url").format,
    urlparse = require("url").parse,
    OAuth = require("oauth").OAuth,
    DatabankObject = require("databank").DatabankObject,
    Host = require("./host"),
    Pump2Status = require("./pump2status"),
    RequestToken = require("./requesttoken");

var StatusNet = DatabankObject.subClass("statusnet");

StatusNet.schema = {
    pkey: "hostname",
    fields: ["client_id",
             "client_secret",
             "request_token_endpoint",
             "access_token_endpoint",
             "authorization_endpoint",
             "whoami_endpoint",
             "created",
             "updated"]
};

StatusNet.ensureStatusNet = function(hostname, callback) {
    StatusNet.get(hostname, function(err, statusnet) {
        if (err && err.name == "NoSuchThingError") {
            StatusNet.discover(hostname, callback);
        } else if (err) {
            callback(err, null);
        } else {
            // XXX: update endpoints?
            callback(null, statusnet);
        }
    });
};

StatusNet.discover = function(hostname, callback) {

    var props = {
        hostname: hostname
    };

    // OAuth credentials

    if (_.has(StatusNet.credentials, hostname)) {
        _.extend(props, StatusNet.credentials[hostname]);
    } else {
        _.extend(props, {
            client_id: "anonymous",
            client_secret: "anonymous"
        });
    }

    async.waterfall([
        // We need to know whether to use HTTPS or HTTP. HEAD the config endpoint with both versions.
        function(callback) {
            async.detectSeries(['https:', 'http:'],
                               function(proto, callback) {
                                   var url = urlfmt({protocol: proto,
                                                     hostname: hostname,
                                                     path: "/api/statusnet/config.json"}),
                                       mod = (proto == 'https:') ? https : http;

                                   mod.request(url, function(resp) {
                                       if (resp.statusCode >= 200 && resp.statusCode < 300) {
                                           callback(true);
                                       } else {
                                           callback(false);
                                       }
                                   }).on('error', function(err) {
                                       callback(false);
                                   }).end();
                               },
                               function(proto) {
                                   if (proto) {
                                       callback(null, proto);
                                   } else {
                                       callback(new Error("Can't retrieve config endpoint"), null);
                                   }
                               });
        },
        function(proto, callback) {
            _.extend(props, {
                request_token_endpoint: proto+"//"+hostname+"/api/oauth/request_token",
                access_token_endpoint: proto+"//"+hostname+"/api/oauth/access_token",
                authorization_endpoint: proto+"//"+hostname+"/api/oauth/authorize",
                whoami_endpoint: proto+"//"+hostname+"/api/account/verify_credentials.json"
            });
            StatusNet.create(props, callback);
        }
    ], callback);
};

StatusNet.prototype.getRequestToken = function(callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    async.waterfall([
        function(callback) {
            oa.getOAuthRequestToken(callback);
        },
        function(token, secret, other, callback) {
            RequestToken.create({token: token,
                                 secret: secret,
                                 hostname: statusnet.hostname},
                                callback);
        }
    ], callback);
};

StatusNet.prototype.authorizeURL = function(rt, callback) {
    var statusnet = this,
        separator;

    if (_.contains(statusnet.authorization_endpoint, "?")) {
        separator = "&";
    } else {
        separator = "?";
    }

    return statusnet.authorization_endpoint + separator + "oauth_token=" + rt.token;
};

StatusNet.prototype.getAccessToken = function(rt, verifier, callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    oa.getOAuthAccessToken(rt.token, rt.secret, verifier, callback);
};

StatusNet.prototype.whoami = function(token, secret, callback) {
    var statusnet = this,
        oa = statusnet.getOAuth();

    // XXX: ssl

    async.waterfall([
        function(callback) {
            oa.get(statusnet.whoami_endpoint, token, secret, callback);
        }
    ], function(err, doc, response) {
        var obj;
        if (err) {
            callback(err, null);
        } else {
            try {
                obj = JSON.parse(doc);
                callback(null, obj);
            } catch(e) {
                callback(e, null);
            }
        }
    });
};

StatusNet.prototype.getOAuth = function() {

    var statusnet = this;

    return new OAuth(statusnet.request_token_endpoint,
                     statusnet.access_token_endpoint,
                     statusnet.client_id,
                     statusnet.client_secret,
                     "1.0",
                     Pump2Status.url("/authorized/statusnet/"+statusnet.hostname),
                     "HMAC-SHA1",
                     null, // nonce size; use default
                     {"User-Agent": "pump2status.net/0.1.0"});
};

// Map of hostname => {client_id: ..., client_secret: ...}

StatusNet.credentials = {
};

module.exports = StatusNet;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'react-proptypes';

import * as Constants from './Constants';

export default class CursorPane extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            dragStart: null,
            nwResizeStart: null,
        };
    }

    onClickCursorLayer(ev) {
        let eventToPoint = (ev) => {
            const x = ev.nativeEvent.offsetX - 2;
            const y = ev.nativeEvent.offsetY + (2 * (this.props.strokeWidth / 3));
            return [x, y];
        };

        if (this.props.mode === Constants.MODE.HAND) {
            this.context.events.startDrawing(...eventToPoint(ev));
        } else {
            this.context.events.stopDrawing();
        }
    }

    onMouseMoveCursorLayer(ev) {
        let eventToPoint = (ev) => {
            const x = ev.nativeEvent.offsetX - 2;
            const y = ev.nativeEvent.offsetY + (2 * (this.props.strokeWidth / 3));
            return [x, y];
        };

        if (this.props.mode === Constants.MODE.DRAW_LINE) {
            this.context.events.pushPoint(...eventToPoint(ev));
        } else if (this.props.mode === Constants.MODE.DRAG_IMAGE) {
            if (ev.target !== this.dragHandle) {
                return;
            }

            const lastImage = this.props.eventStore.lastImage();
            if (!lastImage) {
                return;
            }

            const base = (lastImage.width < lastImage.height) ? lastImage.width : lastImage.height;
            const unit = (base / 8) < 20 ? Math.ceil(base / 8) : 20;

            const moveX = lastImage.x + unit < 0 ? ev.nativeEvent.offsetX - this.state.dragStart.x - (lastImage.x + unit) : ev.nativeEvent.offsetX - this.state.dragStart.x;
            const moveY = lastImage.y + unit < 0 ? ev.nativeEvent.offsetY - this.state.dragStart.y - (lastImage.y + unit) : ev.nativeEvent.offsetY - this.state.dragStart.y;

            this.context.events.dragImage(moveX, moveY);
        } else if (this.props.mode === Constants.MODE.NW_RESIZE_IMAGE) {
            const lastImage = this.props.eventStore.lastImage();
            if (!lastImage) {
                return;
            }

            const moveX = lastImage.x < 0 ? ev.pageX - this.state.nwResizeStart.x - (lastImage.x ) : ev.pageX - this.state.nwResizeStart.x;
            const moveY = lastImage.y < 0 ? ev.pageY - this.state.nwResizeStart.y - (lastImage.y ) : ev.pageY - this.state.nwResizeStart.y;

            this.setState({nwResizeStart: {x: ev.pageX, y: ev.pageY}});
            this.context.events.nwResizeImage(-moveX, -moveY);
        }
    }

    onClickDragHandle(ev) {
        if (this.props.mode === Constants.MODE.HAND) {
            this.setState({dragStart: {x: ev.nativeEvent.offsetX, y: ev.nativeEvent.offsetY}});
            this.context.events.startDragging();
        } else {
            this.setState({dragStart: null});
            this.context.events.stopDragging();
        }
        ev.preventDefault();
        ev.stopPropagation();
    }

    onClickNwResizeHandle(ev) {
        if (this.props.mode === Constants.MODE.HAND) {
            this.setState({nwResizeStart: {x: ev.pageX, y: ev.pageY}});
            this.context.events.startNwResizing();
        } else {
            this.setState({nwResizeStart: null});
            this.context.events.stopNwResizing();
        }
        ev.preventDefault();
        ev.stopPropagation();
    }

    render() {
        const cursorLayerStyle = {
            position: 'absolute',
            zIndex: 2000,
            width: this.props.width,
            height: this.props.height,
            borderStyle: 'none none solid none',
            borderColor: this.props.strokeColor,
        };

        return (

                {this.renderImageHandle()}

        );
    }

    renderImageHandle() {
        const lastImage = this.props.eventStore.lastImage();
        if (!lastImage) {
            return;
        }

        const base = (lastImage.width < lastImage.height) ? lastImage.width : lastImage.height;
        const unit = (base / 8) < 20 ? Math.ceil(base / 8) : 20;

        const mathMinOrMax = (min, max, value) => {
            if (value < min) {
                return min;
            } else if (value > max) {
                return max;
            } else {
                return value;
            }
        };

        const top = mathMinOrMax(0, this.props.height, lastImage.y + unit);
        const bottom = mathMinOrMax(0, this.props.height, lastImage.y + lastImage.height - unit);
        const left = mathMinOrMax(0, this.props.width, lastImage.x + unit);
        const right = mathMinOrMax(0, this.props.width, lastImage.x + lastImage.width - unit);

        const dragHandleStyle = {
            position: 'absolute',
            zIndex: 2500,
            top: top,
            left: left,
            width: right - left,
            height: bottom - top,
            cursor: 'move',
        };

        const nwResizeHandleStyle = {
            position: 'absolute',
            zIndex: 2500,
            top: mathMinOrMax(0, this.props.height, dragHandleStyle.top - unit),
            left: mathMinOrMax(0, this.props.width, dragHandleStyle.left - unit),
            width: dragHandleStyle.left - mathMinOrMax(0, this.props.width, dragHandleStyle.left - unit),
            height: dragHandleStyle.top - mathMinOrMax(0, this.props.height, dragHandleStyle.top - unit),
            cursor: 'nw-resize',
        };

        return ([

 this.dragHandle = dragHandle} onClick={this.onClickDragHandle.bind(this)}/>,

        ]);
    }
}

CursorPane.propTypes = {
    width: PropTypes.number,
    height: PropTypes.number,
    eventStore: PropTypes.object,
    mode: PropTypes.object,
    strokeWidth: PropTypes.number,
    strokeColor: PropTypes.string,
};

CursorPane.contextTypes = {
    events: PropTypes.object,
};
<commit_msg>Don't set image height, or width, to zero<commit_after>import React from 'react';
import PropTypes from 'react-proptypes';

import * as Constants from './Constants';

export default class CursorPane extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            dragStart: null,
            nwResizeStart: null,
        };
    }

    onClickCursorLayer(ev) {
        let eventToPoint = (ev) => {
            const x = ev.nativeEvent.offsetX - 2;
            const y = ev.nativeEvent.offsetY + (2 * (this.props.strokeWidth / 3));
            return [x, y];
        };

        if (this.props.mode === Constants.MODE.HAND) {
            this.context.events.startDrawing(...eventToPoint(ev));
        } else {
            this.context.events.stopDrawing();
        }
    }

    onMouseMoveCursorLayer(ev) {
        let eventToPoint = (ev) => {
            const x = ev.nativeEvent.offsetX - 2;
            const y = ev.nativeEvent.offsetY + (2 * (this.props.strokeWidth / 3));
            return [x, y];
        };

        if (this.props.mode === Constants.MODE.DRAW_LINE) {
            this.context.events.pushPoint(...eventToPoint(ev));
        } else if (this.props.mode === Constants.MODE.DRAG_IMAGE) {
            if (ev.target !== this.dragHandle) {
                return;
            }

            const lastImage = this.props.eventStore.lastImage();
            if (!lastImage) {
                return;
            }

            const base = (lastImage.width < lastImage.height) ? lastImage.width : lastImage.height;
            const unit = (base / 8) < 20 ? Math.ceil(base / 8) : 20;

            const moveX = lastImage.x + unit < 0 ? ev.nativeEvent.offsetX - this.state.dragStart.x - (lastImage.x + unit) : ev.nativeEvent.offsetX - this.state.dragStart.x;
            const moveY = lastImage.y + unit < 0 ? ev.nativeEvent.offsetY - this.state.dragStart.y - (lastImage.y + unit) : ev.nativeEvent.offsetY - this.state.dragStart.y;

            this.context.events.dragImage(moveX, moveY);
        } else if (this.props.mode === Constants.MODE.NW_RESIZE_IMAGE) {
            const lastImage = this.props.eventStore.lastImage();
            if (!lastImage) {
                return;
            }

            const moveX = ev.pageX - this.state.nwResizeStart.x;
            const moveY = ev.pageY - this.state.nwResizeStart.y;

            if (lastImage.width < moveX || lastImage.height < moveY) {
                return;
            }

            this.setState({nwResizeStart: {x: ev.pageX, y: ev.pageY}});
            this.context.events.nwResizeImage(-moveX, -moveY);
        }
    }

    onClickDragHandle(ev) {
        if (this.props.mode === Constants.MODE.HAND) {
            this.setState({dragStart: {x: ev.nativeEvent.offsetX, y: ev.nativeEvent.offsetY}});
            this.context.events.startDragging();
        } else {
            this.setState({dragStart: null});
            this.context.events.stopDragging();
        }
        ev.preventDefault();
        ev.stopPropagation();
    }

    onClickNwResizeHandle(ev) {
        if (this.props.mode === Constants.MODE.HAND) {
            this.setState({nwResizeStart: {x: ev.pageX, y: ev.pageY}});
            this.context.events.startNwResizing();
        } else {
            this.setState({nwResizeStart: null});
            this.context.events.stopNwResizing();
        }
        ev.preventDefault();
        ev.stopPropagation();
    }

    render() {
        const cursorLayerStyle = {
            position: 'absolute',
            zIndex: 2000,
            width: this.props.width,
            height: this.props.height,
            borderStyle: 'none none solid none',
            borderColor: this.props.strokeColor,
        };

        return (

                {this.renderImageHandle()}

        );
    }

    renderImageHandle() {
        const lastImage = this.props.eventStore.lastImage();
        if (!lastImage) {
            return;
        }

        const base = (lastImage.width < lastImage.height) ? lastImage.width : lastImage.height;
        const unit = (base / 8) < 20 ? Math.ceil(base / 8) : 20;

        const mathMinOrMax = (min, max, value) => {
            if (value < min) {
                return min;
            } else if (value > max) {
                return max;
            } else {
                return value;
            }
        };

        const top = mathMinOrMax(0, this.props.height, lastImage.y + unit);
        const bottom = mathMinOrMax(0, this.props.height, lastImage.y + lastImage.height - unit);
        const left = mathMinOrMax(0, this.props.width, lastImage.x + unit);
        const right = mathMinOrMax(0, this.props.width, lastImage.x + lastImage.width - unit);

        const dragHandleStyle = {
            position: 'absolute',
            zIndex: 2500,
            top: top,
            left: left,
            width: right - left,
            height: bottom - top,
            cursor: 'move',
        };

        const nwResizeHandleStyle = {
            position: 'absolute',
            zIndex: 2500,
            top: mathMinOrMax(0, this.props.height, dragHandleStyle.top - unit),
            left: mathMinOrMax(0, this.props.width, dragHandleStyle.left - unit),
            width: dragHandleStyle.left - mathMinOrMax(0, this.props.width, dragHandleStyle.left - unit),
            height: dragHandleStyle.top - mathMinOrMax(0, this.props.height, dragHandleStyle.top - unit),
            cursor: 'nw-resize',
        };

        return ([

 this.dragHandle = dragHandle} onClick={this.onClickDragHandle.bind(this)}/>,

        ]);
    }
}

CursorPane.propTypes = {
    width: PropTypes.number,
    height: PropTypes.number,
    eventStore: PropTypes.object,
    mode: PropTypes.object,
    strokeWidth: PropTypes.number,
    strokeColor: PropTypes.string,
};

CursorPane.contextTypes = {
    events: PropTypes.object,
};
<|endoftext|>


<commit_before>define(function () {

    //region Math and number related functions and methods.
    // Number.prototype.toHex = function () {
    //     return "0123456789ABCDEF".charAt((this - (this % 16)) >> 4) + "0123456789ABCDEF".charAt(this % 16);
    // };
    Number.prototype.clamp = function (low, high) {
        return this < low ? low : this > high ? high : +this;
    };
    //end region

    //region Colour constructor.
    var Colour = function(r, g, b, a){
        r = r.clamp(0, 255);
        g = g.clamp(0, 255);
        b = b.clamp(0, 255);
        a = a.clamp(0, 255);

        //Public members
        //DAVID: TODO: this syntax is VERYYYYY sketchy
        return {
            get r()     { return r; },
            set r(value){ r = value.clamp(0, 255); },
            get g()     { return g; },
            set g(value){ g = value.clamp(0, 255); },
            get b()     { return b; },
            set b(value){ b = value.clamp(0, 255); },
            get a()     { return a; },
            set a(value){ a = value.clamp(0, 255); },

            toHex: function (){
                return "#" + r.toString(16) + g.toString(16) + b.toString(16); // r.toHex() + g.toHex() + b.toHex();
            }
        };
    };
    //endregion

    //region RGBFromHSV function
    Colour.ColourFromHSV = function(h, s, v, a){
        h = h.clamp(0, 1);
        s = s.clamp(0, 1);
        v = v.clamp(0, 1);
        a = a.clamp(0, 1);

        function HSVtoRGB(h, s, v) {
            var r, g, b, i, f, p, q, t;
            if (arguments.length === 1) {
                s = h.s, v = h.v, h = h.h;
            }
            i = Math.floor(h * 6);
            f = h * 6 - i;
            p = v * (1 - s);
            q = v * (1 - f * s);
            t = v * (1 - (1 - f) * s);
            switch (i % 6) {
                case 0: r = v; g = t; b = p; break;
                case 1: r = q; g = v; b = p; break;
                case 2: r = p; g = v; b = t; break;
                case 3: r = p; g = q; b = v; break;
                case 4: r = t; g = p; b = v; break;
                case 5: r = v; g = p; b = q; break;
            }
            return {
                r: Math.round(r * 255),
                g: Math.round(g * 255),
                b: Math.round(b * 255)
            };
        }
        var rgb = HSVtoRGB(h, s, v);
        a = Math.round(a * 255);

       return new Colour(rgb.r, rgb.g, rgb.b, a);
    };
    //endregion

    //region Static Colour instances.
    Colour.clear = new Colour(0, 0, 0, 0);
    Colour.red = new Colour(255, 0, 0, 255);
    Colour.white = new Colour(255, 255, 255, 255);
    Colour.black = new Colour(0, 0, 0, 255);

    Colour.gui = {};
    Colour.gui.base = Colour.ColourFromHSV(0, 0, 0.2, 1);

    Colour.world = {};
    Colour.world.background  = Colour.ColourFromHSV(0, 0, 0.16, 1);
    Colour.world.passive = Colour.ColourFromHSV(0, 0, 0.64, 1);
    Colour.world.focused  = Colour.ColourFromHSV(0, 0, 0.80, 1);
    Colour.world.drag= Colour.ColourFromHSV(0, 0, 0.96, 1);
    //endregion

    return Colour;
});<commit_msg>Update colour.js<commit_after>define(function () {

    //region Colour constructor.
    var Colour = function(r, g, b, a){
        r = r.clamp(0, 255);
        g = g.clamp(0, 255);
        b = b.clamp(0, 255);
        a = a.clamp(0, 255);

        //Public members
        //DAVID: TODO: this syntax is VERYYYYY sketchy
        return {
            get r()     { return r; },
            set r(value){ r = value.clamp(0, 255); },
            get g()     { return g; },
            set g(value){ g = value.clamp(0, 255); },
            get b()     { return b; },
            set b(value){ b = value.clamp(0, 255); },
            get a()     { return a; },
            set a(value){ a = value.clamp(0, 255); },

            toHex: function (){
                return "#" + r.toString(16) + g.toString(16) + b.toString(16); // r.toHex() + g.toHex() + b.toHex();
            }
        };
    };
    //endregion

    //region RGBFromHSV function
    Colour.ColourFromHSV = function(h, s, v, a){
        h = h.clamp(0, 1);
        s = s.clamp(0, 1);
        v = v.clamp(0, 1);
        a = a.clamp(0, 1);

        function HSVtoRGB(h, s, v) {
            var r, g, b, i, f, p, q, t;
            if (arguments.length === 1) {
                s = h.s, v = h.v, h = h.h;
            }
            i = Math.floor(h * 6);
            f = h * 6 - i;
            p = v * (1 - s);
            q = v * (1 - f * s);
            t = v * (1 - (1 - f) * s);
            switch (i % 6) {
                case 0: r = v; g = t; b = p; break;
                case 1: r = q; g = v; b = p; break;
                case 2: r = p; g = v; b = t; break;
                case 3: r = p; g = q; b = v; break;
                case 4: r = t; g = p; b = v; break;
                case 5: r = v; g = p; b = q; break;
            }
            return {
                r: Math.round(r * 255),
                g: Math.round(g * 255),
                b: Math.round(b * 255)
            };
        }
        var rgb = HSVtoRGB(h, s, v);
        a = Math.round(a * 255);

       return new Colour(rgb.r, rgb.g, rgb.b, a);
    };
    //endregion

    //region Static Colour instances.
    Colour.clear = new Colour(0, 0, 0, 0);
    Colour.red = new Colour(255, 0, 0, 255);
    Colour.white = new Colour(255, 255, 255, 255);
    Colour.black = new Colour(0, 0, 0, 255);

    Colour.gui = {};
    Colour.gui.base = Colour.ColourFromHSV(0, 0, 0.2, 1);

    Colour.world = {};
    Colour.world.background  = Colour.ColourFromHSV(0, 0, 0.16, 1);
    Colour.world.passive = Colour.ColourFromHSV(0, 0, 0.64, 1);
    Colour.world.focused  = Colour.ColourFromHSV(0, 0, 0.80, 1);
    Colour.world.drag= Colour.ColourFromHSV(0, 0, 0.96, 1);
    //endregion

    return Colour;
});
<|endoftext|>


<commit_before>//usr/bin/env node $0 $*; exit $?
/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
 */

/**
  The top line of this file will allow this script to be run as
  a UNIX shell script, as well as being a valid Node.js program.
 */

if (typeof WScript != 'undefined') {
  var shell = WScript.CreateObject("WScript.Shell");
  try {
    // Don't worry about passing along arguments here. It's stricly a double-click convenience.
    var ret = shell.Run('cmd /c node "' + WScript.ScriptFullName + '" --pause_on_exit', 1, true);
  } catch (e) {
    shell.Popup('NodeJS is not installed. Please install it from http://nodejs.org');
    ret = 1;
  }
  WScript.Quit(ret);
}

process.on('uncaughtException', function(e) {
  fatal('Uncaught exception: ' + e);
});

var childProcess = require('child_process');
var fs = require('fs');
var path = require('path');

var commandLineFlags = {};
var commandLineArgs = process.argv.slice(2).filter(function(arg) {
  if (arg.slice(0, 2) == '--') {
    commandLineFlags[arg.slice(2)] = true;
  }
  return arg.slice(0, 2) != '--';
});

var origDir = process.cwd();
var isWindows = process.platform.slice(0, 3) == 'win';
var eventQueue = [];
var scriptDir = path.dirname(process.argv[1]);
var scriptName = path.basename(process.argv[1]);
var hasAndroidSdk = false;
var hasAndroidPlatform = false;
var hasXcode = false;

function exit(code) {
  if (eventQueue) {
    eventQueue.length = 0;
  }
  if (commandLineFlags['pause_on_exit']) {
    waitForKey(function() {
      process.exit(code);
    });
  } else {
    process.exit(code);
  }
}

function fatal(msg) {
  console.error(msg);
  exit(1);
}

function exec(cmd, onSuccess, opt_onError, opt_silent) {
  var onError = opt_onError || function(e) {
    fatal('command failed: ' + cmd + '\n' + e);
  };
  if (!opt_silent) {
    console.log('Running: ' + cmd);
  }
  childProcess.exec(cmd, function(error, stdout, stderr) {
    if (error) {
      onError(error);
    } else {
      onSuccess(stdout.trim());
    }
  });
}

function sudo(cmd, onSuccess, opt_onError, silent) {
  if (!isWindows) {
    cmd = 'sudo ' + cmd;
  }
  exec(cmd, onSuccess, opt_onError, silent);
}

function cordovaCmd(args) {
  return '"' + process.argv[0] + '" "' + path.join(scriptDir, 'cordova-cli', 'bin', 'cordova') + '" ' + args.join(' ');
}

function chdir(d) {
  d = path.resolve(scriptDir, d);
  if (process.cwd() != d) {
    console.log('Changing directory to: ' + d);
    process.chdir(d);
  }
}

function copyFile(src, dst, callback) {
  var rd = fs.createReadStream(src);
  var wr = fs.createWriteStream(dst);
  wr.on('error', function(err) {
    fatal('Copy file error: ' + err);
  });
  wr.on('close', callback);
  rd.pipe(wr);
}

function recursiveDelete(dirPath) {
  if (fs.existsSync(dirPath)) {
    console.log('Deleting: ' + dirPath);
    helper(dirPath);
  }
  function helper(dirPath) {
    try {
       var files = fs.readdirSync(dirPath);
    } catch(e) {
      return;
    }
    for (var i = 0; i < files.length; i++) {
      var filePath = path.join(dirPath, files[i]);
      fs.chmodSync(filePath, '777');
      if (fs.statSync(filePath).isFile()) {
        fs.unlinkSync(filePath);
      } else {
        helper(filePath);
      }
    }
    fs.rmdirSync(dirPath);
  }
}

function waitForKey(callback) {
  console.log('press a key');
  function cont(key) {
    if (key == '\u0003') {
      process.exit(2);
    }
    process.stdin.removeListener('data', cont);
    process.stdin.pause();
    callback();
  }
  process.stdin.resume();
  process.stdin.setRawMode(true);
  process.stdin.on('data', cont);
}

function pump() {
  if (eventQueue.length) {
    eventQueue.shift()(pump);
  }
}

////////////////////////// INIT LOGIC //////////////////////////
function initRepoMain() {
  function checkGit(callback) {
    var errMsg = 'git is not installed (or not available on your PATH). Please install it from http://git-scm.com';
    exec('git --version', callback, function() {
      if (isWindows) {
        // See if it's at the default install path.
        process.env['PATH'] += ';' + path.join(process.env['ProgramFiles'], 'Git', 'bin');
        exec('git --version', callback, function() {
          fatal(errMsg);
        }, true);
      } else {
        fatal(errMsg);
      }
    }, true);
  }

  function computeGitVersion(callback) {
    exec('git describe --tags --long', function(stdout) {
      var version = stdout.replace(/^2.5.0-.*?-/, 'dev-');
      callback(version);
    }, null, true);
  }

  function checkOutSelf(callback) {
    console.log('## Checking Out mobile-chrome-apps');
    // If the repo doesn't exist where the script is, then use the CWD as the checkout location.
    var requiresClone = true;
    // First - try the directory of the script.
    if (scriptDir.slice(0, 2) != '\\\\') {
      process.chdir(scriptDir);
      requiresClone = !fs.existsSync('.git');
    }
    // Next - try the CWD.
    if (requiresClone) {
      scriptDir = origDir;
      process.chdir(scriptDir);
      requiresClone = !fs.existsSync('.git');
    }
    // Next - see if it exists within the CWD.
    if (requiresClone) {
      if (fs.existsSync(path.join(origDir, 'mobile-chrome-apps'))) {
        scriptDir = path.join(origDir, 'mobile-chrome-apps');
        process.chdir(scriptDir);
        requiresClone = !fs.existsSync('.git');
      }
    }
    if (requiresClone) {
      scriptDir = origDir;
      chdir(origDir);
      recursiveDelete('mobile-chrome-apps');
      exec('git clone "https://github.com/MobileChromeApps/mobile-chrome-apps.git"', function() {
        scriptDir = path.join(origDir, 'mobile-chrome-apps');
        chdir(scriptDir);
        console.log('Successfully cloned mobile-chrome-apps repo. Delegating to checked-out version of ' + scriptName);
        // TODO: We should quote the args.
        exec('"' + process.argv[0] + '" ' + scriptName + ' ' + process.argv.slice(2).join(' '), function() {
          exit(0);
        });
      });
    } else {
      callback();
    }
  }

  function checkOutSubModules(callback) {
    console.log('## Checking Out SubModules');
    chdir(scriptDir);
    exec('git pull --rebase', function() {
      exec('git submodule init', function() {
        exec('git submodule update', function() {
          chdir(path.join(scriptDir, 'cordova-cli'));
          exec('git submodule init', function() {
            exec('git submodule update', callback);
          });
        });
      });
    });
  }

  function buildCordovaJs(callback) {
    console.log('## Building cordova-js');
    chdir(path.join(scriptDir, 'cordova-js'));
    var needsBuildJs = true;
    computeGitVersion(function(version) {
      if (fs.existsSync('pkg/cordova.ios.js')) {
        needsBuildJs = (fs.readFileSync('pkg/cordova.ios.js').toString().indexOf(version) == -1);
      }
      if (needsBuildJs) {
        console.log('CordovaJS needs to be built.');
        var packager = require('./cordova-js/build/packager');
        packager.generate('ios', version);
        packager.generate('android', version);
      } else {
        console.log('CordovaJS is up-to-date.');
      }
      callback();
    });
  }

  function initCli(callback) {
    console.log('## Setting up cordova-cli');
    // TODO: Figure out when this should be re-run (e.g. upon update).
    if (fs.existsSync('cordova-cli/node_modules')) {
      //console.log('cordova-cli already has its dependencies installed.');
      callback();
    } else {
      process.chdir(path.join(scriptDir, 'cordova-cli'));
      exec('npm install', function() {
        process.chdir(origDir);
        callback();
      });
    }
  }

  eventQueue.push(checkGit);
  eventQueue.push(checkOutSelf);
  eventQueue.push(checkOutSubModules);
  eventQueue.push(buildCordovaJs);
  eventQueue.push(initCli);
}

function createAppMain(appName) {
  if (!/\w+\.\w+\.\w+/.exec(appName)) {
    fatal('App Name must follow the pattern: com.company.id');
  }
  function createApp(callback) {
    console.log('## Creating Your Application');
    chdir(origDir);
    var name = /\w+\.(\w+)$/.exec(appName)[1];

    // TODO: add android.
    var cmds = [];
    if (hasXcode) {
      cmds.push(['platform', 'add', 'ios']);
    }
    if (hasAndroidSdk) {
      cmds.push(['platform', 'add', 'android']);
    }
    ['bootstrap', 'common', 'file-chooser', 'fileSystem', 'i18n', 'identity', 'socket', 'storage'].forEach(function(pluginName) {
      cmds.push(['plugin', 'add', path.join(scriptDir, 'chrome-cordova', 'plugins', pluginName)]);
    });

    function runCmd() {
      var curCmd = cmds.shift();
      if (curCmd) {
        console.log(curCmd.join(' '));
        exec(cordovaCmd(curCmd), runCmd, undefined, true);
      } else {
        // Create a script that runs update.js.
        if (isWindows) {
          fs.writeFileSync('mca_update.bat', '"' + process.argv[0] + '" "' + path.join(scriptDir, scriptName) + '" --update_app');
        } else {
          fs.writeFileSync('mca_update.sh', '#!/bin/sh\ncd "`dirname "$0"`"\n"' + process.argv[0] + '" "' + path.join(scriptDir, scriptName) + '" --update_app');
          fs.chmodSync('mca_update.sh', '777');
        }
        callback();
      }
    }

    var curCmd = ['create', name, appName, name];
    console.log(curCmd.join(' '));
    exec(cordovaCmd(curCmd), function() {
      chdir(path.join(origDir, name));
      runCmd();
    }, undefined, true);
  }

  function setAccessTag(callback) {
    console.log('## Setting <access> tag');
    var configFilePath = path.join('app', 'config.xml');
    if (!fs.existsSync(configFilePath)) {
      fatal('Expected file to exist: ' + configFilePath);
    }

    // Manipulating XML with Regex: See http://stackoverflow.com/a/1732454
    var contents = fs.readFileSync(configFilePath, 'utf8');
    contents = contents.replace(/(access.*)/,"$1\n    <access origin=\"chrome-extension://*\" />");
    fs.writeFileSync(configFilePath, contents);

    callback();
  }

  eventQueue.push(createApp);
  eventQueue.push(setAccessTag);
  eventQueue.push(function(callback) { updateMain(); callback(); });
}

function updateMain() {
  var hasAndroid = fs.existsSync(path.join('platforms', 'android'));
  var hasIos = fs.existsSync(path.join('platforms', 'ios'));

  if (!fs.existsSync('platforms')) {
    fatal('No platforms directory found. Please run script from the root of your project.');
  }

  function runPrepare(callback) {
    console.log('## Preparing your project')
    exec(cordovaCmd(['prepare']), callback, undefined, true);
  }

  function assetDirForPlatform(platform) {
    if (platform === 'android') {
      return path.join('platforms', platform, 'assets','www');
    }
    return path.join('platforms', platform, 'www');
  }

  function createAddJsStep(platform) {
    return function(callback) {
      console.log('## Updating cordova.js for ' + platform);
      copyFile(path.join(scriptDir, 'cordova-js', 'pkg', 'cordova.' + platform + '.js'), path.join(assetDirForPlatform(platform), 'cordova.js'), callback);
    };
  }

  eventQueue.push(runPrepare);
  if (hasAndroid) {
    eventQueue.push(createAddJsStep('android'));
  }
  if (hasIos) {
    eventQueue.push(createAddJsStep('ios'));
  }
}

function parseTargetOutput(targetOutput) {
  var targets = [];
  var target;
  var targetRe = /^id: (\d+) or "([^"]*)"/gm;
  while (target = targetRe.exec(targetOutput)) {
    targets.push(target[2]);
  }
  return targets;
}

function toolsCheckMain() {
  console.log('## Checking that tools are installed');
  function checkAndroid(callback) {
    exec('android list targets', function(targetOutput) {
      hasAndroidSdk = true;
      console.log('Android SDK is installed.');
      var targets = parseTargetOutput(targetOutput);
      /* This is the android SDK version declared in cordova-android/framework/project.properties */
      if (targets.length === 0) {
          console.log('No Android Platforms are installed');
      } else if (targets.indexOf('Google Inc.:Google APIs:17') > -1) {
          hasAndroidPlatform = true;
          console.log('Android 4.2.2 (Google APIs) Platform is installed.');
      } else {
          console.log('Android 4.2.2 (Google APIs) Platform is not installed.');
      }
      callback();
    }, function() {
      console.log('Android SDK is not installed.');
      callback();
    }, true);
  }
  function checkXcode(callback) {
    if (process.platform == 'darwin') {
      exec('which xcodebuild', function() {
        hasXcode = true;
        console.log('Xcode is installed.');
        callback();
      }, function() {
        console.log('Xcode is not installed.');
        callback();
      }, true);
    } else {
      callback();
    }
  }
  function checkAtLeastOneTool(callback) {
    if (!hasAndroidPlatform && !hasXcode) {
      if (process.platform == 'darwin') {
        fatal('No usable build environment could be found. Please install either XCode or the\nAndroid SDK (with the Android 4.2.2 platform and Google APIs) and try again.');
      } else {
        fatal('No usable build environment could be found. Please install the Android SDK (with\nthe Android 4.2.2 platform and Google APIs), make sure that android is on your\npath, and try again.');
      }
    }
    callback();
  }
  eventQueue.push(checkAndroid);
  eventQueue.push(checkXcode);
  eventQueue.push(checkAtLeastOneTool);
}
function main() {
  toolsCheckMain();
  initRepoMain();
  if (commandLineFlags['update_app']) {
    updateMain();
  } else {
    var appName = commandLineArgs[0];
    if (appName) {
      createAppMain(appName);
    }
  }
  pump();
}
main();
<commit_msg>mca_update.js -> mca-update.js<commit_after>//usr/bin/env node $0 $*; exit $?
/**
  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing,
  software distributed under the License is distributed on an
  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  KIND, either express or implied.  See the License for the
  specific language governing permissions and limitations
  under the License.
 */

/**
  The top line of this file will allow this script to be run as
  a UNIX shell script, as well as being a valid Node.js program.
 */

if (typeof WScript != 'undefined') {
  var shell = WScript.CreateObject("WScript.Shell");
  try {
    // Don't worry about passing along arguments here. It's stricly a double-click convenience.
    var ret = shell.Run('cmd /c node "' + WScript.ScriptFullName + '" --pause_on_exit', 1, true);
  } catch (e) {
    shell.Popup('NodeJS is not installed. Please install it from http://nodejs.org');
    ret = 1;
  }
  WScript.Quit(ret);
}

process.on('uncaughtException', function(e) {
  fatal('Uncaught exception: ' + e);
});

var childProcess = require('child_process');
var fs = require('fs');
var path = require('path');

var commandLineFlags = {};
var commandLineArgs = process.argv.slice(2).filter(function(arg) {
  if (arg.slice(0, 2) == '--') {
    commandLineFlags[arg.slice(2)] = true;
  }
  return arg.slice(0, 2) != '--';
});

var origDir = process.cwd();
var isWindows = process.platform.slice(0, 3) == 'win';
var eventQueue = [];
var scriptDir = path.dirname(process.argv[1]);
var scriptName = path.basename(process.argv[1]);
var hasAndroidSdk = false;
var hasAndroidPlatform = false;
var hasXcode = false;

function exit(code) {
  if (eventQueue) {
    eventQueue.length = 0;
  }
  if (commandLineFlags['pause_on_exit']) {
    waitForKey(function() {
      process.exit(code);
    });
  } else {
    process.exit(code);
  }
}

function fatal(msg) {
  console.error(msg);
  exit(1);
}

function exec(cmd, onSuccess, opt_onError, opt_silent) {
  var onError = opt_onError || function(e) {
    fatal('command failed: ' + cmd + '\n' + e);
  };
  if (!opt_silent) {
    console.log('Running: ' + cmd);
  }
  childProcess.exec(cmd, function(error, stdout, stderr) {
    if (error) {
      onError(error);
    } else {
      onSuccess(stdout.trim());
    }
  });
}

function sudo(cmd, onSuccess, opt_onError, silent) {
  if (!isWindows) {
    cmd = 'sudo ' + cmd;
  }
  exec(cmd, onSuccess, opt_onError, silent);
}

function cordovaCmd(args) {
  return '"' + process.argv[0] + '" "' + path.join(scriptDir, 'cordova-cli', 'bin', 'cordova') + '" ' + args.join(' ');
}

function chdir(d) {
  d = path.resolve(scriptDir, d);
  if (process.cwd() != d) {
    console.log('Changing directory to: ' + d);
    process.chdir(d);
  }
}

function copyFile(src, dst, callback) {
  var rd = fs.createReadStream(src);
  var wr = fs.createWriteStream(dst);
  wr.on('error', function(err) {
    fatal('Copy file error: ' + err);
  });
  wr.on('close', callback);
  rd.pipe(wr);
}

function recursiveDelete(dirPath) {
  if (fs.existsSync(dirPath)) {
    console.log('Deleting: ' + dirPath);
    helper(dirPath);
  }
  function helper(dirPath) {
    try {
       var files = fs.readdirSync(dirPath);
    } catch(e) {
      return;
    }
    for (var i = 0; i < files.length; i++) {
      var filePath = path.join(dirPath, files[i]);
      fs.chmodSync(filePath, '777');
      if (fs.statSync(filePath).isFile()) {
        fs.unlinkSync(filePath);
      } else {
        helper(filePath);
      }
    }
    fs.rmdirSync(dirPath);
  }
}

function waitForKey(callback) {
  console.log('press a key');
  function cont(key) {
    if (key == '\u0003') {
      process.exit(2);
    }
    process.stdin.removeListener('data', cont);
    process.stdin.pause();
    callback();
  }
  process.stdin.resume();
  process.stdin.setRawMode(true);
  process.stdin.on('data', cont);
}

function pump() {
  if (eventQueue.length) {
    eventQueue.shift()(pump);
  }
}

////////////////////////// INIT LOGIC //////////////////////////
function initRepoMain() {
  function checkGit(callback) {
    var errMsg = 'git is not installed (or not available on your PATH). Please install it from http://git-scm.com';
    exec('git --version', callback, function() {
      if (isWindows) {
        // See if it's at the default install path.
        process.env['PATH'] += ';' + path.join(process.env['ProgramFiles'], 'Git', 'bin');
        exec('git --version', callback, function() {
          fatal(errMsg);
        }, true);
      } else {
        fatal(errMsg);
      }
    }, true);
  }

  function computeGitVersion(callback) {
    exec('git describe --tags --long', function(stdout) {
      var version = stdout.replace(/^2.5.0-.*?-/, 'dev-');
      callback(version);
    }, null, true);
  }

  function checkOutSelf(callback) {
    console.log('## Checking Out mobile-chrome-apps');
    // If the repo doesn't exist where the script is, then use the CWD as the checkout location.
    var requiresClone = true;
    // First - try the directory of the script.
    if (scriptDir.slice(0, 2) != '\\\\') {
      process.chdir(scriptDir);
      requiresClone = !fs.existsSync('.git');
    }
    // Next - try the CWD.
    if (requiresClone) {
      scriptDir = origDir;
      process.chdir(scriptDir);
      requiresClone = !fs.existsSync('.git');
    }
    // Next - see if it exists within the CWD.
    if (requiresClone) {
      if (fs.existsSync(path.join(origDir, 'mobile-chrome-apps'))) {
        scriptDir = path.join(origDir, 'mobile-chrome-apps');
        process.chdir(scriptDir);
        requiresClone = !fs.existsSync('.git');
      }
    }
    if (requiresClone) {
      scriptDir = origDir;
      chdir(origDir);
      recursiveDelete('mobile-chrome-apps');
      exec('git clone "https://github.com/MobileChromeApps/mobile-chrome-apps.git"', function() {
        scriptDir = path.join(origDir, 'mobile-chrome-apps');
        chdir(scriptDir);
        console.log('Successfully cloned mobile-chrome-apps repo. Delegating to checked-out version of ' + scriptName);
        // TODO: We should quote the args.
        exec('"' + process.argv[0] + '" ' + scriptName + ' ' + process.argv.slice(2).join(' '), function() {
          exit(0);
        });
      });
    } else {
      callback();
    }
  }

  function checkOutSubModules(callback) {
    console.log('## Checking Out SubModules');
    chdir(scriptDir);
    exec('git pull --rebase', function() {
      exec('git submodule init', function() {
        exec('git submodule update', function() {
          chdir(path.join(scriptDir, 'cordova-cli'));
          exec('git submodule init', function() {
            exec('git submodule update', callback);
          });
        });
      });
    });
  }

  function buildCordovaJs(callback) {
    console.log('## Building cordova-js');
    chdir(path.join(scriptDir, 'cordova-js'));
    var needsBuildJs = true;
    computeGitVersion(function(version) {
      if (fs.existsSync('pkg/cordova.ios.js')) {
        needsBuildJs = (fs.readFileSync('pkg/cordova.ios.js').toString().indexOf(version) == -1);
      }
      if (needsBuildJs) {
        console.log('CordovaJS needs to be built.');
        var packager = require('./cordova-js/build/packager');
        packager.generate('ios', version);
        packager.generate('android', version);
      } else {
        console.log('CordovaJS is up-to-date.');
      }
      callback();
    });
  }

  function initCli(callback) {
    console.log('## Setting up cordova-cli');
    // TODO: Figure out when this should be re-run (e.g. upon update).
    if (fs.existsSync('cordova-cli/node_modules')) {
      //console.log('cordova-cli already has its dependencies installed.');
      callback();
    } else {
      process.chdir(path.join(scriptDir, 'cordova-cli'));
      exec('npm install', function() {
        process.chdir(origDir);
        callback();
      });
    }
  }

  eventQueue.push(checkGit);
  eventQueue.push(checkOutSelf);
  eventQueue.push(checkOutSubModules);
  eventQueue.push(buildCordovaJs);
  eventQueue.push(initCli);
}

function createAppMain(appName) {
  if (!/\w+\.\w+\.\w+/.exec(appName)) {
    fatal('App Name must follow the pattern: com.company.id');
  }
  function createApp(callback) {
    console.log('## Creating Your Application');
    chdir(origDir);
    var name = /\w+\.(\w+)$/.exec(appName)[1];

    // TODO: add android.
    var cmds = [];
    if (hasXcode) {
      cmds.push(['platform', 'add', 'ios']);
    }
    if (hasAndroidSdk) {
      cmds.push(['platform', 'add', 'android']);
    }
    ['bootstrap', 'common', 'file-chooser', 'fileSystem', 'i18n', 'identity', 'socket', 'storage'].forEach(function(pluginName) {
      cmds.push(['plugin', 'add', path.join(scriptDir, 'chrome-cordova', 'plugins', pluginName)]);
    });

    function runCmd() {
      var curCmd = cmds.shift();
      if (curCmd) {
        console.log(curCmd.join(' '));
        exec(cordovaCmd(curCmd), runCmd, undefined, true);
      } else {
        // Create a script that runs update.js.
        if (isWindows) {
          fs.writeFileSync('mca-update.bat', '"' + process.argv[0] + '" "' + path.join(scriptDir, scriptName) + '" --update_app');
        } else {
          fs.writeFileSync('mca-update.sh', '#!/bin/sh\ncd "`dirname "$0"`"\n"' + process.argv[0] + '" "' + path.join(scriptDir, scriptName) + '" --update_app');
          fs.chmodSync('mca-update.sh', '777');
        }
        callback();
      }
    }

    var curCmd = ['create', name, appName, name];
    console.log(curCmd.join(' '));
    exec(cordovaCmd(curCmd), function() {
      chdir(path.join(origDir, name));
      runCmd();
    }, undefined, true);
  }

  function setAccessTag(callback) {
    console.log('## Setting <access> tag');
    var configFilePath = path.join('app', 'config.xml');
    if (!fs.existsSync(configFilePath)) {
      fatal('Expected file to exist: ' + configFilePath);
    }

    // Manipulating XML with Regex: See http://stackoverflow.com/a/1732454
    var contents = fs.readFileSync(configFilePath, 'utf8');
    contents = contents.replace(/(access.*)/,"$1\n    <access origin=\"chrome-extension://*\" />");
    fs.writeFileSync(configFilePath, contents);

    callback();
  }

  eventQueue.push(createApp);
  eventQueue.push(setAccessTag);
  eventQueue.push(function(callback) { updateMain(); callback(); });
}

function updateMain() {
  var hasAndroid = fs.existsSync(path.join('platforms', 'android'));
  var hasIos = fs.existsSync(path.join('platforms', 'ios'));

  if (!fs.existsSync('platforms')) {
    fatal('No platforms directory found. Please run script from the root of your project.');
  }

  function runPrepare(callback) {
    console.log('## Preparing your project')
    exec(cordovaCmd(['prepare']), callback, undefined, true);
  }

  function assetDirForPlatform(platform) {
    if (platform === 'android') {
      return path.join('platforms', platform, 'assets','www');
    }
    return path.join('platforms', platform, 'www');
  }

  function createAddJsStep(platform) {
    return function(callback) {
      console.log('## Updating cordova.js for ' + platform);
      copyFile(path.join(scriptDir, 'cordova-js', 'pkg', 'cordova.' + platform + '.js'), path.join(assetDirForPlatform(platform), 'cordova.js'), callback);
    };
  }

  eventQueue.push(runPrepare);
  if (hasAndroid) {
    eventQueue.push(createAddJsStep('android'));
  }
  if (hasIos) {
    eventQueue.push(createAddJsStep('ios'));
  }
}

function parseTargetOutput(targetOutput) {
  var targets = [];
  var target;
  var targetRe = /^id: (\d+) or "([^"]*)"/gm;
  while (target = targetRe.exec(targetOutput)) {
    targets.push(target[2]);
  }
  return targets;
}

function toolsCheckMain() {
  console.log('## Checking that tools are installed');
  function checkAndroid(callback) {
    exec('android list targets', function(targetOutput) {
      hasAndroidSdk = true;
      console.log('Android SDK is installed.');
      var targets = parseTargetOutput(targetOutput);
      /* This is the android SDK version declared in cordova-android/framework/project.properties */
      if (targets.length === 0) {
          console.log('No Android Platforms are installed');
      } else if (targets.indexOf('Google Inc.:Google APIs:17') > -1) {
          hasAndroidPlatform = true;
          console.log('Android 4.2.2 (Google APIs) Platform is installed.');
      } else {
          console.log('Android 4.2.2 (Google APIs) Platform is not installed.');
      }
      callback();
    }, function() {
      console.log('Android SDK is not installed.');
      callback();
    }, true);
  }
  function checkXcode(callback) {
    if (process.platform == 'darwin') {
      exec('which xcodebuild', function() {
        hasXcode = true;
        console.log('Xcode is installed.');
        callback();
      }, function() {
        console.log('Xcode is not installed.');
        callback();
      }, true);
    } else {
      callback();
    }
  }
  function checkAtLeastOneTool(callback) {
    if (!hasAndroidPlatform && !hasXcode) {
      if (process.platform == 'darwin') {
        fatal('No usable build environment could be found. Please install either XCode or the\nAndroid SDK (with the Android 4.2.2 platform and Google APIs) and try again.');
      } else {
        fatal('No usable build environment could be found. Please install the Android SDK (with\nthe Android 4.2.2 platform and Google APIs), make sure that android is on your\npath, and try again.');
      }
    }
    callback();
  }
  eventQueue.push(checkAndroid);
  eventQueue.push(checkXcode);
  eventQueue.push(checkAtLeastOneTool);
}

function main() {
  toolsCheckMain();
  initRepoMain();
  if (commandLineFlags['update_app']) {
    updateMain();
  } else {
    var appName = commandLineArgs[0];
    if (appName) {
      createAppMain(appName);
    }
  }
  pump();
}

main();
<|endoftext|>


<commit_before>import React from 'react'
import { Component } from 'react'
import { Table, Column, Cell } from 'fixed-data-table'
import Immutable from 'immutable'
import hasClass from './utils/hasClass'

class DataSelect extends Component {
  constructor(props) {
    super(props)
    this.state = {
      show: false,
      highlighted: 0,
      selected: undefined,
      search: '',
      list: Immutable.List(),
      canCollapse: true,
      scrolling: false,
    }
  }

  componentWillMount() {
    this.setState({list: this.props.locations})
  }

  shouldComponentUpdate(nextProps, nextState) {
    return     this.state.highlighted !== nextState.highlighted
            || this.state.list.hashCode() !== nextState.list.hashCode()
            || this.state.show !== nextState.show
            || this.state.search !== nextState.search
  }

  componentDidUpdate() {
    this.props.handleChange({
        text: this.state.search,
        selected: this.state.selected,
    })
  }

  handleClick = (rowIndex) => {
    let searchText = this.state.list.getIn([rowIndex, 'wh_aisle_id'])
                      + '-' + this.state.list.getIn([rowIndex, 'wh_location_name'])
    let filtered = Immutable.List().push(this.state.list.get(rowIndex))
    this.setState({
      selected: this.state.list.get(rowIndex),
      highlighted: 0,
      search: searchText,
      show: false,
      list: filtered,
    })
  }

  searchChange = (e) => {
    this.filterList(e.target.value)
  }

  filterList = (search) => {
    const regex = new RegExp(search, 'i')
    let filtered = this.props.locations.filter(item => {
      return ((item.get('wh_aisle_id') + '-' + item.get('wh_location_name')).search(regex) > -1)
    })

    this.setState({
      search: search,
      list: filtered,
      highlighted: 0,
      selected: filtered.get(0)
    })
  }

  handleKeyPress = (e) => {
    if (e.key === 'ArrowDown') {
      if (this.state.highlighted < this.state.list.size - 1) {
        this.setState({highlighted: this.state.highlighted + 1})
      }
    } else if (e.key === 'ArrowUp') {
      if (this.state.highlighted > 0) {
        this.setState({highlighted: this.state.highlighted - 1})
      }
    } else if (e.key === 'Enter') {
      if (this.state.list.size > 0) {
        this._input.blur()
        let searchText = this.state.list.getIn([this.state.highlighted, 'wh_aisle_id'])
                        + '-' + this.state.list.getIn([this.state.highlighted, 'wh_location_name'])
        let filtered = Immutable.List().push(this.state.list.get(this.state.highlighted))
        this.setState({
          selected: this.state.list.get(this.state.highlighted),
          list: filtered,
          highlighted: 0,
          search: searchText,
          show: false,
        })
      }
    }
  }

  /**
   * If the input field is focused, show dropdown.
   */
  handleFocus = () => {
    this.setState({show: true})
  }

  /**
   * If the list is collapsible, collapse it on a blur.
   */
  handleBlur = (e) => {
    if (this.state.canCollapse) {
      this.setState({show: false})
    }
  }

  /**
   * Track start of scroll so if mouseUp happens while hovering
   * cursor over different part of the app, it still knows to refocus
   */
  mouseDown = (e) => {
    if (hasClass(e.target, 'ScrollbarLayout_face')) {
      this.setState({scrolling: true})
    }
  }

  /**
   * After clicking the scrollbar, refocus the input field.
   * By clicking into the scrollbar originally, input loses its focus
   * and needs it back in order to detect if the user clicks outside.
   */
  mouseUp = (e) => {
    if (this.state.scrolling || hasClass(e.target, 'ScrollbarLayout_face')) {
      this.setState({scrolling: false})
      this._input.focus()
    }
  }

  /**
   * If the mouse is hovering over the app, cannot collapse dropdown on a blur
   * unless overridden by clicking an item or pressing 'Enter'.
   */
  mouseEnter = (e) => {
    this.setState({canCollapse: false})
  }

  /**
   * Once the mouse leaves the app we always want to collapse menu on a blur.
   */
  mouseLeave = (e) => {
    this.setState({canCollapse: true})
  }

  render() {
    let dataLength = this.state.show ? this.state.list.size : 0
    return (

          <Column
            header={<InputHeader
                      ref={(c) => this._input = c}
                      searchChange={this.searchChange}
                      handleFocus={this.handleFocus}
                      handleBlur={this.handleBlur}
                      handleKeyPress={this.handleKeyPress}
                      search={this.state.search} />
                  }
            cell={<DataCell data={this.state.list}
                            handleClick={this.handleClick}
                            highlighted={this.state.highlighted} />
                  }
            fixed={true}
            width={275} />

    )
  }
}

class InputHeader extends Component {
  focus = () => {
    this.refs.inputHeader.focus()
  }

  blur = () => {
    this.refs.inputHeader.blur()
  }

  render() {
    return (

        <input type="text"  className="search-field" placeholder="Enter a location..."
                            ref="inputHeader"
                            value={this.props.search}
                            onChange={this.props.searchChange}
                            onKeyDown={this.props.handleKeyPress}
                            onFocus={this.props.handleFocus}
                            onBlur={this.props.handleBlur}
                             />

    )
  }
}

class DataCell extends Component {
  handleClick = (e) => {
    this.props.handleClick(this.props.rowIndex)
  }

  render() {
    const {rowIndex, data,  ...props} = this.props
    return (

        {data.getIn([rowIndex, 'wh_aisle_id']) + '-' + data.getIn([rowIndex, 'wh_location_name'])}

    )
  }
}

export default DataSelect
<commit_msg>Change 'locations' to more general 'data'<commit_after>import React from 'react'
import { Component } from 'react'
import { Table, Column, Cell } from 'fixed-data-table'
import Immutable from 'immutable'
import hasClass from './utils/hasClass'

class DataSelect extends Component {
  constructor(props) {
    super(props)
    this.state = {
      show: false,
      highlighted: 0,
      selected: undefined,
      search: '',
      list: Immutable.List(),
      canCollapse: true,
      scrolling: false,
    }
  }

  componentWillMount() {
    this.setState({list: this.props.data})
  }

  shouldComponentUpdate(nextProps, nextState) {
    return     this.state.highlighted !== nextState.highlighted
            || this.state.list.hashCode() !== nextState.list.hashCode()
            || this.state.show !== nextState.show
            || this.state.search !== nextState.search
  }

  componentDidUpdate() {
    this.props.handleChange({
        text: this.state.search,
        selected: this.state.selected,
    })
  }

  handleClick = (rowIndex) => {
    let searchText = this.state.list.getIn([rowIndex, 'wh_aisle_id'])
                      + '-' + this.state.list.getIn([rowIndex, 'wh_location_name'])
    let filtered = Immutable.List().push(this.state.list.get(rowIndex))
    this.setState({
      selected: this.state.list.get(rowIndex),
      highlighted: 0,
      search: searchText,
      show: false,
      list: filtered,
    })
  }

  searchChange = (e) => {
    this.filterList(e.target.value)
  }

  filterList = (search) => {
    const regex = new RegExp(search, 'i')
    let filtered = this.props.data.filter(item => {
      return ((item.get('wh_aisle_id') + '-' + item.get('wh_location_name')).search(regex) > -1)
    })

    this.setState({
      search: search,
      list: filtered,
      highlighted: 0,
      selected: filtered.get(0)
    })
  }

  handleKeyPress = (e) => {
    if (e.key === 'ArrowDown') {
      if (this.state.highlighted < this.state.list.size - 1) {
        this.setState({highlighted: this.state.highlighted + 1})
      }
    } else if (e.key === 'ArrowUp') {
      if (this.state.highlighted > 0) {
        this.setState({highlighted: this.state.highlighted - 1})
      }
    } else if (e.key === 'Enter') {
      if (this.state.list.size > 0) {
        this._input.blur()
        let searchText = this.state.list.getIn([this.state.highlighted, 'wh_aisle_id'])
                        + '-' + this.state.list.getIn([this.state.highlighted, 'wh_location_name'])
        let filtered = Immutable.List().push(this.state.list.get(this.state.highlighted))
        this.setState({
          selected: this.state.list.get(this.state.highlighted),
          list: filtered,
          highlighted: 0,
          search: searchText,
          show: false,
        })
      }
    }
  }

  /**
   * If the input field is focused, show dropdown.
   */
  handleFocus = () => {
    this.setState({show: true})
  }

  /**
   * If the list is collapsible, collapse it on a blur.
   */
  handleBlur = (e) => {
    if (this.state.canCollapse) {
      this.setState({show: false})
    }
  }

  /**
   * Track start of scroll so if mouseUp happens while hovering
   * cursor over different part of the app, it still knows to refocus
   */
  mouseDown = (e) => {
    if (hasClass(e.target, 'ScrollbarLayout_face')) {
      this.setState({scrolling: true})
    }
  }

  /**
   * After clicking the scrollbar, refocus the input field.
   * By clicking into the scrollbar originally, input loses its focus
   * and needs it back in order to detect if the user clicks outside.
   */
  mouseUp = (e) => {
    if (this.state.scrolling || hasClass(e.target, 'ScrollbarLayout_face')) {
      this.setState({scrolling: false})
      this._input.focus()
    }
  }

  /**
   * If the mouse is hovering over the app, cannot collapse dropdown on a blur
   * unless overridden by clicking an item or pressing 'Enter'.
   */
  mouseEnter = (e) => {
    this.setState({canCollapse: false})
  }

  /**
   * Once the mouse leaves the app we always want to collapse menu on a blur.
   */
  mouseLeave = (e) => {
    this.setState({canCollapse: true})
  }

  render() {
    let dataLength = this.state.show ? this.state.list.size : 0
    return (

          <Column
            header={<InputHeader
                      ref={(c) => this._input = c}
                      searchChange={this.searchChange}
                      handleFocus={this.handleFocus}
                      handleBlur={this.handleBlur}
                      handleKeyPress={this.handleKeyPress}
                      search={this.state.search} />
                  }
            cell={<DataCell data={this.state.list}
                            handleClick={this.handleClick}
                            highlighted={this.state.highlighted} />
                  }
            fixed={true}
            width={275} />

    )
  }
}

class InputHeader extends Component {
  focus = () => {
    this.refs.inputHeader.focus()
  }

  blur = () => {
    this.refs.inputHeader.blur()
  }

  render() {
    return (

        <input type="text"  className="search-field" placeholder="Enter a location..."
                            ref="inputHeader"
                            value={this.props.search}
                            onChange={this.props.searchChange}
                            onKeyDown={this.props.handleKeyPress}
                            onFocus={this.props.handleFocus}
                            onBlur={this.props.handleBlur}
                             />

    )
  }
}

class DataCell extends Component {
  handleClick = (e) => {
    this.props.handleClick(this.props.rowIndex)
  }

  render() {
    const {rowIndex, data,  ...props} = this.props
    return (

        {data.getIn([rowIndex, 'wh_aisle_id']) + '-' + data.getIn([rowIndex, 'wh_location_name'])}

    )
  }
}

export default DataSelect
<|endoftext|>


<commit_before>// Change underscore templates to {{}} and {{= }} to play nice with ERB
_.templateSettings = {
  interpolate: /\{\{\=(.+?)\}\}/g,
  evaluate: /\{\{(.+?)\}\}/g
};

// Global namespace object
window.Notes = {};

Notes.escapeHtml = function(text) {
  return $('
').text(text).html();
}

// How many tabs or spaces does a string start with?
Notes.leadingWhitespaceCount = function(str) {
  var count = 0;
  while(str.charAt(0) === " " || str.charAt(0) === "\t") {
    str = str.substr(1);
    count++;
  }
  return count;
}

// Take an array of lines and return the smallest number of leading whitespaces
// (tabs or spaces) from among them
Notes.minLtrim = function(lines) {
  var counts = lines.map(function(line) { return Notes.leadingWhitespaceCount(line); })
  return Math.min.apply(null, counts);
}

Notes.defaultFlags = ['TODO', 'OPTIMIZE', 'FIXME'];

Notes.getSelectedFlags = function() {
  return Notes.sidebarView.collection
    .filter(function(f) { return f.get('checked'); })
    .map(function(f) { return f.get('name') });
}

// Color classes to be paired against distinct flags (for consistency)
Notes.colors = [
  'lightblue','purple','fuschia','lightgreen','orange','green','blue',
  'pink','turquoise','deepred',
]

// TODO
Notes.buildColorMap = function(flags) {
  var allFlags   = _.uniq(flags.concat(Notes.defaultFlags));
  Notes.colorMap = _.zip(allFlags, Notes.colors);
}

Notes.colorFor = function(flagName) {
  var map;
  for (var i=0; i<Notes.colorMap.length; i++) {
    map = Notes.colorMap[i];
    if (map[0] === flagName) {
      return map[1];
    } else if (map[0] === undefined) {
      // No existing mapping found - add new flag to colorMap
      map[0] = flagName;
      return map[1];
    }
  }

  return Notes.colors[Notes.colors.length-1]; // TODO - default to last color in list
}

Notes.Task = Backbone.Model.extend({
  escapedLine: function() {
    return Notes.escapeHtml(this.get('line'));
  },

  escapedContextLines: function() {
    return this.get('context').split("\n")
               .map(function(line) { return Notes.escapeHtml(line); });
  },

  allLines: function() {
    return [this.escapedLine()].concat(this.escapedContextLines());
  },

  highlightedLine: function() {
    var regex = new RegExp(this.get('flags').join('|'), 'gi');
    return this.escapedLine().replace(regex, function(flag) {
      return ""+flag+"";
    });
  },

  formattedSha: function() {
    var sha = this.get('sha');
    return sha ? "@ " + sha.slice(0,7) : '';
  },

  formattedDate: function() {
    var date  = new Date(this.get('date')),
        month = date.getMonth() + 1,
        day   = date.getDate(),
        year  = date.getFullYear().toString().slice(2);

    return month + '/' + day + '/' + year;
  }
});

// A view for a single task item
Notes.TaskView = Backbone.View.extend({
  tagName: 'div',
  className: 'task',
  tmpl: $('#tmpl-task').html(),

  render: function() {
    $(this.el).html(_.template(this.tmpl, { task: this.model }));
    return this;
  },

  events: {
    'click .task-toggle': 'toggleContext'
  },

  toggleContext: function() {
    var $el = $(this.el),
        $toggle = $el.find('.task-toggle'),
        $ctx    = $el.find('.task-context');

    if ($ctx.is(':visible')) {
      $toggle.removeClass('fa-angle-up').addClass('fa-angle-down');
      $ctx.slideUp();
    } else {
      $toggle.removeClass('fa-angle-down').addClass('fa-angle-up');
      $ctx.slideDown();
    }
  }
});

Notes.TasksCollection = Backbone.Collection.extend({
  model: Notes.Task,

  initialize: function() {
    this.filename = '';
  }
});

// A view for a collection of tasks grouped under a common filename
Notes.TaskCollectionView = Backbone.View.extend({
  tagName: 'div',
  classname: 'tasks-container',

  render: function() {
    var $el = $(this.el);
    $el.append("
"+this.collection.filename+":
");

    this.collection.each(function(task) {
      $el.append(new Notes.TaskView({ model: task }).render().el);
    });
    return this;
  }
});

// A flag accompanied by a checkbox in the sidebar
Notes.Flag = Backbone.Model.extend({
  defaults: { checked: true },

  checkedClass: function() { return this.get('checked') ? 'checked' : ''; }
});

Notes.FlagView = Backbone.View.extend({
  tagName: 'div',
  className: 'flag-container',
  tmpl: $('#tmpl-flag').html(),

  render: function() {
    $(this.el).html(_.template(this.tmpl, { flag: this.model }));
    return this;
  },

  events: {
    'click .checkbox': 'toggleCheck',
    'click .delete-flag-btn': 'deleteFlag'
  },

  $checkbox: function() { return $(this.el).find('.checkbox'); },
  isChecked: function() { return this.$checkbox().hasClass('checked'); },
  check: function() {
    this.model.set('checked', true);
    this.$checkbox().addClass('checked');
  },
  uncheck: function() {
    this.model.set('checked', false);
    this.$checkbox().removeClass('checked');
  },
  toggleCheck: function() {
                 console.log("toggling check");
                 this.isChecked() ? this.uncheck() : this.check(); },

  deleteFlag: function() {
    Notes.sidebarView.collection.remove(this.model);
    var $el = $(this.el);
    $el.slideUp(200, function() { $el.remove(); });
  }
});

Notes.FlagCollection = Backbone.Collection.extend({
  model: Notes.Flag,

  // Add a flag into the collection unless it's already present
  merge: function(flag) {
    var attrs = { name: flag.toUpperCase() }
        match = this.findWhere(attrs);
    if (!match) { this.add(attrs); }
  }
});

Notes.FlagCollectionView = Backbone.View.extend({
  tagName: 'div',

  render: function() {
    var $el = $(this.el);
    $el.html('');

    this.collection.each(function(flag) {
      $el.append(new Notes.FlagView({ model: flag }).render().el);
    });
    return this;
  }
});

// Merge a custom flag into the sidebar and re-render
Notes.addFlag = function(flag) {
  if (flag === '') { return false; }
  Notes.sidebarView.collection.merge(flag);
  Notes.renderSidebar();
}

// Build (or rebuild) the sidebar from the flags
// used to query the server
Notes.buildSidebar = function(flags) {
  var attrs = flags.map(function(f) { return { name: f }; });

  Notes.sidebarView = new Notes.FlagCollectionView({
    collection: new Notes.FlagCollection(attrs)
  });
  Notes.renderSidebar();
}

// TODO: calling this more than once breaks click handlers ???
Notes.renderSidebar = function() {
  var $container = $('.flags-container');
  $container.empty();
  $container.append(Notes.sidebarView.render().el);
}

Notes.renderStats = function(stats) {
  var flag_counts = stats.flag_counts;
  if (jQuery.isEmptyObject(flag_counts)) { return; }

  // TODO: add in stats container
  //

  //

  //

}

Notes.renderTasks = function(tasks) {
  var $container = $('.main-content-container'),
      filename, collection, collectionView;

  $container.empty();

  // filename -> [Notes.Task]
  var taskMap = _.groupBy(tasks, function(t) { return t.get('filename'); });

  if ($.isEmptyObject(taskMap)) {
    // TODO - temporary hack
    $container.html($("
").append(
      "
No tasks matching the criteria were found!
"));
    return;
  }

  for (filename in taskMap) {
    collection = new Notes.TasksCollection(taskMap[filename]);
    collection.filename   = filename;

    collectionView = new Notes.TaskCollectionView({ collection: collection })
    $container.append(collectionView.render().el);
  }
}

Notes.addProgress = function() {
  $('.loading-container').find('p').append('.');
}

Notes.queryPath = function() {
  var path = window.location.pathname;
  return (path === '/' ? '' : path) + "/tasks.json"
}

// Fetch tasks from the server and re-render
Notes.queryTasks = function(queryFlags) {
  console.log("queryFlags: ", queryFlags);
  Notes.buildColorMap(queryFlags); // TODO - don't like how map changes every time

  $('.main-content-container').html("

Loading

");
  var progressInterval = setInterval(Notes.addProgress, 175);

  if (!Notes.sidebarView) {
    Notes.buildSidebar(queryFlags);
  }

  $.getJSON(Notes.queryPath(), { flags: queryFlags }, function(json) {
    var stats = json.stats,
        tasks = json.tasks.map(function(attrs) { return new Notes.Task(attrs) });

    clearInterval(progressInterval);
    Notes.renderStats(stats);
    Notes.renderTasks(tasks);
  });
}

// Page Load
// ----------------------------------
Notes.queryTasks(Notes.defaultFlags);

$(function() {
  var $doc = $(document);

  $doc.on('keyup', '.add-flag', function(e) {
    if (e.keyCode === 13) {
      var $input = $(this);
      Notes.addFlag($input.val());
      $input.val('');
    }
  });

  $doc.on('click', '.add-flag-btn', function() {
    var $input = $('.add-flag');
    Notes.addFlag($input.val());
    $input.val('');
    return false;
  });

  $doc.on('click', '.filter-btn', function() {
    // TODO: check subset query
    Notes.queryTasks(Notes.getSelectedFlags());
    return false;
  });

  $doc.on('click', '.restore-defaults-btn', function() {
    Notes.queryTasks(Notes.defaultFlags);
    return false;
  });
});
<commit_msg>First pass at client-side filtering<commit_after>// Change underscore templates to {{}} and {{= }} to play nice with ERB
_.templateSettings = {
  interpolate: /\{\{\=(.+?)\}\}/g,
  evaluate: /\{\{(.+?)\}\}/g
};

// Global namespace object
window.Notes = {};

Notes.escapeHtml = function(text) {
  return $('
').text(text).html();
}

// Is Array A a subset of Array B?
Notes.isSubset = function(a,b) {
  return a.length === _.intersection(a,b).length;
}

// How many tabs or spaces does a string start with?
Notes.leadingWhitespaceCount = function(str) {
  var count = 0;
  while(str.charAt(0) === " " || str.charAt(0) === "\t") {
    str = str.substr(1);
    count++;
  }
  return count;
}

// Take an array of lines and return the smallest number of leading whitespaces
// (tabs or spaces) from among them
Notes.minLtrim = function(lines) {
  var counts = lines.map(function(line) { return Notes.leadingWhitespaceCount(line); })
  return Math.min.apply(null, counts);
}

Notes.defaultFlags = ['TODO', 'OPTIMIZE', 'FIXME'];

Notes.getSelectedFlags = function() {
  return Notes.sidebarView.collection
    .filter(function(f) { return f.get('checked'); })
    .map(function(f) { return f.get('name') });
}

// Color classes to be paired against distinct flags (for consistency)
Notes.colors = [
  'lightblue','purple','fuschia','lightgreen','orange','green','blue',
  'pink','turquoise','deepred',
]

// TODO
Notes.buildColorMap = function(flags) {
  var allFlags   = _.uniq(flags.concat(Notes.defaultFlags));
  Notes.colorMap = _.zip(allFlags, Notes.colors);
}

Notes.colorFor = function(flagName) {
  var map;
  for (var i=0; i<Notes.colorMap.length; i++) {
    map = Notes.colorMap[i];
    if (map[0] === flagName) {
      return map[1];
    } else if (map[0] === undefined) {
      // No existing mapping found - add new flag to colorMap
      map[0] = flagName;
      return map[1];
    }
  }

  return Notes.colors[Notes.colors.length-1]; // TODO - default to last color in list
}

Notes.Task = Backbone.Model.extend({
  escapedLine: function() {
    return Notes.escapeHtml(this.get('line'));
  },

  escapedContextLines: function() {
    return this.get('context').split("\n")
               .map(function(line) { return Notes.escapeHtml(line); });
  },

  allLines: function() {
    return [this.escapedLine()].concat(this.escapedContextLines());
  },

  highlightedLine: function() {
    var regex = new RegExp(this.get('flags').join('|'), 'gi');
    return this.escapedLine().replace(regex, function(flag) {
      return ""+flag+"";
    });
  },

  formattedSha: function() {
    var sha = this.get('sha');
    return sha ? "@ " + sha.slice(0,7) : '';
  },

  formattedDate: function() {
    var date  = new Date(this.get('date')),
        month = date.getMonth() + 1,
        day   = date.getDate(),
        year  = date.getFullYear().toString().slice(2);

    return month + '/' + day + '/' + year;
  }
});

// A view for a single task item
Notes.TaskView = Backbone.View.extend({
  tagName: 'div',
  className: 'task',
  tmpl: $('#tmpl-task').html(),

  render: function() {
    $(this.el).html(_.template(this.tmpl, { task: this.model }));
    return this;
  },

  events: {
    'click .task-toggle': 'toggleContext'
  },

  toggleContext: function() {
    var $el = $(this.el),
        $toggle = $el.find('.task-toggle'),
        $ctx    = $el.find('.task-context');

    if ($ctx.is(':visible')) {
      $toggle.removeClass('fa-angle-up').addClass('fa-angle-down');
      $ctx.slideUp();
    } else {
      $toggle.removeClass('fa-angle-down').addClass('fa-angle-up');
      $ctx.slideDown();
    }
  }
});

Notes.TasksCollection = Backbone.Collection.extend({
  model: Notes.Task,

  initialize: function() {
    this.filename = '';
  }
});

// A view for a collection of tasks grouped under a common filename
Notes.TaskCollectionView = Backbone.View.extend({
  tagName: 'div',
  classname: 'tasks-container',

  render: function() {
    var $el = $(this.el);
    $el.append("
"+this.collection.filename+":
");

    this.collection.each(function(task) {
      $el.append(new Notes.TaskView({ model: task }).render().el);
    });
    return this;
  }
});

// A flag accompanied by a checkbox in the sidebar
Notes.Flag = Backbone.Model.extend({
  defaults: { checked: true },

  checkedClass: function() { return this.get('checked') ? 'checked' : ''; }
});

Notes.FlagView = Backbone.View.extend({
  tagName: 'div',
  className: 'flag-container',
  tmpl: $('#tmpl-flag').html(),

  render: function() {
    $(this.el).html(_.template(this.tmpl, { flag: this.model }));
    return this;
  },

  events: {
    'click .checkbox': 'toggleCheck',
    'click .delete-flag-btn': 'deleteFlag'
  },

  $checkbox: function() { return $(this.el).find('.checkbox'); },
  isChecked: function() { return this.$checkbox().hasClass('checked'); },
  check: function() {
    this.model.set('checked', true);
    this.$checkbox().addClass('checked');
  },
  uncheck: function() {
    this.model.set('checked', false);
    this.$checkbox().removeClass('checked');
  },
  toggleCheck: function() { this.isChecked() ? this.uncheck() : this.check(); },

  deleteFlag: function() {
    Notes.sidebarView.collection.remove(this.model);
    var $el = $(this.el);
    $el.slideUp(200, function() { $el.remove(); });
  }
});

Notes.FlagCollection = Backbone.Collection.extend({
  model: Notes.Flag,

  // Add a flag into the collection unless it's already present
  merge: function(flag) {
    var attrs = { name: flag.toUpperCase() }
        match = this.findWhere(attrs);
    if (!match) { this.add(attrs); }
  }
});

Notes.FlagCollectionView = Backbone.View.extend({
  tagName: 'div',

  render: function() {
    var $el = $(this.el);
    $el.html('');

    this.collection.each(function(flag) {
      $el.append(new Notes.FlagView({ model: flag }).render().el);
    });
    return this;
  }
});

// Merge a custom flag into the sidebar and re-render
Notes.addFlag = function(flag) {
  if (flag === '') { return false; }
  Notes.sidebarView.collection.merge(flag);
  Notes.renderSidebar();
}

// Build (or rebuild) the sidebar from the flags
// used to query the server
Notes.buildSidebar = function(flags) {
  var attrs = flags.map(function(f) { return { name: f }; });

  Notes.sidebarView = new Notes.FlagCollectionView({
    collection: new Notes.FlagCollection(attrs)
  });
  Notes.renderSidebar();
}

// TODO: calling this more than once breaks click handlers ???
Notes.renderSidebar = function() {
  var $container = $('.flags-container');
  $container.empty();
  $container.append(Notes.sidebarView.render().el);
}

Notes.renderStats = function(stats) {
  var flag_counts = stats.flag_counts;
  if (jQuery.isEmptyObject(flag_counts)) { return; }

  // TODO: add in stats container
  //

  //

  //

}

// tasks - Array[Notes.Task]
Notes.renderTasks = function(tasks) {
  var $container = $('.main-content-container'),
      filename, collection, collectionView;

  $container.empty();

  if (tasks.length === 0) {
    $container.html($("
").append(
      "
No tasks matching the criteria were found!
"));
    return;
  }

  // filename -> Array[Notes.Task]
  var taskMap = _.groupBy(tasks, function(t) { return t.get('filename'); });

  for (filename in taskMap) {
    collection = new Notes.TasksCollection(taskMap[filename]);
    collection.filename   = filename;

    collectionView = new Notes.TaskCollectionView({ collection: collection })
    $container.append(collectionView.render().el);
  }
}

Notes.addProgress = function() {
  $('.loading-container').find('p').append('.');
}

// Check if a set of tasks can be filtered based on flags we've already searched for
// This allows us to avoid hitting the server when we don't need to
//
// flags - Array[String]
Notes.isSubsetQuery = function(flags) {
  return Notes.isSubset(flags, Notes.lastQueryFlags);
}

// Find a subset of locally-cached tasks that match a set of search flags
// Returns Array[Notes.Task]
//
// TODO: this behaves weirdly if a task has multiple flags, punting for now
Notes.filterTasks = function(queryFlags) {
  if (queryFlags.length === 0) { return []; }

  return Notes.tasks.filter(function(task) {
    var taskFlags = task.get('flags');
    return (Notes.isSubset(taskFlags, queryFlags) ||
            Notes.isSubset(queryFlags, taskFlags));
  });
}

// Returns the URL to query the server at
Notes.queryPath = function() {
  var path = window.location.pathname;
  return (path === '/' ? '' : path) + "/tasks.json"
}

// Fetch tasks from the server and re-render
Notes.queryTasks = function(queryFlags) {
  if (!Notes.colorMap) { Notes.buildColorMap(queryFlags); }

  if (Notes.lastQueryFlags && Notes.isSubsetQuery(queryFlags)) {
    // Subset query - don't need to hit server
    var tasks = Notes.filterTasks(queryFlags);
    Notes.renderTasks(tasks);
    return;
  }

  // Can't filter client-side - need to hit server
  $('.main-content-container').html("

Loading

");
  var progressInterval = setInterval(Notes.addProgress, 175);

  if (!Notes.sidebarView) { Notes.buildSidebar(queryFlags); }

  $.getJSON(Notes.queryPath(), { flags: queryFlags }, function(json) {
    var stats = json.stats,
        tasks = json.tasks.map(function(attrs) { return new Notes.Task(attrs) });

    Notes.tasks = tasks;
    Notes.lastQueryFlags = queryFlags; // Save the most recent search terms for checking subsets

    clearInterval(progressInterval);
    Notes.renderStats(stats);
    Notes.renderTasks(tasks);

  });
}

// Page Load
// ----------------------------------
Notes.queryTasks(Notes.defaultFlags);

$(function() {
  var $doc = $(document);

  $doc.on('keyup', '.add-flag', function(e) {
    if (e.keyCode === 13) {
      var $input = $(this);
      Notes.addFlag($input.val());
      $input.val('');
    }
  });

  $doc.on('click', '.add-flag-btn', function() {
    var $input = $('.add-flag');
    Notes.addFlag($input.val());
    $input.val('');
    return false;
  });

  $doc.on('click', '.filter-btn', function() {
    Notes.queryTasks(Notes.getSelectedFlags());
    return false;
  });

  $doc.on('click', '.restore-defaults-btn', function() {
    Notes.queryTasks(Notes.defaultFlags);
    return false;
  });
});
<|endoftext|>


<commit_before>export default class EntityList
{
    constructor(options) {
        this.list = [];
    }

    /**
     * Get the underlying data structure for the EntityList
     * @return {Array} The underlying array containing Entities
     */
    getList ()
    {
        return this.list;
    }

    /**
     * [addEntity description]
     * @param {Entity} entity [description]
     */
    addEntity (entity)
    {
        this.list.push(entity);
        this.dispatchEntityListUpdated();
    }

    reorder()
    {
        this.list.sort((a, b) => b.getInitiative().toString().localeCompare(a.getInitiative().toString()));
        this.dispatchEntityListUpdated();
    }

    /**
     * [getEntity description]
     * @param  {Number} index [description]
     * @return {Entity}       [description]
     */
    getEntity (index)
    {
        return this.list[index];
    }

    dispatchEntityListUpdated()
    {
        let event = new CustomEvent('tracker:EntityListUpdated', {
            detail: {
                entityList: this
            }
        });

        document.dispatchEvent(event);
    }
}
<commit_msg>Take numeric value into account when sorting<commit_after>export default class EntityList
{
    constructor(options) {
        this.list = [];
    }

    /**
     * Get the underlying data structure for the EntityList
     * @return {Array} The underlying array containing Entities
     */
    getList ()
    {
        return this.list;
    }

    /**
     * [addEntity description]
     * @param {Entity} entity [description]
     */
    addEntity (entity)
    {
        this.list.push(entity);
        this.dispatchEntityListUpdated();
    }

    reorder()
    {
        this.list.sort((a, b) => b.getInitiative().toString().localeCompare(
            a.getInitiative().toString(),
            {},
            {
                numeric: true
            }
        ));
        this.dispatchEntityListUpdated();
    }

    /**
     * [getEntity description]
     * @param  {Number} index [description]
     * @return {Entity}       [description]
     */
    getEntity (index)
    {
        return this.list[index];
    }

    dispatchEntityListUpdated()
    {
        let event = new CustomEvent('tracker:EntityListUpdated', {
            detail: {
                entityList: this
            }
        });

        document.dispatchEvent(event);
    }
}
<|endoftext|>


<commit_before>class Type {
  constructor(type) {
    this.type = type;
  }
  toString() {
    return (this.type);
  }
}

module.exports = Type;
<commit_msg>Helper functions borrowed from Iki<commit_after>class Type {
  constructor(type) {
    this.type = type;
  };

  mustBeInteger = (message, location) => {
    return this.mustBeCompatibleWith(Type.INT, message);
  };
  mustBeBoolean = (message, location) => {
    return this.mustBeCompatibleWith(Type.BOOL, message);
  };
  mustBeCompatibleWith = (otherType, message, location) => {
    if (!this.isCompatibleWith(otherType)) {
      return error(message, location);
    }
  };
  mustBeMutuallyCompatibleWith = (otherType, message, location) => {
    if (!(this.isCompatibleWith(otherType || otherType.isCompatibleWith(this)))) {
      return error(message, location);
    }
  };
  isCompatibleWith = (otherType) => {
    return this === otherType || this === Type.ARBITRARY || otherType === Type.ARBITRARY;
  };
  toString() {
    return (this.type);
  };
}

module.exports = Type;
<|endoftext|>