<commit_before>/**
 * @fileoverview Base functions and globals for sactraffic.org
 * @requires jQuery
 */

/**
 * Load any HTTP GET params onto a global _GET object.
 */
var $_GET = {};
document.location.search.replace(/\??(?:([^=]+)(?:=([^&]*))?&?)/g, function () {
	$_GET[decode(arguments[1])] = decode(arguments[2]);

	function decode(s) {
		return decodeURIComponent(s.split("+").join(" "));
	}
});

/**
 * Setup code for the index page.
 */
function init_index () {
	if (screen.width > 480) {
		trafficmap = new TrafficMap("map");
		trafficmap.load_live_cams("/cameras.xml");
		trafficmap.show_gtraffic();

		jQuery("span.traffic").click(function () {
			jQuery("input.traffic").click();
		});
		jQuery("input.traffic").click(function () {
			if (jQuery("input.traffic").attr('checked')) {
				trafficmap.show_gtraffic();
			} else {
				trafficmap.hide_gtraffic();
			}
		});
		jQuery(".traffic").show();

		jQuery("span.live_cams").click(function () {
			jQuery("input.live_cams").click();
		});
		jQuery("input.live_cams").click(function () {
			if (jQuery("input.live_cams").attr('checked')) {
				trafficmap.show_live_cams();
			} else {
				trafficmap.hide_live_cams();
			}
		});
		jQuery(".live_cams").show();

		TrafficNews.show("#sactraffic_news", "http://www.lectroid.net/category/sactrafficorg/feed/", 7);
	}

	get_incidents();
	setInterval('get_incidents()', 60000);
}
window['init_index'] = init_index;	// Closure-style export: http://code.google.com/closure/compiler/docs/api-tutorial3.html#export

/**
 * Setup code for the single incident page.
 */
function init_incident () {
	trafficmap = new TrafficMap("map");

	jQuery.getJSON("/json/STCC-STCC.json", function (incidents) {
		TrafficList.show_incident(incidents, $_GET["id"]);
		trafficmap.show_incident(incidents, $_GET["id"]);
	});
}
window['init_incident'] = init_incident;	// Closure-style export: http://code.google.com/closure/compiler/docs/api-tutorial3.html#export

/**
 * Fetches the incident JSON and processes it accordingly.
 */
function get_incidents () {
	jQuery.getJSON("/json/STCC-STCC.json", function (incidents) {
		TrafficList.show_incidents(incidents);

		if (typeof trafficmap != "undefined") { trafficmap.update(incidents); }
	});
}
window['get_incidents'] = get_incidents;	// Closure-style export: http://code.google.com/closure/compiler/docs/api-tutorial3.html#export

/**
 * Converts the CHP "TBXY" type coordinated to latitude and longitude.
 * @param {String} tbxy_str The TBXY data from the CHP feed.
 * @returns {Object}
 */
function tbxy2latlng (tbxy_str) {
	var tbxy = tbxy_str.split(/:/);

	var lat = tbxy[1] * 0.00000274 +  33.172;
	var lng = tbxy[0] * 0.0000035  - 144.966;

	return { "lat": lat, "lng": lng };
}

/**
 * Provides an ISO 8601 date formate to the Date object
 */
Date.prototype.getISO8601 = function () {
	var month = (this.getMonth() + 1 < 10) ? "0" + this.getMonth() + 1 : this.getMonth() + 1;
	var day = (this.getDate() < 10) ? "0" + this.getDate() : this.getDate();
	var hours = (this.getHours() < 10) ? "0" + this.getHours() : this.getHours();
	var minutes = (this.getMinutes() < 10) ? "0" + this.getMinutes() : this.getMinutes();
	var seconds = (this.getSeconds() < 10) ? "0" + this.getSeconds() : this.getSeconds();

	var offset = this.getTimezoneOffset() / 60;

	return this.getFullYear() + "-" + month + "-" + day + "T" + hours + ":" + minutes + ":" + seconds + "-0" + offset + "00";
};
<commit_msg>Use function reference instead of a string.  I knew I could do this, just took me a bit to remember.<commit_after>/**
 * @fileoverview Base functions and globals for sactraffic.org
 * @requires jQuery
 */

/**
 * Load any HTTP GET params onto a global _GET object.
 */
var $_GET = {};
document.location.search.replace(/\??(?:([^=]+)(?:=([^&]*))?&?)/g, function () {
	$_GET[decode(arguments[1])] = decode(arguments[2]);

	function decode(s) {
		return decodeURIComponent(s.split("+").join(" "));
	}
});

/**
 * Setup code for the index page.
 */
function init_index () {
	if (screen.width > 480) {
		trafficmap = new TrafficMap("map");
		trafficmap.load_live_cams("/cameras.xml");
		trafficmap.show_gtraffic();

		jQuery("span.traffic").click(function () {
			jQuery("input.traffic").click();
		});
		jQuery("input.traffic").click(function () {
			if (jQuery("input.traffic").attr('checked')) {
				trafficmap.show_gtraffic();
			} else {
				trafficmap.hide_gtraffic();
			}
		});
		jQuery(".traffic").show();

		jQuery("span.live_cams").click(function () {
			jQuery("input.live_cams").click();
		});
		jQuery("input.live_cams").click(function () {
			if (jQuery("input.live_cams").attr('checked')) {
				trafficmap.show_live_cams();
			} else {
				trafficmap.hide_live_cams();
			}
		});
		jQuery(".live_cams").show();

		TrafficNews.show("#sactraffic_news", "http://www.lectroid.net/category/sactrafficorg/feed/", 7);
	}

	get_incidents();
	setInterval(get_incidents, 60000);
}
window['init_index'] = init_index;	// Closure-style export: http://code.google.com/closure/compiler/docs/api-tutorial3.html#export

/**
 * Setup code for the single incident page.
 */
function init_incident () {
	trafficmap = new TrafficMap("map");

	jQuery.getJSON("/json/STCC-STCC.json", function (incidents) {
		TrafficList.show_incident(incidents, $_GET["id"]);
		trafficmap.show_incident(incidents, $_GET["id"]);
	});
}
window['init_incident'] = init_incident;	// Closure-style export: http://code.google.com/closure/compiler/docs/api-tutorial3.html#export

/**
 * Fetches the incident JSON and processes it accordingly.
 */
function get_incidents () {
	jQuery.getJSON("/json/STCC-STCC.json", function (incidents) {
		TrafficList.show_incidents(incidents);

		if (typeof trafficmap != "undefined") { trafficmap.update(incidents); }
	});
}

/**
 * Converts the CHP "TBXY" type coordinated to latitude and longitude.
 * @param {String} tbxy_str The TBXY data from the CHP feed.
 * @returns {Object}
 */
function tbxy2latlng (tbxy_str) {
	var tbxy = tbxy_str.split(/:/);

	var lat = tbxy[1] * 0.00000274 +  33.172;
	var lng = tbxy[0] * 0.0000035  - 144.966;

	return { "lat": lat, "lng": lng };
}

/**
 * Provides an ISO 8601 date formate to the Date object
 */
Date.prototype.getISO8601 = function () {
	var month = (this.getMonth() + 1 < 10) ? "0" + this.getMonth() + 1 : this.getMonth() + 1;
	var day = (this.getDate() < 10) ? "0" + this.getDate() : this.getDate();
	var hours = (this.getHours() < 10) ? "0" + this.getHours() : this.getHours();
	var minutes = (this.getMinutes() < 10) ? "0" + this.getMinutes() : this.getMinutes();
	var seconds = (this.getSeconds() < 10) ? "0" + this.getSeconds() : this.getSeconds();

	var offset = this.getTimezoneOffset() / 60;

	return this.getFullYear() + "-" + month + "-" + day + "T" + hours + ":" + minutes + ":" + seconds + "-0" + offset + "00";
};
<|endoftext|>


<commit_before>odoo.define('pos_discount.pos_discount', function (require) {
"use strict";

var core = require('web.core');
var screens = require('point_of_sale.screens');
var models = require('point_of_sale.models');

var _t = core._t;

var existing_models = models.PosModel.prototype.models;
var product_index = _.findIndex(existing_models, function (model) {
    return model.model === "product.product";
});
var product_model = existing_models[product_index];

models.load_models([{
  model:  product_model.model,
  fields: product_model.fields,
  order:  product_model.order,
  domain: function(self) {return [['id', '=', self.config.discount_product_id[0]]];},
  context: product_model.context,
  loaded: product_model.loaded,
}]);

var DiscountButton = screens.ActionButtonWidget.extend({
    template: 'DiscountButton',
    button_click: function(){
        var self = this;
        this.gui.show_popup('number',{
            'title': _t('Discount Percentage'),
            'value': this.pos.config.discount_pc,
            'confirm': function(val) {
                val = Math.round(Math.max(0,Math.min(100,val)));
                self.apply_discount(val);
            },
        });
    },
    apply_discount: function(pc) {
        var order    = this.pos.get_order();
        var lines    = order.get_orderlines();
        var product  = this.pos.db.get_product_by_id(this.pos.config.discount_product_id[0]);
        if (product === undefined) {
            this.gui.show_popup('error', {
                title : _t("No discount product found"),
                body  : _t("The discount product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'."),
            });
            return;
        }

        // Remove existing discounts
        var i = 0;
        while ( i < lines.length ) {
            if (lines[i].get_product() === product) {
                order.remove_orderline(lines[i]);
            } else {
                i++;
            }
        }

        // Add discount
        // We add the price as manually set to avoid recomputation when changing customer.
        var base_to_discount = order.get_total_without_tax();
        if (product.taxes_id.length){
            var first_tax = this.pos.taxes_by_id[product.taxes_id[0]];
            if (first_tax.price_include) {
                base_to_discount = order.get_total_with_tax();
            }
        }
        var discount = - pc / 100.0 * base_to_discount;

        if( discount < 0 ){
            order.add_product(product, {
                price: discount,
                lst_price: discount,
                extras: {
                    price_manually_set: true,
                },
            });
        }
    },
});

screens.define_action_button({
    'name': 'discount',
    'widget': DiscountButton,
    'condition': function(){
        return this.pos.config.module_pos_discount && this.pos.config.discount_product_id;
    },
});

return {
    DiscountButton: DiscountButton,
}

});
<commit_msg>[FIX] pos_discount: correctly get discount amount<commit_after>odoo.define('pos_discount.pos_discount', function (require) {
"use strict";

var core = require('web.core');
var screens = require('point_of_sale.screens');
var models = require('point_of_sale.models');
var field_utils = require('web.field_utils');

var _t = core._t;

var existing_models = models.PosModel.prototype.models;
var product_index = _.findIndex(existing_models, function (model) {
    return model.model === "product.product";
});
var product_model = existing_models[product_index];

models.load_models([{
  model:  product_model.model,
  fields: product_model.fields,
  order:  product_model.order,
  domain: function(self) {return [['id', '=', self.config.discount_product_id[0]]];},
  context: product_model.context,
  loaded: product_model.loaded,
}]);

var DiscountButton = screens.ActionButtonWidget.extend({
    template: 'DiscountButton',
    button_click: function(){
        var self = this;
        this.gui.show_popup('number',{
            'title': _t('Discount Percentage'),
            'value': this.pos.config.discount_pc,
            'confirm': function(val) {
                val = Math.round(Math.max(0,Math.min(100,field_utils.parse.float(val))));
                self.apply_discount(val);
            },
        });
    },
    apply_discount: function(pc) {
        var order    = this.pos.get_order();
        var lines    = order.get_orderlines();
        var product  = this.pos.db.get_product_by_id(this.pos.config.discount_product_id[0]);
        if (product === undefined) {
            this.gui.show_popup('error', {
                title : _t("No discount product found"),
                body  : _t("The discount product seems misconfigured. Make sure it is flagged as 'Can be Sold' and 'Available in Point of Sale'."),
            });
            return;
        }

        // Remove existing discounts
        var i = 0;
        while ( i < lines.length ) {
            if (lines[i].get_product() === product) {
                order.remove_orderline(lines[i]);
            } else {
                i++;
            }
        }

        // Add discount
        // We add the price as manually set to avoid recomputation when changing customer.
        var base_to_discount = order.get_total_without_tax();
        if (product.taxes_id.length){
            var first_tax = this.pos.taxes_by_id[product.taxes_id[0]];
            if (first_tax.price_include) {
                base_to_discount = order.get_total_with_tax();
            }
        }
        var discount = - pc / 100.0 * base_to_discount;

        if( discount < 0 ){
            order.add_product(product, {
                price: discount,
                lst_price: discount,
                extras: {
                    price_manually_set: true,
                },
            });
        }
    },
});

screens.define_action_button({
    'name': 'discount',
    'widget': DiscountButton,
    'condition': function(){
        return this.pos.config.module_pos_discount && this.pos.config.discount_product_id;
    },
});

return {
    DiscountButton: DiscountButton,
}

});
<|endoftext|>


<commit_before>// upgrader.js
//
// Do in-place upgrades of activity objects as needed
//
// Copyright 2011, 2013 E14N https://e14n.com/
//
// 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 fs = require("fs"),
    os = require("os"),
    path = require("path"),
    urlparse = require("url").parse,
    Step = require("step"),
    _ = require("underscore"),
    thumbnail = require("./thumbnail"),
    URLMaker = require("./urlmaker").URLMaker,
    Activity = require("./model/activity").Activity,
    ActivityObject = require("./model/activityobject").ActivityObject,
    Image = require("./model/image").Image,
    Person = require("./model/person").Person,
    Stream = require("./model/stream").Stream,
    mover = require("./mover");

var Upgrader = new (function() {

    var upg = this,
        autorotateImage = function(img, callback) {

            var fname = path.join(Image.uploadDir, img._slug),
                tmpdir = (_.isFunction(os.tmpdir)) ? os.tmpdir() :
                    (_.isFunction(os.tmpDir)) ? os.tmpDir() : "/tmp",
                tmpname = path.join(tmpdir, img._uuid);

            Step(
                function() {
                    thumbnail.autorotate(fname, tmpname, this);
                },
                function(err) {
                    if (err) throw err;
                    mover.safeMove(tmpname, fname, this);
                },
                callback
            );
        },
        upgradePersonAvatar = function(person, callback) {

            var img,
                urlToSlug = function(person, url) {
                    var start = url.indexOf("/" + person.preferredUsername + "/");
                    return url.substr(start + 1);
                },
                slug,
                reupgrade = function(slug, callback) {
                    Step(
                        function() {
                            Image.search({_fslug: slug}, this);
                        },
                        function(err, images) {
                            var img;
                            if (err) throw err;
                            if (!images || images.length === 0) {
                                throw new Error("No image record found for slug: " + slug);
                            } else {
                                img = images[0];
                                img.image = {
                                    url: img.fullImage.url
                                };
                                img.fullImage = {
                                    url: img.fullImage.url
                                };
                                img._slug = slug;
                                delete img._fslug;
                                callback(null, img);
                            }
                        }
                    );
                };

            // Automated update from v0.2.x, which had no thumbnailing of images
            // This checks for local persons with no "width" in their image
            // and tries to update the user data.

            if (person._user && _.isObject(person.image) && !_.has(person.image, "width") && _.isString(person.image.url)) {

                if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading person avatar");

                slug = urlToSlug(person, person.image.url);

                Step(
                    function() {
                        fs.stat(path.join(Image.uploadDir, slug), this);
                    },
                    function(err, stat) {
                        if (err && err.code == 'ENOENT') {
                            // If we don't have this file, just skip
                            callback(null);
                        } else if (err) {
                            throw err;
                        } else {
                            this(null);
                        }
                    },
                    function(err) {
                        if (err) throw err;
                        Image.search({"_slug": slug}, this);
                    },
                    function(err, images) {
                        var cb = this;
                        if (err) throw err;
                        if (!images || images.length === 0) {
                            reupgrade(slug, this);
                        } else {
                            this(null, images[0]);
                        }
                    },
                    function(err, results) {
                        if (err) throw err;
                        img = results;
                        upgradeAsAvatar(img, this);
                    },
                    function(err) {
                        if (err) throw err;
                        // Save person first, to avoid a loop
                        person.image = img.image;
                        if (!person.pump_io) {
                            person.pump_io = {};
                        }
                        person.pump_io.fullImage = img.fullImage;
                        person.save(this);
                    },
                    function(err) {
                        if (err) throw err;
                        // Save image next, to avoid a loop
                        img.save(this);
                    },
                    function(err, saved) {
                        var iu, pu;
                        if (err) throw err;
                        // Send out an activity so everyone knows
                        iu = new Activity({actor: person,
                                           verb: "update",
                                           object: img});
                        iu.fire(this.parallel());
                        pu = new Activity({actor: person,
                                           verb: "update",
                                           object: person});
                        pu.fire(this.parallel());
                    },
                    callback
                );
            } else {
                callback(null);
            }
        },
        isRemoteURL = function(url) {
            return urlparse(url).hostname != URLMaker.hostname;
        },
        isMismatchURL = function(person, url) {
            return urlparse(url).hostname != URLMaker.hostname;
        },
        upgradePersonFeeds = function(person, callback) {

            if (person._user &&
                (_.some(["replies", "likes", "shares"], function(feed) { return person[feed] && isRemoteURL(person[feed].url); }) ||
                 _.some(["followers", "following", "lists", "favorites"], function(feed) { return !_.has(person, feed) || isRemoteURL(person[feed].url); }) ||
                 _.some(["self", "activity-inbox", "activity-outbox"], function(rel) { return !_.has(person.links, rel) || isRemoteURL(person.links[rel].href); }))) {
                upgradeUserFeeds(person, callback);
            } else if (!person._user &&
                       (_.some(["replies", "likes", "shares"], function(feed) { return person[feed] && isMismatchURL(person, person[feed].url); }) ||
                        _.some(["followers", "following", "lists", "favorites"], function(feed) { return !_.has(person, feed) || isMismatchURL(person, person[feed].url); }) ||
                        _.some(["self", "activity-inbox", "activity-outbox"], function(rel) { return !_.has(person.links, rel) || isMismatchURL(person, person.links[rel].href); }))) {
                upgradeRemotePersonFeeds(person, callback);
            } else {
                callback(null);
            }
        },
        upgradeRemotePersonFeeds = function(person, callback) {

            if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading remote person feeds");

            Step(
                function() {
                    ActivityObject.discover(person, this);
                },
                function(err, discovered) {
                    if (err) throw err;
                    // These get added accidentally; remove them if they look wrong
                    _.each(["replies", "likes", "shares"], function(feed) {
                        if (person[feed] && isMismatchURL(person, person[feed].url)) {
                            delete person[feed];
                        }
                    });
                    person.update(discovered, this);
                },
                function(err) {
                    if (err) {
                        // Log it here
                    }
                    callback(null);
                }
            );
        },
        upgradeUserFeeds = function(person, callback) {

            if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading user feeds");

            if (!_.has(person, "links")) {
                person.links = {};
            }

            person.links["activity-inbox"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/inbox")
            };

            person.links["activity-outbox"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/feed")
            };

            person.links["self"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/profile")
            };

            Person.ensureFeeds(person, person.preferredUsername);

            _.each(["likes", "replies", "shares"], function(feed) {
                person[feed] = {
                    url: URLMaker.makeURL("api/person/" + person._uuid + "/" + feed)
                };
            });

            Step(
                function() {
                    person.save(this);
                },
                function(err) {
                    var pu;
                    if (err) throw err;
                    pu = new Activity({actor: person,
                                       verb: "update",
                                       object: person});
                    pu.fire(this);
                },
                callback
            );
        },
        upgradeAsImage = function(img, callback) {
            Step(
                function() {
                    autorotateImage(img, this);
                },
                function(err) {
                    if (err) throw err;
                    thumbnail.addImageMetadata(img, Image.uploadDir, this);
                },
                callback
            );
        },
        upgradeAsAvatar = function(img, callback) {
            Step(
                function() {
                    autorotateImage(img, this);
                },
                function(err) {
                    if (err) throw err;
                    thumbnail.addAvatarMetadata(img, Image.uploadDir, this);
                },
                callback
            );
        };

    upg.upgradeImage = function(img, callback) {

        if (img._slug && _.isObject(img.image) && !_.has(img.image, "width")) {

            if (Upgrader.log) Upgrader.log.info({image: img}, "Upgrading image");

            Step(
                function() {
                    fs.stat(path.join(Image.uploadDir, img._slug), this);
                },
                function(err, stat) {
                    if (err && err.code == 'ENOENT') {
                        // If we don't have this file, just skip
                        callback(null);
                    } else if (err) {
                        throw err;
                    } else {
                        this(null);

                    }
                },
                function(err) {
                    if (err) throw err;
                    Person.search({"image.url": img.image.url}, this);
                },
                function(err, people) {
                    if (err) throw err;
                    if (!people || people.length === 0) {
                        upgradeAsImage(img, this);
                    } else {
                        upgradeAsAvatar(img, this);
                    }
                },
                function(err) {
                    if (err) throw err;
                    img.save(this);
                },
                function(err, saved) {
                    var act;
                    if (err) throw err;
                    // Send out an activity so everyone knows
                    act = new Activity({actor: img.author,
                                        verb: "update",
                                        object: img});
                    act.fire(this);
                },
                callback
            );
        } else {
            callback(null);
        }
    };

    upg.upgradePerson = function(person, callback) {

        Step(
            function() {
                upgradePersonAvatar(person, this);
            },
            function(err) {
                if (err) throw err;
                upgradePersonFeeds(person, this);
            },
            callback
        );
    };

    upg.upgradeGroup = function(group, callback) {

        if (group.members && group.documents) {
            callback(null);
            return;
        }

        if (Upgrader.log) Upgrader.log.info({group: group}, "Upgrading group");

        Step(
            function() {
                group.isLocal(this);
            },
            function(err, isLocal) {
                if (err) throw err;
                if (!isLocal) {
                    callback(null);
                } else {
                    if (!group.members) {
                        group.members = {
                            url: URLMaker.makeURL("api/group/"+group._uuid+"/members")
                        };
                    }
                    if (!group.documents) {
                        group.documents = {
                            url: URLMaker.makeURL("api/group/"+group._uuid+"/documents")
                        };
                    }
                    group.save(this);
                }
            },
            function(err) {
                callback(err);
            }
        );
    };

    upg.upgradeActivity = function(act, callback) {

        var ActivityObject = require("./model/activityobject").ActivityObject,
            oprops = ["generator",
                      "provider",
                      "target",
                      "context",
                      "location",
                      "source"],
            isOK = function(act, prop) {
                return (!act[prop] || (_.isObject(act[prop]) && _.isString(act[prop].id)));
            },
            fixupProperty = function(act, prop, defaultType, callback) {

                var val;

                if (isOK(act, prop)) {
                    callback(null);
                    return;
                }

                val = act[prop];

                Step(
                    function() {
                        if (!val.objectType) {
                            val.objectType = defaultType;
                        }
                        ActivityObject.ensureObject(val, this);
                    },
                    function(err, ensured) {
                        if (err) throw err;
                        act[prop] = ensured;
                        this(null);
                    },
                    callback
                );
            };

        // If all the object properties look OK, continue

        if (_.every(oprops, function(prop) { return isOK(act, prop); })) {
            callback(null);
            return;
        }

        if (Upgrader.log) Upgrader.log.info({activity: act}, "Upgrading activity");

        // Otherwise, fix them up

        Step(
            function() {
                fixupProperty(act, "location", ActivityObject.PLACE, this.parallel());
                fixupProperty(act, "provider", ActivityObject.SERVICE, this.parallel());
                fixupProperty(act, "generator", ActivityObject.APPLICATION, this.parallel());
                fixupProperty(act, "target", ActivityObject.COLLECTION, this.parallel());
                fixupProperty(act, "source", ActivityObject.COLLECTION, this.parallel());
                fixupProperty(act, "context", ActivityObject.ISSUE, this.parallel());
            },
            function(err) {
                if (err) throw err;
                act.save(this);
            },
            function(err) {
                callback(err);
            }
        );
    };

})();

module.exports = Upgrader;
<commit_msg>Log when we're finished<commit_after>// upgrader.js
//
// Do in-place upgrades of activity objects as needed
//
// Copyright 2011, 2013 E14N https://e14n.com/
//
// 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 fs = require("fs"),
    os = require("os"),
    path = require("path"),
    urlparse = require("url").parse,
    Step = require("step"),
    _ = require("underscore"),
    thumbnail = require("./thumbnail"),
    URLMaker = require("./urlmaker").URLMaker,
    Activity = require("./model/activity").Activity,
    ActivityObject = require("./model/activityobject").ActivityObject,
    Image = require("./model/image").Image,
    Person = require("./model/person").Person,
    Stream = require("./model/stream").Stream,
    mover = require("./mover");

var Upgrader = new (function() {

    var upg = this,
        autorotateImage = function(img, callback) {

            var fname = path.join(Image.uploadDir, img._slug),
                tmpdir = (_.isFunction(os.tmpdir)) ? os.tmpdir() :
                    (_.isFunction(os.tmpDir)) ? os.tmpDir() : "/tmp",
                tmpname = path.join(tmpdir, img._uuid);

            Step(
                function() {
                    thumbnail.autorotate(fname, tmpname, this);
                },
                function(err) {
                    if (err) throw err;
                    mover.safeMove(tmpname, fname, this);
                },
                callback
            );
        },
        upgradePersonAvatar = function(person, callback) {

            var img,
                urlToSlug = function(person, url) {
                    var start = url.indexOf("/" + person.preferredUsername + "/");
                    return url.substr(start + 1);
                },
                slug,
                reupgrade = function(slug, callback) {
                    Step(
                        function() {
                            Image.search({_fslug: slug}, this);
                        },
                        function(err, images) {
                            var img;
                            if (err) throw err;
                            if (!images || images.length === 0) {
                                throw new Error("No image record found for slug: " + slug);
                            } else {
                                img = images[0];
                                img.image = {
                                    url: img.fullImage.url
                                };
                                img.fullImage = {
                                    url: img.fullImage.url
                                };
                                img._slug = slug;
                                delete img._fslug;
                                callback(null, img);
                            }
                        }
                    );
                };

            // Automated update from v0.2.x, which had no thumbnailing of images
            // This checks for local persons with no "width" in their image
            // and tries to update the user data.

            if (person._user && _.isObject(person.image) && !_.has(person.image, "width") && _.isString(person.image.url)) {

                if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading person avatar");

                slug = urlToSlug(person, person.image.url);

                Step(
                    function() {
                        fs.stat(path.join(Image.uploadDir, slug), this);
                    },
                    function(err, stat) {
                        if (err && err.code == 'ENOENT') {
                            // If we don't have this file, just skip
                            callback(null);
                        } else if (err) {
                            throw err;
                        } else {
                            this(null);
                        }
                    },
                    function(err) {
                        if (err) throw err;
                        Image.search({"_slug": slug}, this);
                    },
                    function(err, images) {
                        var cb = this;
                        if (err) throw err;
                        if (!images || images.length === 0) {
                            reupgrade(slug, this);
                        } else {
                            this(null, images[0]);
                        }
                    },
                    function(err, results) {
                        if (err) throw err;
                        img = results;
                        upgradeAsAvatar(img, this);
                    },
                    function(err) {
                        if (err) throw err;
                        // Save person first, to avoid a loop
                        person.image = img.image;
                        if (!person.pump_io) {
                            person.pump_io = {};
                        }
                        person.pump_io.fullImage = img.fullImage;
                        person.save(this);
                    },
                    function(err) {
                        if (err) throw err;
                        // Save image next, to avoid a loop
                        img.save(this);
                    },
                    function(err, saved) {
                        var iu, pu;
                        if (err) throw err;
                        // Send out an activity so everyone knows
                        iu = new Activity({actor: person,
                                           verb: "update",
                                           object: img});
                        iu.fire(this.parallel());
                        pu = new Activity({actor: person,
                                           verb: "update",
                                           object: person});
                        pu.fire(this.parallel());
                    },
                    callback
                );
            } else {
                callback(null);
            }
        },
        isRemoteURL = function(url) {
            return urlparse(url).hostname != URLMaker.hostname;
        },
        isMismatchURL = function(person, url) {
            return urlparse(url).hostname != URLMaker.hostname;
        },
        needsUpgradeUserFeeds = function(person) {
            return person._user &&
                (_.some(["replies", "likes", "shares"], function(feed) { return person[feed] && isRemoteURL(person[feed].url); }) ||
                 _.some(["followers", "following", "lists", "favorites"], function(feed) { return !_.has(person, feed) || isRemoteURL(person[feed].url); }) ||
                 _.some(["self", "activity-inbox", "activity-outbox"], function(rel) { return !_.has(person.links, rel) || isRemoteURL(person.links[rel].href); }));
        },
        needsUpgradeRemotePersonFeeds = function(person) {
            return !person._user &&
                (_.some(["replies", "likes", "shares"], function(feed) { return person[feed] && isMismatchURL(person, person[feed].url); }) ||
                 _.some(["followers", "following", "lists", "favorites"], function(feed) { return !_.has(person, feed) || isMismatchURL(person, person[feed].url); }) ||
                 _.some(["self", "activity-inbox", "activity-outbox"], function(rel) { return !_.has(person.links, rel) || isMismatchURL(person, person.links[rel].href); }));
        },
        upgradePersonFeeds = function(person, callback) {

            if (needsUpgradeUserFeeds(person)) {
                upgradeUserFeeds(person, callback);
            } else if (needsUpgradeRemotePersonFeeds(person)) {
                upgradeRemotePersonFeeds(person, callback);
            } else {
                callback(null);
            }
        },
        upgradeRemotePersonFeeds = function(person, callback) {

            if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading remote person feeds");

            Step(
                function() {
                    ActivityObject.discover(person, this);
                },
                function(err, discovered) {
                    if (err) throw err;
                    // These get added accidentally; remove them if they look wrong
                    _.each(["replies", "likes", "shares"], function(feed) {
                        if (person[feed] && isMismatchURL(person, person[feed].url)) {
                            delete person[feed];
                        }
                    });
                    person.update(discovered, this);
                },
                function(err) {
                    if (err) {
                        // Log it here
                    }
                    if (Upgrader.log) Upgrader.log.info({person: person, stillNeedsUpgrade: needsUpgradeRemotePersonFeeds(person)}, "Finished upgrading remote person");
                    callback(null);
                }
            );
        },
        upgradeUserFeeds = function(person, callback) {

            if (Upgrader.log) Upgrader.log.info({person: person}, "Upgrading user feeds");

            if (!_.has(person, "links")) {
                person.links = {};
            }

            person.links["activity-inbox"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/inbox")
            };

            person.links["activity-outbox"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/feed")
            };

            person.links["self"] = {
                href: URLMaker.makeURL("api/user/" + person.preferredUsername + "/profile")
            };

            Person.ensureFeeds(person, person.preferredUsername);

            _.each(["likes", "replies", "shares"], function(feed) {
                person[feed] = {
                    url: URLMaker.makeURL("api/person/" + person._uuid + "/" + feed)
                };
            });

            Step(
                function() {
                    person.save(this);
                },
                function(err) {
                    var pu;
                    if (err) throw err;
                    if (Upgrader.log) Upgrader.log.info({person: person, stillNeedsUpgrade: needsUpgradeUserFeeds(person)}, "Finished upgrading user");
                    pu = new Activity({actor: person,
                                       verb: "update",
                                       object: person});
                    pu.fire(this);
                },
                callback
            );
        },
        upgradeAsImage = function(img, callback) {
            Step(
                function() {
                    autorotateImage(img, this);
                },
                function(err) {
                    if (err) throw err;
                    thumbnail.addImageMetadata(img, Image.uploadDir, this);
                },
                callback
            );
        },
        upgradeAsAvatar = function(img, callback) {
            Step(
                function() {
                    autorotateImage(img, this);
                },
                function(err) {
                    if (err) throw err;
                    thumbnail.addAvatarMetadata(img, Image.uploadDir, this);
                },
                callback
            );
        };

    upg.upgradeImage = function(img, callback) {

        if (img._slug && _.isObject(img.image) && !_.has(img.image, "width")) {

            if (Upgrader.log) Upgrader.log.info({image: img}, "Upgrading image");

            Step(
                function() {
                    fs.stat(path.join(Image.uploadDir, img._slug), this);
                },
                function(err, stat) {
                    if (err && err.code == 'ENOENT') {
                        // If we don't have this file, just skip
                        callback(null);
                    } else if (err) {
                        throw err;
                    } else {
                        this(null);

                    }
                },
                function(err) {
                    if (err) throw err;
                    Person.search({"image.url": img.image.url}, this);
                },
                function(err, people) {
                    if (err) throw err;
                    if (!people || people.length === 0) {
                        upgradeAsImage(img, this);
                    } else {
                        upgradeAsAvatar(img, this);
                    }
                },
                function(err) {
                    if (err) throw err;
                    img.save(this);
                },
                function(err, saved) {
                    var act;
                    if (err) throw err;
                    // Send out an activity so everyone knows
                    act = new Activity({actor: img.author,
                                        verb: "update",
                                        object: img});
                    act.fire(this);
                },
                callback
            );
        } else {
            callback(null);
        }
    };

    upg.upgradePerson = function(person, callback) {

        Step(
            function() {
                upgradePersonAvatar(person, this);
            },
            function(err) {
                if (err) throw err;
                upgradePersonFeeds(person, this);
            },
            callback
        );
    };

    upg.upgradeGroup = function(group, callback) {

        if (group.members && group.documents) {
            callback(null);
            return;
        }

        if (Upgrader.log) Upgrader.log.info({group: group}, "Upgrading group");

        Step(
            function() {
                group.isLocal(this);
            },
            function(err, isLocal) {
                if (err) throw err;
                if (!isLocal) {
                    callback(null);
                } else {
                    if (!group.members) {
                        group.members = {
                            url: URLMaker.makeURL("api/group/"+group._uuid+"/members")
                        };
                    }
                    if (!group.documents) {
                        group.documents = {
                            url: URLMaker.makeURL("api/group/"+group._uuid+"/documents")
                        };
                    }
                    group.save(this);
                }
            },
            function(err) {
                callback(err);
            }
        );
    };

    upg.upgradeActivity = function(act, callback) {

        var ActivityObject = require("./model/activityobject").ActivityObject,
            oprops = ["generator",
                      "provider",
                      "target",
                      "context",
                      "location",
                      "source"],
            isOK = function(act, prop) {
                return (!act[prop] || (_.isObject(act[prop]) && _.isString(act[prop].id)));
            },
            fixupProperty = function(act, prop, defaultType, callback) {

                var val;

                if (isOK(act, prop)) {
                    callback(null);
                    return;
                }

                val = act[prop];

                Step(
                    function() {
                        if (!val.objectType) {
                            val.objectType = defaultType;
                        }
                        ActivityObject.ensureObject(val, this);
                    },
                    function(err, ensured) {
                        if (err) throw err;
                        act[prop] = ensured;
                        this(null);
                    },
                    callback
                );
            };

        // If all the object properties look OK, continue

        if (_.every(oprops, function(prop) { return isOK(act, prop); })) {
            callback(null);
            return;
        }

        if (Upgrader.log) Upgrader.log.info({activity: act}, "Upgrading activity");

        // Otherwise, fix them up

        Step(
            function() {
                fixupProperty(act, "location", ActivityObject.PLACE, this.parallel());
                fixupProperty(act, "provider", ActivityObject.SERVICE, this.parallel());
                fixupProperty(act, "generator", ActivityObject.APPLICATION, this.parallel());
                fixupProperty(act, "target", ActivityObject.COLLECTION, this.parallel());
                fixupProperty(act, "source", ActivityObject.COLLECTION, this.parallel());
                fixupProperty(act, "context", ActivityObject.ISSUE, this.parallel());
            },
            function(err) {
                if (err) throw err;
                act.save(this);
            },
            function(err) {
                callback(err);
            }
        );
    };

})();

module.exports = Upgrader;
<|endoftext|>


<commit_before>/**
 * Created by ksinger on 17.06.2016.
 */

;

import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

window.Medium4dbg = Medium;

function genComponentConf() {
    let template = `

                {{ self.charactersLeft() }} characters left

        <button
            class="wdt__submitbutton red"
            ng-show="::self.submitButtonLabel"
            ng-click="::self.submit()">
            {{ ::(self.submitButtonLabel || 'Send') }}
        </button>
    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux',/*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ctf4dbg = this;

            /*
            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);
            */

            this.initMedium();

            this.mediumMountNg().bind('input', e => {
                this.input()
            });
            this.mediumMountNg().bind('keydown', e =>
                this.keydown(e)
            );
        }
        keydown(e) {
            if(e.keyCode === 13) {
                this.submit();
                return false;
            }
        }
        input() {
            const payload = {
                value: this.value(),
                valid: this.valid(),
            };
            this.onInput(payload);
            dispatchEvent(this.$element[0], 'input', payload);
        }
        submit() {
            const payload = {
                value: this.value(),
                valid: this.valid(),
            };
            this.onSubmit(payload);
            dispatchEvent(this.$element[0], 'submit', payload);

            this.medium.clear(); // clear text
        }
        charactersLeft() {
            return this.maxChars - this.value().length;
        }
        valid() {
            return !this.maxChars || this.charactersLeft() >= 0;
        }
        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '')
                .replace(' ','')
                .trim();
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: this.placeholder,
                autoHR: false, //if true, inserts
 after two empty lines
                mode: Medium.inlineMode, // no newlines, no styling
                //mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it
                tags: {
                    /*
                     'break': 'br',
                     'horizontalRule': 'hr',
                     'paragraph': 'p',
                     'outerLevel': ['pre', 'blockquote', 'figure'],
                     'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                     */
                },
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: {
            placeholder: '=',
            maxChars: '=',
            /*
             * Usage:
             *  on-input="::myCallback(value, valid)"
             */
            onInput: '&',

            submitButtonLabel: '=',
            /*
             * Usage:
             *  on-submit="::myCallback(value)"
             */
            onSubmit: '&',

        },
        template: template
    }
}

export default angular.module('won.owner.components.chatTextfield', [ ])
    .directive('chatTextfield', genComponentConf)
    .name;<commit_msg>Only triggering a submit-event when a message has been entered.<commit_after>/**
 * Created by ksinger on 17.06.2016.
 */

;

import Medium from '../mediumjs-es6';
import angular from 'angular';
import 'ng-redux';
import Immutable from 'immutable';
import 'angular-sanitize';
import { dispatchEvent, attach, delay } from '../utils';
import { actionCreators }  from '../actions/actions';

window.Medium4dbg = Medium;

function genComponentConf() {
    let template = `

                {{ self.charactersLeft() }} characters left

        <button
            class="wdt__submitbutton red"
            ng-show="::self.submitButtonLabel"
            ng-click="::self.submit()">
            {{ ::(self.submitButtonLabel || 'Send') }}
        </button>
    `;

    const serviceDependencies = ['$scope', '$element', '$ngRedux',/*injections as strings here*/];

    class Controller {
        constructor(/* arguments <- serviceDependencies */) {
            attach(this, serviceDependencies, arguments);
            window.ctf4dbg = this;

            /*
            const selectFromState = (state) => ({
                draftId: state.getIn(['router', 'currentParams', 'draftId'])
            })
            const disconnect = this.$ngRedux.connect(selectFromState, actionCreators)(this);
            this.$scope.$on('$destroy', disconnect);
            */

            this.initMedium();

            this.mediumMountNg().bind('input', e => {
                this.input()
            });
            this.mediumMountNg().bind('keydown', e =>
                this.keydown(e)
            );
        }
        keydown(e) {
            if(e.keyCode === 13) {
                this.submit();
                return false;
            }
        }
        input() {
            const payload = {
                value: this.value(),
                valid: this.valid(),
            };
            this.onInput(payload);
            dispatchEvent(this.$element[0], 'input', payload);
        }
        submit() {
            const value = this.value();
            const valid = this.valid();
            if(value && valid) {
                const payload = { value, valid };
                this.onSubmit(payload);
                dispatchEvent(this.$element[0], 'submit', payload);

                this.medium.clear(); // clear text
            }
        }
        charactersLeft() {
            return this.maxChars - this.value().length;
        }
        valid() {
            return !this.maxChars || this.charactersLeft() >= 0;
        }
        value() {
            return this.medium
                .value()
                /*
                 * the replace fixes odd behaviour of FF. it inserts
                 * a `<br>` at the end after the first space is
                 * typed -- unless the space is the first character
                 * in the field.
                 */
                .replace(/
$/, '')
                .replace(' ','')
                .trim();
        }

        initMedium() {
            // initialising editor. see http://jakiestfu.github.io/Medium.js/docs/
            this.medium = new Medium({
                element: this.mediumMount(),

                modifier: 'auto',
                placeholder: this.placeholder,
                autoHR: false, //if true, inserts
 after two empty lines
                mode: Medium.inlineMode, // no newlines, no styling
                //mode: Medium.partialMode, // allows newlines, no styling
                //maxLength: this.maxChars, // -1 would disable it
                tags: {
                    /*
                     'break': 'br',
                     'horizontalRule': 'hr',
                     'paragraph': 'p',
                     'outerLevel': ['pre', 'blockquote', 'figure'],
                     'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
                     */
                },
                attributes: {
                    //remove: ['style', 'class'] //TODO does this remove the ng-class?
                    remove: ['style'] //TODO does this remove the ng-class?
                },
            });

            //remove the inline-styles placed by medium.js
            this.medium.placeholder.style = "";
            this.mediumMount().addEventListener('blur', e =>
                delay(0) //push to end end of task-queue (and thus all other `blur`-listeners
                    .then(() => {
                        const style = this.medium.placeholder.style;
                        style.minHeight = 0;
                        style.minWidth = 0;
                    })
            );
        }

        mediumMountNg() {
            if(!this._mediumMount) {
                this._mediumMount = this.textFieldNg().find('.medium-mount')
            }
            return this._mediumMount;
        }

        mediumMount() {
            return this.mediumMountNg()[0];
        }

        textFieldNg() {
            if(!this._textField) {
                this._textField = this.$element.find('.wdt__text');
            }
            return this._textField;
        }
        textField() {
            return this.textFieldNg()[0];
        }
    }
    Controller.$inject = serviceDependencies;

    return {
        restrict: 'E',
        controller: Controller,
        controllerAs: 'self',
        bindToController: true, //scope-bindings -> ctrl
        scope: {
            placeholder: '=',
            maxChars: '=',
            /*
             * Usage:
             *  on-input="::myCallback(value, valid)"
             */
            onInput: '&',

            submitButtonLabel: '=',
            /*
             * Usage:
             *  on-submit="::myCallback(value)"
             */
            onSubmit: '&',

        },
        template: template
    }
}

export default angular.module('won.owner.components.chatTextfield', [ ])
    .directive('chatTextfield', genComponentConf)
    .name;<|endoftext|>


<commit_before>/*
 *  Copyright 2011 Rackspace
 *
 *  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 fs = require('fs');
var path = require('path');

var async = require('async');
var mkdirp = require('mkdirp');

var misc = require('./misc');
var sprintf = require('./sprintf');

function git(args, callback) {
  args = ['git'].concat(args);
  misc.spawn(args, null, callback);
}

function gitd(repo, args, callback) {
  args = [sprintf('--git-dir=%s/.git', repo), sprintf('--work-tree=%s', repo)].concat(args);
  git(args, callback);
}

exports.refresh = function(repo, url, remote, branch, callback) {
  var ref;

  if (callback === undefined) {
    callback = remote;
    remote = 'origin';
    branch = 'master';
  }

  ref = sprintf('%s/%s', remote, branch);

  path.exists(repo, function(exists) {
    if (exists) {
      async.series([
        exports.fetch.bind(null, repo),
        exports.checkout.bind(null, repo, branch),
        exports.resetHard.bind(null, repo, ref)
      ], callback);
    } else {
      async.series([
        exports.clone.bind(null, repo, url),
        exports.checkout.bind(null, repo, branch),
        exports.resetHard.bind(null, repo, ref)
      ], callback);
    }
  });
};

exports.hasChanges = function(repo, callback) {
  // When this succeeds but there are changes, it silently exits with code 1
  gitd(repo, ['diff-index', '--quiet', 'HEAD', '--'], function(err) {
    if (err) {
      if (err.message.indexOf('\nstderr:\n\nstdout:\n') < 0) {
        callback(err);
      } else {
        callback(null, true);
      }
    } else {
      callback(null, false);
    }
  });
};

exports.checkout = function(repo, branch, callback) {
  gitd(repo, ['checkout', branch], callback);
};

exports.add = function(repo, addPath, callback) {
  gitd(repo, ['add', addPath], callback);
};

exports.commit = function(repo, message, author, callback) {
  gitd(repo, ['commit', '-m', message, sprintf('--author=%s', author)], callback);
};

exports.push = function(repo, callback) {
  gitd(repo, ['push'], callback);
};

exports.resetHard = function(repo, ref, callback) {
  gitd(repo, ['reset', '--hard', ref], callback);
};

exports.merge = function(repo, ref, callback) {
  gitd(repo, ['merge', ref], callback);
};

exports.fetch = function(repo, callback) {
  gitd(repo, ['fetch'], callback);
};

exports.revParse = function(repo, ref, callback) {
  gitd(repo, ['rev-parse', ref], callback);
};

exports.clone = function(repo, url, callback) {
  async.series([
    // 0755 = 493
    mkdirp.bind(null, path.dirname(repo), 493),
    git.bind(null, ['clone', url, repo])
  ],
  function(err) {
    callback(err);
  });
};

exports.getLatestRevision = function(url, ref, callback) {
  git(['ls-remote', url, ref], function(err, stdout) {
    if (err) {
      callback(err);
    } else {
      callback(null, stdout.split('\t')[0]);
    }
  });
};

exports.trimRevision = function(rev) {
  return rev.slice(0, 7);
};
<commit_msg>Allow user to pass (repo, url, ref, callback) to refresh.<commit_after>/*
 *  Copyright 2011 Rackspace
 *
 *  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 fs = require('fs');
var path = require('path');

var async = require('async');
var mkdirp = require('mkdirp');

var misc = require('./misc');
var sprintf = require('./sprintf');

function git(args, callback) {
  args = ['git'].concat(args);
  misc.spawn(args, null, callback);
}

function gitd(repo, args, callback) {
  args = [sprintf('--git-dir=%s/.git', repo), sprintf('--work-tree=%s', repo)].concat(args);
  git(args, callback);
}

exports.refresh = function(repo, url, remote, branch, callback) {
  var ref;

  if (arguments.length === 3) {
    /* (repo, url, callback) */
    callback = remote;
    remote = 'origin';
    branch = 'master';
    ref = sprintf('%s/%s', remote, branch);
  }
  else if (arguments.length === 4) {
    /* (repo, url, ref, callback) */
    ref = remote;
    callback = branch;
  }
  else {
    /* (repo, url, remote, branch, callback) */
    ref = sprintf('%s/%s', remote, branch);
  }

  path.exists(repo, function(exists) {
    if (exists) {
      async.series([
        exports.fetch.bind(null, repo),
        exports.checkout.bind(null, repo, ref),
        exports.resetHard.bind(null, repo, ref)
      ], callback);
    } else {
      async.series([
        exports.clone.bind(null, repo, url),
        exports.checkout.bind(null, repo, ref),
        exports.resetHard.bind(null, repo, ref)
      ], callback);
    }
  });
};

exports.hasChanges = function(repo, callback) {
  // When this succeeds but there are changes, it silently exits with code 1
  gitd(repo, ['diff-index', '--quiet', 'HEAD', '--'], function(err) {
    if (err) {
      if (err.message.indexOf('\nstderr:\n\nstdout:\n') < 0) {
        callback(err);
      } else {
        callback(null, true);
      }
    } else {
      callback(null, false);
    }
  });
};

exports.checkout = function(repo, branch, callback) {
  gitd(repo, ['checkout', branch], callback);
};

exports.add = function(repo, addPath, callback) {
  gitd(repo, ['add', addPath], callback);
};

exports.commit = function(repo, message, author, callback) {
  gitd(repo, ['commit', '-m', message, sprintf('--author=%s', author)], callback);
};

exports.push = function(repo, callback) {
  gitd(repo, ['push'], callback);
};

exports.resetHard = function(repo, ref, callback) {
  gitd(repo, ['reset', '--hard', ref], callback);
};

exports.merge = function(repo, ref, callback) {
  gitd(repo, ['merge', ref], callback);
};

exports.fetch = function(repo, callback) {
  gitd(repo, ['fetch'], callback);
};

exports.revParse = function(repo, ref, callback) {
  gitd(repo, ['rev-parse', ref], callback);
};

exports.clone = function(repo, url, callback) {
  async.series([
    // 0755 = 493
    mkdirp.bind(null, path.dirname(repo), 493),
    git.bind(null, ['clone', url, repo])
  ],
  function(err) {
    callback(err);
  });
};

exports.getLatestRevision = function(url, ref, callback) {
  git(['ls-remote', url, ref], function(err, stdout) {
    if (err) {
      callback(err);
    } else {
      callback(null, stdout.split('\t')[0]);
    }
  });
};

exports.trimRevision = function(rev) {
  return rev.slice(0, 7);
};
<|endoftext|>


<commit_before>
module.exports = cp

var fs = require("fs")
  , Buffer = require("buffer").Buffer

function cp (from, to, cb) {
  if (from === to) return cb()
  fs.open(from, "r", 0666, function (er, fdFrom) {
    if (er) return cb(er)
    fs.open(to, "w+", 0755, function (er, fdTo) {
      if (er) {
        fs.close(fdFrom)
        return cb(er)
      }
      return cp_(fdFrom, fdTo, cb)
    })
  })
}
function cp_ (from, to, cb) {
  fs.read(from, function (er, chunk) {
    if (er) {
      fs.close(from)
      fs.close(to)
      return cb(er)
    }
    if (!chunk || !chunk.length) {
      return chain([fs, "close",from], [fs,"close",to], cb)
    }
    if (chunk instanceof Buffer) {
      fs.write(fd, chunk, 0, chunk.length, null
              , function (er) {
                  if (er) return cb(er)
                  cp_(from, to, cb)
                })
    } else {
      fs.write(fd, chunk, "ascii", null
              , function (er) {
                  if (er) return cb(er)
                  cp_(from, to, cb)
                })
    }
  })
}

<commit_msg>Remove unused "cp" module.<commit_after><|endoftext|>


<commit_before>'use strict';
var jsonlint = require('jsonlint-lines');
var reference = require('./reference');
var parseCSSColor = require('csscolorparser').parseCSSColor;

module.exports = {};
module.exports.value = value;

['v2','v3','v4'].forEach(function(version) {
    var ref = reference(version);
    // Create validator for version
    module.exports[version] = validator(ref);
    // Create validators for each container ('style', 'bucket', etc.)
    for (var container in ref) {
        if (typeof ref[container] !== 'object') continue;
        if (ref[container].type && typeof ref[container].type !== 'object') continue;
        module.exports[version][container] = validateContainer(ref, container);
    }
});

// Generates a validation function for a container object.
function validateContainer(ref, container) {
    return function (property, val, line) {
        var errors = [];
        var spec = ref[container][property];
        if (!spec) {
            errors.push({
                message: container + ' property unrecognized: ' + property,
                line: line
            });
        } else {
            value(property, val, {}, ref, spec, errors);
        }
        return errors;
    };
}

function validator(ref) {
    var validate = function(str) {
        var style, errors = [];
        try {
            style = jsonlint.parse(str.toString());
        } catch(e) {
            var match = e.message.match(/line (\d+)/),
                lineNumber = 0;
            if (match) lineNumber = parseInt(match[1], 10);
            return [{
                line: lineNumber - 1,
                message: e.message,
                error: e
            }];
        }

        style = jsonlint.parse(str.toString());

        value('', style, style.constants||{}, ref, ref.$root, errors);

        return errors;
    };
    return validate;
}

// Main recursive validation function. Tracks:
//
// - key: string representing location of validation in style tree. Used only
//   for more informative error reporting. Example: `styles.default.road`
// - val: current value from style being evaluated. May be anything from a
//   high level object that needs to be descended into deeper or a simple
//   scalar value.
// - constants: object of constants for the style to resolve constant values.
// - ref: full reference object. Used if any point in validation refers to
//   a type defined at the root-level of the reference object.
// - spec: current spec being evaluated. Tracks val.
// - errors: array of errors passed by reference.
//
// Returns true if the `val` passed (and any children recursed into) pass
// the validation defined by `spec`. Returns false if validation fails.
// Validation errors will be pushed onto the errors array.
function value(key, val, constants, ref, spec, errors) {
    var pass = true;

    // Resolve constants.
    if (typeof val === 'string' && constants[val] !== undefined) val = constants[val];

    // Spec specifies a non-native type (bucket, style, etc.)
    if (spec.type && ref[spec.type]) {
        return value(key, val, constants, ref, ref[spec.type], errors);
    // Spec specifies a type, but val must be an array of those (layers only atm).
    } else if (spec.type === 'array' && spec.value) {
        if (Array.isArray(val)) {
            if (spec.length && val.length !== spec.length) {
                errors.push({
                    message: key + ': array length ' + spec.length + ' expected, length ' + val.length + ' found',
                    line: val.__line__
                });
                return false;
            }
            for (var i = 0; i < val.length; i++) {
                var valspec = ref[spec.value]||spec.value;
                if (typeof valspec === 'string') {
                    pass = validateNative(key + '[' + i + ']', val[i], valspec, errors) && pass;
                } else {
                    pass = value(key + '[' + i + ']', val[i], constants, ref, valspec, errors) && pass;
                }
            }
            return pass;
        } else {
            errors.push({
                message: key + ': array expected, ' + typeof val + ' found',
                line: val.__line__
            });
            return false;
        }
    // Spec specifies an array of specs val may match.
    } else if (Array.isArray(spec)) {
        var sub = [];
        var valid = spec.some(function(s) {
            var spec = typeof s === 'string' ? (ref[s] || s) : s;
            if (typeof spec === 'string') {
                return validateNative(key, val, spec, sub);
            } else {
                return value(key, val, constants, ref, spec, sub);
            }
        });
        if (!valid) sub.forEach(function(err) { errors.push(err); });
        return valid;
    // Val is a function.
    } else if (spec.function && typeof val === 'object' && val.fn) {
        return value(key, val, constants, ref, ref.function, errors);
    // Val must be one of enumerated values.
    } else if (spec.type === 'enum') {
        return validateEnum(key, val, spec.values, errors);
    // Val must match a type.
    } else if (spec.type && typeof spec.type === 'string') {
        return validateNative(key, val, spec.type, errors);
    // No type defined: spec is a container. Val must be an object
    // and must have keys matching the container object definition.
    } else {
        for (var k in val) {
            var childkey = (key ? key + '.' : key) + k;
            var speckey = k.split('.')[0]; // treat 'style.*' as 'style'
            var def = spec[speckey] || spec['*'] || undefined;
            if (!def) {
                errors.push({
                    message: spec.__name__ + ' property unrecognized: ' + k,
                    line: val.__line__
                });
                pass = false;
                continue;
            }
            pass = value(childkey, val[k], constants, ref, def, errors) && pass;
        }
        for (var l in spec) {
            if (spec[l].required && spec[l]['default'] === undefined && val[l] === undefined) {
                errors.push({
                    message: spec.__name__ + ' property ' + l + ' required',
                    line: val.__line__
                });
                pass = false;
            }
        }
        return pass;
    }
}

function validateNative(key, val, spec, errors) {
    if (spec === '*') return true;

    var type = Array.isArray(val) ? 'array' : typeof val;

    if (spec === 'color') {
        if (type !== 'string') {
            errors.push({
                message: key + ': ' + spec + ' expected, ' + type + ' found',
                line: val.__line__
            });
            return false;
        } else if (parseCSSColor(val) === null) {
            errors.push({
                message: key + ': ' + spec + ' expected, ' + val + ' found',
                line: val.__line__
            });
            return false;
        } else {
            return true;
        }
    }

    if (type !== spec) {
        errors.push({
            message: key + ': ' + spec + ' expected, ' + (typeof val) + ' found',
            line: val.__line__
        });
        return false;
    } else {
        return true;
    }
}

function validateEnum(key, val, spec, errors) {
    if (spec.indexOf(val) === -1) {
        errors.push({
            message: key + ': expected one of [' + spec.join(', ') + '], ' + val + ' found',
            line: val.__line__
        });
        return false;
    } else {
        return true;
    }
}

<commit_msg>Fix validation for arrays of functions<commit_after>'use strict';
var jsonlint = require('jsonlint-lines');
var reference = require('./reference');
var parseCSSColor = require('csscolorparser').parseCSSColor;

module.exports = {};
module.exports.value = value;

['v2','v3','v4'].forEach(function(version) {
    var ref = reference(version);
    // Create validator for version
    module.exports[version] = validator(ref);
    // Create validators for each container ('style', 'bucket', etc.)
    for (var container in ref) {
        if (typeof ref[container] !== 'object') continue;
        if (ref[container].type && typeof ref[container].type !== 'object') continue;
        module.exports[version][container] = validateContainer(ref, container);
    }
});

// Generates a validation function for a container object.
function validateContainer(ref, container) {
    return function (property, val, line) {
        var errors = [];
        var spec = ref[container][property];
        if (!spec) {
            errors.push({
                message: container + ' property unrecognized: ' + property,
                line: line
            });
        } else {
            value(property, val, {}, ref, spec, errors);
        }
        return errors;
    };
}

function validator(ref) {
    var validate = function(str) {
        var style, errors = [];
        try {
            style = jsonlint.parse(str.toString());
        } catch(e) {
            var match = e.message.match(/line (\d+)/),
                lineNumber = 0;
            if (match) lineNumber = parseInt(match[1], 10);
            return [{
                line: lineNumber - 1,
                message: e.message,
                error: e
            }];
        }

        style = jsonlint.parse(str.toString());

        value('', style, style.constants||{}, ref, ref.$root, errors);

        return errors;
    };
    return validate;
}

// Main recursive validation function. Tracks:
//
// - key: string representing location of validation in style tree. Used only
//   for more informative error reporting. Example: `styles.default.road`
// - val: current value from style being evaluated. May be anything from a
//   high level object that needs to be descended into deeper or a simple
//   scalar value.
// - constants: object of constants for the style to resolve constant values.
// - ref: full reference object. Used if any point in validation refers to
//   a type defined at the root-level of the reference object.
// - spec: current spec being evaluated. Tracks val.
// - errors: array of errors passed by reference.
//
// Returns true if the `val` passed (and any children recursed into) pass
// the validation defined by `spec`. Returns false if validation fails.
// Validation errors will be pushed onto the errors array.
function value(key, val, constants, ref, spec, errors) {
    var pass = true;

    // Resolve constants.
    if (typeof val === 'string' && constants[val] !== undefined) val = constants[val];

    // Spec specifies a non-native type (bucket, style, etc.)
    if (spec.type && ref[spec.type]) {
        return value(key, val, constants, ref, ref[spec.type], errors);
    // Spec specifies a type, but val must be an array of those (layers only atm).
    } else if (spec.type === 'array' && spec.value) {
        if (Array.isArray(val)) {
            if (spec.length && val.length !== spec.length) {
                errors.push({
                    message: key + ': array length ' + spec.length + ' expected, length ' + val.length + ' found',
                    line: val.__line__
                });
                return false;
            }
            for (var i = 0; i < val.length; i++) {
                var valspec = ref[spec.value]||spec.value;
                if (typeof valspec === 'string') {
                    if (spec.function && typeof val[i] === 'object') {
                        pass = value(key, val[i], constants, ref, ref.function, errors);
                    } else {
                        pass = validateNative(key + '[' + i + ']', val[i], valspec, errors) && pass;
                    }
                } else {
                    pass = value(key + '[' + i + ']', val[i], constants, ref, valspec, errors) && pass;
                }
            }
            return pass;
        } else {
            errors.push({
                message: key + ': array expected, ' + typeof val + ' found',
                line: val ? val.__line__ : null
            });
            return false;
        }
    // Spec specifies an array of specs val may match.
    } else if (Array.isArray(spec)) {
        var sub = [];
        var valid = spec.some(function(s) {
            var spec = typeof s === 'string' ? (ref[s] || s) : s;
            if (typeof spec === 'string') {
                return validateNative(key, val, spec, sub);
            } else {
                return value(key, val, constants, ref, spec, sub);
            }
        });
        if (!valid) sub.forEach(function(err) { errors.push(err); });
        return valid;
    // Val is a function.
    } else if (spec.function && typeof val === 'object' && val.fn) {
        return value(key, val, constants, ref, ref.function, errors);
    // Val must be one of enumerated values.
    } else if (spec.type === 'enum') {
        return validateEnum(key, val, spec.values, errors);
    // Val must match a type.
    } else if (spec.type && typeof spec.type === 'string') {
        return validateNative(key, val, spec.type, errors);
    // No type defined: spec is a container. Val must be an object
    // and must have keys matching the container object definition.
    } else {
        for (var k in val) {
            var childkey = (key ? key + '.' : key) + k;
            var speckey = k.split('.')[0]; // treat 'style.*' as 'style'
            var def = spec[speckey] || spec['*'] || undefined;
            if (!def) {
                errors.push({
                    message: spec.__name__ + ' property unrecognized: ' + k,
                    line: val.__line__
                });
                pass = false;
                continue;
            }
            pass = value(childkey, val[k], constants, ref, def, errors) && pass;
        }
        for (var l in spec) {
            if (spec[l].required && spec[l]['default'] === undefined && val[l] === undefined) {
                errors.push({
                    message: spec.__name__ + ' property ' + l + ' required',
                    line: val.__line__
                });
                pass = false;
            }
        }
        return pass;
    }
}

function validateNative(key, val, spec, errors) {
    if (spec === '*') return true;

    var type = Array.isArray(val) ? 'array' : typeof val;

    if (spec === 'color') {
        if (type !== 'string') {
            errors.push({
                message: key + ': ' + spec + ' expected, ' + type + ' found',
                line: val.__line__
            });
            return false;
        } else if (parseCSSColor(val) === null) {
            errors.push({
                message: key + ': ' + spec + ' expected, ' + val + ' found',
                line: val.__line__
            });
            return false;
        } else {
            return true;
        }
    }

    if (type !== spec) {
        errors.push({
            message: key + ': ' + spec + ' expected, ' + (typeof val) + ' found',
            line: val ? val.__line__ : null
        });
        return false;
    } else {
        return true;
    }
}

function validateEnum(key, val, spec, errors) {
    if (spec.indexOf(val) === -1) {
        errors.push({
            message: key + ': expected one of [' + spec.join(', ') + '], ' + val + ' found',
            line: val.__line__
        });
        return false;
    } else {
        return true;
    }
}
<|endoftext|>