<commit_before>define(['sprd/model/ProductBase', 'js/core/List', 'js/data/AttributeTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType'],
    function (ProductBase, List, AttributeTypeResolver, DesignConfiguration, TextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType) {

    var undefined;

    return ProductBase.inherit("sprd.model.Product", {

        schema: {
            productType: ProductType,
            configurations: [new AttributeTypeResolver({
                attribute: "type",
                mapping: {
                    "design": DesignConfiguration,
                    "text": TextConfiguration
                }
            })],
            creator: String
        },

        defaults: {
            configurations: List,
            creator: null
        },

        inject: {
            manager: IProductManager
        },

        ctor: function () {
            this.configurationsOnViewCache = {};

            this.callBase();

            var priceChangeHandler = function () {
                this.trigger("priceChanged");
            };

            var productChangeHandler = function () {
                this.trigger("productChanged");
            };

            var configurationAdd = function (e) {
                var configuration = e.$.item,
                    viewId = configuration.get("printArea.getDefaultView().id");

                if (viewId) {
                    // clear configuration cache
                    this.configurationsOnViewCache[viewId] = null;
                    this.trigger("configurationValidChanged");
                }

                this._setUpConfiguration(configuration);

                if(configuration instanceof TextConfiguration){
                    configuration._composeText();
                }
                priceChangeHandler.call(this);
                productChangeHandler.call(this);

            };

            var configurationRemove = function (e) {
                var configuration = e.$.item,
                    viewId = configuration.get("printArea.getDefaultView().id");

                if (viewId) {
                    // clear configuration cache
                    this.configurationsOnViewCache[viewId] = null;
                    this.trigger("configurationValidChanged");
                }

                this._tearDownConfiguration(configuration);

                productChangeHandler.call(this);
                priceChangeHandler.call(this);
            };

            this.bind("configurations", "add", configurationAdd, this);
            this.bind("configurations", "remove", configurationRemove, this);
            this.bind("configurations", "reset", function() {
                this.configurationsOnViewCache = {};
                this.trigger("configurationValidChanged");

                priceChangeHandler.call(this);
                productChangeHandler.call(this);
            }, this);

            this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
            this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
            this.bind('change:configurations', productChangeHandler, this);
            this.bind('configurations', 'item:change:printArea', function() {
                this.configurationsOnViewCache = {};
                this.trigger("configurationValidChanged");

                productChangeHandler.call(this);
            }, this);

            this.bind('configurations', 'item:isValidChanged', function() {
                this.trigger("configurationValidChanged");
            }, this);

            this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);

        },

        getDefaultAppearance: function () {
            var productType = this.$.productType;

            if (productType) {
                return productType.getAppearanceById(this.get('appearance.id'));
            }

            return null;
        }.onChange('appearance', 'productType'),

        getDefaultView: function() {
            var productType = this.$.productType,
                viewId = this.get("defaultValues.defaultView.id");

            if (productType) {
                return productType.getViewById(viewId) || productType.getDefaultView();
            }

            return null;
        },

        _onConfigurationOffsetChanged: function(e) {

            var manager = this.$.manager,
                configuration = e.$.item;

            if (manager && manager.checkConfigurationOffset) {
                manager.checkConfigurationOffset(this, configuration)
            }
        },

        configurationsOnViewErrorKey: function(view) {

            var errorKey = null,
                configurations;

            if (!view) {
                return null;
            }

            var viewId = view.$.id;

            var productType = this.$.productType;

            configurations = this.configurationsOnViewCache[viewId];
            if (!configurations && productType && productType.containsView(view)) {
                this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
            }

            if (!configurations) {
                return null;
            }

            for (var i = 0; i < configurations.length; i++) {
                var configuration = configurations[i];

                if (!configuration.isValid() && configuration.$errors) {

                    for (var key in configuration.$errors.$) {
                        if (configuration.$errors.$.hasOwnProperty(key)) {
                            errorKey = configuration.$errors.$[key];
                            if (errorKey) {
                                errorKey = key;
                                break;
                            }
                        }
                    }
                }
            }

            this.configurationsOnViewCache[viewId].errorKey = errorKey;

            return errorKey;

        }.on("configurationValidChanged"),

        _setUpConfiguration: function (configuration) {

            if (!this.$stage) {
                return;
            }

            this.$stage.$bus.setUp(configuration);
        },

        _tearDownConfiguration: function (configuration) {

            if (!this.$stage) {
                return;
            }

            this.$stage.$bus.tearDown(configuration);
        },

        _postConstruct: function () {

            var configurations = this.$.configurations,
                self = this;

            if (configurations) {
                configurations.each(function (configuration) {
                    self._setUpConfiguration(configuration);
                });
            }
        },

        _preDestroy: function () {
            var configurations = this.$.configurations,
                self = this;

            if (configurations) {
                configurations.each(function (configuration) {
                    self._tearDownConfiguration(configuration);
                });
            }
        },

        price: function () {

            // calculate price
            var price = new Price({
                vatIncluded: this.get("productType.price.vatIncluded"),
                currency: this.get('productType.price.currency')
            });
            this.$.configurations.each(function (configuration) {
                price.add(configuration.price());
            });

            return price;

        }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

        _addConfiguration: function (configuration) {
            this.trigger('beforeConfigurationAdd', configuration);
            this.$.configurations.add(configuration);
        },

        getConfigurationsOnView: function (view) {

            view = view || this.$.view;

            var productType = this.$.productType;

            if (view && productType) {

                if (productType.containsView(view)) {
                    return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                } else {
                    throw new Error("View not on product type");
                }

            }

            return [];

        },

        getConfigurationsOnPrintAreas: function (printAreas) {
            printAreas = printAreas || [];

            if (!(printAreas instanceof Array)) {
                printAreas = [printAreas];
            }

            var ret = [];

            for (var i = 0; i < this.$.configurations.$items.length; i++) {
                var configuration = this.$.configurations.$items[i];
                if (_.contains(printAreas, configuration.$.printArea)) {
                    ret.push(configuration);
                }
            }

            return ret;

        },

        compose: function () {
            var ret = this.callBase();

            ret.restrictions = {
                freeColorSelection: false,
                example: false
            };

            var viewId = this.get("view.id");

            if (viewId) {
                ret.defaultValues = {
                    defaultView: {
                        id: viewId
                    }
                };
            }

            return ret;
        },

        fetch: function (options, callback) {
            var self = this;
            this.callBase(options, function (err) {
                if (!err) {
                    self.$originalProduct = self.clone();
                }
                callback && callback(err, self);
            });
        },

        hasChanges: function () {
            return !this.equals(this.$originalProduct);
        },

        equals: function(product) {
            return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                this.$.appearance.isDeepEqual(product.$.appearance) &&
                this.$.productType.isDeepEqual(product.$.productType));
        },

        save: function (options, callback) {

            if (this.$originalProduct) {
                if (this.hasChanges()) {
                    this.set('id', undefined);
                } else {
                    this.set({
                        id: this.$originalProduct.$.id,
                        href: this.$originalProduct.$.href
                    });

                    callback && callback(null, this);
                    return;
                }
            }

            var self = this;

            if (!this.$.creator) {
                if (this.$stage && this.$stage.$application.name) {
                    this.set("creator", this.$stage.$application.name);
                }
            }

            this.callBase(options, function (err) {
                if (!err) {
                    self.$originalProduct = self.clone();
                } else {
                    err = ProductCreationError.createFromResponse(err);
                }

                callback && callback(err, self);
            });
        },

        init: function (callback) {
            var self = this;

            flow()
                .seq(function (cb) {
                    if (self.isNew()) {
                        cb();
                    } else {
                        self.fetch(null, cb);
                    }
                })
                .seq(function (cb) {
                    var productType = self.$.productType;
                    productType.fetch(null, cb);
                })
                .seq(function () {
                    var productType = self.$.productType;

                    var appearance;

                    if (self.$.appearance) {
                        appearance = productType.getAppearanceById(self.$.appearance.$.id);
                    }

                    appearance = appearance || productType.getDefaultAppearance();

                    self.set({
                        appearance: appearance,
                        view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                    });
                })
                .seq(function (cb) {
                    flow()
                        .parEach(self.$.configurations.$items, function (configuration, cb) {
                            self._setUpConfiguration(configuration);
                            configuration.init(cb);
                        })
                        .exec(cb);
                })
                .exec(function (err) {

                    self.trigger("priceChanged");

                    if (err) {
                        callback && callback(err);
                    } else {
                        callback && callback(null, self);
                    }
                });
        },

        appearanceBrightness: function () {

            var color = this.get("appearance.getMainColor()");

            if (color) {
                return color.distanceTo("#000000") < color.distanceTo("#FFFFFF") ?
                    "dark" : "bright";
            }

            return "";
        }.onChange(["appearance"]),

        isReadyForCompose: function () {
            var ready = true;

            if (this.$.configurations) {
                this.$.configurations.each(function (configuration) {
                    ready = ready && configuration.isReadyForCompose();
                });
            }

            return ready;
        },

        _commitChangedAttributes: function (attributes) {
            this.callBase();
            if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                this.trigger("productChanged");
            }
        }
    });
});
<commit_msg>DEV-62713 - don't save originalProduct if product is already fetched<commit_after>define(['sprd/model/ProductBase', 'js/core/List', 'js/data/AttributeTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType'],
    function (ProductBase, List, AttributeTypeResolver, DesignConfiguration, TextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType) {

    var undefined;

    return ProductBase.inherit("sprd.model.Product", {

        schema: {
            productType: ProductType,
            configurations: [new AttributeTypeResolver({
                attribute: "type",
                mapping: {
                    "design": DesignConfiguration,
                    "text": TextConfiguration
                }
            })],
            creator: String
        },

        defaults: {
            configurations: List,
            creator: null
        },

        inject: {
            manager: IProductManager
        },

        ctor: function () {
            this.configurationsOnViewCache = {};

            this.callBase();

            var priceChangeHandler = function () {
                this.trigger("priceChanged");
            };

            var productChangeHandler = function () {
                this.trigger("productChanged");
            };

            var configurationAdd = function (e) {
                var configuration = e.$.item,
                    viewId = configuration.get("printArea.getDefaultView().id");

                if (viewId) {
                    // clear configuration cache
                    this.configurationsOnViewCache[viewId] = null;
                    this.trigger("configurationValidChanged");
                }

                this._setUpConfiguration(configuration);

                if(configuration instanceof TextConfiguration){
                    configuration._composeText();
                }
                priceChangeHandler.call(this);
                productChangeHandler.call(this);

            };

            var configurationRemove = function (e) {
                var configuration = e.$.item,
                    viewId = configuration.get("printArea.getDefaultView().id");

                if (viewId) {
                    // clear configuration cache
                    this.configurationsOnViewCache[viewId] = null;
                    this.trigger("configurationValidChanged");
                }

                this._tearDownConfiguration(configuration);

                productChangeHandler.call(this);
                priceChangeHandler.call(this);
            };

            this.bind("configurations", "add", configurationAdd, this);
            this.bind("configurations", "remove", configurationRemove, this);
            this.bind("configurations", "reset", function() {
                this.configurationsOnViewCache = {};
                this.trigger("configurationValidChanged");

                priceChangeHandler.call(this);
                productChangeHandler.call(this);
            }, this);

            this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
            this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
            this.bind('change:configurations', productChangeHandler, this);
            this.bind('configurations', 'item:change:printArea', function() {
                this.configurationsOnViewCache = {};
                this.trigger("configurationValidChanged");

                productChangeHandler.call(this);
            }, this);

            this.bind('configurations', 'item:isValidChanged', function() {
                this.trigger("configurationValidChanged");
            }, this);

            this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);

        },

        getDefaultAppearance: function () {
            var productType = this.$.productType;

            if (productType) {
                return productType.getAppearanceById(this.get('appearance.id'));
            }

            return null;
        }.onChange('appearance', 'productType'),

        getDefaultView: function() {
            var productType = this.$.productType,
                viewId = this.get("defaultValues.defaultView.id");

            if (productType) {
                return productType.getViewById(viewId) || productType.getDefaultView();
            }

            return null;
        },

        _onConfigurationOffsetChanged: function(e) {

            var manager = this.$.manager,
                configuration = e.$.item;

            if (manager && manager.checkConfigurationOffset) {
                manager.checkConfigurationOffset(this, configuration)
            }
        },

        configurationsOnViewErrorKey: function(view) {

            var errorKey = null,
                configurations;

            if (!view) {
                return null;
            }

            var viewId = view.$.id;

            var productType = this.$.productType;

            configurations = this.configurationsOnViewCache[viewId];
            if (!configurations && productType && productType.containsView(view)) {
                this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
            }

            if (!configurations) {
                return null;
            }

            for (var i = 0; i < configurations.length; i++) {
                var configuration = configurations[i];

                if (!configuration.isValid() && configuration.$errors) {

                    for (var key in configuration.$errors.$) {
                        if (configuration.$errors.$.hasOwnProperty(key)) {
                            errorKey = configuration.$errors.$[key];
                            if (errorKey) {
                                errorKey = key;
                                break;
                            }
                        }
                    }
                }
            }

            this.configurationsOnViewCache[viewId].errorKey = errorKey;

            return errorKey;

        }.on("configurationValidChanged"),

        _setUpConfiguration: function (configuration) {

            if (!this.$stage) {
                return;
            }

            this.$stage.$bus.setUp(configuration);
        },

        _tearDownConfiguration: function (configuration) {

            if (!this.$stage) {
                return;
            }

            this.$stage.$bus.tearDown(configuration);
        },

        _postConstruct: function () {

            var configurations = this.$.configurations,
                self = this;

            if (configurations) {
                configurations.each(function (configuration) {
                    self._setUpConfiguration(configuration);
                });
            }
        },

        _preDestroy: function () {
            var configurations = this.$.configurations,
                self = this;

            if (configurations) {
                configurations.each(function (configuration) {
                    self._tearDownConfiguration(configuration);
                });
            }
        },

        price: function () {

            // calculate price
            var price = new Price({
                vatIncluded: this.get("productType.price.vatIncluded"),
                currency: this.get('productType.price.currency')
            });
            this.$.configurations.each(function (configuration) {
                price.add(configuration.price());
            });

            return price;

        }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

        _addConfiguration: function (configuration) {
            this.trigger('beforeConfigurationAdd', configuration);
            this.$.configurations.add(configuration);
        },

        getConfigurationsOnView: function (view) {

            view = view || this.$.view;

            var productType = this.$.productType;

            if (view && productType) {

                if (productType.containsView(view)) {
                    return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                } else {
                    throw new Error("View not on product type");
                }

            }

            return [];

        },

        getConfigurationsOnPrintAreas: function (printAreas) {
            printAreas = printAreas || [];

            if (!(printAreas instanceof Array)) {
                printAreas = [printAreas];
            }

            var ret = [];

            for (var i = 0; i < this.$.configurations.$items.length; i++) {
                var configuration = this.$.configurations.$items[i];
                if (_.contains(printAreas, configuration.$.printArea)) {
                    ret.push(configuration);
                }
            }

            return ret;

        },

        compose: function () {
            var ret = this.callBase();

            ret.restrictions = {
                freeColorSelection: false,
                example: false
            };

            var viewId = this.get("view.id");

            if (viewId) {
                ret.defaultValues = {
                    defaultView: {
                        id: viewId
                    }
                };
            }

            return ret;
        },

        fetch: function (options, callback) {
            var self = this,
                fetchState = this._fetch.state;

            this.callBase(options, function (err) {
                if (!err && fetchState !== 2) {
                    self.$originalProduct = self.clone();
                }
                callback && callback(err, self);
            });
        },

        hasChanges: function () {
            return !this.equals(this.$originalProduct);
        },

        equals: function(product) {
            return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                this.$.appearance.isDeepEqual(product.$.appearance) &&
                this.$.productType.isDeepEqual(product.$.productType));
        },

        save: function (options, callback) {

            if (this.$originalProduct) {
                if (this.hasChanges()) {
                    this.set('id', undefined);
                } else {
                    this.set({
                        id: this.$originalProduct.$.id,
                        href: this.$originalProduct.$.href
                    });

                    callback && callback(null, this);
                    return;
                }
            }

            var self = this;

            if (!this.$.creator) {
                if (this.$stage && this.$stage.$application.name) {
                    this.set("creator", this.$stage.$application.name);
                }
            }

            this.callBase(options, function (err) {
                if (!err) {
                    self.$originalProduct = self.clone();
                } else {
                    err = ProductCreationError.createFromResponse(err);
                }

                callback && callback(err, self);
            });
        },

        init: function (callback) {
            var self = this;

            flow()
                .seq(function (cb) {
                    if (self.isNew()) {
                        cb();
                    } else {
                        self.fetch(null, cb);
                    }
                })
                .seq(function (cb) {
                    var productType = self.$.productType;
                    productType.fetch(null, cb);
                })
                .seq(function () {
                    var productType = self.$.productType;

                    var appearance;

                    if (self.$.appearance) {
                        appearance = productType.getAppearanceById(self.$.appearance.$.id);
                    }

                    appearance = appearance || productType.getDefaultAppearance();

                    self.set({
                        appearance: appearance,
                        view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                    });
                })
                .seq(function (cb) {
                    flow()
                        .parEach(self.$.configurations.$items, function (configuration, cb) {
                            self._setUpConfiguration(configuration);
                            configuration.init(cb);
                        })
                        .exec(cb);
                })
                .exec(function (err) {

                    self.trigger("priceChanged");

                    if (err) {
                        callback && callback(err);
                    } else {
                        callback && callback(null, self);
                    }
                });
        },

        appearanceBrightness: function () {

            var color = this.get("appearance.getMainColor()");

            if (color) {
                return color.distanceTo("#000000") < color.distanceTo("#FFFFFF") ?
                    "dark" : "bright";
            }

            return "";
        }.onChange(["appearance"]),

        isReadyForCompose: function () {
            var ready = true;

            if (this.$.configurations) {
                this.$.configurations.each(function (configuration) {
                    ready = ready && configuration.isReadyForCompose();
                });
            }

            return ready;
        },

        _commitChangedAttributes: function (attributes) {
            this.callBase();
            if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                this.trigger("productChanged");
            }
        }
    });
});
<|endoftext|>


<commit_before>import Component from './component';
import Popover from './popover';

/*
    anchor: element
    content: html
    align: top|bottom (default: top)
    container: document.body

    methods
    ------
    enable
    disable
    hide
    show
    timeout
*/
export default class Tooltip extends Component {
    initialize (options) {
        this.el = options.anchor;
        options = options || {}
        this.options = {
            align: 'top',
            container: document.body
        };
        Object.assign(this.options, this.pick(options, Object.keys(this.options)));

        this.enabled = true;
        this.content = options.content;
        this.el.tooltip = this;
        this.timeout = options.timeout

        this.listenTo(this.el, 'mouseenter', this.show);
        this.listenTo(this.el, 'mouseleave', this.hide);
    }

    render () {
        return this;
    }

    show () {
        if(!this.enabled) return;
        clearTimeout(this.hide_timeout);
        this.el.classList.add('-active');
        if (this.popup) {
          this.popup.show()
        } else {
          const pointerDirection = {
              top: 'bottom',
              bottom: 'top',
              left: 'right',
              right: 'left'
          }[this.options.align]
          const align = {
              left: 'left center',
              right: 'right center',
              top: 'center top',
              bottom: 'center bottom'
          }[this.options.align]
          const offset = { [this.options.align]: -7}
          this.popup = new Popover({
              content: this.content,
              class: `uniformPointer -${pointerDirection} bg-gray-70 bg-opacity-85 text-white rounded pad-1/2x text-sm max-width-300-px `,
              anchor: this.el,
              align:  align || this.options.align || 'center 100%',
              offset: offset,
              container: this.options.container
          }).render();
        }
    }

    hide () {
      // this.hide_timeout = setTimeout(() => {
      //   this.popup.remove();
      //   this.el.classList.remove('-active');
      //   delete this.popup;
      // }, this.timeout)
    }

    disable () {
        this.enabled = false;
    }

    enabled () {
        this.enabled = true;
    }

}<commit_msg>Fix bug<commit_after>import Component from './component';
import Popover from './popover';

/*
    anchor: element
    content: html
    align: top|bottom (default: top)
    container: document.body

    methods
    ------
    enable
    disable
    hide
    show
    timeout
*/
export default class Tooltip extends Component {
    initialize (options) {
        this.el = options.anchor;
        options = options || {}
        this.options = {
            align: 'top',
            container: document.body
        };
        Object.assign(this.options, this.pick(options, Object.keys(this.options)));

        this.enabled = true;
        this.content = options.content;
        this.el.tooltip = this;
        this.timeout = options.timeout

        this.listenTo(this.el, 'mouseenter', this.show);
        this.listenTo(this.el, 'mouseleave', this.hide);
    }

    render () {
        return this;
    }

    show () {
        if(!this.enabled) return;
        clearTimeout(this.hide_timeout);
        this.el.classList.add('-active');
        if (this.popup) {
          this.popup.show()
        } else {
          const pointerDirection = {
              top: 'bottom',
              bottom: 'top',
              left: 'right',
              right: 'left'
          }[this.options.align]
          const align = {
              left: 'left center',
              right: 'right center',
              top: 'center top',
              bottom: 'center bottom'
          }[this.options.align]
          const offset = { [this.options.align]: -7}
          this.popup = new Popover({
              content: this.content,
              class: `uniformPointer -${pointerDirection} bg-gray-70 bg-opacity-85 text-white rounded pad-1/2x text-sm max-width-300-px `,
              anchor: this.el,
              align:  align || this.options.align || 'center 100%',
              offset: offset,
              container: this.options.container
          }).render();
        }
    }

    hide () {
      this.hide_timeout = setTimeout(() => {
        this.popup.remove();
        this.el.classList.remove('-active');
        delete this.popup;
      }, this.timeout)
    }

    disable () {
        this.enabled = false;
    }

    enabled () {
        this.enabled = true;
    }

}<|endoftext|>


<commit_before>define(['sprd/model/ProductBase', 'js/core/List', 'sprd/data/ConfigurationTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/SpecialTextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType', 'sprd/entity/Appearance'],
    function (ProductBase, List, ConfigurationTypeResolver, DesignConfiguration, TextConfiguration, SpecialTextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType, Appearance) {

        var undefined;

        return ProductBase.inherit("sprd.model.Product", {

            schema: {
                productType: ProductType,
                configurations: [new ConfigurationTypeResolver({
                    mapping: {
                        "design": DesignConfiguration,
                        "text": TextConfiguration,
                        "specialText": SpecialTextConfiguration
                    }
                })],
                appearance: {
                    type: Appearance,
                    isReference: true
                },
                restrictions: Object,
                defaultValues: Object,
                price: {
                    type: Price,
                    generated: true
                },
                creator: String
            },

            defaults: {
                configurations: List,
                creator: null
            },

            inject: {
                manager: IProductManager
            },

            ctor: function () {
                this.configurationsOnViewCache = {};

                this.callBase();

                var priceChangeHandler = function () {
                    this.trigger("priceChanged");
                };

                var productChangeHandler = function () {
                    this.trigger("productChanged");
                };

                var configurationAdd = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._setUpConfiguration(configuration);

                    if (configuration instanceof TextConfiguration) {
                        configuration._composeText();
                    }
                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);

                };

                var configurationRemove = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._tearDownConfiguration(configuration);

                    productChangeHandler.call(this);
                    priceChangeHandler.call(this);
                };

                this.bind("configurations", "add", configurationAdd, this);
                this.bind("configurations", "remove", configurationRemove, this);
                this.bind("configurations", "reset", function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);
                }, this);

                this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
                this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
                this.bind('change:configurations', productChangeHandler, this);
                this.bind('configurations', 'item:change:printArea', function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    productChangeHandler.call(this);
                }, this);

                this.bind('configurations', 'item:isValidChanged', function () {
                    this.trigger("configurationValidChanged");
                }, this);

                this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);

            },

            getDefaultAppearance: function () {
                var productType = this.$.productType;

                if (productType) {
                    return productType.getAppearanceById(this.get('appearance.id'));
                }

                return null;
            }.onChange('appearance', 'productType'),

            _onConfigurationOffsetChanged: function (e) {

                var manager = this.$.manager,
                    configuration = e.$.item;

                if (manager && manager.checkConfigurationOffset) {
                    manager.checkConfigurationOffset(this, configuration)
                }
            },

            configurationsOnViewError: function (view) {

                var errorValue = null,
                    configurations;

                if (!view) {
                    return null;
                }

                var viewId = view.$.id;

                var productType = this.$.productType;

                configurations = this.configurationsOnViewCache[viewId];
                if (!configurations && productType && productType.containsView(view)) {
                    this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
                }

                if (!configurations) {
                    return null;
                }

                for (var i = 0; i < configurations.length; i++) {
                    var configuration = configurations[i];

                    if (!configuration.isValid() && configuration.$errors) {

                        for (var key in configuration.$errors.$) {
                            if (configuration.$errors.$.hasOwnProperty(key)) {
                                errorValue = configuration.$errors.$[key];
                                if (errorValue) {
                                    return {
                                        key: key,
                                        value: errorValue
                                    };
                                }
                            }
                        }
                    }
                }

                return null;

            }.on("configurationValidChanged"),

            _setUpConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.setUp(configuration);
            },

            _tearDownConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.tearDown(configuration);
            },

            _postConstruct: function () {

                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._setUpConfiguration(configuration);
                    });
                }
            },

            _preDestroy: function () {
                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._tearDownConfiguration(configuration);
                    });
                }
            },

            price: function () {

                // calculate price
                var price = new Price({
                    vatIncluded: this.get("productType.price.vatIncluded"),
                    currency: this.get('productType.price.currency')
                });
                this.$.configurations.each(function (configuration) {
                    price.add(configuration.price());
                });

                return price;

            }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

            _addConfiguration: function (configuration) {
                this.$.configurations.add(configuration);
            },

            removeExampleConfiguration: function () {
                if (this.get('restrictions.example') === true && this.$.configurations.size()) {
                    this.$.restrictions.example = false;
                    this.$.configurations.removeAt(0);
                }
            },

            getConfigurationsOnView: function (view) {

                view = view || this.$.view;

                var productType = this.$.productType;

                if (view && productType) {

                    if (productType.containsView(view)) {
                        return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                    } else {
                        throw new Error("View not on product type");
                    }

                }

                return [];

            },

            getConfigurationsOnPrintAreas: function (printAreas) {
                printAreas = printAreas || [];

                if (!(printAreas instanceof Array)) {
                    printAreas = [printAreas];
                }

                var ret = [];

                for (var i = 0; i < this.$.configurations.$items.length; i++) {
                    var configuration = this.$.configurations.$items[i];
                    if (_.contains(printAreas, configuration.$.printArea)) {
                        ret.push(configuration);
                    }
                }

                return ret;

            },

            fetch: function (options, callback) {
                var self = this,
                    fetchState = this._fetch.state;

                this.callBase(options, function (err) {
                    if (!err && fetchState !== 2) {
                        self.$originalProduct = self.clone();
                    }
                    callback && callback(err, self);
                });
            },

            hasChanges: function () {
                return !this.equals(this.$originalProduct);
            },

            equals: function (product) {
                return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                    this.$.appearance.isDeepEqual(product.$.appearance) &&
                    this.$.productType.isDeepEqual(product.$.productType));
            },

            save: function (options, callback) {
                if (this.$originalProduct) {
                    if (this.hasChanges()) {
                        this.set('id', undefined);
                    } else {
                        this.set({
                            id: this.$originalProduct.$.id,
                            href: this.$originalProduct.$.href
                        });

                        callback && callback(null, this);
                        return;
                    }
                }

                var self = this;

                if (!this.$.creator) {
                    if (this.$stage && this.$stage.$application.name) {
                        this.set("creator", this.$stage.$application.name);
                    }
                }

                flow()
                    .parEach(this.$.configurations.$items, function (configuration, cb) {
                        configuration.save(cb);
                    })
                    .seq(function (cb) {
                        ProductBase.prototype.save.call(self, options, cb);
                    })
                    .exec(function (err) {
                        if (!err) {
                            var clone = self.clone();
                            // change original product against the clone in the entity
                            // so that the application can work with the original product
                            self.$context.removeEntityFromCache(self);
                            self.$context.addEntityToCache(clone);

                            self.$originalProduct = clone;
                        } else {
                            err = ProductCreationError.createFromResponse(err);
                        }

                        callback && callback(err, self);
                    });

            },

            init: function (callback) {
                if (this.initialized) {
                    callback && callback(null, this);
                    return;
                }
                var self = this;

                flow()
                    .seq(function (cb) {
                        if (self.isNew()) {
                            cb();
                        } else {
                            self.fetch(null, cb);
                        }
                    })
                    .seq(function (cb) {
                        var productType = self.$.productType;
                        productType.fetch(null, cb);
                    })
                    .seq(function () {
                        var productType = self.$.productType;

                        var appearance;

                        if (self.$.appearance) {
                            appearance = productType.getAppearanceById(self.$.appearance.$.id);
                        }

                        appearance = appearance || productType.getDefaultAppearance();

                        self.set({
                            appearance: appearance,
                            view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                        });
                    })
                    .seq(function (cb) {
                        flow()
                            .parEach(self.$.configurations.$items, function (configuration, cb) {
                                self._setUpConfiguration(configuration);
                                configuration.init(cb);
                            })
                            .exec(function(err) {
                                cb(err);
                            });
                    })
                    .exec(function (err) {
                        self.trigger("productInitialized");
                        self.trigger("priceChanged");

                        if (err) {
                            callback && callback(err);
                        } else {
                            self.initialized = true;
                            callback && callback(null, self);
                        }
                    });
            },

            getAvailableAppearances: function () {
                var productType = this.$.productType;
                if (productType) {
                    var appearances = productType.getAvailableAppearances(),
                        configurations = this.$.configurations;
                    if (configurations) {
                        var ret = new List(appearances.toArray());
                        configurations.each(function (config) {
                            appearances.each(function (appearance) {
                                if (ret.contains(appearance)) {
                                    var printTypes = config.getPossiblePrintTypes(appearance);
                                    if (!printTypes || !printTypes.length) {
                                        ret.remove(appearance);
                                    }
                                }
                            });
                        });
                        return ret;
                    }
                    return appearances;
                }
                return null;
            }.onChange('productType.stockStates'),

            appearanceBrightness: function () {

                var color = this.get("appearance.getMainColor()");

                if (color) {
                    return color.distanceTo("#000000") < color.distanceTo("#FFFFFF") ?
                        "dark" : "bright";
                }

                return "";
            }.onChange(["appearance"]),

            isReadyForCompose: function () {
                var ready = true;

                if (this.$.configurations) {
                    this.$.configurations.each(function (configuration) {
                        ready = ready && configuration.isReadyForCompose();
                    });
                }

                return ready;
            },

            _commitChangedAttributes: function (attributes) {
                this.callBase();
                if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                    this.trigger("productChanged");
                }
            },
            sync: function () {
                var ret = this.callBase();
                if (ret) {
                    this._$source.initialized = false;
                }
                return ret;
            }
        });
    });
<commit_msg>DEV-110006 - Tracking to identify how many templates are sold<commit_after>define(['sprd/model/ProductBase', 'js/core/List', 'sprd/data/ConfigurationTypeResolver', 'sprd/entity/DesignConfiguration', 'sprd/entity/TextConfiguration', 'sprd/entity/SpecialTextConfiguration', 'sprd/entity/Price', 'js/data/TypeResolver', 'js/data/Entity', "underscore", "flow", "sprd/manager/IProductManager", "sprd/error/ProductCreationError", 'sprd/model/ProductType', 'sprd/entity/Appearance'],
    function (ProductBase, List, ConfigurationTypeResolver, DesignConfiguration, TextConfiguration, SpecialTextConfiguration, Price, TypeResolver, Entity, _, flow, IProductManager, ProductCreationError, ProductType, Appearance) {

        var undefined;

        return ProductBase.inherit("sprd.model.Product", {

            schema: {
                productType: ProductType,
                configurations: [new ConfigurationTypeResolver({
                    mapping: {
                        "design": DesignConfiguration,
                        "text": TextConfiguration,
                        "specialText": SpecialTextConfiguration
                    }
                })],
                appearance: {
                    type: Appearance,
                    isReference: true
                },
                restrictions: Object,
                defaultValues: Object,
                price: {
                    type: Price,
                    generated: true
                },
                creator: String,
                templateId: {
                    type: String,
                    required: false
                }
            },

            defaults: {
                configurations: List,
                creator: null
            },

            inject: {
                manager: IProductManager
            },

            ctor: function () {
                this.configurationsOnViewCache = {};

                this.callBase();

                var priceChangeHandler = function () {
                    this.trigger("priceChanged");
                };

                var productChangeHandler = function () {
                    this.trigger("productChanged");
                };

                var configurationAdd = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._setUpConfiguration(configuration);

                    if (configuration instanceof TextConfiguration) {
                        configuration._composeText();
                    }
                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);

                };

                var configurationRemove = function (e) {
                    var configuration = e.$.item,
                        viewId = configuration.get("printArea.getDefaultView().id");

                    if (viewId) {
                        // clear configuration cache
                        this.configurationsOnViewCache[viewId] = null;
                        this.trigger("configurationValidChanged");
                    }

                    this._tearDownConfiguration(configuration);

                    productChangeHandler.call(this);
                    priceChangeHandler.call(this);
                };

                this.bind("configurations", "add", configurationAdd, this);
                this.bind("configurations", "remove", configurationRemove, this);
                this.bind("configurations", "reset", function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    priceChangeHandler.call(this);
                    productChangeHandler.call(this);
                }, this);

                this.bind("configurations", "item:priceChanged", priceChangeHandler, this);
                this.bind('configurations', 'item:configurationChanged', productChangeHandler, this);
                this.bind('change:configurations', productChangeHandler, this);
                this.bind('configurations', 'item:change:printArea', function () {
                    this.configurationsOnViewCache = {};
                    this.trigger("configurationValidChanged");

                    productChangeHandler.call(this);
                }, this);

                this.bind('configurations', 'item:isValidChanged', function () {
                    this.trigger("configurationValidChanged");
                }, this);

                this.bind('configurations', 'item:change:offset', this._onConfigurationOffsetChanged, this);

            },

            getDefaultAppearance: function () {
                var productType = this.$.productType;

                if (productType) {
                    return productType.getAppearanceById(this.get('appearance.id'));
                }

                return null;
            }.onChange('appearance', 'productType'),

            _onConfigurationOffsetChanged: function (e) {

                var manager = this.$.manager,
                    configuration = e.$.item;

                if (manager && manager.checkConfigurationOffset) {
                    manager.checkConfigurationOffset(this, configuration)
                }
            },

            configurationsOnViewError: function (view) {

                var errorValue = null,
                    configurations;

                if (!view) {
                    return null;
                }

                var viewId = view.$.id;

                var productType = this.$.productType;

                configurations = this.configurationsOnViewCache[viewId];
                if (!configurations && productType && productType.containsView(view)) {
                    this.configurationsOnViewCache[viewId] = configurations = this.getConfigurationsOnView(view);
                }

                if (!configurations) {
                    return null;
                }

                for (var i = 0; i < configurations.length; i++) {
                    var configuration = configurations[i];

                    if (!configuration.isValid() && configuration.$errors) {

                        for (var key in configuration.$errors.$) {
                            if (configuration.$errors.$.hasOwnProperty(key)) {
                                errorValue = configuration.$errors.$[key];
                                if (errorValue) {
                                    return {
                                        key: key,
                                        value: errorValue
                                    };
                                }
                            }
                        }
                    }
                }

                return null;

            }.on("configurationValidChanged"),

            _setUpConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.setUp(configuration);
            },

            _tearDownConfiguration: function (configuration) {

                if (!this.$stage) {
                    return;
                }

                this.$stage.$bus.tearDown(configuration);
            },

            _postConstruct: function () {

                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._setUpConfiguration(configuration);
                    });
                }
            },

            _preDestroy: function () {
                var configurations = this.$.configurations,
                    self = this;

                if (configurations) {
                    configurations.each(function (configuration) {
                        self._tearDownConfiguration(configuration);
                    });
                }
            },

            price: function () {

                // calculate price
                var price = new Price({
                    vatIncluded: this.get("productType.price.vatIncluded"),
                    currency: this.get('productType.price.currency')
                });
                this.$.configurations.each(function (configuration) {
                    price.add(configuration.price());
                });

                return price;

            }.on("priceChanged", ["productType", 'change:price']).onChange('productType'),

            _addConfiguration: function (configuration) {
                this.$.configurations.add(configuration);
            },

            removeExampleConfiguration: function () {
                if (this.get('restrictions.example') === true && this.$.configurations.size()) {
                    this.$.restrictions.example = false;
                    this.$.configurations.removeAt(0);
                }
            },

            getConfigurationsOnView: function (view) {

                view = view || this.$.view;

                var productType = this.$.productType;

                if (view && productType) {

                    if (productType.containsView(view)) {
                        return this.getConfigurationsOnPrintAreas(view.getPrintAreas());
                    } else {
                        throw new Error("View not on product type");
                    }

                }

                return [];

            },

            getConfigurationsOnPrintAreas: function (printAreas) {
                printAreas = printAreas || [];

                if (!(printAreas instanceof Array)) {
                    printAreas = [printAreas];
                }

                var ret = [];

                for (var i = 0; i < this.$.configurations.$items.length; i++) {
                    var configuration = this.$.configurations.$items[i];
                    if (_.contains(printAreas, configuration.$.printArea)) {
                        ret.push(configuration);
                    }
                }

                return ret;

            },

            fetch: function (options, callback) {
                var self = this,
                    fetchState = this._fetch.state;

                this.callBase(options, function (err) {
                    if (!err && fetchState !== 2) {
                        self.$originalProduct = self.clone();
                    }
                    callback && callback(err, self);
                });
            },

            hasChanges: function () {
                return !this.equals(this.$originalProduct);
            },

            equals: function (product) {
                return (this.$.configurations.isDeepEqual(product.$.configurations) &&
                    this.$.appearance.isDeepEqual(product.$.appearance) &&
                    this.$.productType.isDeepEqual(product.$.productType));
            },

            save: function (options, callback) {
                if (this.$originalProduct) {
                    if (this.hasChanges()) {
                        this.set('id', undefined);
                    } else {
                        this.set({
                            id: this.$originalProduct.$.id,
                            href: this.$originalProduct.$.href
                        });

                        callback && callback(null, this);
                        return;
                    }
                }

                var self = this;

                if (!this.$.creator) {
                    if (this.$stage && this.$stage.$application.name) {
                        this.set("creator", this.$stage.$application.name);
                    }
                }

                flow()
                    .parEach(this.$.configurations.$items, function (configuration, cb) {
                        configuration.save(cb);
                    })
                    .seq(function (cb) {
                        ProductBase.prototype.save.call(self, options, cb);
                    })
                    .exec(function (err) {
                        if (!err) {
                            var clone = self.clone();
                            // change original product against the clone in the entity
                            // so that the application can work with the original product
                            self.$context.removeEntityFromCache(self);
                            self.$context.addEntityToCache(clone);

                            self.$originalProduct = clone;
                        } else {
                            err = ProductCreationError.createFromResponse(err);
                        }

                        callback && callback(err, self);
                    });

            },

            init: function (callback) {
                if (this.initialized) {
                    callback && callback(null, this);
                    return;
                }
                var self = this;

                flow()
                    .seq(function (cb) {
                        if (self.isNew()) {
                            cb();
                        } else {
                            self.fetch(null, cb);
                        }
                    })
                    .seq(function (cb) {
                        var productType = self.$.productType;
                        productType.fetch(null, cb);
                    })
                    .seq(function () {
                        var productType = self.$.productType;

                        var appearance;

                        if (self.$.appearance) {
                            appearance = productType.getAppearanceById(self.$.appearance.$.id);
                        }

                        appearance = appearance || productType.getDefaultAppearance();

                        self.set({
                            appearance: appearance,
                            view: self.$.view || productType.getViewById(self.get("defaultValues.defaultView.id")) || productType.getDefaultView()
                        });
                    })
                    .seq(function (cb) {
                        flow()
                            .parEach(self.$.configurations.$items, function (configuration, cb) {
                                self._setUpConfiguration(configuration);
                                configuration.init(cb);
                            })
                            .exec(function(err) {
                                cb(err);
                            });
                    })
                    .exec(function (err) {
                        self.trigger("productInitialized");
                        self.trigger("priceChanged");

                        if (err) {
                            callback && callback(err);
                        } else {
                            self.initialized = true;
                            callback && callback(null, self);
                        }
                    });
            },

            getAvailableAppearances: function () {
                var productType = this.$.productType;
                if (productType) {
                    var appearances = productType.getAvailableAppearances(),
                        configurations = this.$.configurations;
                    if (configurations) {
                        var ret = new List(appearances.toArray());
                        configurations.each(function (config) {
                            appearances.each(function (appearance) {
                                if (ret.contains(appearance)) {
                                    var printTypes = config.getPossiblePrintTypes(appearance);
                                    if (!printTypes || !printTypes.length) {
                                        ret.remove(appearance);
                                    }
                                }
                            });
                        });
                        return ret;
                    }
                    return appearances;
                }
                return null;
            }.onChange('productType.stockStates'),

            appearanceBrightness: function () {

                var color = this.get("appearance.getMainColor()");

                if (color) {
                    return color.distanceTo("#000000") < color.distanceTo("#FFFFFF") ?
                        "dark" : "bright";
                }

                return "";
            }.onChange(["appearance"]),

            isReadyForCompose: function () {
                var ready = true;

                if (this.$.configurations) {
                    this.$.configurations.each(function (configuration) {
                        ready = ready && configuration.isReadyForCompose();
                    });
                }

                return ready;
            },

            _commitChangedAttributes: function (attributes) {
                this.callBase();
                if (attributes.hasOwnProperty("appearance") || attributes.hasOwnProperty("productType")) {
                    this.trigger("productChanged");
                }
            },
            sync: function () {
                var ret = this.callBase();
                if (ret) {
                    this._$source.initialized = false;
                }
                return ret;
            }
        });
    });
<|endoftext|>


<commit_before>angular.module('Common')
    .factory('BasePageController', function (NavigationService) {
        function BasePageController($scope, $location, $controller, pageName) {
            var page = NavigationService.findPage(pageName);
            if (page === null) {
                page = {
                    pageTitle:'Page Title Not Specified',
                    actions:[]
                };
            }
            NavigationService.setCurrentPage(page);
            $scope.$on('$locationChangeStart', function( event ) {
                if (!page.onLeave()) {
                    event.preventDefault();
                }
            });
        }
        return (BasePageController);
    });<commit_msg>-add page property to controllers<commit_after>angular.module('Common')
    .factory('BasePageController', function (NavigationService) {
        function BasePageController($scope, $location, $controller, pageName) {
            var page = NavigationService.findPage(pageName);
            this.page = page;
            if (page === null) {
                page = {
                    pageTitle:'Page Title Not Specified',
                    actions:[]
                };
            }
            NavigationService.setCurrentPage(page);
            $scope.$on('$locationChangeStart', function( event ) {
                if (!page.onLeave()) {
                    event.preventDefault();
                }
            });
        }
        return (BasePageController);
    });<|endoftext|>


<commit_before>'use strict';

//External
require('angular');
require('angular-ui-router');
require('angular-ui-bootstrap');
require('angular-animate');

//Internal
var views = require('./views/module');
var models = require('./models/module');
var components = require('./components/module');
var appController = require('./app_controller');
var appRouter = require('./app_router');
var appSettings = require('./app_settings');
var appRun = require('./app_run');
var appAuthInterceptor = require('./app_auth_interceptor');

var appModule = angular.module("app", [
    //External
    'ui.router',
    'ui.bootstrap',
    'ngAnimate',

    //Internal
    appSettings.name,
    models.name,
    components.name,
    views.name
])
.controller('AppController', appController)
.factory('SessionInjector', appAuthInterceptor)
.config(appRouter)
.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('SessionInjector');
}])
.run(appRun);

<commit_msg>ADD: ng file upload  dependency<commit_after>'use strict';

//External
require('angular');
require('angular-ui-router');
require('angular-ui-bootstrap');
require('angular-animate');
require('ng-file-upload');

//Internal
var views = require('./views/module');
var models = require('./models/module');
var components = require('./components/module');
var appController = require('./app_controller');
var appRouter = require('./app_router');
var appSettings = require('./app_settings');
var appRun = require('./app_run');
var appAuthInterceptor = require('./app_auth_interceptor');

var appModule = angular.module("app", [
    //External
    'ui.router',
    'ui.bootstrap',
    'ngAnimate',
    'ngFileUpload',

    //Internal
    appSettings.name,
    models.name,
    components.name,
    views.name
])
.controller('AppController', appController)
.factory('SessionInjector', appAuthInterceptor)
.config(appRouter)
.config(['$httpProvider', function($httpProvider) {
    $httpProvider.interceptors.push('SessionInjector');
}])
.run(appRun);

<|endoftext|>


<commit_before>import { AmbientLight, DirectionalLight } from 'three';

const three = window.THREE
  ? window.THREE // Prefer consumption from global THREE, if exists
  : { AmbientLight, DirectionalLight };

import ThreeDragControls from 'three-dragcontrols';
import ThreeForceGraph from 'three-forcegraph';
import ThreeRenderObjects from 'three-render-objects';

import accessorFn from 'accessor-fn';
import Kapsule from 'kapsule';

import linkKapsule from './kapsule-link.js';

//

const CAMERA_DISTANCE2NODES_FACTOR = 150;

//

// Expose config from forceGraph
const bindFG = linkKapsule('forceGraph', ThreeForceGraph);
const linkedFGProps = Object.assign(...[
  'jsonUrl',
  'graphData',
  'numDimensions',
  'dagMode',
  'dagLevelDistance',
  'nodeRelSize',
  'nodeId',
  'nodeVal',
  'nodeResolution',
  'nodeColor',
  'nodeAutoColorBy',
  'nodeOpacity',
  'nodeVisibility',
  'nodeThreeObject',
  'nodeThreeObjectExtend',
  'linkSource',
  'linkTarget',
  'linkVisibility',
  'linkColor',
  'linkAutoColorBy',
  'linkOpacity',
  'linkWidth',
  'linkResolution',
  'linkCurvature',
  'linkCurveRotation',
  'linkMaterial',
  'linkThreeObject',
  'linkThreeObjectExtend',
  'linkPositionUpdate',
  'linkDirectionalArrowLength',
  'linkDirectionalArrowColor',
  'linkDirectionalArrowRelPos',
  'linkDirectionalArrowResolution',
  'linkDirectionalParticles',
  'linkDirectionalParticleSpeed',
  'linkDirectionalParticleWidth',
  'linkDirectionalParticleColor',
  'linkDirectionalParticleResolution',
  'forceEngine',
  'd3AlphaDecay',
  'd3VelocityDecay',
  'warmupTicks',
  'cooldownTicks',
  'cooldownTime',
  'onEngineTick',
  'onEngineStop'
].map(p => ({ [p]: bindFG.linkProp(p)})));
const linkedFGMethods = Object.assign(...[
  'refresh',
  'd3Force'
].map(p => ({ [p]: bindFG.linkMethod(p)})));

// Expose config from renderObjs
const bindRenderObjs = linkKapsule('renderObjs', ThreeRenderObjects);
const linkedRenderObjsProps = Object.assign(...[
  'width',
  'height',
  'backgroundColor',
  'showNavInfo',
  'enablePointerInteraction'
].map(p => ({ [p]: bindRenderObjs.linkProp(p)})));
const linkedRenderObjsMethods = Object.assign(...[
  'cameraPosition'
].map(p => ({ [p]: bindRenderObjs.linkMethod(p)})));

//

export default Kapsule({

  props: {
    nodeLabel: { default: 'name', triggerUpdate: false },
    linkLabel: { default: 'name', triggerUpdate: false },
    linkHoverPrecision: { default: 1, onChange: (p, state) => state.renderObjs.lineHoverPrecision(p), triggerUpdate: false },
    enableNavigationControls: {
      default: true,
      onChange(enable, state) {
        const controls = state.renderObjs.controls();
        if (controls) {
          controls.enabled = enable;
        }
      },
      triggerUpdate: false
    },
    enableNodeDrag: { default: true, triggerUpdate: false },
    onNodeDrag: { default: () => {}, triggerUpdate: false },
    onNodeDragEnd: { default: () => {}, triggerUpdate: false },
    onNodeClick: { default: () => {}, triggerUpdate: false },
    onNodeRightClick: { default: () => {}, triggerUpdate: false },
    onNodeHover: { default: () => {}, triggerUpdate: false },
    onLinkClick: { default: () => {}, triggerUpdate: false },
    onLinkRightClick: { default: () => {}, triggerUpdate: false },
    onLinkHover: { default: () => {}, triggerUpdate: false },
    ...linkedFGProps,
    ...linkedRenderObjsProps
  },

  methods: {
    pauseAnimation: function(state) {
      if (state.animationFrameRequestId !== null) {
        cancelAnimationFrame(state.animationFrameRequestId);
        state.animationFrameRequestId = null;
      }
      return this;
    },

    resumeAnimation: function(state) {
      if (state.animationFrameRequestId === null) {
        this._animationCycle();
      }
      return this;
    },
    _animationCycle(state) {
      if (state.enablePointerInteraction) {
        // reset canvas cursor (override dragControls cursor)
        this.renderer().domElement.style.cursor = null;
      }

      // Frame cycle
      state.forceGraph.tickFrame();
      state.renderObjs.tick();
      state.animationFrameRequestId = requestAnimationFrame(this._animationCycle);
    },
    scene: state => state.renderObjs.scene(), // Expose scene
    camera: state => state.renderObjs.camera(), // Expose camera
    renderer: state => state.renderObjs.renderer(), // Expose renderer
    controls: state => state.renderObjs.controls(), // Expose controls
    tbControls: state => state.renderObjs.tbControls(), // To be deprecated
    _destructor: function() {
      this.pauseAnimation();
      this.graphData({ nodes: [], links: []});
    },
    ...linkedFGMethods,
    ...linkedRenderObjsMethods
  },

  stateInit: ({ controlType, rendererConfig }) => ({
    forceGraph: new ThreeForceGraph(),
    renderObjs: ThreeRenderObjects({ controlType, rendererConfig })
  }),

  init: function(domNode, state) {
    // Wipe DOM
    domNode.innerHTML = '';

    // Add relative container
    domNode.appendChild(state.container = document.createElement('div'));
    state.container.style.position = 'relative';

    // Add renderObjs
    const roDomNode = document.createElement('div');
    state.container.appendChild(roDomNode);
    state.renderObjs(roDomNode);
    const camera = state.renderObjs.camera();
    const renderer = state.renderObjs.renderer();
    const controls = state.renderObjs.controls();
    controls.enabled = !!state.enableNavigationControls;
    state.lastSetCameraZ = camera.position.z;

    // Add info space
    let infoElem;
    state.container.appendChild(infoElem = document.createElement('div'));
    infoElem.className = 'graph-info-msg';
    infoElem.textContent = '';

    // config forcegraph
    state.forceGraph.onLoading(() => { infoElem.textContent = 'Loading...' });
    state.forceGraph.onFinishLoading(() => {
      infoElem.textContent = '';

      // sync graph data structures
      state.graphData = state.forceGraph.graphData();

      // re-aim camera, if still in default position (not user modified)
      if (camera.position.x === 0 && camera.position.y === 0 && camera.position.z === state.lastSetCameraZ && state.graphData.nodes.length) {
        camera.lookAt(state.forceGraph.position);
        state.lastSetCameraZ = camera.position.z = Math.cbrt(state.graphData.nodes.length) * CAMERA_DISTANCE2NODES_FACTOR;
      }

      // Setup node drag interaction
      if (state.enableNodeDrag && state.enablePointerInteraction && state.forceEngine === 'd3') { // Can't access node positions programatically in ngraph
        const dragControls = new ThreeDragControls(
          state.graphData.nodes.map(node => node.__threeObj),
          camera,
          renderer.domElement
        );

        dragControls.addEventListener('dragstart', function (event) {
          controls.enabled = false; // Disable controls while dragging

          const node = event.object.__data;
          node.__initialFixedPos = {fx: node.fx, fy: node.fy, fz: node.fz};

          // lock node
          ['x', 'y', 'z'].forEach(c => node[`f${c}`] = node[c]);

          // keep engine running at low intensity throughout drag
          state.forceGraph.d3AlphaTarget(0.3);

          // drag cursor
          renderer.domElement.classList.add('grabbable');
        });

        dragControls.addEventListener('drag', function (event) {
          state.ignoreOneClick = true; // Don't click the node if it's being dragged

          const node = event.object.__data;

          // Move fx/fy/fz (and x/y/z) of nodes based on object new position
          ['x', 'y', 'z'].forEach(c => node[`f${c}`] = node[c] = event.object.position[c]);

          // prevent freeze while dragging
          state.forceGraph.resetCountdown();

          state.onNodeDrag(node);
        });

        dragControls.addEventListener('dragend', function (event) {
          const node = event.object.__data;
          const initPos = node.__initialFixedPos;

          if (initPos) {
            ['x', 'y', 'z'].forEach(c => {
              const fc = `f${c}`;
              if (initPos[fc] === undefined) {
                node[fc] = undefined
              }
            });
            delete(node.__initialFixedPos);

            state.onNodeDragEnd(node);
          }

          state.forceGraph
            .d3AlphaTarget(0)   // release engine low intensity
            .resetCountdown();  // let the engine readjust after releasing fixed nodes

          if (state.enableNavigationControls) {
            controls.enabled = true; // Re-enable controls
          }

          // clear cursor
          renderer.domElement.classList.remove('grabbable');
        });
      }
    });

    // config renderObjs
    const getGraphObj = object => {
      let obj = object;
      // recurse up object chain until finding the graph object
      while (obj && !obj.hasOwnProperty('__graphObjType')) {
        obj = obj.parent;
      }
      return obj;
    };

    state.renderObjs
      .objects([ // Populate scene
        new three.AmbientLight(0xbbbbbb),
        new three.DirectionalLight(0xffffff, 0.6),
        state.forceGraph
      ])
      .hoverOrderComparator((a, b) => {
        // Prioritize graph objects
        const aObj = getGraphObj(a);
        if (!aObj) return 1;
        const bObj = getGraphObj(b);
        if (!bObj) return -1;

        // Prioritize nodes over links
        const isNode = o => o.__graphObjType === 'node';
        return isNode(bObj) - isNode(aObj);
      })
      .tooltipContent(obj => {
        const graphObj = getGraphObj(obj);
        return graphObj ? accessorFn(state[`${graphObj.__graphObjType}Label`])(graphObj.__data) || '' : '';
      })
      .onHover(obj => {
        // Update tooltip and trigger onHover events
        const hoverObj = getGraphObj(obj);

        if (hoverObj !== state.hoverObj) {
          const prevObjType = state.hoverObj ? state.hoverObj.__graphObjType : null;
          const prevObjData = state.hoverObj ? state.hoverObj.__data : null;
          const objType = hoverObj ? hoverObj.__graphObjType : null;
          const objData = hoverObj ? hoverObj.__data : null;
          if (prevObjType && prevObjType !== objType) {
            // Hover out
            state[`on${prevObjType === 'node' ? 'Node' : 'Link'}Hover`](null, prevObjData);
          }
          if (objType) {
            // Hover in
            state[`on${objType === 'node' ? 'Node' : 'Link'}Hover`](objData, prevObjType === objType ? prevObjData : null);
          }

          state.hoverObj = hoverObj;
        }
      })
      .onClick(obj => {
        // Handle click events on objects
        if (state.ignoreOneClick) {
          // f.e. because of dragend event
          state.ignoreOneClick = false;
          return;
        }

        const graphObj = getGraphObj(obj);
        if (graphObj) {
          state[`on${graphObj.__graphObjType === 'node' ? 'Node' : 'Link'}Click`](graphObj.__data);
        }
      })
      .onRightClick(obj => {
        // Handle right-click events
        const graphObj = getGraphObj(obj);
        if (graphObj) {
          state[`on${graphObj.__graphObjType === 'node' ? 'Node' : 'Link'}RightClick`](graphObj.__data);
        }
      });

    //

    // Kick-off renderer
    this._animationCycle();
  }
});<commit_msg>Adjust camera distance<commit_after>import { AmbientLight, DirectionalLight } from 'three';

const three = window.THREE
  ? window.THREE // Prefer consumption from global THREE, if exists
  : { AmbientLight, DirectionalLight };

import ThreeDragControls from 'three-dragcontrols';
import ThreeForceGraph from 'three-forcegraph';
import ThreeRenderObjects from 'three-render-objects';

import accessorFn from 'accessor-fn';
import Kapsule from 'kapsule';

import linkKapsule from './kapsule-link.js';

//

const CAMERA_DISTANCE2NODES_FACTOR = 200;

//

// Expose config from forceGraph
const bindFG = linkKapsule('forceGraph', ThreeForceGraph);
const linkedFGProps = Object.assign(...[
  'jsonUrl',
  'graphData',
  'numDimensions',
  'dagMode',
  'dagLevelDistance',
  'nodeRelSize',
  'nodeId',
  'nodeVal',
  'nodeResolution',
  'nodeColor',
  'nodeAutoColorBy',
  'nodeOpacity',
  'nodeVisibility',
  'nodeThreeObject',
  'nodeThreeObjectExtend',
  'linkSource',
  'linkTarget',
  'linkVisibility',
  'linkColor',
  'linkAutoColorBy',
  'linkOpacity',
  'linkWidth',
  'linkResolution',
  'linkCurvature',
  'linkCurveRotation',
  'linkMaterial',
  'linkThreeObject',
  'linkThreeObjectExtend',
  'linkPositionUpdate',
  'linkDirectionalArrowLength',
  'linkDirectionalArrowColor',
  'linkDirectionalArrowRelPos',
  'linkDirectionalArrowResolution',
  'linkDirectionalParticles',
  'linkDirectionalParticleSpeed',
  'linkDirectionalParticleWidth',
  'linkDirectionalParticleColor',
  'linkDirectionalParticleResolution',
  'forceEngine',
  'd3AlphaDecay',
  'd3VelocityDecay',
  'warmupTicks',
  'cooldownTicks',
  'cooldownTime',
  'onEngineTick',
  'onEngineStop'
].map(p => ({ [p]: bindFG.linkProp(p)})));
const linkedFGMethods = Object.assign(...[
  'refresh',
  'd3Force'
].map(p => ({ [p]: bindFG.linkMethod(p)})));

// Expose config from renderObjs
const bindRenderObjs = linkKapsule('renderObjs', ThreeRenderObjects);
const linkedRenderObjsProps = Object.assign(...[
  'width',
  'height',
  'backgroundColor',
  'showNavInfo',
  'enablePointerInteraction'
].map(p => ({ [p]: bindRenderObjs.linkProp(p)})));
const linkedRenderObjsMethods = Object.assign(...[
  'cameraPosition'
].map(p => ({ [p]: bindRenderObjs.linkMethod(p)})));

//

export default Kapsule({

  props: {
    nodeLabel: { default: 'name', triggerUpdate: false },
    linkLabel: { default: 'name', triggerUpdate: false },
    linkHoverPrecision: { default: 1, onChange: (p, state) => state.renderObjs.lineHoverPrecision(p), triggerUpdate: false },
    enableNavigationControls: {
      default: true,
      onChange(enable, state) {
        const controls = state.renderObjs.controls();
        if (controls) {
          controls.enabled = enable;
        }
      },
      triggerUpdate: false
    },
    enableNodeDrag: { default: true, triggerUpdate: false },
    onNodeDrag: { default: () => {}, triggerUpdate: false },
    onNodeDragEnd: { default: () => {}, triggerUpdate: false },
    onNodeClick: { default: () => {}, triggerUpdate: false },
    onNodeRightClick: { default: () => {}, triggerUpdate: false },
    onNodeHover: { default: () => {}, triggerUpdate: false },
    onLinkClick: { default: () => {}, triggerUpdate: false },
    onLinkRightClick: { default: () => {}, triggerUpdate: false },
    onLinkHover: { default: () => {}, triggerUpdate: false },
    ...linkedFGProps,
    ...linkedRenderObjsProps
  },

  methods: {
    pauseAnimation: function(state) {
      if (state.animationFrameRequestId !== null) {
        cancelAnimationFrame(state.animationFrameRequestId);
        state.animationFrameRequestId = null;
      }
      return this;
    },

    resumeAnimation: function(state) {
      if (state.animationFrameRequestId === null) {
        this._animationCycle();
      }
      return this;
    },
    _animationCycle(state) {
      if (state.enablePointerInteraction) {
        // reset canvas cursor (override dragControls cursor)
        this.renderer().domElement.style.cursor = null;
      }

      // Frame cycle
      state.forceGraph.tickFrame();
      state.renderObjs.tick();
      state.animationFrameRequestId = requestAnimationFrame(this._animationCycle);
    },
    scene: state => state.renderObjs.scene(), // Expose scene
    camera: state => state.renderObjs.camera(), // Expose camera
    renderer: state => state.renderObjs.renderer(), // Expose renderer
    controls: state => state.renderObjs.controls(), // Expose controls
    tbControls: state => state.renderObjs.tbControls(), // To be deprecated
    _destructor: function() {
      this.pauseAnimation();
      this.graphData({ nodes: [], links: []});
    },
    ...linkedFGMethods,
    ...linkedRenderObjsMethods
  },

  stateInit: ({ controlType, rendererConfig }) => ({
    forceGraph: new ThreeForceGraph(),
    renderObjs: ThreeRenderObjects({ controlType, rendererConfig })
  }),

  init: function(domNode, state) {
    // Wipe DOM
    domNode.innerHTML = '';

    // Add relative container
    domNode.appendChild(state.container = document.createElement('div'));
    state.container.style.position = 'relative';

    // Add renderObjs
    const roDomNode = document.createElement('div');
    state.container.appendChild(roDomNode);
    state.renderObjs(roDomNode);
    const camera = state.renderObjs.camera();
    const renderer = state.renderObjs.renderer();
    const controls = state.renderObjs.controls();
    controls.enabled = !!state.enableNavigationControls;
    state.lastSetCameraZ = camera.position.z;

    // Add info space
    let infoElem;
    state.container.appendChild(infoElem = document.createElement('div'));
    infoElem.className = 'graph-info-msg';
    infoElem.textContent = '';

    // config forcegraph
    state.forceGraph.onLoading(() => { infoElem.textContent = 'Loading...' });
    state.forceGraph.onFinishLoading(() => {
      infoElem.textContent = '';

      // sync graph data structures
      state.graphData = state.forceGraph.graphData();

      // re-aim camera, if still in default position (not user modified)
      if (camera.position.x === 0 && camera.position.y === 0 && camera.position.z === state.lastSetCameraZ && state.graphData.nodes.length) {
        camera.lookAt(state.forceGraph.position);
        state.lastSetCameraZ = camera.position.z = Math.cbrt(state.graphData.nodes.length) * CAMERA_DISTANCE2NODES_FACTOR;
      }

      // Setup node drag interaction
      if (state.enableNodeDrag && state.enablePointerInteraction && state.forceEngine === 'd3') { // Can't access node positions programatically in ngraph
        const dragControls = new ThreeDragControls(
          state.graphData.nodes.map(node => node.__threeObj),
          camera,
          renderer.domElement
        );

        dragControls.addEventListener('dragstart', function (event) {
          controls.enabled = false; // Disable controls while dragging

          const node = event.object.__data;
          node.__initialFixedPos = {fx: node.fx, fy: node.fy, fz: node.fz};

          // lock node
          ['x', 'y', 'z'].forEach(c => node[`f${c}`] = node[c]);

          // keep engine running at low intensity throughout drag
          state.forceGraph.d3AlphaTarget(0.3);

          // drag cursor
          renderer.domElement.classList.add('grabbable');
        });

        dragControls.addEventListener('drag', function (event) {
          state.ignoreOneClick = true; // Don't click the node if it's being dragged

          const node = event.object.__data;

          // Move fx/fy/fz (and x/y/z) of nodes based on object new position
          ['x', 'y', 'z'].forEach(c => node[`f${c}`] = node[c] = event.object.position[c]);

          // prevent freeze while dragging
          state.forceGraph.resetCountdown();

          state.onNodeDrag(node);
        });

        dragControls.addEventListener('dragend', function (event) {
          const node = event.object.__data;
          const initPos = node.__initialFixedPos;

          if (initPos) {
            ['x', 'y', 'z'].forEach(c => {
              const fc = `f${c}`;
              if (initPos[fc] === undefined) {
                node[fc] = undefined
              }
            });
            delete(node.__initialFixedPos);

            state.onNodeDragEnd(node);
          }

          state.forceGraph
            .d3AlphaTarget(0)   // release engine low intensity
            .resetCountdown();  // let the engine readjust after releasing fixed nodes

          if (state.enableNavigationControls) {
            controls.enabled = true; // Re-enable controls
          }

          // clear cursor
          renderer.domElement.classList.remove('grabbable');
        });
      }
    });

    // config renderObjs
    const getGraphObj = object => {
      let obj = object;
      // recurse up object chain until finding the graph object
      while (obj && !obj.hasOwnProperty('__graphObjType')) {
        obj = obj.parent;
      }
      return obj;
    };

    state.renderObjs
      .objects([ // Populate scene
        new three.AmbientLight(0xbbbbbb),
        new three.DirectionalLight(0xffffff, 0.6),
        state.forceGraph
      ])
      .hoverOrderComparator((a, b) => {
        // Prioritize graph objects
        const aObj = getGraphObj(a);
        if (!aObj) return 1;
        const bObj = getGraphObj(b);
        if (!bObj) return -1;

        // Prioritize nodes over links
        const isNode = o => o.__graphObjType === 'node';
        return isNode(bObj) - isNode(aObj);
      })
      .tooltipContent(obj => {
        const graphObj = getGraphObj(obj);
        return graphObj ? accessorFn(state[`${graphObj.__graphObjType}Label`])(graphObj.__data) || '' : '';
      })
      .onHover(obj => {
        // Update tooltip and trigger onHover events
        const hoverObj = getGraphObj(obj);

        if (hoverObj !== state.hoverObj) {
          const prevObjType = state.hoverObj ? state.hoverObj.__graphObjType : null;
          const prevObjData = state.hoverObj ? state.hoverObj.__data : null;
          const objType = hoverObj ? hoverObj.__graphObjType : null;
          const objData = hoverObj ? hoverObj.__data : null;
          if (prevObjType && prevObjType !== objType) {
            // Hover out
            state[`on${prevObjType === 'node' ? 'Node' : 'Link'}Hover`](null, prevObjData);
          }
          if (objType) {
            // Hover in
            state[`on${objType === 'node' ? 'Node' : 'Link'}Hover`](objData, prevObjType === objType ? prevObjData : null);
          }

          state.hoverObj = hoverObj;
        }
      })
      .onClick(obj => {
        // Handle click events on objects
        if (state.ignoreOneClick) {
          // f.e. because of dragend event
          state.ignoreOneClick = false;
          return;
        }

        const graphObj = getGraphObj(obj);
        if (graphObj) {
          state[`on${graphObj.__graphObjType === 'node' ? 'Node' : 'Link'}Click`](graphObj.__data);
        }
      })
      .onRightClick(obj => {
        // Handle right-click events
        const graphObj = getGraphObj(obj);
        if (graphObj) {
          state[`on${graphObj.__graphObjType === 'node' ? 'Node' : 'Link'}RightClick`](graphObj.__data);
        }
      });

    //

    // Kick-off renderer
    this._animationCycle();
  }
});<|endoftext|>