<commit_before>define(["sprd/entity/DesignConfigurationBase", "sprd/entity/Size", "sprd/entity/Font", "sprd/util/ProductUtil", "sprd/lib/Text2Path", "sprd/entity/BlobImage", "sprd/data/IImageUploadService", "flow", "underscore", "sprd/util/ArrayUtil", "sprd/extensions/CanvasToBlob", "xaml!sprd/data/DesignerApiDataSource", "sprd/model/Transformer", "sprd/model/AbstractShop", "sprd/entity/TextConfiguration", "xaml!sprd/view/svg/BendingTextConfigurationUploadRenderer"],
    function(DesignConfigurationBase, Size, Font, ProductUtil, Text2Path, BlobImage, IImageUploadService, flow, _, ArrayUtil, CanvasToBlob, DesignerApiDataSource, Transformer, Shop, TextConfiguration, BendingTextConfigurationUploadRenderer) {
        var PATH_TYPE = {
            OUTER_CIRCLE: "outer_circle",
            INNER_CIRCLE: "inner_circle",
            HEART: "heart"
        };

        var designCache = {},
            copyrightWordList = TextConfiguration.getCopyrightWordList();

        return DesignConfigurationBase.inherit('sprd.model.BendingTextConfiguration', {

            defaults: {
                fontSize: 16,

                _size: Size,
                aspectRatio: 1,
                _allowScale: true,
                loading: false,
                initialized: false,
                isNew: false,
                isTemplate: false,

                angle: 50,
                path: PATH_TYPE.OUTER_CIRCLE,
                textPath: "{textPath()}",
                dy: "{dy()}",

                textPathOffsetX: 0,
                textPathOffsetY: 0,
                transformer: null,
                copyrightWordList: null,
                alignmentMatters: false
            },

            type: "bendingText",
            representationType: "text",

            $events: [
                "recalculateSize"
            ],

            inject: {
                imageUploadService: IImageUploadService,
                designerApi: DesignerApiDataSource,
                context: "context"
            },

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

                copyrightWordList.bind("add", function() {
                    this._validateText();
                }, this);

                this.$synchronizeCache = designCache;
            },

            init: function(options, callback) {
                var properties = this.$.properties,
                    context = this.$.context,
                    self = this;
                options = options || {};

                this.initTransformer();
                if (!_.isEmpty(properties)) {
                    flow()
                        .seq(function(cb) {
                            options = _.clone(options);
                            options.noDesignFetch = true;

                            DesignConfigurationBase.prototype.init.call(self, options, cb);
                        })
                        .seq(function(cb) {
                            var fontFamilies = context.$.fontFamilies;
                            if (fontFamilies.size()) {
                                cb();
                            } else {
                                fontFamilies.fetch({fullData: true}, cb);
                            }
                        })
                        .seq("fontFamily", function() {
                            var fontFamilyId = properties.fontFamilyId;
                            if (fontFamilyId) {
                                var items = context.$.fontFamilies.$items;

                                for (var i = items.length; i--;) {
                                    if (items[i].$.id == properties.fontFamilyId) {
                                        return items[i];
                                    }
                                }
                            }
                        })
                        .seq(function() {
                            var printType = self.$.printType;
                            if (printType) {
                                self.setColor(null, printType.getClosestPrintColor(properties.fill))
                            }
                        })
                        .seq(function() {
                            if (properties.text) {

                                var fontFamily = this.vars.fontFamily,
                                    fontWeight = properties.fontWeight,
                                    fontStyle = properties.fontStyle;

                                var scale = {
                                    x: properties.scale,
                                    y: properties.scale
                                };

                                var size = new Size({
                                    width: (properties.size && properties.size.width) || 0,
                                    height: (properties.size && properties.size.height) || 0
                                });

                                self.set({
                                    text: properties.text,
                                    angle: properties.angle || 50,
                                    path: properties.path || PATH_TYPE.OUTER_CIRCLE,
                                    font: fontFamily.getFont(fontWeight, fontStyle),
                                    fontSize: properties.fontSize || 16,
                                    _size: size,
                                    scale: scale
                                })
                            }
                        })
                        .exec(function(err) {
                            self.set("initialized", true);
                            callback && callback(err);
                        });
                } else {
                    callback && callback();
                }

            },

            _commitText: function() {
                this._validateText();
            },

            initTransformer: function() {
                var shop = this.$.designerApi.createEntity(Shop, this.$.context.$.id);
                this.set('transformer', shop.createEntity(Transformer));
            },

            size: function() {
                return this.$._size || Size.empty;
            }.onChange("_size").on("sizeChanged"),

            compose: function() {
                var ret = this.callBase();
                var font = this.$.font;
                ret.properties.type = "bendingText";
                ret.properties.text = this.$.text;
                ret.properties.angle = this.$.angle;
                ret.properties.fontFamilyId = font.getFontFamily().$.id;
                ret.properties.fontWeight = font.$.weight;
                ret.properties.fontStyle = font.$.style;
                ret.properties.fontSize = this.$.fontSize;
                ret.properties.fill = this.$.printColors.at(0).toHexString();
                ret.properties.path = this.$.path;
                ret.properties.scale = this.$.scale.x;
                ret.properties.size = this.$._size.$;

                return ret;
            },

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

                var recalculateSize = function() {
                    var self = this;
                    self.trigger("recalculateSize", self);
                    this.trigger('configurationChanged');
                };

                this.bind("change:text", recalculateSize, this);
                this.bind("change:angle", recalculateSize, this);
                this.bind("change:font", recalculateSize, this);
                this.bind("change:fontSize", recalculateSize, this);

                this.bind("change:printColors", function() {
                    this.trigger('configurationChanged');
                }, this)
            },

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

                if (!printType || !scale) {
                    return ret;
                }

                ret.minBound = this._isScaleTooSmall(printType, scale);

                return ret;
            },

            _isScaleTooSmall: function(printType, scale) {
                var font = this.$.font,
                    fontSize = this.$.fontSize;

                if (printType.isShrinkable()) {
                    return false;
                }

                if (font && fontSize) {
                    return Math.min(Math.abs(scale.x), Math.abs(scale.y)) < font.$.minimalSize / fontSize;
                }

                return false;
            },

            textPath: function() {
                var a = this.$.angle;

                this.set("path", PATH_TYPE.OUTER_CIRCLE);
                if (a < 0) {
                    a = -a;

                    return "M 0, 0 m oneTime, twoTime a oneTime, oneTime 0 1, 0 0, twoTime a oneTime, oneTime 0 1, 0 0, -twoTime"
                        .replace(/oneTime/g, "" + a)
                        .replace(/twoTime/g, "" + (2 * a));

                } else {
                    return "M 0, 0 m oneTime, 0 a oneTime, oneTime 0 1, 1 0, -twoTime a oneTime, oneTime 0 1, 1 0, twoTime"
                        .replace(/oneTime/g, "" + a)
                        .replace(/twoTime/g, "" + (2 * a));
                }

            }.on("recalculateSize"),

            dy: function() {
                return this.$.angle < 0 ? 16 : 0;
            }.onChange("angle"),

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

                if (!printArea || !font) {
                    return ret;
                }

                tmp = this.getPossiblePrintTypesForPrintArea(printArea, appearance);
                _.each(tmp, function(element) {
                    if (ret.indexOf(element) === -1) {
                        ret.push(element);
                    }
                });

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

            setColor: function(layerIndex, color) {
                var printColors = this.$.printColors;
                if (printColors) {
                    printColors.reset([color]);
                }
            },

            getPossiblePrintTypesForPrintArea: function(printArea, appearance) {
                var fontFamily = this.$.font.getFontFamily(),
                    text = this.$.text;

                if (text) {
                    var possiblePrintTypes = ProductUtil.getPossiblePrintTypesForTextOnPrintArea(fontFamily, printArea, appearance),
                        digitalPrintTypes = _.filter(possiblePrintTypes, function(printType) {
                            return !printType.isPrintColorColorSpace();
                        });
                    return ArrayUtil.moveToStart(possiblePrintTypes, digitalPrintTypes);
                }
            },

            save: function(callback) {
                var text = this.mainConfigurationRenderer.$.text,
                    self = this,
                    digitalPrint = !this.$.printType.isPrintColorColorSpace();

                var cacheId = [self.$.angle, self.$.text, self.$.font.$.id, self.$.fontSize, self.$.printType.$.id];
                var fill = self.$.printColors.at(0).toHexString();

                if (digitalPrint) {
                    cacheId.push(fill);
                }

                cacheId = cacheId.join("-");

                this.synchronizeFunctionCall(function(callback) {

                    flow()
                        .seq('svg', function(cb) {
                            self.transformTextPath(cb);
                        })
                        .seq("blob", function(cb) {
                            var svg = this.vars.svg;

                            if (digitalPrint) {
                                var image = new Image();
                                image.onload = function() {
                                    try {
                                        var canvas = document.createElement("canvas");
                                        canvas.width = image.naturalWidth;
                                        canvas.height = image.naturalHeight;
                                        canvas.getContext('2d').drawImage(image, 0, 0);

                                        canvas.toBlob(function(blob) {
                                            cb(null, blob);
                                        }, "image/png");
                                    } catch (e) {
                                        cb(e);
                                    }
                                };

                                image.onerror = cb;

                                image.src = "data:image/svg+xml;base64," + btoa(svg);

                            } else {
                                cb(null, new Blob([svg], {type: "image/svg"}));
                            }
                        })
                        .seq('uploadDesign', function(cb) {
                            var img = new BlobImage({
                                blob: this.vars.blob,
                                filename: "bending-text" + (digitalPrint ? ".png" : ".svg")
                            });

                            self.$.imageUploadService.upload(img, cb);
                        })
                        .seq("design", function() {
                            return this.vars.uploadDesign.$.design;
                        })
                        .exec(function(err, results) {
                            callback(err, results.design);
                        });

                }, cacheId, function(err, design) {
                    self.set('design', design);
                    callback(err);
                }, this);

            },

            getPrintColor: function() {
                var configuration = this,
                    printColors = configuration.$.printColors,
                    printColor = null;

                if (printColors && printColors.size()) {
                    printColor = printColors.at(0).toHexString();
                }

                return printColor;
            }.on("printColors"),

            transformTextPath: function(callback) {
                try {
                    var uploadRenderer = this.$stage.createComponent(BendingTextConfigurationUploadRenderer, {
                        configuration: this
                    });
                    this.$stage.addChild(uploadRenderer);
                    var svgContent = uploadRenderer.getElementAsString();
                } catch (e) {
                    callback(e, null);
                } finally {
                    this.$stage.removeChild(uploadRenderer);
                }

                var transformer = this.$.transformer;

                transformer.set('content', svgContent);
                transformer.save(null, function(err, transformer) {
                    callback(err, transformer.get("content"));
                })
            },

            saveTakesTime: function() {
                return true;
            },

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

            _additionalValidation: function($, options) {
                if (this._hasSome($, ["angle", "text", "fontSize"])) {
                    return {
                        angle: $.angle,
                        text: $.text,
                        validateHardBoundary: true
                    };
                }

            },

            _validateText: function() {
                var text = (this.$.text || "").toLowerCase(),
                    badWord;

                if (text.length > 1) {
                    // check that we don't contain copyright content
                    if (copyrightWordList && copyrightWordList.size()) {
                        badWord = copyrightWordList.find(function(word) {
                            return text.indexOf(word.toLowerCase()) !== -1;
                        });

                        this._setError("copyright", badWord);
                    }
                }
            }
        });
    });
<commit_msg>DEV-121488 cannot open sketchomat with pre-release bending text configurations.<commit_after>define(["sprd/entity/DesignConfigurationBase", "sprd/entity/Size", "sprd/entity/Font", "sprd/util/ProductUtil", "sprd/lib/Text2Path", "sprd/entity/BlobImage", "sprd/data/IImageUploadService", "flow", "underscore", "sprd/util/ArrayUtil", "sprd/extensions/CanvasToBlob", "xaml!sprd/data/DesignerApiDataSource", "sprd/model/Transformer", "sprd/model/AbstractShop", "sprd/entity/TextConfiguration", "xaml!sprd/view/svg/BendingTextConfigurationUploadRenderer"],
    function(DesignConfigurationBase, Size, Font, ProductUtil, Text2Path, BlobImage, IImageUploadService, flow, _, ArrayUtil, CanvasToBlob, DesignerApiDataSource, Transformer, Shop, TextConfiguration, BendingTextConfigurationUploadRenderer) {
        var PATH_TYPE = {
            OUTER_CIRCLE: "outer_circle",
            INNER_CIRCLE: "inner_circle",
            HEART: "heart"
        };

        var designCache = {},
            copyrightWordList = TextConfiguration.getCopyrightWordList();

        return DesignConfigurationBase.inherit('sprd.model.BendingTextConfiguration', {

            defaults: {
                fontSize: 16,

                _size: Size,
                aspectRatio: 1,
                _allowScale: true,
                loading: false,
                initialized: false,
                isNew: false,
                isTemplate: false,

                angle: 50,
                path: PATH_TYPE.OUTER_CIRCLE,
                textPath: "{textPath()}",
                dy: "{dy()}",

                textPathOffsetX: 0,
                textPathOffsetY: 0,
                transformer: null,
                copyrightWordList: null,
                alignmentMatters: false
            },

            type: "bendingText",
            representationType: "text",

            $events: [
                "recalculateSize"
            ],

            inject: {
                imageUploadService: IImageUploadService,
                designerApi: DesignerApiDataSource,
                context: "context"
            },

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

                copyrightWordList.bind("add", function() {
                    this._validateText();
                }, this);

                this.$synchronizeCache = designCache;
            },

            init: function(options, callback) {
                var properties = this.$.properties,
                    context = this.$.context,
                    self = this;
                options = options || {};

                this.initTransformer();
                if (!_.isEmpty(properties)) {
                    flow()
                        .seq(function(cb) {
                            options = _.clone(options);
                            options.noDesignFetch = true;

                            DesignConfigurationBase.prototype.init.call(self, options, cb);
                        })
                        .seq(function(cb) {
                            var fontFamilies = context.$.fontFamilies;
                            if (fontFamilies.size()) {
                                cb();
                            } else {
                                fontFamilies.fetch({fullData: true}, cb);
                            }
                        })
                        .seq("fontFamily", function() {
                            var fontFamilyId = properties.fontFamilyId;
                            if (fontFamilyId) {
                                var items = context.$.fontFamilies.$items;

                                for (var i = items.length; i--;) {
                                    if (items[i].$.id == properties.fontFamilyId) {
                                        return items[i];
                                    }
                                }
                            }
                        })
                        .seq(function() {
                            var printType = self.$.printType;
                            if (printType && properties.fill) {
                                self.setColor(null, printType.getClosestPrintColor(properties.fill))
                            }
                        })
                        .seq(function() {
                            if (properties.text) {

                                var fontFamily = this.vars.fontFamily,
                                    fontWeight = properties.fontWeight,
                                    fontStyle = properties.fontStyle;

                                var scale = {
                                    x: properties.scale,
                                    y: properties.scale
                                };

                                var size = new Size({
                                    width: (properties.size && properties.size.width) || 0,
                                    height: (properties.size && properties.size.height) || 0
                                });

                                self.set({
                                    text: properties.text,
                                    angle: properties.angle || 50,
                                    path: properties.path || PATH_TYPE.OUTER_CIRCLE,
                                    font: fontFamily.getFont(fontWeight, fontStyle),
                                    fontSize: properties.fontSize || 16,
                                    _size: size,
                                    scale: scale
                                })
                            }
                        })
                        .exec(function(err) {
                            self.set("initialized", true);
                            callback && callback(err);
                        });
                } else {
                    callback && callback();
                }

            },

            _commitText: function() {
                this._validateText();
            },

            initTransformer: function() {
                var shop = this.$.designerApi.createEntity(Shop, this.$.context.$.id);
                this.set('transformer', shop.createEntity(Transformer));
            },

            size: function() {
                return this.$._size || Size.empty;
            }.onChange("_size").on("sizeChanged"),

            compose: function() {
                var ret = this.callBase();
                var font = this.$.font;
                ret.properties.type = "bendingText";
                ret.properties.text = this.$.text;
                ret.properties.angle = this.$.angle;
                ret.properties.fontFamilyId = font.getFontFamily().$.id;
                ret.properties.fontWeight = font.$.weight;
                ret.properties.fontStyle = font.$.style;
                ret.properties.fontSize = this.$.fontSize;
                if (!this.$.printColors.isEmpty()) {
                    ret.properties.fill = this.$.printColors.at(0).toHexString();
                }
                ret.properties.path = this.$.path;
                ret.properties.scale = this.$.scale.x;
                ret.properties.size = this.$._size.$;

                return ret;
            },

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

                var recalculateSize = function() {
                    var self = this;
                    self.trigger("recalculateSize", self);
                    this.trigger('configurationChanged');
                };

                this.bind("change:text", recalculateSize, this);
                this.bind("change:angle", recalculateSize, this);
                this.bind("change:font", recalculateSize, this);
                this.bind("change:fontSize", recalculateSize, this);

                this.bind("change:printColors", function() {
                    this.trigger('configurationChanged');
                }, this)
            },

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

                if (!printType || !scale) {
                    return ret;
                }

                ret.minBound = this._isScaleTooSmall(printType, scale);

                return ret;
            },

            _isScaleTooSmall: function(printType, scale) {
                var font = this.$.font,
                    fontSize = this.$.fontSize;

                if (printType.isShrinkable()) {
                    return false;
                }

                if (font && fontSize) {
                    return Math.min(Math.abs(scale.x), Math.abs(scale.y)) < font.$.minimalSize / fontSize;
                }

                return false;
            },

            textPath: function() {
                var a = this.$.angle;

                this.set("path", PATH_TYPE.OUTER_CIRCLE);
                if (a < 0) {
                    a = -a;

                    return "M 0, 0 m oneTime, twoTime a oneTime, oneTime 0 1, 0 0, twoTime a oneTime, oneTime 0 1, 0 0, -twoTime"
                        .replace(/oneTime/g, "" + a)
                        .replace(/twoTime/g, "" + (2 * a));

                } else {
                    return "M 0, 0 m oneTime, 0 a oneTime, oneTime 0 1, 1 0, -twoTime a oneTime, oneTime 0 1, 1 0, twoTime"
                        .replace(/oneTime/g, "" + a)
                        .replace(/twoTime/g, "" + (2 * a));
                }

            }.on("recalculateSize"),

            dy: function() {
                return this.$.angle < 0 ? 16 : 0;
            }.onChange("angle"),

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

                if (!printArea || !font) {
                    return ret;
                }

                tmp = this.getPossiblePrintTypesForPrintArea(printArea, appearance);
                _.each(tmp, function(element) {
                    if (ret.indexOf(element) === -1) {
                        ret.push(element);
                    }
                });

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

            setColor: function(layerIndex, color) {
                var printColors = this.$.printColors;
                if (printColors && color) {
                    printColors.reset([color]);
                }
            },

            getPossiblePrintTypesForPrintArea: function(printArea, appearance) {
                var fontFamily = this.$.font.getFontFamily(),
                    text = this.$.text;

                if (text) {
                    var possiblePrintTypes = ProductUtil.getPossiblePrintTypesForTextOnPrintArea(fontFamily, printArea, appearance),
                        digitalPrintTypes = _.filter(possiblePrintTypes, function(printType) {
                            return !printType.isPrintColorColorSpace();
                        });
                    return ArrayUtil.moveToStart(possiblePrintTypes, digitalPrintTypes);
                }
            },

            save: function(callback) {
                var text = this.mainConfigurationRenderer.$.text,
                    self = this,
                    digitalPrint = !this.$.printType.isPrintColorColorSpace();

                var cacheId = [self.$.angle, self.$.text, self.$.font.$.id, self.$.fontSize, self.$.printType.$.id];
                var fill = self.$.printColors.at(0).toHexString();

                if (digitalPrint) {
                    cacheId.push(fill);
                }

                cacheId = cacheId.join("-");

                this.synchronizeFunctionCall(function(callback) {

                    flow()
                        .seq('svg', function(cb) {
                            self.transformTextPath(cb);
                        })
                        .seq("blob", function(cb) {
                            var svg = this.vars.svg;

                            if (digitalPrint) {
                                var image = new Image();
                                image.onload = function() {
                                    try {
                                        var canvas = document.createElement("canvas");
                                        canvas.width = image.naturalWidth;
                                        canvas.height = image.naturalHeight;
                                        canvas.getContext('2d').drawImage(image, 0, 0);

                                        canvas.toBlob(function(blob) {
                                            cb(null, blob);
                                        }, "image/png");
                                    } catch (e) {
                                        cb(e);
                                    }
                                };

                                image.onerror = cb;

                                image.src = "data:image/svg+xml;base64," + btoa(svg);

                            } else {
                                cb(null, new Blob([svg], {type: "image/svg"}));
                            }
                        })
                        .seq('uploadDesign', function(cb) {
                            var img = new BlobImage({
                                blob: this.vars.blob,
                                filename: "bending-text" + (digitalPrint ? ".png" : ".svg")
                            });

                            self.$.imageUploadService.upload(img, cb);
                        })
                        .seq("design", function() {
                            return this.vars.uploadDesign.$.design;
                        })
                        .exec(function(err, results) {
                            callback(err, results.design);
                        });

                }, cacheId, function(err, design) {
                    self.set('design', design);
                    callback(err);
                }, this);

            },

            getPrintColor: function() {
                var configuration = this,
                    printColors = configuration.$.printColors,
                    printColor = null;

                if (printColors && printColors.size()) {
                    printColor = printColors.at(0).toHexString();
                }

                return printColor;
            }.on("printColors"),

            transformTextPath: function(callback) {
                try {
                    var uploadRenderer = this.$stage.createComponent(BendingTextConfigurationUploadRenderer, {
                        configuration: this
                    });
                    this.$stage.addChild(uploadRenderer);
                    var svgContent = uploadRenderer.getElementAsString();
                } catch (e) {
                    callback(e, null);
                } finally {
                    this.$stage.removeChild(uploadRenderer);
                }

                var transformer = this.$.transformer;

                transformer.set('content', svgContent);
                transformer.save(null, function(err, transformer) {
                    callback(err, transformer.get("content"));
                })
            },

            saveTakesTime: function() {
                return true;
            },

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

            _additionalValidation: function($, options) {
                if (this._hasSome($, ["angle", "text", "fontSize"])) {
                    return {
                        angle: $.angle,
                        text: $.text,
                        validateHardBoundary: true
                    };
                }

            },

            _validateText: function() {
                var text = (this.$.text || "").toLowerCase(),
                    badWord;

                if (text.length > 1) {
                    // check that we don't contain copyright content
                    if (copyrightWordList && copyrightWordList.size()) {
                        badWord = copyrightWordList.find(function(word) {
                            return text.indexOf(word.toLowerCase()) !== -1;
                        });

                        this._setError("copyright", badWord);
                    }
                }
            }
        });
    });
<|endoftext|>


<commit_before>import CheddarVariable from './var';
import CheddarScope from './scope';
import CheddarClass from './class';
import NIL from '../consts/nil';

// I have the details, planing, structure,
// and design of functions all on my
// whiteboard. I'll copy it here later

export default class CheddarFunction extends CheddarClass {
    static Name = "Function";

    constructor(args, body, preset = null) {
        super();

        this.args = args;
        this.body = body;
        this.preset = preset;

        // TODO: Redo optimizations lost due to git
        this.cache = {};
    }

    // Initalizes from primitive arguments
    init(args, body) {
        // Move the scope argument to correct prop
        this.inherited = this.args;
        this.Reference = this.body;

        if (args === "") {
            args = [];
        } else {

            if (args.constructor.name !== "CheddarArrayToken") {
                args = { _Tokens: [ args ] };
            }

            let argument, res = Array(args._Tokens.length);
            for (var i = 0; i < args._Tokens.length; i++) {
                argument = args._Tokens[i];

                // Argument is a arg object
                let props = {};
                let name;

                // Store the argument tokens
                let arg = argument._Tokens;
                let nameargs = arg[0]._Tokens;

                props.Optional = arg[1] === '?';
                props.Default = arg[2] ||
                    arg[1] &&
                    arg[1].constructor.name === "CheddarExpressionToken" &&
                    arg[1];

                props.Type = nameargs.length > 1 && nameargs[1];

                if (props.Type) {
                    let type = this.inherited.accessor(props.Type._Tokens[0]);
                    if (!type) {
                        return `${props.Type._Tokens[0]} is not defined`;
                    }

                    type = type.Value;

                    if (!(type.prototype instanceof CheddarClass)) {
                        return `${props.Type._Tokens[0]} is not a class`;
                    }

                    props.Type = type;
                }

                name = nameargs[0]._Tokens[0];

                res[i] = [name, props];
            }

            args = res;
        }

        this.args = args;
        this.body = body;

        return true;
    }

    exec(input, self) {
        let scope = this.generateScope(input, self);

        if (!(scope instanceof CheddarScope))
            return scope;

        let tmp;
        if (typeof this.body === 'function') {
            return this.body(
                scope,
                name => (tmp = scope.accessor(name)) && tmp.Value
            );
        } else {
            let executor = require(
                this.body.constructor.name === "StatementExpression" ?
                '../eval/eval' :
                '../../exec'
            );

            let res = new executor(
                this.body.constructor.name === "StatementExpression" ?
                this.body :
                this.body._Tokens[0],
                scope
            ).exec();

            return res;
        }
    }

    generateScope(input, self) {
        let args = new CheddarScope(this.inherited || null);
        //console.log(this.inherited);
        //console.log(args.Scope);

        let CheddarArray = require('../primitives/Array');
        let tmp;

        if (self)
            args.setter("self", new CheddarVariable(self, {
                Writeable: false
            }));

        for (let i = 0; i < this.args.length; i++) {
            tmp = this.args[i][1];

            // Pass if undefined
            if (!tmp) continue;

            if (tmp.Splat === true) {
                let splat = new CheddarArray();
                splat.init(...input.slice(i));

                args.setter(this.args[i][0], new CheddarVariable(
                    splat
                ));

                break;
            }
            else if (input[i]) {
                if (tmp.Type && !(input[i] instanceof tmp.Type)) {
                    return `${this.Reference || "function"} expected arg @${i} to be ${
                        tmp.Type.Name ||
                        tmp.Type.constructor.Name ||
                        "object"
                    }, recieved ${
                        input[i].Name ||
                        input[i].constructor.Name ||
                        "object"
                    }`;
                }
                args.setter(this.args[i][0], new CheddarVariable(
                    input[i]
                ));
            }
            else {
                if (tmp.Optional === true) {
                    args.setter(this.args[i][0], new CheddarVariable(
                        new NIL
                    ));
                }
                else if (tmp.Default) {
                    args.setter(this.args[i][0], new CheddarVariable(
                        tmp.Default
                    ));
                }
                else {
                    return `Missing argument for ${this.args[i][0]}`
                }
            }
        }

        return args;
    }
}
<commit_msg>feature [func]: Added bonding with `&`<commit_after>import CheddarVariable from './var';
import CheddarScope from './scope';
import CheddarClass from './class';
import NIL from '../consts/nil';

// I have the details, planing, structure,
// and design of functions all on my
// whiteboard. I'll copy it here later

export default class CheddarFunction extends CheddarClass {
    static Name = "Function";

    constructor(args, body, preset = null) {
        super();

        this.args = args;
        this.body = body;
        this.preset = preset;

        // TODO: Redo optimizations lost due to git
        this.cache = {};
    }

    // Initalizes from primitive arguments
    init(args, body) {
        // Move the scope argument to correct prop
        this.inherited = this.args;
        this.Reference = this.body;

        if (args === "") {
            args = [];
        } else {

            if (args.constructor.name !== "CheddarArrayToken") {
                args = { _Tokens: [ args ] };
            }

            let argument, res = Array(args._Tokens.length);
            for (var i = 0; i < args._Tokens.length; i++) {
                argument = args._Tokens[i];

                // Argument is a arg object
                let props = {};
                let name;

                // Store the argument tokens
                let arg = argument._Tokens;
                let nameargs = arg[0]._Tokens;

                props.Optional = arg[1] === '?';
                props.Default = arg[2] ||
                    arg[1] &&
                    arg[1].constructor.name === "CheddarExpressionToken" &&
                    arg[1];

                props.Type = nameargs.length > 1 && nameargs[1];

                if (props.Type) {
                    let type = this.inherited.accessor(props.Type._Tokens[0]);
                    if (!type) {
                        return `${props.Type._Tokens[0]} is not defined`;
                    }

                    type = type.Value;

                    if (!(type.prototype instanceof CheddarClass)) {
                        return `${props.Type._Tokens[0]} is not a class`;
                    }

                    props.Type = type;
                }

                name = nameargs[0]._Tokens[0];

                res[i] = [name, props];
            }

            args = res;
        }

        this.args = args;
        this.body = body;

        return true;
    }

    exec(input, self) {
        let scope = this.generateScope(input, self);

        if (!(scope instanceof CheddarScope))
            return scope;

        let tmp;
        if (typeof this.body === 'function') {
            return this.body(
                scope,
                name => (tmp = scope.accessor(name)) && tmp.Value,
                input
            );
        } else {
            let executor = require(
                this.body.constructor.name === "StatementExpression" ?
                '../eval/eval' :
                '../../exec'
            );

            let res = new executor(
                this.body.constructor.name === "StatementExpression" ?
                this.body :
                this.body._Tokens[0],
                scope
            ).exec();

            return res;
        }
    }

    generateScope(input, self) {
        let args = new CheddarScope(this.inherited || null);
        //console.log(this.inherited);
        //console.log(args.Scope);

        let CheddarArray = require('../primitives/Array');
        let tmp;

        if (self)
            args.setter("self", new CheddarVariable(self, {
                Writeable: false
            }));

        for (let i = 0; i < this.args.length; i++) {
            tmp = this.args[i][1];

            // Pass if undefined
            if (!tmp) continue;

            if (tmp.Splat === true) {
                let splat = new CheddarArray();
                splat.init(...input.slice(i));

                args.setter(this.args[i][0], new CheddarVariable(
                    splat
                ));

                break;
            }
            else if (input[i]) {
                if (tmp.Type && !(input[i] instanceof tmp.Type)) {
                    return `${this.Reference || "function"} expected arg @${i} to be ${
                        tmp.Type.Name ||
                        tmp.Type.constructor.Name ||
                        "object"
                    }, recieved ${
                        input[i].Name ||
                        input[i].constructor.Name ||
                        "object"
                    }`;
                }
                args.setter(this.args[i][0], new CheddarVariable(
                    input[i]
                ));
            }
            else {
                if (tmp.Optional === true) {
                    args.setter(this.args[i][0], new CheddarVariable(
                        new NIL
                    ));
                }
                else if (tmp.Default) {
                    args.setter(this.args[i][0], new CheddarVariable(
                        tmp.Default
                    ));
                }
                else {
                    return `Missing argument for ${this.args[i][0]}`
                }
            }
        }

        return args;
    }

    static Operator = new Map([...CheddarClass.Operator,
        ['&', (self, value) => {
            // Copy args to new function
            let new_args = self.args.slice(1);
            return new self.constructor(new_args, function(scope, input, rargs) {
                return self.exec([value, ...rargs], null);
            });
        }]
    ]);
}
<|endoftext|>


<commit_before>//By Rajeshwar Patlolla
//https://github.com/rajeshwarpatlolla

(function () {
  'use strict';

  angular.module('ionic-timepicker')
    .directive('ionicTimepicker', ionicTimepicker);

  ionicTimepicker.$inject = ['$ionicPopup'];
  function ionicTimepicker($ionicPopup) {
    return {
      restrict: 'AE',
      replace: true,
      scope: {
        inputObj: "=inputObj"
      },
      link: function (scope, element, attrs) {

        var today = new Date();
        var currentEpoch = ((new Date()).getHours() * 60 * 60) + ((new Date()).getMinutes() * 60);

        scope.inputEpochTime = scope.inputObj.inputEpochTime ? scope.inputObj.inputEpochTime : currentEpoch;
        scope.step = scope.inputObj.step ? scope.inputObj.step : 15;
        scope.format = scope.inputObj.format ? scope.inputObj.format : 24;
        scope.titleLabel = scope.inputObj.titleLabel ? scope.inputObj.titleLabel : 'Time Picker';
        scope.setLabel = scope.inputObj.setLabel ? scope.inputObj.setLabel : 'Set';
        scope.closeLabel = scope.inputObj.closeLabel ? scope.inputObj.closeLabel : 'Close';
        scope.setButtonType = scope.inputObj.setButtonType ? scope.inputObj.setButtonType : 'button-positive';
        scope.closeButtonType = scope.inputObj.closeButtonType ? scope.inputObj.closeButtonType : 'button-stable';

        var obj = {epochTime: scope.inputEpochTime, step: scope.step, format: scope.format};
        scope.time = {hours: 0, minutes: 0, meridian: ""};
        var objDate = new Date(obj.epochTime * 1000);       // Epoch time in milliseconds.

        scope.increaseHours = function () {
          scope.time.hours = Number(scope.time.hours);
          if (obj.format == 12) {
            if (scope.time.hours != 12) {
              scope.time.hours += 1;
            } else {
              scope.time.hours = 1;
            }
          }
          if (obj.format == 24) {
            scope.time.hours = (scope.time.hours + 1) % 24;
          }
          scope.time.hours = (scope.time.hours < 10) ? ('0' + scope.time.hours) : scope.time.hours;
        };

        scope.decreaseHours = function () {
          scope.time.hours = Number(scope.time.hours);
          if (obj.format == 12) {
            if (scope.time.hours > 1) {
              scope.time.hours -= 1;
            } else {
              scope.time.hours = 12;
            }
          }
          if (obj.format == 24) {
            scope.time.hours = (scope.time.hours + 23) % 24;
          }
          scope.time.hours = (scope.time.hours < 10) ? ('0' + scope.time.hours) : scope.time.hours;
        };

        scope.increaseMinutes = function () {
          scope.time.minutes = Number(scope.time.minutes);
          scope.time.minutes = (scope.time.minutes + obj.step) % 60;
          scope.time.minutes = (scope.time.minutes < 10) ? ('0' + scope.time.minutes) : scope.time.minutes;
        };

        scope.decreaseMinutes = function () {
          scope.time.minutes = Number(scope.time.minutes);
          scope.time.minutes = (scope.time.minutes + (60 - obj.step)) % 60;
          scope.time.minutes = (scope.time.minutes < 10) ? ('0' + scope.time.minutes) : scope.time.minutes;
        };

        scope.changeMeridian = function () {
          scope.time.meridian = (scope.time.meridian === "AM") ? "PM" : "AM";
        };

        element.on("click", function () {
          if (typeof scope.inputObj.inputEpochTime === 'undefined' || scope.inputObj.inputEpochTime === null) {
            objDate = new Date();
          } else {
            objDate = new Date(scope.inputObj.inputEpochTime * 1000);
          }

          if (obj.format == 12) {
            scope.time.meridian = (objDate.getUTCHours() >= 12) ? "PM" : "AM";
            scope.time.hours = (objDate.getUTCHours() > 12) ? ((objDate.getUTCHours() - 12)) : (objDate.getUTCHours());
            scope.time.minutes = (objDate.getUTCMinutes());

            scope.time.hours = (scope.time.hours < 10) ? ("0" + scope.time.hours) : (scope.time.hours);
            scope.time.minutes = (scope.time.minutes < 10) ? ("0" + scope.time.minutes) : (scope.time.minutes);

            if (scope.time.hours === 0 && scope.time.meridian === "AM") {
              scope.time.hours = 12;
            }

            $ionicPopup.show({
              templateUrl: 'ionic-timepicker-12-hour.html',
              title: scope.titleLabel,
              subTitle: '',
              scope: scope,
              buttons: [
                {
                  text: scope.closeLabel,
                  type: scope.closeButtonType,
                  onTap: function (e) {
                    scope.inputObj.callback(undefined);
                  }
                },
                {
                  text: scope.setLabel,
                  type: scope.setButtonType,
                  onTap: function (e) {
                    scope.loadingContent = true;

                    var totalSec = 0;

                    if (scope.time.hours != 12) {
                      totalSec = (scope.time.hours * 60 * 60) + (scope.time.minutes * 60);
                    } else {
                      totalSec = scope.time.minutes * 60;
                    }

                    if (scope.time.meridian === "AM") {
                      totalSec += 0;
                    } else if (scope.time.meridian === "PM") {
                      totalSec += 43200;
                    }
                    scope.etime = totalSec;
                    scope.inputObj.callback(scope.etime);
                  }
                }
              ]
            });

          } else if (obj.format == 24) {

            scope.time.hours = (objDate.getUTCHours());
            scope.time.minutes = (objDate.getUTCMinutes());

            scope.time.hours = (scope.time.hours < 10) ? ("0" + scope.time.hours) : (scope.time.hours);
            scope.time.minutes = (scope.time.minutes < 10) ? ("0" + scope.time.minutes) : (scope.time.minutes);

            $ionicPopup.show({
              templateUrl: 'ionic-timepicker-24-hour.html',
              title: scope.titleLabel,
              subTitle: '',
              scope: scope,
              buttons: [
                {
                  text: scope.closeLabel,
                  type: scope.closeButtonType,
                  onTap: function (e) {
                    scope.inputObj.callback(undefined);
                  }
                },
                {
                  text: scope.setLabel,
                  type: scope.setButtonType,
                  onTap: function (e) {

                    scope.loadingContent = true;

                    var totalSec = 0;

                    if (scope.time.hours != 24) {
                      totalSec = (scope.time.hours * 60 * 60) + (scope.time.minutes * 60);
                    } else {
                      totalSec = scope.time.minutes * 60;
                    }
                    scope.etime = totalSec;
                    scope.inputObj.callback(scope.etime);
                  }
                }
              ]
            });
          }
        });
      }
    };
  }

})();
<commit_msg>comments added in source code<commit_after>//By Rajeshwar Patlolla
//https://github.com/rajeshwarpatlolla

(function () {
  'use strict';

  angular.module('ionic-timepicker')
    .directive('ionicTimepicker', ionicTimepicker);

  ionicTimepicker.$inject = ['$ionicPopup'];
  function ionicTimepicker($ionicPopup) {
    return {
      restrict: 'AE',
      replace: true,
      scope: {
        inputObj: "=inputObj"
      },
      link: function (scope, element, attrs) {

        var today = new Date();
        var currentEpoch = ((new Date()).getHours() * 60 * 60) + ((new Date()).getMinutes() * 60);

        //set up base variables and options for customization
        scope.inputEpochTime = scope.inputObj.inputEpochTime ? scope.inputObj.inputEpochTime : currentEpoch;
        scope.step = scope.inputObj.step ? scope.inputObj.step : 15;
        scope.format = scope.inputObj.format ? scope.inputObj.format : 24;
        scope.titleLabel = scope.inputObj.titleLabel ? scope.inputObj.titleLabel : 'Time Picker';
        scope.setLabel = scope.inputObj.setLabel ? scope.inputObj.setLabel : 'Set';
        scope.closeLabel = scope.inputObj.closeLabel ? scope.inputObj.closeLabel : 'Close';
        scope.setButtonType = scope.inputObj.setButtonType ? scope.inputObj.setButtonType : 'button-positive';
        scope.closeButtonType = scope.inputObj.closeButtonType ? scope.inputObj.closeButtonType : 'button-stable';

        var obj = {epochTime: scope.inputEpochTime, step: scope.step, format: scope.format};
        scope.time = {hours: 0, minutes: 0, meridian: ""};
        var objDate = new Date(obj.epochTime * 1000);       // Epoch time in milliseconds.

        //Increasing the hours
        scope.increaseHours = function () {
          scope.time.hours = Number(scope.time.hours);
          if (obj.format == 12) {
            if (scope.time.hours != 12) {
              scope.time.hours += 1;
            } else {
              scope.time.hours = 1;
            }
          }
          if (obj.format == 24) {
            scope.time.hours = (scope.time.hours + 1) % 24;
          }
          scope.time.hours = (scope.time.hours < 10) ? ('0' + scope.time.hours) : scope.time.hours;
        };

        //Decreasing the hours
        scope.decreaseHours = function () {
          scope.time.hours = Number(scope.time.hours);
          if (obj.format == 12) {
            if (scope.time.hours > 1) {
              scope.time.hours -= 1;
            } else {
              scope.time.hours = 12;
            }
          }
          if (obj.format == 24) {
            scope.time.hours = (scope.time.hours + 23) % 24;
          }
          scope.time.hours = (scope.time.hours < 10) ? ('0' + scope.time.hours) : scope.time.hours;
        };

        //Increasing the minutes
        scope.increaseMinutes = function () {
          scope.time.minutes = Number(scope.time.minutes);
          scope.time.minutes = (scope.time.minutes + obj.step) % 60;
          scope.time.minutes = (scope.time.minutes < 10) ? ('0' + scope.time.minutes) : scope.time.minutes;
        };

        //Decreasing the minutes
        scope.decreaseMinutes = function () {
          scope.time.minutes = Number(scope.time.minutes);
          scope.time.minutes = (scope.time.minutes + (60 - obj.step)) % 60;
          scope.time.minutes = (scope.time.minutes < 10) ? ('0' + scope.time.minutes) : scope.time.minutes;
        };

        //Changing the meridian
        scope.changeMeridian = function () {
          scope.time.meridian = (scope.time.meridian === "AM") ? "PM" : "AM";
        };

        //onclick of the button
        element.on("click", function () {
          if (typeof scope.inputObj.inputEpochTime === 'undefined' || scope.inputObj.inputEpochTime === null) {
            objDate = new Date();
          } else {
            objDate = new Date(scope.inputObj.inputEpochTime * 1000);
          }

          if (obj.format == 12) {
            scope.time.meridian = (objDate.getUTCHours() >= 12) ? "PM" : "AM";
            scope.time.hours = (objDate.getUTCHours() > 12) ? ((objDate.getUTCHours() - 12)) : (objDate.getUTCHours());
            scope.time.minutes = (objDate.getUTCMinutes());

            scope.time.hours = (scope.time.hours < 10) ? ("0" + scope.time.hours) : (scope.time.hours);
            scope.time.minutes = (scope.time.minutes < 10) ? ("0" + scope.time.minutes) : (scope.time.minutes);

            if (scope.time.hours === 0 && scope.time.meridian === "AM") {
              scope.time.hours = 12;
            }

            $ionicPopup.show({
              templateUrl: 'ionic-timepicker-12-hour.html',
              title: scope.titleLabel,
              subTitle: '',
              scope: scope,
              buttons: [
                {
                  text: scope.closeLabel,
                  type: scope.closeButtonType,
                  onTap: function (e) {
                    scope.inputObj.callback(undefined);
                  }
                },
                {
                  text: scope.setLabel,
                  type: scope.setButtonType,
                  onTap: function (e) {
                    scope.loadingContent = true;

                    var totalSec = 0;

                    if (scope.time.hours != 12) {
                      totalSec = (scope.time.hours * 60 * 60) + (scope.time.minutes * 60);
                    } else {
                      totalSec = scope.time.minutes * 60;
                    }

                    if (scope.time.meridian === "AM") {
                      totalSec += 0;
                    } else if (scope.time.meridian === "PM") {
                      totalSec += 43200;
                    }
                    scope.etime = totalSec;
                    scope.inputObj.callback(scope.etime);
                  }
                }
              ]
            });

          } else if (obj.format == 24) {

            scope.time.hours = (objDate.getUTCHours());
            scope.time.minutes = (objDate.getUTCMinutes());

            scope.time.hours = (scope.time.hours < 10) ? ("0" + scope.time.hours) : (scope.time.hours);
            scope.time.minutes = (scope.time.minutes < 10) ? ("0" + scope.time.minutes) : (scope.time.minutes);

            $ionicPopup.show({
              templateUrl: 'ionic-timepicker-24-hour.html',
              title: scope.titleLabel,
              subTitle: '',
              scope: scope,
              buttons: [
                {
                  text: scope.closeLabel,
                  type: scope.closeButtonType,
                  onTap: function (e) {
                    scope.inputObj.callback(undefined);
                  }
                },
                {
                  text: scope.setLabel,
                  type: scope.setButtonType,
                  onTap: function (e) {

                    scope.loadingContent = true;

                    var totalSec = 0;

                    if (scope.time.hours != 24) {
                      totalSec = (scope.time.hours * 60 * 60) + (scope.time.minutes * 60);
                    } else {
                      totalSec = scope.time.minutes * 60;
                    }
                    scope.etime = totalSec;
                    scope.inputObj.callback(scope.etime);
                  }
                }
              ]
            });
          }
        });
      }
    };
  }

})();
<|endoftext|>


<commit_before>require('./lib/polyfills/element.matches');

/**
 * Write loading image to a container for ajax request
 *
 * @param container: a DOM element
 * @param theme: dark or white
 */
const showLoadingImage = (container, theme = 'dark') => {
    const loading_div = createElement('div', { class: `barspinner ${theme}`, html: Array.from(new Array(5)).map((x, i) => `<div class="rect${i + 1}"></div>`).join('') });
    container.html(loading_div);
};

/**
 * Returns the highest z-index in the page.
 * Accepts a jquery style selector to only check those elements,
 * uses all container tags by default
 * If no element found, returns null.
 *
 * @param selector: a jquery style selector for target elements
 * @return int|null
 */
const getHighestZIndex = (selector = 'div,p,area,nav,section,header,canvas,aside,span') => {
    const elements = selector.split(',');
    const all      = [];

    for (let i = 0; i < elements.length; i++) {
        const els = document.getElementsByTagName(elements);
        for (let j = 0; j < els.length; j++) {
            if (els[i].offsetParent) {
                const z = els[i].style['z-index'];
                if (!isNaN(z)) {
                    all.push(z);
                }
            }
        }
    }

    return all.length ? Math.max(...all) : null;
};

const downloadCSV = (csv_contents, filename = 'data.csv') => {
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(new Blob([csv_contents], { type: 'text/csv;charset=utf-8;' }), filename);
    } else { // Other browsers
        const csv           = `data:text/csv;charset=utf-8,${csv_contents}`;
        const download_link = createElement('a', { href: encodeURI(csv), download: filename });

        if (document.body) {
            document.body.appendChild(download_link);
            download_link.click();
            document.body.removeChild(download_link);
        }
    }
};

const template = (string, content) => string.replace(/\[_(\d+)]/g, (s, index) => content[(+index) - 1]);

const isEmptyObject = (obj) => {
    let is_empty = true;
    if (obj && obj instanceof Object) {
        Object.keys(obj).forEach((key) => {
            if (Object.prototype.hasOwnProperty.call(obj, key)) is_empty = false;
        });
    }
    return is_empty;
};

const cloneObject = obj => (!isEmptyObject(obj) ? $.extend(true, Array.isArray(obj) ? [] : {}, obj) : obj);

const getPropertyValue = (obj, k) => {
    let keys = k;
    if (!Array.isArray(keys)) keys = [keys];
    if (!isEmptyObject(obj) && keys[0] in obj && keys && keys.length > 1) {
        return getPropertyValue(obj[keys[0]], keys.slice(1));
    }
    // else return clone of object to avoid overwriting data
    return obj ? cloneObject(obj[keys[0]]) : undefined;
};

const handleHash = () => {
    const hash = window.location.hash;
    if (hash) {
        document.querySelector(`a[href="${hash}"]`).click();
    }
};

const clearable = (element) => {
    element.addClass('clear');
    document.addEventListener('mousemove', (e) => {
        if (/clear/.test(e.target.classList)) {
            e.stopPropagation();
            e.target.toggleClass('onClear', e.target.offsetWidth - 18 < e.clientX - e.target.getBoundingClientRect().left);
        }
    });
    document.addEventListener('mousedown', (e) => {
        if (/onClear/.test(e.target.classList)) {
            e.stopPropagation();
            e.target.setAttribute('data-value', '');
            e.target.classList.remove('clear', 'onClear');
            e.target.value = '';
            e.target.dispatchEvent(new Event('change'));
        }
    });
};

/**
 * Creates a DOM element and adds any attributes to it.
 *
 * @param {String} tag_name: the tag to create, e.g. 'div', 'a', etc
 * @param {Object} attributes: all the attributes to assign, e.g. { id: '...', class: '...', html: '...', ... }
 * @return the created DOM element
 */
const createElement = (tag_name, attributes) => {
    const el = document.createElement(tag_name);
    Object.keys(attributes).forEach((attr) => {
        const value = attributes[attr];
        if (attr === 'text') {
            el.textContent = value;
        } else if (attr === 'html') {
            el.html(value);
        } else {
            el.setAttribute(attr, value);
        }
    });
    return el;
};

/**
 * Apply function to all elements based on selector passed
 *
 * @param {String|Element} selector: selector of the elements to apply the function to, e.g. '.class', '#id', 'tag', etc
 * can also be a DOM element
 * @param {Function} funcToRun: function to apply
 * @param {String} func_selector: method of finding the selector, optional
 * @param {Element} el_parent: parent of the selector, document by default
 */
const applyToAllElements = (selector, funcToRun, func_selector, el_parent) => {
    if (!selector || !funcToRun) {
        return;
    }

    let function_selector = func_selector;
    let element_to_select = selector;
    if (!func_selector && !element_to_select.nodeName) {
        if (/[\s#]/.test(element_to_select) || element_to_select.lastIndexOf('.') !== 0) {
            function_selector = 'querySelectorAll';
        } else if (element_to_select.lastIndexOf('.') === 0) {
            function_selector = 'getElementsByClassName';
            element_to_select = element_to_select.substring(1);
        } else if (/^[a-zA-Z]+$/.test(element_to_select)) {
            function_selector = 'getElementsByTagName';
        }
    }
    const parent_element = el_parent || document;
    const el = element_to_select.nodeName || typeof element_to_select === 'object' ? element_to_select : parent_element[function_selector](element_to_select);
    for (let i = 0; i < el.length; i++) {
        funcToRun(el[i]);
    }
};

/**
 * Returns the first parent element that matches the selector (including el itself)
 *
 * @param {Element} el      : element to start looking for parent
 * @param {String}  selector: selector to find the element that matches to, e.g. '.class', '#id', 'tag', or a combination of them
 */
const findParent = (el, selector) => {
    if (el && el.nodeName !== 'BODY') {
        return el.matches(selector) ? el : findParent(el.parentNode, selector);
    }
    return null;
};

module.exports = {
    showLoadingImage,
    getHighestZIndex,
    downloadCSV,
    template,
    isEmptyObject,
    getPropertyValue,
    handleHash,
    clearable,
    createElement,
    applyToAllElements,
    findParent,
};
<commit_msg>Fix trackjs issue<commit_after>require('./lib/polyfills/element.matches');

/**
 * Write loading image to a container for ajax request
 *
 * @param container: a DOM element
 * @param theme: dark or white
 */
const showLoadingImage = (container, theme = 'dark') => {
    const loading_div = createElement('div', { class: `barspinner ${theme}`, html: Array.from(new Array(5)).map((x, i) => `<div class="rect${i + 1}"></div>`).join('') });
    container.html(loading_div);
};

/**
 * Returns the highest z-index in the page.
 * Accepts a jquery style selector to only check those elements,
 * uses all container tags by default
 * If no element found, returns null.
 *
 * @param selector: a jquery style selector for target elements
 * @return int|null
 */
const getHighestZIndex = (selector = 'div,p,area,nav,section,header,canvas,aside,span') => {
    const elements = selector.split(',');
    const all      = [];

    for (let i = 0; i < elements.length; i++) {
        const els = document.getElementsByTagName(elements);
        for (let j = 0; j < els.length; j++) {
            if (els[i].offsetParent) {
                const z = els[i].style['z-index'];
                if (!isNaN(z)) {
                    all.push(z);
                }
            }
        }
    }

    return all.length ? Math.max(...all) : null;
};

const downloadCSV = (csv_contents, filename = 'data.csv') => {
    if (navigator.msSaveBlob) { // IE 10+
        navigator.msSaveBlob(new Blob([csv_contents], { type: 'text/csv;charset=utf-8;' }), filename);
    } else { // Other browsers
        const csv           = `data:text/csv;charset=utf-8,${csv_contents}`;
        const download_link = createElement('a', { href: encodeURI(csv), download: filename });

        if (document.body) {
            document.body.appendChild(download_link);
            download_link.click();
            document.body.removeChild(download_link);
        }
    }
};

const template = (string, content) => string.replace(/\[_(\d+)]/g, (s, index) => content[(+index) - 1]);

const isEmptyObject = (obj) => {
    let is_empty = true;
    if (obj && obj instanceof Object) {
        Object.keys(obj).forEach((key) => {
            if (Object.prototype.hasOwnProperty.call(obj, key)) is_empty = false;
        });
    }
    return is_empty;
};

const cloneObject = obj => (!isEmptyObject(obj) ? $.extend(true, Array.isArray(obj) ? [] : {}, obj) : obj);

const getPropertyValue = (obj, k) => {
    let keys = k;
    if (!Array.isArray(keys)) keys = [keys];
    if (!isEmptyObject(obj) && keys[0] in obj && keys && keys.length > 1) {
        return getPropertyValue(obj[keys[0]], keys.slice(1));
    }
    // else return clone of object to avoid overwriting data
    return obj ? cloneObject(obj[keys[0]]) : undefined;
};

const handleHash = () => {
    const hash = window.location.hash;
    if (hash) {
        document.querySelector(`a[href="${hash}"]`).click();
    }
};

const clearable = (element) => {
    element.addClass('clear');
    document.addEventListener('mousemove', (e) => {
        if (/clear/.test(e.target.classList)) {
            e.stopPropagation();
            e.target.toggleClass('onClear', e.target.offsetWidth - 18 < e.clientX - e.target.getBoundingClientRect().left);
        }
    });
    document.addEventListener('mousedown', (e) => {
        if (/onClear/.test(e.target.classList)) {
            e.stopPropagation();
            e.target.setAttribute('data-value', '');
            e.target.classList.remove('clear', 'onClear');
            e.target.value = '';
            e.target.dispatchEvent(new Event('change'));
        }
    });
};

/**
 * Creates a DOM element and adds any attributes to it.
 *
 * @param {String} tag_name: the tag to create, e.g. 'div', 'a', etc
 * @param {Object} attributes: all the attributes to assign, e.g. { id: '...', class: '...', html: '...', ... }
 * @return the created DOM element
 */
const createElement = (tag_name, attributes) => {
    const el = document.createElement(tag_name);
    Object.keys(attributes).forEach((attr) => {
        const value = attributes[attr];
        if (attr === 'text') {
            el.textContent = value;
        } else if (attr === 'html') {
            el.html(value);
        } else {
            el.setAttribute(attr, value);
        }
    });
    return el;
};

/**
 * Apply function to all elements based on selector passed
 *
 * @param {String|Element} selector: selector of the elements to apply the function to, e.g. '.class', '#id', 'tag', etc
 * can also be a DOM element
 * @param {Function} funcToRun: function to apply
 * @param {String} func_selector: method of finding the selector, optional
 * @param {Element} el_parent: parent of the selector, document by default
 */
const applyToAllElements = (selector, funcToRun, func_selector, el_parent) => {
    if (!selector || !funcToRun) {
        return;
    }

    let function_selector = func_selector;
    let element_to_select = selector;
    if (!func_selector && !element_to_select.nodeName) {
        if (/[\s#]/.test(element_to_select) || element_to_select.lastIndexOf('.') !== 0) {
            function_selector = 'querySelectorAll';
        } else if (element_to_select.lastIndexOf('.') === 0) {
            function_selector = 'getElementsByClassName';
            element_to_select = element_to_select.substring(1);
        } else if (/^[a-zA-Z]+$/.test(element_to_select)) {
            function_selector = 'getElementsByTagName';
        }
    }
    const parent_element = el_parent || document;
    const el = element_to_select.nodeName || typeof element_to_select === 'object' ? element_to_select : parent_element[function_selector](element_to_select);
    for (let i = 0; i < el.length; i++) {
        funcToRun(el[i]);
    }
};

/**
 * Returns the first parent element that matches the selector (including el itself)
 *
 * @param {Element} el      : element to start looking for parent
 * @param {String}  selector: selector to find the element that matches to, e.g. '.class', '#id', 'tag', or a combination of them
 */
const findParent = (el, selector) => {
    if (el && el.nodeName !== 'BODY' && typeof el.matches === 'function') {
        return el.matches(selector) ? el : findParent(el.parentNode, selector);
    }
    return null;
};

module.exports = {
    showLoadingImage,
    getHighestZIndex,
    downloadCSV,
    template,
    isEmptyObject,
    getPropertyValue,
    handleHash,
    clearable,
    createElement,
    applyToAllElements,
    findParent,
};
<|endoftext|>


<commit_before>app.createMessageView = (function(_const, utils, uikit, apiHelper){
	var tpl = '

';

	return function(opt){
		var parentContainer = opt.parentContainer;
		var isNewUser = opt.isNewUser;
		var chat = opt.chat;
		var officialAccountId = opt.id;
		var el = utils.createElementFromHTML(tpl);

		var currHistoryMsgSeqId = 0;
		var hasHistoryMessage;

		var msgTimeSpanBegin = new Date(2099, 0).getTime();
		var msgTimeSpanEnd = new Date(1970, 0).getTime();

		var recentMsg = [];

		parentContainer.appendChild(el);

		if (!isNewUser){
			// 拉取历史消息
			_getHistory(_scrollToBottom);

			// 初始化历史消息拉取
			_initHistoryPuller();
		}

		function _getRecentMsg(maxCount){
			return _.map(recentMsg.slice(0, maxCount), function(item){
				var type = item.msg.type;
				var date = utils.formatDate(item.date);
				var role = item.isReceived ? '客服坐席' : '访客';
				var value = item.msg.value;

				switch (type){
					case 'txt':
						break;
					case 'img':
						value = '[图片]';
						break;
					case 'file':
						value = '[文件]';
						break;
					case 'list':
						value = '[菜单]';
						break;
					default:
						value = '[未知消息类型]';
						break;
				}

				return '[' + date + '] ' + role + '\n' + value;
			}).join('\n');
		}

		function _appendEventMsg(msg) {
			_appendDate();
			// todo: xss defence
			utils.appendHTMLTo(el, [
				'
',
				'' + msg + '',
				'
'
			].join(''));
			_scrollToBottom();
		}

		function _appendMsg(isReceived, msg, isHistory, timestamp){
			var dom = easemobim.genDomFromMsg(msg, isReceived);
			var img = dom.querySelector('.em-widget-imgview');

			date = timestamp || _.now();

			if (isHistory) {
				el.insertBefore(dom, el.firstChild);
				// 历史消息是向前插入，所以时间戳应在消息之后上屏
				_appendDate(date, isHistory);
			}
			else {
				// 时间戳上屏
				_appendDate(date, isHistory);

				if (img) {
					// 如果包含图片，则需要等待图片加载后再滚动消息
					el.appendChild(dom);
					messageView.scrollToBottom();
					utils.one(img, 'load', function () {
						_scrollToBottom();
					});
				}
				else {
					// 非图片消息直接滚到底
					el.appendChild(dom);
					_scrollToBottom();
				}
			}
			// 缓存上屏的消息
			var msgData = {
				isReceived: isReceived,
				msg: msg,
				date: date
			};
			isHistory ? recentMsg.push(msgData) : recentMsg.unshift(msgData);
		}

		function _appendDate(timestamp, isHistory) {
			var dom = utils.createElementFromHTML([
				'
',
				'' + utils.formatDate(date) + '',
				'
'
			].join(''));
			var date = timestamp || _.now();

			if (isHistory) {
				msgTimeSpanBegin - date > _const.MESSAGE_TIME_SPAN_INTERVAL
					&& el.insertBefore(dom, el.firstChild);
			}
			else {
				date - msgTimeSpanEnd > _const.MESSAGE_TIME_SPAN_INTERVAL
					&& el.appendChild(dom);
			}

			// 更新时间范围
			date < msgTimeSpanBegin && (msgTimeSpanBegin = date);
			date > msgTimeSpanEnd && (msgTimeSpanEnd = date);
		}

		function _getHistory(callback){
			if (hasHistoryMessage === false) return;
			apiHelper.getOfficalAccountMessage(
				officialAccountId,
				currHistoryMsgSeqId
			).then(function(msgList){
				var length = msgList.length;
				var earliestMsg = msgList[length - 1] || {};
				var nextMsgSeq = earliestMsg.id;

				currHistoryMsgSeqId = nextMsgSeq;
				hasHistoryMessage = length === _const.GET_HISTORY_MESSAGE_COUNT_EACH_TIME && nextMsgSeq > 0;
				_.each(msgList, chat.channel.handleHistoryMsg);
				typeof callback === 'function' && callback();
			});
		}

		function _initHistoryPuller(){
			var st;
			var _startY;
			var _y;

			if (utils.isMobile){
				// wap
				utils.live('div.em-widget-date', 'touchstart', function (ev) {
					_startY = utils.getDataByPath(ev, 'touches.0.pageY');
				});
				utils.live('div.em-widget-date', 'touchmove', function (ev) {
					_y = utils.getDataByPath(ev, 'touches.0.pageY');
					if (_y - _startY > 8 && this.getBoundingClientRect().top >= 0) {
						clearTimeout(st);
						st = setTimeout(function () {
							_getHistory();
						}, 100);
					}
				});
			}
			else {
				// pc端
				utils.on(parentContainer, 'mousewheel DOMMouseScroll', function (ev) {
					var that = this;

					if (ev.wheelDelta / 120 > 0 || ev.detail < 0) {
						clearTimeout(st);
						st = setTimeout(function () {
							if (that.getBoundingClientRect().top >= 0) {
								_getHistory();
							}
						}, 400);
					}
				});
			}
		}

		function _scrollToBottom(){
			parentContainer.scrollTop = parentContainer.scrollHeight - parentContainer.offsetHeight + 9999;
		}

		function _hide(){
			utils.addClass(el, 'hide');
		}

		function _show(){
			utils.removeClass(el, 'hide');
		}

		return {
			show: function(){
				_show();
				return this;
			},
			hide: function(){
				_hide();
				return this;
			},
			scrollToBottom: _scrollToBottom,
			appendMsg: _appendMsg,
			appendEventMsg: _appendEventMsg,
			getRecentMsg: _getRecentMsg,
			el: el
		};
	};
}(easemobim._const, easemobim.utils, easemobim.uikit, easemobim.apiHelper));
<commit_msg>bugfix|不能发送图片<commit_after>app.createMessageView = (function(_const, utils, uikit, apiHelper){
	var tpl = '

';

	return function(opt){
		var parentContainer = opt.parentContainer;
		var isNewUser = opt.isNewUser;
		var chat = opt.chat;
		var officialAccountId = opt.id;
		var el = utils.createElementFromHTML(tpl);

		var currHistoryMsgSeqId = 0;
		var hasHistoryMessage;

		var msgTimeSpanBegin = new Date(2099, 0).getTime();
		var msgTimeSpanEnd = new Date(1970, 0).getTime();

		var recentMsg = [];

		parentContainer.appendChild(el);

		if (!isNewUser){
			// 拉取历史消息
			_getHistory(_scrollToBottom);

			// 初始化历史消息拉取
			_initHistoryPuller();
		}

		function _getRecentMsg(maxCount){
			return _.map(recentMsg.slice(0, maxCount), function(item){
				var type = item.msg.type;
				var date = utils.formatDate(item.date);
				var role = item.isReceived ? '客服坐席' : '访客';
				var value = item.msg.value;

				switch (type){
					case 'txt':
						break;
					case 'img':
						value = '[图片]';
						break;
					case 'file':
						value = '[文件]';
						break;
					case 'list':
						value = '[菜单]';
						break;
					default:
						value = '[未知消息类型]';
						break;
				}

				return '[' + date + '] ' + role + '\n' + value;
			}).join('\n');
		}

		function _appendEventMsg(msg) {
			_appendDate();
			// todo: xss defence
			utils.appendHTMLTo(el, [
				'
',
				'' + msg + '',
				'
'
			].join(''));
			_scrollToBottom();
		}

		function _appendMsg(isReceived, msg, isHistory, timestamp){
			var dom = easemobim.genDomFromMsg(msg, isReceived);
			var img = dom.querySelector('.em-widget-imgview');

			date = timestamp || _.now();

			if (isHistory) {
				el.insertBefore(dom, el.firstChild);
				// 历史消息是向前插入，所以时间戳应在消息之后上屏
				_appendDate(date, isHistory);
			}
			else {
				// 时间戳上屏
				_appendDate(date, isHistory);

				if (img) {
					// 如果包含图片，则需要等待图片加载后再滚动消息
					el.appendChild(dom);
					_scrollToBottom();
					utils.one(img, 'load', function () {
						_scrollToBottom();
					});
				}
				else {
					// 非图片消息直接滚到底
					el.appendChild(dom);
					_scrollToBottom();
				}
			}
			// 缓存上屏的消息
			var msgData = {
				isReceived: isReceived,
				msg: msg,
				date: date
			};
			isHistory ? recentMsg.push(msgData) : recentMsg.unshift(msgData);
		}

		function _appendDate(timestamp, isHistory) {
			var dom = utils.createElementFromHTML([
				'
',
				'' + utils.formatDate(date) + '',
				'
'
			].join(''));
			var date = timestamp || _.now();

			if (isHistory) {
				msgTimeSpanBegin - date > _const.MESSAGE_TIME_SPAN_INTERVAL
					&& el.insertBefore(dom, el.firstChild);
			}
			else {
				date - msgTimeSpanEnd > _const.MESSAGE_TIME_SPAN_INTERVAL
					&& el.appendChild(dom);
			}

			// 更新时间范围
			date < msgTimeSpanBegin && (msgTimeSpanBegin = date);
			date > msgTimeSpanEnd && (msgTimeSpanEnd = date);
		}

		function _getHistory(callback){
			if (hasHistoryMessage === false) return;
			apiHelper.getOfficalAccountMessage(
				officialAccountId,
				currHistoryMsgSeqId
			).then(function(msgList){
				var length = msgList.length;
				var earliestMsg = msgList[length - 1] || {};
				var nextMsgSeq = earliestMsg.id;

				currHistoryMsgSeqId = nextMsgSeq;
				hasHistoryMessage = length === _const.GET_HISTORY_MESSAGE_COUNT_EACH_TIME && nextMsgSeq > 0;
				_.each(msgList, chat.channel.handleHistoryMsg);
				typeof callback === 'function' && callback();
			});
		}

		function _initHistoryPuller(){
			var st;
			var _startY;
			var _y;

			if (utils.isMobile){
				// wap
				utils.live('div.em-widget-date', 'touchstart', function (ev) {
					_startY = utils.getDataByPath(ev, 'touches.0.pageY');
				});
				utils.live('div.em-widget-date', 'touchmove', function (ev) {
					_y = utils.getDataByPath(ev, 'touches.0.pageY');
					if (_y - _startY > 8 && this.getBoundingClientRect().top >= 0) {
						clearTimeout(st);
						st = setTimeout(function () {
							_getHistory();
						}, 100);
					}
				});
			}
			else {
				// pc端
				utils.on(parentContainer, 'mousewheel DOMMouseScroll', function (ev) {
					var that = this;

					if (ev.wheelDelta / 120 > 0 || ev.detail < 0) {
						clearTimeout(st);
						st = setTimeout(function () {
							if (that.getBoundingClientRect().top >= 0) {
								_getHistory();
							}
						}, 400);
					}
				});
			}
		}

		function _scrollToBottom(){
			parentContainer.scrollTop = parentContainer.scrollHeight - parentContainer.offsetHeight + 9999;
		}

		function _hide(){
			utils.addClass(el, 'hide');
		}

		function _show(){
			utils.removeClass(el, 'hide');
		}

		return {
			show: function(){
				_show();
				return this;
			},
			hide: function(){
				_hide();
				return this;
			},
			scrollToBottom: _scrollToBottom,
			appendMsg: _appendMsg,
			appendEventMsg: _appendEventMsg,
			getRecentMsg: _getRecentMsg,
			el: el
		};
	};
}(easemobim._const, easemobim.utils, easemobim.uikit, easemobim.apiHelper));
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import Dialog from 'material-ui/Dialog';
import styled from 'styled-components';

import Button from './Button';

const ButtonListWrapper = styled.div`
    display: flex;
    justify-content: center;
    & > button {
        font-size: 0.8rem;
        margin: 0 1vw;
    }
`;

// override material-ui style
const bodyStyle = {
    backgroundColor: '#374867',
    border: '1px solid #374867',
    overflowX: 'hidden'
};

const contentStyle = {
    width: '35%',
    maxWidth: 'none'
};

const titleStyle = {
    backgroundColor: '#374867',
    border: '1px solid #374867',
    color: 'white',
    fontFamily: 'Microsoft JhengHei',
    padding: '2vw 0 2vw 0',
    textAlign: 'center'
};

const DiscardAlert = ({
    handleDialogClose,
    handleDiscardAlertClose,
    isDiscardAlertOpen
}) => (
    <Dialog
        bodyStyle={bodyStyle}
        contentStyle={contentStyle}
        open={isDiscardAlertOpen}
        onRequestClose={handleDiscardAlertClose}
        titleStyle={titleStyle}
        title="關閉將捨棄本次回報"
    >
        <ButtonListWrapper>
            <Button
                backgroundColor={'#FFFFFF'}
                label="取消"
                onClick={handleDiscardAlertClose}
                textColor={'#707070'}
            />
            <Button
                backgroundColor={'#FF6262'}
                label="捨棄回報"
                onClick={handleDialogClose}
            />
        </ButtonListWrapper>
    </Dialog>
);

export default DiscardAlert;
<commit_msg>[Modify] text in discard alert<commit_after>import React, { Component } from 'react';
import Dialog from 'material-ui/Dialog';
import styled from 'styled-components';

import Button from './Button';

const ButtonListWrapper = styled.div`
    display: flex;
    justify-content: center;
    & > button {
        font-size: 0.8rem;
        margin: 0 1vw;
    }
`;

// override material-ui style
const bodyStyle = {
    backgroundColor: '#374867',
    border: '1px solid #374867',
    overflowX: 'hidden'
};

const contentStyle = {
    width: '35%',
    maxWidth: 'none'
};

const titleStyle = {
    backgroundColor: '#374867',
    border: '1px solid #374867',
    color: 'white',
    fontFamily: 'Microsoft JhengHei',
    padding: '2vw 0 2vw 0',
    textAlign: 'center'
};

const DiscardAlert = ({
    handleDialogClose,
    handleDiscardAlertClose,
    isDiscardAlertOpen
}) => (
    <Dialog
        bodyStyle={bodyStyle}
        contentStyle={contentStyle}
        open={isDiscardAlertOpen}
        onRequestClose={handleDiscardAlertClose}
        titleStyle={titleStyle}
        title="關閉將捨棄已輸入的內容"
    >
        <ButtonListWrapper>
            <Button
                backgroundColor={'#FFFFFF'}
                label="取消"
                onClick={handleDiscardAlertClose}
                textColor={'#707070'}
            />
            <Button
                backgroundColor={'#FF6262'}
                label="捨棄"
                onClick={handleDialogClose}
            />
        </ButtonListWrapper>
    </Dialog>
);

export default DiscardAlert;
<|endoftext|>


<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

function IconGrid(canvas, icons, iconWidth, iconHeight, border, reflowTime) {
  this.iconWidth = iconWidth;
  this.iconHeight = iconHeight;
  this.reflowTime = reflowTime || 250;
  this.sceneGraph = new SceneGraph(canvas);
  this.border = border || 0.1;

  // Initialize the scene graph
  for (var n = 0; n < icons.length; ++n) {
    var icon = icons[n];
    // Create a sprite for this icon
    var sprite = new Sprite();
    sprite.onclick = icon.onclick;
    // Load the image
    var img = new Image();
    img.src = icon.src;
    img.sprite = sprite;
    img.onload = function() {
      // After the image loads, update the sprite
      var canvas = document.createElement('canvas');
      canvas.width = iconWidth;
      canvas.height = iconHeight;
      var ctx = canvas.getContext('2d');
      ctx.drawImage(this, iconWidth * border, iconHeight * border,
                    iconWidth * (1 - border * 2),
                    iconHeight * (1 - border * 2));
      this.sprite.setCanvas(canvas);
    }
  }

  // reflow the icon grid (initial reflow, no animation)
  this.reflow(canvas.width, canvas.height, false);
}

IconGrid.prototype = {
  // return the X coordinate of the top left corner of a slot
  slotLeft: function(slot) {
    return this.itemBoxWidth * (slot % this.itemBoxHeight);
  },
  // return the Y coordinate of the top left corner of a slot
  slotTop: function(slot) {
    return Math.floor(slot / this.columnCount) * this.itemBoxHeight;
  },
  // reflow the icon grid
  reflow: function(width, height, animated) {
    // first recalculate all the layout information
    this.containerWidth = width;
    this.containerHeight = height;
    this.panelWidth = this.containerWidth;
    this.pageIndicatorWidth = this.containerWidth;
    this.pageIndicatorHeight = Math.min(Math.max(this.containerHeight * 0.7, 14), 20);
    this.panelHeight = this.containerHeight - this.pageindicatorHeight;
    this.rows = Math.floor(this.panelWidth / this.iconWidth);
    this.columns = Math.floor(this.panelHeight / this.iconHeight);
    this.itemBoxWidth = Math.floor(this.panelWidth / this.columnCount);
    this.itemBoxHeight = Math.floor(this.panelHeight / this.rowCount);

    // now (re-)position all the sprites
    var duration = animated ? this.reflowTime : 0;
    var count = 0;
    this.sceneGraph.forAll(function(sprite) {
        var slot = count++ % this.itemsPerPage;
        var page = n / this.itemsPerPage;
        sprite.setPosition(page * this.containerWidth + this.slotLeft(slot),
                           this.slotTop(slot),
                           duration);
      });
  }
}

function OnLoad() {
  var icons = [
               {
                 label: 'Strawberry',
                 src: 'images/strawberry.png'
               },
               {
                 label: 'Watermelon',
                 src: 'images/watermelon.png'
               },
               {
                 label: 'Apple',
                 src: 'images/apple.png'
               },
               {
                 label: 'Banana',
                 src: 'images/banana.png'
               },
               {
                 label: 'Grape',
                 src: 'images/grape.png'
               },
               {
                 label: 'Orange',
                 src: 'images/orange.png'
               },
               {
                 label: 'Papaya',
                 src: 'images/papaya.png'
               },
               {
                 label: 'Pineapple',
                 src: 'images/pineapple.png'
               }
              ];
  new IconGrid(document.getElementById("screen"),
               icons, 120, 120, 0.1, 250);
}
<commit_msg>more bugfixes<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

function IconGrid(canvas, icons, iconWidth, iconHeight, border, reflowTime) {
  this.iconWidth = iconWidth;
  this.iconHeight = iconHeight;
  this.reflowTime = reflowTime || 250;
  this.sceneGraph = new SceneGraph(canvas);
  this.border = border || 0.1;

  // Initialize the scene graph
  for (var n = 0; n < icons.length; ++n) {
    var icon = icons[n];
    // Create a sprite for this icon
    var sprite = new Sprite();
    sprite.onclick = icon.onclick;
    this.sceneGraph.add(sprite);
    // Load the image
    var img = new Image();
    img.src = icon.src;
    img.sprite = sprite;
    img.onload = function() {
      // After the image loads, update the sprite
      var canvas = document.createElement('canvas');
      canvas.width = iconWidth;
      canvas.height = iconHeight;
      var ctx = canvas.getContext('2d');
      ctx.drawImage(this, iconWidth * border, iconHeight * border,
                    iconWidth * (1 - border * 2),
                    iconHeight * (1 - border * 2));
      this.sprite.setCanvas(canvas);
    }
  }

  // reflow the icon grid (initial reflow, no animation)
  this.reflow(canvas.width, canvas.height, false);
}

IconGrid.prototype = {
  // return the X coordinate of the top left corner of a slot
  slotLeft: function(slot) {
    return this.itemBoxWidth * (slot % this.itemBoxHeight);
  },
  // return the Y coordinate of the top left corner of a slot
  slotTop: function(slot) {
    return Math.floor(slot / this.columns) * this.itemBoxHeight;
  },
  // reflow the icon grid
  reflow: function(width, height, animated) {
    // first recalculate all the layout information
    this.containerWidth = width;
    this.containerHeight = height;
    this.panelWidth = this.containerWidth;
    this.pageIndicatorWidth = this.containerWidth;
    this.pageIndicatorHeight = Math.min(Math.max(this.containerHeight * 0.7, 14), 20);
    this.panelHeight = this.containerHeight - this.pageIndicatorHeight;
    this.rows = Math.floor(this.panelWidth / this.iconWidth);
    this.columns = Math.floor(this.panelHeight / this.iconHeight);
    this.itemsPerPage = this.rows * this.columns;
    this.itemBoxWidth = Math.floor(this.panelWidth / this.columns);
    this.itemBoxHeight = Math.floor(this.panelHeight / this.rows);

    // now (re-)position all the sprites
    var duration = animated ? this.reflowTime : 0;
    var count = 0;
    var self = this;
    this.sceneGraph.forAll(function(sprite) {
        var slot = count % self.itemsPerPage;
        var page = count / self.itemsPerPage;
        sprite.setPosition(page * self.containerWidth + self.slotLeft(slot),
                           self.slotTop(slot),
                           duration);
        ++count;
      });
  }
}

function OnLoad() {
  var icons = [
               {
                 label: 'Strawberry',
                 src: 'images/strawberry.png'
               },
               {
                 label: 'Watermelon',
                 src: 'images/watermelon.png'
               },
               {
                 label: 'Apple',
                 src: 'images/apple.png'
               },
               {
                 label: 'Banana',
                 src: 'images/banana.png'
               },
               {
                 label: 'Grape',
                 src: 'images/grape.png'
               },
               {
                 label: 'Orange',
                 src: 'images/orange.png'
               },
               {
                 label: 'Papaya',
                 src: 'images/papaya.png'
               },
               {
                 label: 'Pineapple',
                 src: 'images/pineapple.png'
               }
              ];
  new IconGrid(document.getElementById("screen"),
               icons, 120, 120, 0.1, 250);
}
<|endoftext|>