<commit_before>define(['sprd/entity/DesignConfigurationBase', 'sprd/entity/Size', 'sprd/util/UnitUtil', 'sprd/model/Design', "sprd/entity/PrintTypeColor", "underscore",
        "sprd/model/PrintType", "sprd/util/ProductUtil", "js/core/List", "flow", "sprd/manager/IDesignConfigurationManager", "sprd/data/IImageUploadService"
        , "sprd/entity/BlobImage", "sprd/data/MaskService", "sprd/data/ImageService", "sprd/manager/ImageMeasurer", "designer/manager/FeatureManager"],
    function (DesignConfigurationBase, Size, UnitUtil, Design, PrintTypeColor, _, PrintType, ProductUtil, List, flow
        , IDesignConfigurationManager, IImageUploadService, BlobImage, MaskService, ImageService, ImageMeasurer, FeatureManager) {

        return DesignConfigurationBase.inherit('sprd.model.DesignConfiguration', {
            defaults: {
                _dpi: "{printType.dpi}",

                _designCommission: "{design.price}",
                _allowScale: "{design.restrictions.allowScale}",

                afterEffect: null,
                processedDesign: null,
                processedImage: null,
                processedSize: null,
                innerRect: null,
                defaultInnerRect: null
            },

            ctor: function () {
                this.callBase();

                this.bind('change:processedImage', this._setProcessedSize, this);
                this.bind('afterEffect', "processingParametersChanged", function () {
                    this.trigger("configurationChanged");
                }, this);
                this.bind('change:processedDesign', function () {
                    this._setOriginalDesignProperties()
                }, this);
                this.bind('change:rotation', function () {
                    this._debounceFunctionCall(this.setInnerRect, "innerRect", 300, this)
                }, this);

                this.set('defaultInnerRect', {
                    x: 0,
                    y: 0,
                    width: 1,
                    height: 1
                });
            },

            inject: {
                imageUploadService: IImageUploadService,
                maskService: MaskService,
                imageService: ImageService,
                context: "context",
                featureManager: FeatureManager
            },

            type: "design",

            _commitPrintType: function (newPrintType, oldPrintType) {
                // print type changed -> convert colors

                if (!newPrintType) {
                    return;
                }

                var colors = [],
                    printColors = this.$.printColors;

                printColors.each(function (printColor) {
                    colors.push(newPrintType.getClosestPrintColor(printColor.color()));
                });

                if (newPrintType.$.id === PrintType.Mapping.SpecialFlex) {
                    // convert all colors to the first one
                    for (var i = 1; i < colors.length; i++) {
                        colors[i] = colors[0];
                    }
                }

                var original = this.get('originalPrintType');
                if (original && original !== oldPrintType) {
                    this.set('originalPrintType', null);
                }

                printColors.reset(colors);
                this.trigger('configurationChanged');
                this.trigger("priceChanged");
            },

            getDesignColors: function (transformer) {
                var design = this.$.design,
                    printType = this.$.printType,
                    printColors = this.$.printColors;

                if (!design || !printType || !printColors || !design.isVectorDesign()) {
                    return;
                }

                var designColors = design.$.colors;

                if (!designColors || !designColors.length) {
                    return;
                }

                var newPrintColors = [];
                designColors.each(function (designColor) {
                    var color = (designColor.$["default"] || designColor.$["origin"]).toRGB();
                    color = transformer instanceof Function ? transformer(color) : color;

                    var closestPrintColor = printType.getClosestPrintColor(color);
                    newPrintColors.push(closestPrintColor);
                });

                return newPrintColors;
            },

            _commitAfterEffect: function (afterEffect) {
                var self = this;

                if (!afterEffect) {
                    this.set('processedImage', null);
                } else {
                    this.$.maskService.applyAfterEffect(self.$.design, afterEffect, null, function (err, ctx) {
                        if (!err) {
                            self.setProcessedImage(ctx);
                        } else {
                            console.error(err);
                        }

                        afterEffect.callback && afterEffect.callback(err, ctx);
                    });
                }

                this.trigger('configurationChanged');
            },

            _validatePrintTypeSize: function (printType, width, height, scale) {
                var ret = this.callBase();
                var design = this.$.design;

                if (!printType || !scale || !_.isNumber(width) || !_.isNumber(height) || !design) {
                    return ret;
                }

                if (!design.isVectorDesign()) {
                    ret.dpiBound = scale.x > 1 || scale.y > 1;
                }

                return ret;
            },

            getMinScale: function () {
                var minScale = this.callBase() || Number.MIN_VALUE,
                    printType = this.$.printType;

                if (printType && !printType.isShrinkable()) {
                    minScale = Math.max(minScale, this.minimumScale());
                }

                return minScale;
            }.onChange("minimumScale()", "printType"),

            getMaxScale: function () {
                var maxScale = this.callBase(),
                    design = this.$.design;

                if (design && !design.isVectorDesign()) {
                    maxScale = Math.min(1, maxScale);
                }

                return maxScale;
            }.onChange("printType", "size()", "design"),

            _setProcessedSize: function () {
                var afterEffect = this.$.afterEffect;
                var design = this.$.design;

                if (afterEffect && this.$.processedImage) {
                    this.set('processedSize', this.$.maskService.getProcessedSize(afterEffect, design));
                } else {
                    this.set('processedSize', null);
                }

                this.trigger('sizeChanged');
            },

            applyAfterEffect: function (ctx) {
                this.setProcessedImage(ctx);
            },

            setProcessedImage: function (ctx) {
                var self = this;

                if (!self.$.design || !self.$.afterEffect) {
                    return;
                }

                var cacheId = [self.$.design.$.id, self.$.afterEffect.id()].join('#');
                self.synchronizeFunctionCall.call(self, function (cb) {
                    var img = self.$.maskService.trimAndExport(ctx, self.$.afterEffect, null);
                    cb(null, img);
                }, cacheId, function (err, result) {
                    self.set('processedImage', result);
                }, self);
            },

            getPrintColorsAsRGB: function () {
                var ret = [];

                if (this.$.design.$.colors.size() === this.$.printColors.size()) {
                    // go in the direction of the layers of the design
                    for (var i = 0; i < this.$.design.$.colors.$items.length; i++) {
                        var designColor = this.$.design.$.colors.$items[i];
                        var printColor = this.$.printColors.$items[i];
                        ret[designColor.$.layer] = printColor.color().toRGB().toHexString();
                    }
                }

                return ret;
            },

            setColor: function (layerIndex, color) {
                var printType = this.$.printType;

                if (!(color instanceof PrintTypeColor)) {
                    color = printType.getClosestPrintColor(color);
                }

                if (!printType.containsPrintTypeColor(color)) {
                    throw new Error("Color not contained in print type");
                }

                var printColors = this.$.printColors.$items;

                if (printColors[layerIndex] === color) {
                    return;
                }

                printColors.splice(layerIndex, 1, color);

                if (printType.$.id === PrintType.Mapping.SpecialFlex) {
                    // convert all other layers to the new color
                    for (var i = 0; i < printColors.length; i++) {
                        if (i !== layerIndex) {
                            printColors[i] = printColors[layerIndex];
                        }
                    }
                }

                this.$.printColors.reset(printColors);

                this.trigger('configurationChanged');
                this.trigger("priceChanged");
            },

            size: function () {
                return this.getSizeForPrintType(this.$.printType);
            }.onChange("_dpi", "design", "processedSize"),

            getSizeInPx: function (design, options) {
                options = options || {};
                design = design || this.$.design;
                return options.original ? design.$.size : this.$.processedSize || design.$.size;
            },

            getSizeForPrintType: function (printType, design, options) {
                options = options || {};
                design = design || this.$.design;
                printType = printType || this.$.printType;

                if (design && design.$.size && printType && printType.$.dpi) {
                    var dpi = printType.$.dpi;
                    var size = this.getSizeInPx(design, options);
                    return UnitUtil.convertSizeToMm(size, dpi);
                }

                return Size.empty;
            },

            isScalable: function () {
                return this.get("printType.isScalable()") && this.$._allowScale;
            }.onChange("printType", "_allowScale"),

            allowScale: function () {
                return this.$._allowScale;
            },

            getImageUrl: function () {
                var design = this.$.design,
                    imageService = this.$.imageService;

                if (!design || !imageService) {
                    return;
                }

                return this.$.processedImage || design.$.localImage || this.$.imageService.designImageFromCache(design.$.wtfMbsId, {
                    width: 100,
                    sameOrigin: true
                });
            },

            setInnerRect: function () {
                var self = this,
                    isMeasuringEnabled = this.isMeasuringEnabled();

                if (!isMeasuringEnabled) {
                    return;
                }

                this.getInnerRect(this.$.rotation, function (err, rect) {
                    if (!err) {
                        self.set("innerRect", rect);
                    }
                });
            },

            isMeasuringEnabled: function () {
                var featureEnabled = this.$.featureManager && this.$.featureManager.getFeatureState("calcInnerRects"),
                    design = this.$.design;

                if (!design) {
                    return false;
                }

                return featureEnabled && design.isVectorDesign();
            },

            getInnerRect: function (rotation, callback) {
                var url = this.getImageUrl();

                if (!url) {
                    callback && callback(new Error("Not able to get an image from current configuration."));
                    return;
                }

                flow()
                    .seq("image", function (cb) {
                        ImageMeasurer.toImage(url, cb)
                    })
                    .seq("rect", function () {
                        return ImageMeasurer.getRealDesignSize(this.vars.image, rotation);
                    })
                    .exec(function (err, results) {
                        callback && callback(err, results && results.rect)
                    })
            },

            price: function () {

                var usedPrintColors = [],
                    price = this.callBase();

                this.$.printColors.each(function (printColor) {
                    if (_.indexOf(usedPrintColors, printColor) === -1) {
                        usedPrintColors.push(printColor);
                    }
                });

                for (var i = 0; i < usedPrintColors.length; i++) {
                    if (usedPrintColors[i]) {
                        price.add((usedPrintColors[i]).get("price"));
                    }
                }

                price.add(this.get('_designCommission'));

                return price;

            }.on("priceChanged").onChange("_designCommission", "_printTypePrice"),

            getPossiblePrintTypes: function (appearance) {
                var ret = [],
                    printArea = this.$.printArea,
                    design = this.$.design;

                if (this.$context) {
                    appearance = appearance || this.$context.$contextModel.get('appearance');
                }

                if (printArea && appearance && design) {
                    ret = this.getPossiblePrintTypesForPrintArea(printArea, appearance)
                }

                return ret;
            }.onChange("printArea", "design"),

            _setAfterEffectProperties: function () {
                var afterEffect = this.get('afterEffect');
                var properties = this.get('properties');

                if (afterEffect) {
                    properties.afterEffect = _.extend(properties.afterEffect || {}, afterEffect.compose());
                    properties.type = 'afterEffect';
                } else if (properties.afterEffect || properties.type === "afterEffect") {
                    delete properties.afterEffect;
                    delete properties.type;
                }
            },

            _setOriginalDesignProperties: function (design) {
                var properties = this.get('properties'),
                    afterEffect = this.get('afterEffect');

                design = design || this.$.processedDesign;

                if (design && afterEffect) {
                    //Mask is already applied and processedImage uploaded
                    properties.afterEffect = properties.afterEffect || {};
                    properties.afterEffect.originalDesign = {
                        id: design.get('wtfMbsId'),
                        href: "/" + design.get("id")
                    };
                }
            },

            compose: function () {
                var processedDesign = this.get('processedDesign'),
                    originalDesign = this.get('design');

                if (processedDesign) {
                    this.set('design', processedDesign, {silent: true});
                }

                var ret = this.callBase();
                this._setAfterEffectProperties();
                this._setOriginalDesignProperties(originalDesign);
                ret.properties = this.$.properties;

                if (processedDesign && originalDesign) {
                    this.set('design', originalDesign);
                }

                return ret;
            },

            save: function (callback) {
                var self = this,
                    afterEffect = this.$.afterEffect;

                if (!afterEffect) {
                    callback && callback();
                } else {
                    flow()
                        .seq("design", function (cb) {
                            var afterEffect = self.$.afterEffect;
                            if (afterEffect) {
                                self.$.maskService.applyMask(afterEffect, self.$.design, self.$.context.$.id, cb);
                            }
                        })
                        .seq(function () {
                            if (this.vars.design) {
                                self.set('processedDesign', this.vars.design);
                            }
                        })
                        .exec(callback);
                }
            },

            parse: function (data) {
                data = this.callBase();

                if (data.designs) {
                    this.$$.design = data.designs[0];
                }

                data.designs = undefined;

                if (data.printArea) {
                    // remove printArea from payload since it is the wrong one
                    // it will be set within the initSchema methods
                    this.$$.printArea = data.printArea;
                    data.printArea = null;
                }

                if (data.content) {
                    this.$$.svg = data.content.svg;
                }

                return data;
            },

            saveTakesTime: function () {
                var designColors = this.get("design.colors");
                return this.get('afterEffect') || !designColors || designColors.length != 1;
            },

            init: function (options, callback) {
                this.$.manager.initializeConfiguration(this, options, callback);
                this.setInnerRect();
            },

            isAllowedOnPrintArea: function (printArea) {
                return printArea && printArea.get("restrictions.designAllowed");
            },

            getPossiblePrintTypesForPrintArea: function (printArea, appearance) {
                return ProductUtil.getPossiblePrintTypesForDesignOnPrintArea(this.$.design, printArea, appearance);
            },

            minimumScale: function () {
                return (this.get("design.restrictions.minimumScale") || 100 ) / 100;
            }.onChange("design.restrictions.minimumScale"),

            clone: function () {
                var afterEffect = this.$.afterEffect,
                    clone = this.callBase();

                if (afterEffect) {
                    var afterEffectClone = afterEffect.clone();
                    clone.set('afterEffect', afterEffectClone);
                }

                return clone;
            }
        });
    })
;
<commit_msg>DEV-128805 fixed the broken masks<commit_after>define(['sprd/entity/DesignConfigurationBase', 'sprd/entity/Size', 'sprd/util/UnitUtil', 'sprd/model/Design', "sprd/entity/PrintTypeColor", "underscore",
        "sprd/model/PrintType", "sprd/util/ProductUtil", "js/core/List", "flow", "sprd/manager/IDesignConfigurationManager", "sprd/data/IImageUploadService"
        , "sprd/entity/BlobImage", "sprd/data/MaskService", "sprd/data/ImageService", "sprd/manager/ImageMeasurer", "designer/manager/FeatureManager"],
    function (DesignConfigurationBase, Size, UnitUtil, Design, PrintTypeColor, _, PrintType, ProductUtil, List, flow
        , IDesignConfigurationManager, IImageUploadService, BlobImage, MaskService, ImageService, ImageMeasurer, FeatureManager) {

        return DesignConfigurationBase.inherit('sprd.model.DesignConfiguration', {
            defaults: {
                _dpi: "{printType.dpi}",

                _designCommission: "{design.price}",
                _allowScale: "{design.restrictions.allowScale}",

                afterEffect: null,
                processedDesign: null,
                processedImage: null,
                processedSize: null,
                innerRect: null,
                defaultInnerRect: null
            },

            ctor: function () {
                this.callBase();

                this.bind('change:processedImage', this._setProcessedSize, this);
                this.bind('afterEffect', "processingParametersChanged", function () {
                    this.trigger("configurationChanged");
                }, this);
                this.bind('change:processedDesign', function () {
                    this._setOriginalDesignProperties()
                }, this);
                this.bind('change:rotation', function () {
                    this._debounceFunctionCall(this.setInnerRect, "innerRect", 300, this)
                }, this);

                this.set('defaultInnerRect', {
                    x: 0,
                    y: 0,
                    width: 1,
                    height: 1
                });
            },

            inject: {
                imageUploadService: IImageUploadService,
                maskService: MaskService,
                imageService: ImageService,
                context: "context",
                featureManager: FeatureManager
            },

            type: "design",

            _commitPrintType: function (newPrintType, oldPrintType) {
                // print type changed -> convert colors

                if (!newPrintType) {
                    return;
                }

                var colors = [],
                    printColors = this.$.printColors;

                printColors.each(function (printColor) {
                    colors.push(newPrintType.getClosestPrintColor(printColor.color()));
                });

                if (newPrintType.$.id === PrintType.Mapping.SpecialFlex) {
                    // convert all colors to the first one
                    for (var i = 1; i < colors.length; i++) {
                        colors[i] = colors[0];
                    }
                }

                var original = this.get('originalPrintType');
                if (original && original !== oldPrintType) {
                    this.set('originalPrintType', null);
                }

                printColors.reset(colors);
                this.trigger('configurationChanged');
                this.trigger("priceChanged");
            },

            getDesignColors: function (transformer) {
                var design = this.$.design,
                    printType = this.$.printType,
                    printColors = this.$.printColors;

                if (!design || !printType || !printColors || !design.isVectorDesign()) {
                    return;
                }

                var designColors = design.$.colors;

                if (!designColors || !designColors.length) {
                    return;
                }

                var newPrintColors = [];
                designColors.each(function (designColor) {
                    var color = (designColor.$["default"] || designColor.$["origin"]).toRGB();
                    color = transformer instanceof Function ? transformer(color) : color;

                    var closestPrintColor = printType.getClosestPrintColor(color);
                    newPrintColors.push(closestPrintColor);
                });

                return newPrintColors;
            },

            _commitAfterEffect: function (afterEffect) {
                var self = this;

                if (!afterEffect) {
                    this.set('processedImage', null);
                } else {
                    this.$.maskService.applyAfterEffect(self.$.design, afterEffect, {crossOrigin: true}, function (err, ctx) {
                        if (!err) {
                            self.setProcessedImage(ctx);
                        } else {
                            console.error(err);
                        }

                        afterEffect.callback && afterEffect.callback(err, ctx);
                    });
                }

                this.trigger('configurationChanged');
            },

            _validatePrintTypeSize: function (printType, width, height, scale) {
                var ret = this.callBase();
                var design = this.$.design;

                if (!printType || !scale || !_.isNumber(width) || !_.isNumber(height) || !design) {
                    return ret;
                }

                if (!design.isVectorDesign()) {
                    ret.dpiBound = scale.x > 1 || scale.y > 1;
                }

                return ret;
            },

            getMinScale: function () {
                var minScale = this.callBase() || Number.MIN_VALUE,
                    printType = this.$.printType;

                if (printType && !printType.isShrinkable()) {
                    minScale = Math.max(minScale, this.minimumScale());
                }

                return minScale;
            }.onChange("minimumScale()", "printType"),

            getMaxScale: function () {
                var maxScale = this.callBase(),
                    design = this.$.design;

                if (design && !design.isVectorDesign()) {
                    maxScale = Math.min(1, maxScale);
                }

                return maxScale;
            }.onChange("printType", "size()", "design"),

            _setProcessedSize: function () {
                var afterEffect = this.$.afterEffect;
                var design = this.$.design;

                if (afterEffect && this.$.processedImage) {
                    this.set('processedSize', this.$.maskService.getProcessedSize(afterEffect, design));
                } else {
                    this.set('processedSize', null);
                }

                this.trigger('sizeChanged');
            },

            applyAfterEffect: function (ctx) {
                this.setProcessedImage(ctx);
            },

            setProcessedImage: function (ctx) {
                var self = this;

                if (!self.$.design || !self.$.afterEffect) {
                    return;
                }

                var cacheId = [self.$.design.$.id, self.$.afterEffect.id()].join('#');
                self.synchronizeFunctionCall.call(self, function (cb) {
                    var img = self.$.maskService.trimAndExport(ctx, self.$.afterEffect, null);
                    cb(null, img);
                }, cacheId, function (err, result) {
                    self.set('processedImage', result);
                }, self);
            },

            getPrintColorsAsRGB: function () {
                var ret = [];

                if (this.$.design.$.colors.size() === this.$.printColors.size()) {
                    // go in the direction of the layers of the design
                    for (var i = 0; i < this.$.design.$.colors.$items.length; i++) {
                        var designColor = this.$.design.$.colors.$items[i];
                        var printColor = this.$.printColors.$items[i];
                        ret[designColor.$.layer] = printColor.color().toRGB().toHexString();
                    }
                }

                return ret;
            },

            setColor: function (layerIndex, color) {
                var printType = this.$.printType;

                if (!(color instanceof PrintTypeColor)) {
                    color = printType.getClosestPrintColor(color);
                }

                if (!printType.containsPrintTypeColor(color)) {
                    throw new Error("Color not contained in print type");
                }

                var printColors = this.$.printColors.$items;

                if (printColors[layerIndex] === color) {
                    return;
                }

                printColors.splice(layerIndex, 1, color);

                if (printType.$.id === PrintType.Mapping.SpecialFlex) {
                    // convert all other layers to the new color
                    for (var i = 0; i < printColors.length; i++) {
                        if (i !== layerIndex) {
                            printColors[i] = printColors[layerIndex];
                        }
                    }
                }

                this.$.printColors.reset(printColors);

                this.trigger('configurationChanged');
                this.trigger("priceChanged");
            },

            size: function () {
                return this.getSizeForPrintType(this.$.printType);
            }.onChange("_dpi", "design", "processedSize"),

            getSizeInPx: function (design, options) {
                options = options || {};
                design = design || this.$.design;
                return options.original ? design.$.size : this.$.processedSize || design.$.size;
            },

            getSizeForPrintType: function (printType, design, options) {
                options = options || {};
                design = design || this.$.design;
                printType = printType || this.$.printType;

                if (design && design.$.size && printType && printType.$.dpi) {
                    var dpi = printType.$.dpi;
                    var size = this.getSizeInPx(design, options);
                    return UnitUtil.convertSizeToMm(size, dpi);
                }

                return Size.empty;
            },

            isScalable: function () {
                return this.get("printType.isScalable()") && this.$._allowScale;
            }.onChange("printType", "_allowScale"),

            allowScale: function () {
                return this.$._allowScale;
            },

            getImageUrl: function () {
                var design = this.$.design,
                    imageService = this.$.imageService;

                if (!design || !imageService) {
                    return;
                }

                return this.$.processedImage || design.$.localImage || this.$.imageService.designImageFromCache(design.$.wtfMbsId, {
                    width: 100,
                    sameOrigin: true
                });
            },

            setInnerRect: function () {
                var self = this,
                    isMeasuringEnabled = this.isMeasuringEnabled();

                if (!isMeasuringEnabled) {
                    return;
                }

                this.getInnerRect(this.$.rotation, function (err, rect) {
                    if (!err) {
                        self.set("innerRect", rect);
                    }
                });
            },

            isMeasuringEnabled: function () {
                var featureEnabled = this.$.featureManager && this.$.featureManager.getFeatureState("calcInnerRects"),
                    design = this.$.design;

                if (!design) {
                    return false;
                }

                return featureEnabled && design.isVectorDesign();
            },

            getInnerRect: function (rotation, callback) {
                var url = this.getImageUrl();

                if (!url) {
                    callback && callback(new Error("Not able to get an image from current configuration."));
                    return;
                }

                flow()
                    .seq("image", function (cb) {
                        ImageMeasurer.toImage(url, cb)
                    })
                    .seq("rect", function () {
                        return ImageMeasurer.getRealDesignSize(this.vars.image, rotation);
                    })
                    .exec(function (err, results) {
                        callback && callback(err, results && results.rect)
                    })
            },

            price: function () {

                var usedPrintColors = [],
                    price = this.callBase();

                this.$.printColors.each(function (printColor) {
                    if (_.indexOf(usedPrintColors, printColor) === -1) {
                        usedPrintColors.push(printColor);
                    }
                });

                for (var i = 0; i < usedPrintColors.length; i++) {
                    if (usedPrintColors[i]) {
                        price.add((usedPrintColors[i]).get("price"));
                    }
                }

                price.add(this.get('_designCommission'));

                return price;

            }.on("priceChanged").onChange("_designCommission", "_printTypePrice"),

            getPossiblePrintTypes: function (appearance) {
                var ret = [],
                    printArea = this.$.printArea,
                    design = this.$.design;

                if (this.$context) {
                    appearance = appearance || this.$context.$contextModel.get('appearance');
                }

                if (printArea && appearance && design) {
                    ret = this.getPossiblePrintTypesForPrintArea(printArea, appearance)
                }

                return ret;
            }.onChange("printArea", "design"),

            _setAfterEffectProperties: function () {
                var afterEffect = this.get('afterEffect');
                var properties = this.get('properties');

                if (afterEffect) {
                    properties.afterEffect = _.extend(properties.afterEffect || {}, afterEffect.compose());
                    properties.type = 'afterEffect';
                } else if (properties.afterEffect || properties.type === "afterEffect") {
                    delete properties.afterEffect;
                    delete properties.type;
                }
            },

            _setOriginalDesignProperties: function (design) {
                var properties = this.get('properties'),
                    afterEffect = this.get('afterEffect');

                design = design || this.$.processedDesign;

                if (design && afterEffect) {
                    //Mask is already applied and processedImage uploaded
                    properties.afterEffect = properties.afterEffect || {};
                    properties.afterEffect.originalDesign = {
                        id: design.get('wtfMbsId'),
                        href: "/" + design.get("id")
                    };
                }
            },

            compose: function () {
                var processedDesign = this.get('processedDesign'),
                    originalDesign = this.get('design');

                if (processedDesign) {
                    this.set('design', processedDesign, {silent: true});
                }

                var ret = this.callBase();
                this._setAfterEffectProperties();
                this._setOriginalDesignProperties(originalDesign);
                ret.properties = this.$.properties;

                if (processedDesign && originalDesign) {
                    this.set('design', originalDesign);
                }

                return ret;
            },

            save: function (callback) {
                var self = this,
                    afterEffect = this.$.afterEffect;

                if (!afterEffect) {
                    callback && callback();
                } else {
                    flow()
                        .seq("design", function (cb) {
                            var afterEffect = self.$.afterEffect;
                            if (afterEffect) {
                                self.$.maskService.applyMask(afterEffect, self.$.design, self.$.context.$.id, cb);
                            }
                        })
                        .seq(function () {
                            if (this.vars.design) {
                                self.set('processedDesign', this.vars.design);
                            }
                        })
                        .exec(callback);
                }
            },

            parse: function (data) {
                data = this.callBase();

                if (data.designs) {
                    this.$$.design = data.designs[0];
                }

                data.designs = undefined;

                if (data.printArea) {
                    // remove printArea from payload since it is the wrong one
                    // it will be set within the initSchema methods
                    this.$$.printArea = data.printArea;
                    data.printArea = null;
                }

                if (data.content) {
                    this.$$.svg = data.content.svg;
                }

                return data;
            },

            saveTakesTime: function () {
                var designColors = this.get("design.colors");
                return this.get('afterEffect') || !designColors || designColors.length != 1;
            },

            init: function (options, callback) {
                this.$.manager.initializeConfiguration(this, options, callback);
                this.setInnerRect();
            },

            isAllowedOnPrintArea: function (printArea) {
                return printArea && printArea.get("restrictions.designAllowed");
            },

            getPossiblePrintTypesForPrintArea: function (printArea, appearance) {
                return ProductUtil.getPossiblePrintTypesForDesignOnPrintArea(this.$.design, printArea, appearance);
            },

            minimumScale: function () {
                return (this.get("design.restrictions.minimumScale") || 100 ) / 100;
            }.onChange("design.restrictions.minimumScale"),

            clone: function () {
                var afterEffect = this.$.afterEffect,
                    clone = this.callBase();

                if (afterEffect) {
                    var afterEffectClone = afterEffect.clone();
                    clone.set('afterEffect', afterEffectClone);
                }

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


<commit_before>const util = require('util');

const baseSwitch = require('./baseSwitch');

let Service, Characteristic, Accessory;

class LearnIRAccessory {
    constructor(config, platform) {
        this.init(config, platform);
        Accessory = platform.Accessory;
        Service = platform.Service;
        Characteristic = platform.Characteristic;

        //Characteristic
        this.activeState = null;
        this.onState = Characteristic.On.NO;
        this.lastState = this.onState;

        //value
        this.lastIRCode;
        this.closeTimer;

        this._setCharacteristic();
    }
    _setCharacteristic() {
        this.services = [];

        this.infoService = new Service.AccessoryInformation();
        this.infoService
            .setCharacteristic(Characteristic.Manufacturer, "XiaoMi")
            .setCharacteristic(Characteristic.Model, "AC Partner Learn Switch")
            .setCharacteristic(Characteristic.SerialNumber, "Undefined");
        this.services.push(this.infoService);

        this.switchService = new Service.Switch(this.name);

        this.activeState = this.switchService.getCharacteristic(Characteristic.On)
            .on('set', this.setSwitchState.bind(this))
            .updateValue(this.onState);

        this.services.push(this.switchService);
    }
    setSwitchState(value, callback) {
        if (!this.platform._enterSyncState()) {
            this.platform.syncLockEvent.once("lockDrop", (() => {
                this.setSwitchState(value, callback);
            }));
            return;
        }
        this.onState = value;

        if (value) {
            //Switch on
            this.platform.devices[this.deviceIndex].call('start_ir_learn', [30])
                .then(() => {
                    this._switchUpdateState();
                    this.log("[%s]Start IR learn", this.name);
                    this.closeTimer = setInterval(() => {
                        this.showIRCode();
                    }, 500);
                    setTimeout(() => {
                        clearInterval(this.closeTimer);
                    }, 30 * 1000);
                })
                .catch((err) => {
                    this.log.error("[ERROR]Start failed! %s", err);
                    this._switchRevertState();
                })
                .then(() => {
                    callback();
                    this.platform._exitSyncState();
                });
        } else {
            //Switch off
            this.platform.devices[this.deviceIndex].call('end_ir_learn', [])
                .then(() => {
                    this.log("[%s]End IR learn", this.name);
                    this._switchUpdateState();
                    clearTimeout(this.closeTimer);
                })
                .catch((err) => {
                    this.log.error("[ERROR]End failed! %s", err);
                    this._switchRevertState();
                })
                .then(() => {
                    this.platform._exitSyncState();
                    callback();
                });
        }
    }
    showIRCode() {
        this.platform.devices[this.deviceIndex].call('get_ir_learn_result', [])
            .then((ret) => {
                const code = ret[0];
                if (code !== '(null)' && code !== this.lastIRCode) {
                    this.lastIRCode = code;
                    //Recovert the return IR code to new code
                    let _code = code.substr(0, 14) + "94701FFF96FF" + code.substr(26);
                    this.log("[%s]IR code: %s", this.name, _code);
                }
            })
            .catch((err) => this.log.error("[ERROR]Learn Switch error! %s", err));
    }
}
util.inherits(LearnIRAccessory, baseSwitch);
module.exports = LearnIRAccessory;<commit_msg>fix IR learner<commit_after>const util = require('util');

const baseSwitch = require('./baseSwitch');

let Service, Characteristic, Accessory;

class LearnIRAccessory {
    constructor(config, platform) {
        this.init(config, platform);
        Accessory = platform.Accessory;
        Service = platform.Service;
        Characteristic = platform.Characteristic;

        //Characteristic
        this.activeState = null;
        this.onState = Characteristic.On.NO;
        this.lastState = this.onState;

        //value
        this.lastIRCode;
        this.closeTimer;

        this._setCharacteristic();
    }
    _setCharacteristic() {
        this.services = [];

        this.infoService = new Service.AccessoryInformation();
        this.infoService
            .setCharacteristic(Characteristic.Manufacturer, "XiaoMi")
            .setCharacteristic(Characteristic.Model, "AC Partner Learn Switch")
            .setCharacteristic(Characteristic.SerialNumber, "Undefined");
        this.services.push(this.infoService);

        this.switchService = new Service.Switch(this.name);

        this.activeState = this.switchService.getCharacteristic(Characteristic.On)
            .on('set', this.setSwitchState.bind(this))
            .updateValue(this.onState);

        this.services.push(this.switchService);
    }
    setSwitchState(value, callback) {
        if (!this.platform._enterSyncState()) {
            this.platform.syncLockEvent.once("lockDrop", (() => {
                this.setSwitchState(value, callback);
            }));
            return;
        }
        this.onState = value;

        if (value) {
            //Switch on
            this.platform.devices[this.deviceIndex].call('start_ir_learn', [30])
                .then(() => {
                    this._switchUpdateState();
                    this.log("[%s]Start IR learn", this.name);
                    this.closeTimer = setInterval(() => {
                        this.showIRCode();
                    }, 500);
                    setTimeout(() => {
                        clearInterval(this.closeTimer);
                        this.activeState.updateValue(Characteristic.On.NO);
                    }, 30 * 1000);
                })
                .catch((err) => {
                    this.log.error("[ERROR]Start failed! %s", err);
                    this._switchRevertState();
                })
                .then(() => {
                    callback();
                    this.platform._exitSyncState();
                });
        } else {
            //Switch off
            this.platform.devices[this.deviceIndex].call('end_ir_learn', [])
                .then(() => {
                    this.log("[%s]End IR learn", this.name);
                    this._switchUpdateState();
                })
                .catch((err) => {
                    this.log.error("[ERROR]End failed! %s", err);
                    this._switchRevertState();
                })
                .then(() => {
                    clearInterval(this.closeTimer);
                    this.platform._exitSyncState();
                    callback();
                });
        }
    }
    showIRCode() {
        this.platform.devices[this.deviceIndex].call('get_ir_learn_result', [])
            .then((ret) => {
                const code = ret[0];
                if (code !== '(null)' && code !== this.lastIRCode) {
                    this.lastIRCode = code;
                    //Recovert the return IR code to new code
                    let _code = code.substr(0, 14) + "94701FFF96FF" + code.substr(26);
                    this.log("[%s]IR Code -> %s", this.name, _code);
                }
            })
            .catch((err) => this.log.error("[ERROR]Learn Switch error! %s", err));
    }
}
util.inherits(LearnIRAccessory, baseSwitch);
module.exports = LearnIRAccessory;<|endoftext|>


<commit_before>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2015 Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var React = require('react');

var BasicContainer = require('../components/BasicContainer');
var CalendarContainer = require('../components/CalendarContainer');
var SummaryGroup = React.createFactory(require('../components/misc/SummaryGroup'));

var BasalBolusRatio = React.createFactory(require('../components/chart/BasalBolusRatio'));
var BGDistribution = React.createFactory(require('../components/chart/BGDistribution'));
var WrapCount = React.createFactory(require('../components/chart/WrapCount'));
var SiteChange = React.createFactory(require('../components/chart/SiteChange'));
var TotalDailyDose = React.createFactory(require('../components/chart/TotalDailyDose'));
var InfusionHoverDisplay = React.createFactory(require('../components/day/hover/InfusionHoverDisplay'));

var basicsActions = require('./actions');

var basicsState = {
  sections: {
    basals: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'basals',
      index: 4,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { key: 'total', label: 'Basal Events' },
        rows: [
          [
            { key: 'temp', label: 'Temp Basals' },
            { key: 'suspend', label: 'Suspends' }
            // commented out because there's a problem with scheduleName in OmniPod data :(
            // { key: 'scheduleChange', label: 'Schedule Changes' }
          ]
        ]
      },
      title: 'Basals',
      type: 'basal'
    },
    basalBolusRatio: {
      active: true,
      chart: BasalBolusRatio,
      container: BasicContainer,
      column: 'left',
      id: 'basalBolusRatio',
      index: 3,
      title: 'Basal : bolus ratio',
      open: true
    },
    bgDistribution: {
      active: true,
      chart: BGDistribution,
      container: BasicContainer,
      column: 'left',
      id: 'bgDistribution',
      index: 1,
      title: 'BG distribution',
      open: true
    },
    boluses: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'boluses',
      index: 2,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { key: 'total', label: 'All Boluses' },
        rows: [
          [
            { key: 'wizard', label: 'Calculator', percentage: true  },
            { key: 'correction', label: 'Correction', percentage: true  },
            { key: 'override', label: 'Override', percentage: true  }
          ],
          [
            { key: 'manual', label: 'Manual', percentage: true  },
            { key: 'extended', label: 'Extended', percentage: true  },
            { key: 'interrupted', label : 'Interrupted', percentage: true  }
          ]
        ]
      },
      title: 'Bolusing',
      type: 'bolus'
    },
    fingersticks: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'fingersticks',
      index: 1,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { path: 'smbg', key: 'total', label: 'All BGs' },
        rows: [
          [
            { path: 'smbg', key: 'meter', label: 'Meter', percentage: true },
            { path: 'smbg', key: 'manual', label: 'Manual', percentage: true },
            { path: 'calibration', key: 'calibration', label: 'Calibrations' }
          ],
          [
            { path: 'smbg', key: 'verylow', labelOpts: {type: 'bg', key: 'verylow'}, percentage: true },
            { path: 'smbg', key: 'veryhigh', labelOpts: {type: 'bg', key: 'veryhigh'}, percentage: true }
          ]
        ]
      },
      title: 'BG readings',
      type: 'fingerstick'
    },
    siteChanges: {
      active: true,
      chart: SiteChange,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      hoverDisplay: InfusionHoverDisplay,
      id: 'siteChanges',
      index: 3,
      noDataMessage: 'Infusion site changes for CareLink data are coming soon.',
      open: true,
      title: 'Infusion site changes',
      type: 'reservoirChange'
    },
    totalDailyDose: {
      active: true,
      chart: TotalDailyDose,
      container: BasicContainer,
      column: 'left',
      id: 'totalDailyDose',
      index: 2,
      title: 'Avg total daily dose',
      open: true
    }
  }
};

module.exports = basicsState;
<commit_msg>generalize no data message for infusion site changes<commit_after>/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2015 Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

var _ = require('lodash');
var React = require('react');

var BasicContainer = require('../components/BasicContainer');
var CalendarContainer = require('../components/CalendarContainer');
var SummaryGroup = React.createFactory(require('../components/misc/SummaryGroup'));

var BasalBolusRatio = React.createFactory(require('../components/chart/BasalBolusRatio'));
var BGDistribution = React.createFactory(require('../components/chart/BGDistribution'));
var WrapCount = React.createFactory(require('../components/chart/WrapCount'));
var SiteChange = React.createFactory(require('../components/chart/SiteChange'));
var TotalDailyDose = React.createFactory(require('../components/chart/TotalDailyDose'));
var InfusionHoverDisplay = React.createFactory(require('../components/day/hover/InfusionHoverDisplay'));

var basicsActions = require('./actions');

var basicsState = {
  sections: {
    basals: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'basals',
      index: 4,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { key: 'total', label: 'Basal Events' },
        rows: [
          [
            { key: 'temp', label: 'Temp Basals' },
            { key: 'suspend', label: 'Suspends' }
            // commented out because there's a problem with scheduleName in OmniPod data :(
            // { key: 'scheduleChange', label: 'Schedule Changes' }
          ]
        ]
      },
      title: 'Basals',
      type: 'basal'
    },
    basalBolusRatio: {
      active: true,
      chart: BasalBolusRatio,
      container: BasicContainer,
      column: 'left',
      id: 'basalBolusRatio',
      index: 3,
      title: 'Basal : bolus ratio',
      open: true
    },
    bgDistribution: {
      active: true,
      chart: BGDistribution,
      container: BasicContainer,
      column: 'left',
      id: 'bgDistribution',
      index: 1,
      title: 'BG distribution',
      open: true
    },
    boluses: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'boluses',
      index: 2,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { key: 'total', label: 'All Boluses' },
        rows: [
          [
            { key: 'wizard', label: 'Calculator', percentage: true  },
            { key: 'correction', label: 'Correction', percentage: true  },
            { key: 'override', label: 'Override', percentage: true  }
          ],
          [
            { key: 'manual', label: 'Manual', percentage: true  },
            { key: 'extended', label: 'Extended', percentage: true  },
            { key: 'interrupted', label : 'Interrupted', percentage: true  }
          ]
        ]
      },
      title: 'Bolusing',
      type: 'bolus'
    },
    fingersticks: {
      active: true,
      chart: WrapCount,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      id: 'fingersticks',
      index: 1,
      open: true,
      selector: SummaryGroup,
      selectorOptions: {
        primary: { path: 'smbg', key: 'total', label: 'All BGs' },
        rows: [
          [
            { path: 'smbg', key: 'meter', label: 'Meter', percentage: true },
            { path: 'smbg', key: 'manual', label: 'Manual', percentage: true },
            { path: 'calibration', key: 'calibration', label: 'Calibrations' }
          ],
          [
            { path: 'smbg', key: 'verylow', labelOpts: {type: 'bg', key: 'verylow'}, percentage: true },
            { path: 'smbg', key: 'veryhigh', labelOpts: {type: 'bg', key: 'veryhigh'}, percentage: true }
          ]
        ]
      },
      title: 'BG readings',
      type: 'fingerstick'
    },
    siteChanges: {
      active: true,
      chart: SiteChange,
      column: 'right',
      container: CalendarContainer,
      hasHover: true,
      hoverDisplay: InfusionHoverDisplay,
      id: 'siteChanges',
      index: 3,
      noDataMessage: 'Infusion site changes are not yet available for all pumps. Coming soon!',
      open: true,
      title: 'Infusion site changes',
      type: 'reservoirChange'
    },
    totalDailyDose: {
      active: true,
      chart: TotalDailyDose,
      container: BasicContainer,
      column: 'left',
      id: 'totalDailyDose',
      index: 2,
      title: 'Avg total daily dose',
      open: true
    }
  }
};

module.exports = basicsState;
<|endoftext|>


<commit_before>import React from 'react'
import Demo from '../components/Demo'
import Debug from 'debug'
import MyCustomLatLonInput from './MyCustomLatLonInput'
import {whyDidYouUpdate} from 'why-did-you-update'
import schemas from '../schemas'
import {
  compileSchema,
  fieldInputs,
  fieldRenderers
} from '../../../src'

Debug.disable('*')

whyDidYouUpdate(React)

if (process.env.DEBUG) {
  Debug.enable(process.env.DEBUG)
}

const VALID_SCHEMA_NAMES = Object.keys(schemas)

const [schemaName, typeName] = document.location.pathname.split('/').filter(Boolean)

function renderSchemas() {
  return (

      {VALID_SCHEMA_NAMES.map(name => (

          <a href={`/${name}`}>{name}

      ))}

  )
}

function renderTypes(compiledSchema) {
  const typeNames = Object.keys(compiledSchema.types)

  return (

      {typeNames.map(name => (

          <a href={`/${schemaName}/${name}`}>{name}

      ))}

  )
}

const compiledSchema = schemaName && compileSchema(schemas[schemaName])

function renderDemo(compiledSchema) {
  const schemaFieldInputs = Object.assign({}, fieldInputs)
  Object.keys(compiledSchema.types).forEach(typeName => {
    const typeDef = compiledSchema.types[typeName]
    if (!fieldInputs[typeName] && fieldInputs[typeDef.type]) {
      schemaFieldInputs[typeName] = fieldInputs[typeDef.type]
    }
  })

  function resolveFieldInput(field) {
    if (field.type === 'latlon') {
      return MyCustomLatLonInput
    }
    return schemaFieldInputs[field.type]
  }
  function resolveFieldRenderer(field) {
    return fieldRenderers[field.type]
  }
  return (
    <Demo
      schema={compiledSchema}
      type={compiledSchema.types[typeName]}
      resolveFieldInput={resolveFieldInput}
      resolveFieldRenderer={resolveFieldRenderer}
    />
  )
}

export default function Main() {
  return (

        Schemas: {renderSchemas()}
        {compiledSchema && (

            {typeName ? 'Types:' : 'Select type:'}
            {renderTypes(compiledSchema)}

        )}

      {typeName && compiledSchema && renderDemo(compiledSchema)}

  )
}
<commit_msg>Make main root component a class in order to facilitate HMR<commit_after>import React from 'react'
import Demo from '../components/Demo'
import Debug from 'debug'
import MyCustomLatLonInput from './MyCustomLatLonInput'
import {whyDidYouUpdate} from 'why-did-you-update'
import schemas from '../schemas'
import {
  compileSchema,
  fieldInputs,
  fieldRenderers
} from '../../../src'

Debug.disable('*')

whyDidYouUpdate(React)

if (process.env.DEBUG) {
  Debug.enable(process.env.DEBUG)
}

const VALID_SCHEMA_NAMES = Object.keys(schemas)

const [schemaName, typeName] = document.location.pathname.split('/').filter(Boolean)

function renderSchemas() {
  return (

      {VALID_SCHEMA_NAMES.map(name => (

          <a href={`/${name}`}>{name}

      ))}

  )
}

function renderTypes(compiledSchema) {
  const typeNames = Object.keys(compiledSchema.types)

  return (

      {typeNames.map(name => (

          <a href={`/${schemaName}/${name}`}>{name}

      ))}

  )
}

const compiledSchema = schemaName && compileSchema(schemas[schemaName])

function renderDemo(compiledSchema) {
  const schemaFieldInputs = Object.assign({}, fieldInputs)
  Object.keys(compiledSchema.types).forEach(typeName => {
    const typeDef = compiledSchema.types[typeName]
    if (!fieldInputs[typeName] && fieldInputs[typeDef.type]) {
      schemaFieldInputs[typeName] = fieldInputs[typeDef.type]
    }
  })

  function resolveFieldInput(field) {
    if (field.type === 'latlon') {
      return MyCustomLatLonInput
    }
    return schemaFieldInputs[field.type]
  }
  function resolveFieldRenderer(field) {
    return fieldRenderers[field.type]
  }
  return (
    <Demo
      schema={compiledSchema}
      type={compiledSchema.types[typeName]}
      resolveFieldInput={resolveFieldInput}
      resolveFieldRenderer={resolveFieldRenderer}
    />
  )
}

class Main extends React.Component {
  render() {
    return (

          Schemas: {renderSchemas()}
          {compiledSchema && (

              {typeName ? 'Types:' : 'Select type:'}
              {renderTypes(compiledSchema)}

          )}

        {typeName && compiledSchema && renderDemo(compiledSchema)}

    )
  }
}

export default Main
<|endoftext|>


<commit_before>var express = require('express');
var path = require('path');
var logger = require('morgan');
var browserify = require('browserify-middleware');
var axios = require('axios');
var compileSass = require('express-compile-sass');
var app = express();
var requestProxy = require('express-request-proxy');

if (app.get('env') === 'development') {
  var livereload = require('express-livereload');
  livereload(app, {});

  app.use(require('connect-livereload')({
    port: 35729
  }));
}
var Server;

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/../public')));

app.get('/js/bundle.js', browserify(['./client/src/main', 'jquery', 'ractive-decorators-select2', 'select2', 'ractive-multi-decorator', {'./client/src/bootstrap': {run: true}}]));
app.get('/js/bundle_for_old.js', browserify(['./client/src/main_old']));

app.get('/css/vendor/:cssFile', function (request, response) {
  response.sendFile(request.params.cssFile, {root: path.resolve(path.join(__dirname, "/../node_modules/select2/dist/css/"))});
});

function newResource(type) {
  return axios.post(process.env.SERVICES_PORT + "/" + type, {}, {
    headers: {
      Accept: "application/ld+json",
      "Content-Type": "application/ld+json"
    }
  })
        .catch(function (response) {
          if (response instanceof Error) {
            // Something happened in setting up the request that triggered an Error
            console.log('Error', response.message);
          } else {
            // The request was made, but the server responded with a status code
            // that falls out of the range of 2xx
            console.log(response.data);
            console.log(response.status);
            console.log(response.headers);
            console.log(response.config);
          }
        });
}

app.get('/', function (res) {
  res.redirect("index.html");
});

app.get('/cataloguing/*', function (req, res, next) {
  res.sendFile('main_old.html', {title: 'Katalogisering', root: __dirname + '/../public/'});
});

app.get('/workflow', function (req, res, next) {
  res.sendFile('main.html', {title: 'Katalogisering', root: __dirname + '/../public/'});
});

app.get('/:type(person|work|publication|placeOfPublication)', function (req, res, next) {
  newResource(req.params.type).then(function (response) {
    res.redirect('/cataloguing/' + req.params.type + '?resource=' + response.headers.location);
  });
});

app.get('/config', function (request, response) {
  var config =
    {
      kohaOpacUri: (process.env.KOHA_OPAC_PORT || 'http://192.168.50.12:8080').replace(/^tcp:\//, 'http:/'),
      kohaIntraUri: (process.env.KOHA_INTRA_PORT || 'http://192.168.50.12:8081').replace(/^tcp:\//, 'http:/'),
      ontologyUri:  '/services/ontology',
      resourceApiUri: '/services/',
      tabs: [
            {
              id: "confirm-person",
              rdfType: "Work",
              label: "Bekreft person",
              inputs: [
                  {
                      rdfProperty: "creator",
                      type: "searchable-person",
                      dependentResourceTypes:["Work", "Publication"] // when the creator is changed, unload curent work an publication
                  }
              ],
              nextStep: {
                buttonLabel: "Bekreft verk",
                createNewResource: "Work"
              }
            },
            {
              id: "confirm-work",
              rdfType: "Work",
              label: "Bekreft verk",
              inputs: [
                  {rdfProperty: "mainTitle"},
                  {rdfProperty: "subtitle"},
                  {rdfProperty: "partTitle"},
                  {rdfProperty: "partNumber"}
              ],
              nextStep: {
                buttonLabel: "Beskriv verket"
              }
            },
            {
              id: "describe-work",
              rdfType: "Work",
              label: "Beskriv verket",
              inputs: [
                  {rdfProperty: "publicationYear"},
                  {rdfProperty: "language", multiple: true},
                  {rdfProperty: "literaryForm", multiple: true},
                  {rdfProperty: "audience", multiple: true},
                  {rdfProperty: "biography", multiple: true},
                  {rdfProperty: "adaptationOfWorkForParticularUserGroups", multiple: true}
              ],
              nextStep: {
                buttonLabel: "Beskriv utgivelsen",
                createNewResource: "Publication"
              }
            },
            {
              id: "describe-publication",
              rdfType: "Publication",
              label: "Beskriv utgivelsen",
              inputs: [
                  {rdfProperty: "publicationOf", type: "entity"},
                  {rdfProperty: "mainTitle"},
                  {rdfProperty: "subtitle"},
                  {rdfProperty: "partTitle"},
                  {rdfProperty: "partNumber"},
                  {rdfProperty: "edition"},
                  {rdfProperty: "publicationYear"},
                  {rdfProperty: "numberOfPages"},
                  {rdfProperty: "illustrativeMatter"},
                  {rdfProperty: "isbn"},
                  {rdfProperty: "binding"},
                  {rdfProperty: "language"},
                  {rdfProperty: "format", multiple: true},
                  {rdfProperty: "writingSystem", multiple: true},
                  {rdfProperty: "adaptationOfPublicationForParticularUserGroups", multiple: true},
                  {
                    rdfProperty: "placeOfPublication",
                    authority: true, // this indicates it is an authorized entity
                    nameProperties: ["place", "country"], // these are proeprty names used to label already connected entities
                    indexType: "placeOfPublication", // this is the name of the elasticsearch index type from which authorities are searched within
                    indexDocumentFields: ["place", "country"] // these are indexed document JSON properties from which the labels for authoroty select list are concatenated
                  }
                ],
              nextStep: {
                buttonLabel: "Bekreft biinførsler"
              }
            },
            {
              // additional entries, such as translator, illustrator, composer etc
              id: "confirm-addedentry",
              rdfType: "Work",
              label: "Bekreft biinførsler",
              inputs: [
                    {
                      predicateType: "role", // the possible values for this input is fetched from predefined list in services with this name (...ontology#role in this case)
                      subjects: ["Work", "Publication"], // the triple may have a subject of one of these types as subject, fetched from one of the loaded resources
                      type: "searchable-person", // type if input widget
                      label: "Legg til biinførsel",
                      multiple: true, // there may be more than one additional entry
                      dataAutomationId: "search_role_player", // override or define automation-id for test purposes
                      widgetOptions: {
                          hideSelectWork: true
                      }
                    }
                ],
              nextStep: {
                buttonLabel: "Avslutt registrering av utgivelsen",
                restart: true
              }
            }

        ]
    };
  response.json(config);
});

app.get('/version', function (request, response) {
  response.json({ 'buildTag': process.env.BUILD_TAG, 'gitref': process.env.GITREF });
});

var services = (process.env.SERVICES_PORT || 'http://services:8005').replace(/^tcp:\//, 'http:/');
app.all('/services/*', requestProxy({
  url: services + '/*'
}));

app.use("/style", compileSass({
  root: path.join(__dirname, '/../client/scss'),
  sourceMap: true, // Includes Base64 encoded source maps in output css
  sourceComments: false, // Includes source comments in output css
  watchFiles: true, // Watches sass files and updates mtime on main files for each change
  logToConsole: true // If true, will log to console.error on errors
}));

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

Server = app.listen(process.env.BIND_PORT || 8010, process.env.BIND_IP, function () {
  var host = Server.address().address,
      port = Server.address().port;
  console.log('Catalinker server listening at http://%s:%s', host, port);
});

module.exports = app;
<commit_msg>catalinker: fix typo<commit_after>var express = require('express');
var path = require('path');
var logger = require('morgan');
var browserify = require('browserify-middleware');
var axios = require('axios');
var compileSass = require('express-compile-sass');
var app = express();
var requestProxy = require('express-request-proxy');

if (app.get('env') === 'development') {
  var livereload = require('express-livereload');
  livereload(app, {});

  app.use(require('connect-livereload')({
    port: 35729
  }));
}
var Server;

app.use(logger('dev'));
app.use(express.static(path.join(__dirname, '/../public')));

app.get('/js/bundle.js', browserify(['./client/src/main', 'jquery', 'ractive-decorators-select2', 'select2', 'ractive-multi-decorator', {'./client/src/bootstrap': {run: true}}]));
app.get('/js/bundle_for_old.js', browserify(['./client/src/main_old']));

app.get('/css/vendor/:cssFile', function (request, response) {
  response.sendFile(request.params.cssFile, {root: path.resolve(path.join(__dirname, "/../node_modules/select2/dist/css/"))});
});

function newResource(type) {
  return axios.post(process.env.SERVICES_PORT + "/" + type, {}, {
    headers: {
      Accept: "application/ld+json",
      "Content-Type": "application/ld+json"
    }
  })
        .catch(function (response) {
          if (response instanceof Error) {
            // Something happened in setting up the request that triggered an Error
            console.log('Error', response.message);
          } else {
            // The request was made, but the server responded with a status code
            // that falls out of the range of 2xx
            console.log(response.data);
            console.log(response.status);
            console.log(response.headers);
            console.log(response.config);
          }
        });
}

app.get('/', function (res) {
  res.redirect("index.html");
});

app.get('/cataloguing/*', function (req, res, next) {
  res.sendFile('main_old.html', {title: 'Katalogisering', root: __dirname + '/../public/'});
});

app.get('/workflow', function (req, res, next) {
  res.sendFile('main.html', {title: 'Katalogisering', root: __dirname + '/../public/'});
});

app.get('/:type(person|work|publication|placeOfPublication)', function (req, res, next) {
  newResource(req.params.type).then(function (response) {
    res.redirect('/cataloguing/' + req.params.type + '?resource=' + response.headers.location);
  });
});

app.get('/config', function (request, response) {
  var config =
    {
      kohaOpacUri: (process.env.KOHA_OPAC_PORT || 'http://192.168.50.12:8080').replace(/^tcp:\//, 'http:/'),
      kohaIntraUri: (process.env.KOHA_INTRA_PORT || 'http://192.168.50.12:8081').replace(/^tcp:\//, 'http:/'),
      ontologyUri:  '/services/ontology',
      resourceApiUri: '/services/',
      tabs: [
            {
              id: "confirm-person",
              rdfType: "Work",
              label: "Bekreft person",
              inputs: [
                  {
                    rdfProperty: "creator",
                    type: "searchable-person",
                    dependentResourceTypes: ["Work", "Publication"] // when the creator is changed, unload current work and publication
                  }
              ],
              nextStep: {
                buttonLabel: "Bekreft verk",
                createNewResource: "Work"
              }
            },
            {
              id: "confirm-work",
              rdfType: "Work",
              label: "Bekreft verk",
              inputs: [
                  {rdfProperty: "mainTitle"},
                  {rdfProperty: "subtitle"},
                  {rdfProperty: "partTitle"},
                  {rdfProperty: "partNumber"}
              ],
              nextStep: {
                buttonLabel: "Beskriv verket"
              }
            },
            {
              id: "describe-work",
              rdfType: "Work",
              label: "Beskriv verket",
              inputs: [
                  {rdfProperty: "publicationYear"},
                  {rdfProperty: "language", multiple: true},
                  {rdfProperty: "literaryForm", multiple: true},
                  {rdfProperty: "audience", multiple: true},
                  {rdfProperty: "biography", multiple: true},
                  {rdfProperty: "adaptationOfWorkForParticularUserGroups", multiple: true}
              ],
              nextStep: {
                buttonLabel: "Beskriv utgivelsen",
                createNewResource: "Publication"
              }
            },
            {
              id: "describe-publication",
              rdfType: "Publication",
              label: "Beskriv utgivelsen",
              inputs: [
                  {rdfProperty: "publicationOf", type: "entity"},
                  {rdfProperty: "mainTitle"},
                  {rdfProperty: "subtitle"},
                  {rdfProperty: "partTitle"},
                  {rdfProperty: "partNumber"},
                  {rdfProperty: "edition"},
                  {rdfProperty: "publicationYear"},
                  {rdfProperty: "numberOfPages"},
                  {rdfProperty: "illustrativeMatter"},
                  {rdfProperty: "isbn"},
                  {rdfProperty: "binding"},
                  {rdfProperty: "language"},
                  {rdfProperty: "format", multiple: true},
                  {rdfProperty: "writingSystem", multiple: true},
                  {rdfProperty: "adaptationOfPublicationForParticularUserGroups", multiple: true},
                  {
                    rdfProperty: "placeOfPublication",
                    authority: true, // this indicates it is an authorized entity
                    nameProperties: ["place", "country"], // these are proeprty names used to label already connected entities
                    indexType: "placeOfPublication", // this is the name of the elasticsearch index type from which authorities are searched within
                    indexDocumentFields: ["place", "country"] // these are indexed document JSON properties from which the labels for authoroty select list are concatenated
                  }
                ],
              nextStep: {
                buttonLabel: "Bekreft biinførsler"
              }
            },
            {
              // additional entries, such as translator, illustrator, composer etc
              id: "confirm-addedentry",
              rdfType: "Work",
              label: "Bekreft biinførsler",
              inputs: [
                    {
                      predicateType: "role", // the possible values for this input is fetched from predefined list in services with this name (...ontology#role in this case)
                      subjects: ["Work", "Publication"], // the triple may have a subject of one of these types as subject, fetched from one of the loaded resources
                      type: "searchable-person", // type if input widget
                      label: "Legg til biinførsel",
                      multiple: true, // there may be more than one additional entry
                      dataAutomationId: "search_role_player", // override or define automation-id for test purposes
                      widgetOptions: {
                        hideSelectWork: true
                      }
                    }
                ],
              nextStep: {
                buttonLabel: "Avslutt registrering av utgivelsen",
                restart: true
              }
            }

        ]
    };
  response.json(config);
});

app.get('/version', function (request, response) {
  response.json({ 'buildTag': process.env.BUILD_TAG, 'gitref': process.env.GITREF });
});

var services = (process.env.SERVICES_PORT || 'http://services:8005').replace(/^tcp:\//, 'http:/');
app.all('/services/*', requestProxy({
  url: services + '/*'
}));

app.use("/style", compileSass({
  root: path.join(__dirname, '/../client/scss'),
  sourceMap: true, // Includes Base64 encoded source maps in output css
  sourceComments: false, // Includes source comments in output css
  watchFiles: true, // Watches sass files and updates mtime on main files for each change
  logToConsole: true // If true, will log to console.error on errors
}));

// catch 404 and forward to error handler
app.use(function (req, res, next) {
  var err = new Error('Not Found');
  err.status = 404;
  next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
  app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
      message: err.message,
      error: err
    });
  });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: {}
  });
});

Server = app.listen(process.env.BIND_PORT || 8010, process.env.BIND_IP, function () {
  var host = Server.address().address,
      port = Server.address().port;
  console.log('Catalinker server listening at http://%s:%s', host, port);
});

module.exports = app;
<|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React from 'react'
import styled from 'styled-components'
import { Box, Card } from 'rebass/styled-components'
import Text from './Text'

const StyledTooltipIconBox = styled(Box)`
  border-radius: 50%;
  height: 13px;
  width: 13px;
  position: relative;
  text-align: center;
  z-index: 2;
  &:hover {
    cursor: pointer;
  }
`
StyledTooltipIconBox.displayName = 'TooltipIconBox'

const StyledTooltipWrapper = styled(Card)`
  position: absolute;
  top: -5px;
  width: 225px;
  z-index: 1;
`
StyledTooltipWrapper.displayName = 'TooltipWrapper'

/**
 * @name Tooltip
 * @example
 * <Tooltip>Some explanation text here</Tooltip>
 */
class Tooltip extends React.Component {
  static displayName = 'Tooltip'

  static propTypes = {
    children: PropTypes.node,
  }

  state = { hover: false }

  hoverOn = () => {
    this.setState({ hover: true })
  }

  hoverOff = () => {
    this.setState({ hover: false })
  }

  tooltipIcon = () => {
    /* eslint-disable shopify/jsx-no-hardcoded-content */
    return (
      <StyledTooltipIconBox
        bg="primaryText"
        ml={1}
        onMouseEnter={this.hoverOn}
        onMouseLeave={this.hoverOff}
      >
        <Text color="primaryColor" fontSize="s" fontWeight="bold">
          ?
        </Text>
      </StyledTooltipIconBox>
    )
    /* eslint-enable shopify/jsx-no-hardcoded-content */
  }

  render() {
    const { children, ...rest } = this.props
    const { hover } = this.state

    return (
      <Box sx={{ position: 'relative' }} {...rest}>
        {this.tooltipIcon()}
        {hover && (
          <StyledTooltipWrapper
            bg="secondaryColor"
            p={2}
            pl={3}
            sx={{
              borderRadius: 's',
              boxShadow: 's',
              borderWidth: 1,
              borderStyle: 'solid',
              borderColor: 'gray',
            }}
          >
            <Text fontWeight="light" ml={2}>
              {children}
            </Text>
          </StyledTooltipWrapper>
        )}
      </Box>
    )
  }
}

export default Tooltip
<commit_msg>fix(ui): dim tooltip icons<commit_after>import PropTypes from 'prop-types'
import React from 'react'
import styled from 'styled-components'
import { Box, Card } from 'rebass/styled-components'
import Text from './Text'

const StyledTooltipIconBox = styled(Box)`
  border-radius: 50%;
  height: 13px;
  width: 13px;
  position: relative;
  text-align: center;
  z-index: 2;
  &:hover {
    cursor: pointer;
  }
`
StyledTooltipIconBox.displayName = 'TooltipIconBox'

const StyledTooltipWrapper = styled(Card)`
  position: absolute;
  top: -5px;
  width: 225px;
  z-index: 1;
`
StyledTooltipWrapper.displayName = 'TooltipWrapper'

/**
 * @name Tooltip
 * @example
 * <Tooltip>Some explanation text here</Tooltip>
 */
class Tooltip extends React.Component {
  static displayName = 'Tooltip'

  static propTypes = {
    children: PropTypes.node,
  }

  state = { hover: false }

  hoverOn = () => {
    this.setState({ hover: true })
  }

  hoverOff = () => {
    this.setState({ hover: false })
  }

  tooltipIcon = () => {
    const { hover } = this.state

    /* eslint-disable shopify/jsx-no-hardcoded-content */
    return (
      <StyledTooltipIconBox
        bg={hover ? 'primaryText' : 'grey'}
        ml={1}
        onMouseEnter={this.hoverOn}
        onMouseLeave={this.hoverOff}
      >
        <Text color="primaryColor" fontSize="s" fontWeight="bold">
          ?
        </Text>
      </StyledTooltipIconBox>
    )
    /* eslint-enable shopify/jsx-no-hardcoded-content */
  }

  render() {
    const { children, ...rest } = this.props
    const { hover } = this.state

    return (
      <Box sx={{ position: 'relative' }} {...rest}>
        {this.tooltipIcon()}
        {hover && (
          <StyledTooltipWrapper
            bg="secondaryColor"
            p={2}
            pl={3}
            sx={{
              borderRadius: 's',
              boxShadow: 's',
              borderWidth: 1,
              borderStyle: 'solid',
              borderColor: 'gray',
            }}
          >
            <Text fontWeight="light" ml={2}>
              {children}
            </Text>
          </StyledTooltipWrapper>
        )}
      </Box>
    )
  }
}

export default Tooltip
<|endoftext|>


<commit_before>import get from 'lodash/get'
import partition from 'lodash/partition'
import { createSelector } from 'reselect'
import truncateNodePubkey from '@zap/utils/truncateNodePubkey'
import { getNodeDisplayName, networkSelectors } from './network'

// ------------------------------------
// Initial State
// ------------------------------------

const initialState = {
  searchQuery: null,
}

// ------------------------------------
// Constants
// ------------------------------------

export const UPDATE_CONTACT_FORM_SEARCH_QUERY = 'UPDATE_CONTACT_FORM_SEARCH_QUERY'

// ------------------------------------
// Helpers
// ------------------------------------

/**
 *fromSuggestedToRegular - Converts suggested nodes to a list of entries compatible with contact search results.
 *
 * @param {Array} suggestedNodes Suggested nodes list
 * @returns {Array} Search results compatible version of suggested nodes array
 */
const fromSuggestedToRegular = suggestedNodes =>
  suggestedNodes &&
  suggestedNodes.map(node => ({
    ...node,
    addresses: [{ addr: node.host }],
  }))

// ------------------------------------
// Actions
// ------------------------------------

/**
 * updateContactFormSearchQuery - Set the current contacts serach string.
 *
 * @param {string} searchQuery Search query
 * @returns {object} Action
 */
export function updateContactFormSearchQuery(searchQuery) {
  return {
    type: UPDATE_CONTACT_FORM_SEARCH_QUERY,
    searchQuery,
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------

const ACTION_HANDLERS = {
  [UPDATE_CONTACT_FORM_SEARCH_QUERY]: (state, { searchQuery }) => ({ ...state, searchQuery }),
}

// ------------------------------------
// Selectors
// ------------------------------------

const contactFormSelectors = {}
const networkNodesSelector = state => networkSelectors.nodes(state)
const searchQuerySelector = state => state.contactsform.searchQuery
const peersSelector = state => state.peers.peers
const contactable = node => node.addresses.length > 0
const networkSelector = state => state.info.network
const chainSelector = state => state.info.chain
const suggestedNodesSelector = state => state.channels.suggestedNodes

// comparator to sort the contacts list with contactable contacts first
const contactableFirst = (a, b) => {
  if (contactable(a) && !contactable(b)) {
    return -1
  } else if (!contactable(a) && contactable(b)) {
    return 1
  }
  return 0
}

contactFormSelectors.suggestedNodes = createSelector(
  chainSelector,
  networkSelector,
  suggestedNodesSelector,
  (chain, network, suggestedNodes) => {
    return get(suggestedNodes, `${chain}.${network}`, [])
  }
)

contactFormSelectors.filteredNetworkNodes = createSelector(
  networkNodesSelector,
  searchQuerySelector,
  peersSelector,
  contactFormSelectors.suggestedNodes,
  (nodes, searchQuery, peers, suggestedNodes) => {
    const LIMIT = 50

    // If there is no search query default to showing the first 50 nodes from the nodes array
    // (performance hit to render the entire thing by default)
    if (!searchQuery) {
      const peerPubKeys = peers.map(peer => peer.pub_key)
      const [peerNodes, nonPeerNodes] = partition(nodes, node => peerPubKeys.includes(node.pub_key))
      return peerNodes
        .concat(nonPeerNodes)
        .sort(contactableFirst)
        .slice(0, LIMIT)
    }

    // if there is an '@' in the search query we are assuming they are using the format pubkey@host
    // we can ignore the '@' and the host and just grab the pubkey for our search
    const query = searchQuery.includes('@') ? searchQuery.split('@')[0] : searchQuery
    const queryLowerCase = query && query.toLowerCase()
    // list of the nodes
    return nodes
      .concat(fromSuggestedToRegular(suggestedNodes) || [])
      .filter(node => {
        const { alias, pub_key, addresses } = node
        const matchesSearch =
          (alias && alias.toLowerCase().includes(queryLowerCase)) ||
          (pub_key && pub_key.includes(queryLowerCase))
        const hasAddress = addresses.length > 0
        return matchesSearch && hasAddress
      })
      .sort(contactableFirst)
      .slice(0, LIMIT)
  }
)

contactFormSelectors.isSearchValidNodeAddress = createSelector(
  searchQuerySelector,
  searchQuery => {
    if (!searchQuery || searchQuery.length < 3) {
      return false
    }
    const [pubkey, host] = searchQuery.split('@')
    return Boolean(pubkey && host)
  }
)

contactFormSelectors.selectedNodeDisplayName = createSelector(
  searchQuerySelector,
  contactFormSelectors.isSearchValidNodeAddress,
  networkNodesSelector,
  (searchQuery, isSearchValidNodeAddress, nodes) => {
    if (isSearchValidNodeAddress) {
      const pubkey = searchQuery.split('@')[0]
      const node = nodes.find(n => n.pub_key === pubkey)
      return node ? getNodeDisplayName(node) : truncateNodePubkey(pubkey)
    }
    return null
  }
)

export { contactFormSelectors }

// ------------------------------------
// Reducer
// ------------------------------------

/**
 * contactFormReducer - Contactsform reducer.
 *
 * @param  {object} state = initialState Initial state
 * @param  {object} action Action
 * @returns {object} Next state
 */
export default function contactFormReducer(state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<commit_msg>refactor: use immer in contactsform reducer<commit_after>import get from 'lodash/get'
import partition from 'lodash/partition'
import { createSelector } from 'reselect'
import truncateNodePubkey from '@zap/utils/truncateNodePubkey'
import { getNodeDisplayName, networkSelectors } from './network'
import createReducer from './utils/createReducer'
// ------------------------------------
// Initial State
// ------------------------------------

const initialState = {
  searchQuery: null,
}

// ------------------------------------
// Constants
// ------------------------------------

export const UPDATE_CONTACT_FORM_SEARCH_QUERY = 'UPDATE_CONTACT_FORM_SEARCH_QUERY'

// ------------------------------------
// Helpers
// ------------------------------------

/**
 *fromSuggestedToRegular - Converts suggested nodes to a list of entries compatible with contact search results.
 *
 * @param {Array} suggestedNodes Suggested nodes list
 * @returns {Array} Search results compatible version of suggested nodes array
 */
const fromSuggestedToRegular = suggestedNodes =>
  suggestedNodes &&
  suggestedNodes.map(node => ({
    ...node,
    addresses: [{ addr: node.host }],
  }))

// ------------------------------------
// Actions
// ------------------------------------

/**
 * updateContactFormSearchQuery - Set the current contacts serach string.
 *
 * @param {string} searchQuery Search query
 * @returns {object} Action
 */
export function updateContactFormSearchQuery(searchQuery) {
  return {
    type: UPDATE_CONTACT_FORM_SEARCH_QUERY,
    searchQuery,
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------

const ACTION_HANDLERS = {
  [UPDATE_CONTACT_FORM_SEARCH_QUERY]: (state, { searchQuery }) => {
    state.searchQuery = searchQuery
  },
}

// ------------------------------------
// Selectors
// ------------------------------------

const contactFormSelectors = {}
const networkNodesSelector = state => networkSelectors.nodes(state)
const searchQuerySelector = state => state.contactsform.searchQuery
const peersSelector = state => state.peers.peers
const contactable = node => node.addresses.length > 0
const networkSelector = state => state.info.network
const chainSelector = state => state.info.chain
const suggestedNodesSelector = state => state.channels.suggestedNodes

// comparator to sort the contacts list with contactable contacts first
const contactableFirst = (a, b) => {
  if (contactable(a) && !contactable(b)) {
    return -1
  } else if (!contactable(a) && contactable(b)) {
    return 1
  }
  return 0
}

contactFormSelectors.suggestedNodes = createSelector(
  chainSelector,
  networkSelector,
  suggestedNodesSelector,
  (chain, network, suggestedNodes) => {
    return get(suggestedNodes, `${chain}.${network}`, [])
  }
)

contactFormSelectors.filteredNetworkNodes = createSelector(
  networkNodesSelector,
  searchQuerySelector,
  peersSelector,
  contactFormSelectors.suggestedNodes,
  (nodes, searchQuery, peers, suggestedNodes) => {
    const LIMIT = 50

    // If there is no search query default to showing the first 50 nodes from the nodes array
    // (performance hit to render the entire thing by default)
    if (!searchQuery) {
      const peerPubKeys = peers.map(peer => peer.pub_key)
      const [peerNodes, nonPeerNodes] = partition(nodes, node => peerPubKeys.includes(node.pub_key))
      return peerNodes
        .concat(nonPeerNodes)
        .sort(contactableFirst)
        .slice(0, LIMIT)
    }

    // if there is an '@' in the search query we are assuming they are using the format pubkey@host
    // we can ignore the '@' and the host and just grab the pubkey for our search
    const query = searchQuery.includes('@') ? searchQuery.split('@')[0] : searchQuery
    const queryLowerCase = query && query.toLowerCase()
    // list of the nodes
    return nodes
      .concat(fromSuggestedToRegular(suggestedNodes) || [])
      .filter(node => {
        const { alias, pub_key, addresses } = node
        const matchesSearch =
          (alias && alias.toLowerCase().includes(queryLowerCase)) ||
          (pub_key && pub_key.includes(queryLowerCase))
        const hasAddress = addresses.length > 0
        return matchesSearch && hasAddress
      })
      .sort(contactableFirst)
      .slice(0, LIMIT)
  }
)

contactFormSelectors.isSearchValidNodeAddress = createSelector(
  searchQuerySelector,
  searchQuery => {
    if (!searchQuery || searchQuery.length < 3) {
      return false
    }
    const [pubkey, host] = searchQuery.split('@')
    return Boolean(pubkey && host)
  }
)

contactFormSelectors.selectedNodeDisplayName = createSelector(
  searchQuerySelector,
  contactFormSelectors.isSearchValidNodeAddress,
  networkNodesSelector,
  (searchQuery, isSearchValidNodeAddress, nodes) => {
    if (isSearchValidNodeAddress) {
      const pubkey = searchQuery.split('@')[0]
      const node = nodes.find(n => n.pub_key === pubkey)
      return node ? getNodeDisplayName(node) : truncateNodePubkey(pubkey)
    }
    return null
  }
)

export { contactFormSelectors }

export default createReducer(initialState, ACTION_HANDLERS)
<|endoftext|>