<commit_before>define( [
    "require",
    "dojo/_base/declare",
    "dojo/_base/lang",
    "dojo/promise/all",
    "dojo/topic",
    "dojo/Deferred",
    "dojo/dom-class",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "../../_include/_NotificationMixin",
    "../../_include/widget/GridWidget",
    "../../_include/widget/Button",
    "./ExportWidget",
    "./ImportWidget",
    "../../../action/CheckPermissions",
    "../../../model/meta/Model",
    "../../../persistence/RelationStore",
    "../../../action/Edit",
    "../../../action/Copy",
    "../../../action/Link",
    "../../../action/Unlink",
    "../../../action/Delete",
    "../../../action/CreateInRelation",
    "../../../action/Permissions",
    "../../../locale/Dictionary",
    "dojo/text!./template/EntityRelationWidget.html"
],
function(
    require,
    declare,
    lang,
    all,
    topic,
    Deferred,
    domClass,
    _WidgetBase,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    _NotificationMixin,
    GridWidget,
    Button,
    Export,
    Import,
    CheckPermissions,
    Model,
    RelationStore,
    Edit,
    Copy,
    Link,
    Unlink,
    Delete,
    CreateInRelation,
    Permissions,
    Dict,
    template
) {
    return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _NotificationMixin], {

        templateString: lang.replace(template, Dict.tplTranslate),
        contextRequire: require,

        route: '',
        entity: {},
        relation: {},
        type: null,
        typeClass: null,
        page: null,
        gridWidget: null,

        constructor: function(args) {
            declare.safeMixin(this, args);

            // labels
            var relationName = this.relation.name +
                    (parseInt(this.relation.maxMultiplicity) !== 1 ? " [Pl.]" : '');
            this.relationName = Dict.translate(relationName);

            this.type = Model.getFullyQualifiedTypeName(this.relation.type);
            this.typeClass = Model.getType(this.type);
        },

        postCreate: function() {
            this.inherited(arguments);

            var deferredList = [];

            // check permissions
            var oid = this.entity.get('oid');
            var requiredPermissions = [
                oid+'.'+this.relation.name+'??update',
                this.type+'??create',
                this.type+'??associate',
                this.type+'??copy',
                this.type+'??delete',
                '??setPermissions',
                this.type+'??exportCSV',
                this.type+'??importCSV'
            ];
            deferredList.push(new CheckPermissions({
                operations: requiredPermissions
            }).execute());

            all(deferredList).then(lang.hitch(this, function(results) {
                this.permissions = results[0].result ? results[0].result : {};

                var enabledFeatures = [];
                if (this.relation.isSortable) {
                    enabledFeatures.push('DnD');
                }

                this.gridWidget = new GridWidget({
                    type: this.relation.type,
                    store: this.getGridStore(),
                    columns: Model.getType(this.type).getAttributes({exclude: ['DATATYPE_IGNORE']}).map(function(attribute) {
                        return attribute.name;
                    }),
                    actions: this.getGridActions(),
                    enabledFeatures: enabledFeatures,
                    autoHeight: true
                }, this.gridNode);
                this.gridWidget.startup();
                domClass.add(this.gridWidget.gridNode, "multiplicity-"+this.relation.maxMultiplicity);
                domClass.add(this.gridWidget.gridNode, "relation-"+this.relation.thisEndName+"-"+this.relation.name);
                this.setDefaultButtonStates();
            }));

            this.own(
                topic.subscribe('ui/_include/widget/GridWidget/dnd-start', lang.hitch(this, function(error) {
                    this.showNotification({
                        type: "process",
                        message: Dict.translate("Saving data")
                    });
                })),
                topic.subscribe('ui/_include/widget/GridWidget/dnd-end', lang.hitch(this, function(error) {
                    this.showNotification({
                        type: "ok",
                        message: Dict.translate("Finished"),
                        fadeOut: true
                    });
                })),
                topic.subscribe('ui/_include/widget/GridWidget/refresh-complete', lang.hitch(this, function(grid) {
                    if (this.gridWidget && grid == this.gridWidget.grid) {
                        if (this.gridWidget.grid && this.gridWidget.grid.get('total') >= this.relation.maxMultiplicity) {
                            this.createBtn.set("disabled", true);
                            this.linkBtn.set("disabled", true);
                        }
                        else {
                            this.setDefaultButtonStates();
                        }
                    }
                }))
            );
        },

        getGridActions: function() {
            var actions = [];

            var editAction = new Edit({
                page: this.page,
                route: this.route
            });
            actions.push(editAction);

            var oid = this.entity.get('oid');

            if (this.permissions[this.type+'??copy'] === true && this.permissions[this.type+'??create'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                var copyAction = new Copy({
                    targetoid: this.entity.get('oid'),
                    init: lang.hitch(this, function() {
                        this.showNotification({
                            type: "process",
                            message: Dict.translate("Copying %0%", [this.typeClass.getDisplayValue(copyAction.entity)])
                        });
                    }),
                    callback: lang.hitch(this, function(response) {
                        // success
                        this.gridWidget.refresh();
                        this.showNotification({
                            type: "ok",
                            message: Dict.translate("%0% was successfully copied", [this.typeClass.getDisplayValue(response)]),
                            fadeOut: true
                        });
                    }),
                    errback: lang.hitch(this, function(error) {
                        // error
                        this.showBackendError(error);
                    })
                });
                actions.push(copyAction);
            }

            if (this.relation.aggregationKind === "composite") {
                if (this.permissions[this.type+'??delete'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                    var deleteAction = new Delete({
                        callback: lang.hitch(this, function(response) {
                            // success
                            this.gridWidget.refresh();
                            this.showNotification({
                                type: "ok",
                                message: Dict.translate("%0% was successfully deleted", [this.typeClass.getDisplayValue(response)]),
                                fadeOut: true
                            });
                        }),
                        errback: lang.hitch(this, function(error) {
                            // error
                            this.showBackendError(error);
                        })
                    });
                    actions.push(deleteAction);
                }
            }
            else {
                if (this.permissions[this.type+'??disassociate'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                    var unlinkAction = new Unlink({
                        source: this.entity,
                        relation: this.relation,
                        callback: lang.hitch(this, function(response) {
                            // success
                            this.gridWidget.refresh();
                            this.showNotification({
                                type: "ok",
                                message: Dict.translate("%0% was successfully unlinked", [this.typeClass.getDisplayValue(response)]),
                                fadeOut: true
                            });
                        }),
                        errback: lang.hitch(this, function(error) {
                            // error
                            this.showBackendError(error);
                        })
                    });
                    actions.push(unlinkAction);
                }
            }

            if (this.permissions['??setPermissions'] === true) {
                var permissionsAction = new Permissions();
                actions.push(permissionsAction);
            }

            return actions;
        },

        getGridStore: function() {
            return RelationStore.getStore(this.entity.get('oid'), this.relation.name);
        },

        setBtnState: function(btnName, isEnabled) {
            var btn = this[btnName+"Btn"];
            if (btn) {
                btn.set("disabled", !isEnabled);
            }
        },

        setDefaultButtonStates: function() {
            var oid = this.entity.get('oid');
            var type = Model.getTypeFromOid(oid);

            this.setBtnState("create", this.relation.aggregationKind !== "none" && this.permissions[this.type+'??create'] === true &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true);
            this.setBtnState("link", this.relation.aggregationKind !== "composite" && this.permissions[this.type+'??associate'] === true &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true);

            var canImport = this.relation.aggregationKind === "composite" && !type.isManyToManyRelation(this.relation.name) &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true && this.permissions[this.type+'??importCSV'] === true;
            if (!canImport) {
                domClass.add(this.importBtn.domNode, "hidden");
            }

            var canExport = this.permissions[this.type+'??exportCSV'] === true;
            if (!canExport) {
                domClass.add(this.exportBtn.domNode, "hidden");
            }
        },

        _import: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new Import({
                type: this.type
            }).execute().then(
                lang.hitch(this, function(result) {
                    this.gridWidget.refresh();
                    this.importBtn.reset();
                }),
                lang.hitch(this, function(error) {
                    this.showBackendError(error);
                    this.importBtn.reset();
                }),
                lang.hitch(this, function(status) {
                    this.importBtn.setProcessing();
                    var progress = status.stepNumber/status.numberOfSteps;
                    this.importBtn.setProgress(progress);
                })
            );
        },

        _export: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            // get filter query
            var gridFilter = this.gridWidget ? this.gridWidget.getFilter() : undefined;
            var query = gridFilter ? this.getGridStore()._renderFilterParams(gridFilter)[0] : null;
            var oid = this.entity.get('oid');

            new Export({
                type: Model.getTypeNameFromOid(oid),
                query: query,
                parentId: Model.getIdFromOid(oid),
                relation: this.relation.name
            }).execute();
        },

        _create: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new CreateInRelation({
                page: this.page,
                route: this.route,
                source: this.entity,
                relation: this.relation,
                init: lang.hitch(this, function() {
                    this.hideNotification();
                })
            }).execute();
        },

        _link: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new Link({
                source: this.entity,
                relation: this.relation,
                init: lang.hitch(this, function() {
                    this.hideNotification();
                }),
                beforeCallback: lang.hitch(this, function(oids) {
                    this.showNotification({
                        type: "process",
                        message: Dict.translate("Linking objects")
                    });
                })
            }).execute().then(lang.hitch(this, function(response) {
                var message = Dict.translate("Objects were successfully linked");
                this.showNotification({
                    type: "ok",
                    message: message,
                    fadeOut: true
                });
                // success
                this.gridWidget.refresh();
            }), lang.hitch(this, function(error) {
                // error
                this.showBackendError(error);
                this.gridWidget.refresh();
            }));
        }
    });
});<commit_msg>Fixed disassociate btn in relations<commit_after>define( [
    "require",
    "dojo/_base/declare",
    "dojo/_base/lang",
    "dojo/promise/all",
    "dojo/topic",
    "dojo/Deferred",
    "dojo/dom-class",
    "dijit/_WidgetBase",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "../../_include/_NotificationMixin",
    "../../_include/widget/GridWidget",
    "../../_include/widget/Button",
    "./ExportWidget",
    "./ImportWidget",
    "../../../action/CheckPermissions",
    "../../../model/meta/Model",
    "../../../persistence/RelationStore",
    "../../../action/Edit",
    "../../../action/Copy",
    "../../../action/Link",
    "../../../action/Unlink",
    "../../../action/Delete",
    "../../../action/CreateInRelation",
    "../../../action/Permissions",
    "../../../locale/Dictionary",
    "dojo/text!./template/EntityRelationWidget.html"
],
function(
    require,
    declare,
    lang,
    all,
    topic,
    Deferred,
    domClass,
    _WidgetBase,
    _TemplatedMixin,
    _WidgetsInTemplateMixin,
    _NotificationMixin,
    GridWidget,
    Button,
    Export,
    Import,
    CheckPermissions,
    Model,
    RelationStore,
    Edit,
    Copy,
    Link,
    Unlink,
    Delete,
    CreateInRelation,
    Permissions,
    Dict,
    template
) {
    return declare([_WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, _NotificationMixin], {

        templateString: lang.replace(template, Dict.tplTranslate),
        contextRequire: require,

        route: '',
        entity: {},
        relation: {},
        type: null,
        typeClass: null,
        page: null,
        gridWidget: null,

        constructor: function(args) {
            declare.safeMixin(this, args);

            // labels
            var relationName = this.relation.name +
                    (parseInt(this.relation.maxMultiplicity) !== 1 ? " [Pl.]" : '');
            this.relationName = Dict.translate(relationName);

            this.type = Model.getFullyQualifiedTypeName(this.relation.type);
            this.typeClass = Model.getType(this.type);
        },

        postCreate: function() {
            this.inherited(arguments);

            var deferredList = [];

            // check permissions
            var oid = this.entity.get('oid');
            var requiredPermissions = [
                oid+'.'+this.relation.name+'??update',
                this.type+'??create',
                this.type+'??associate',
                this.type+'??disassociate',
                this.type+'??copy',
                this.type+'??delete',
                '??setPermissions',
                this.type+'??exportCSV',
                this.type+'??importCSV'
            ];
            deferredList.push(new CheckPermissions({
                operations: requiredPermissions
            }).execute());

            all(deferredList).then(lang.hitch(this, function(results) {
                this.permissions = results[0].result ? results[0].result : {};

                var enabledFeatures = [];
                if (this.relation.isSortable) {
                    enabledFeatures.push('DnD');
                }

                this.gridWidget = new GridWidget({
                    type: this.relation.type,
                    store: this.getGridStore(),
                    columns: Model.getType(this.type).getAttributes({exclude: ['DATATYPE_IGNORE']}).map(function(attribute) {
                        return attribute.name;
                    }),
                    actions: this.getGridActions(),
                    enabledFeatures: enabledFeatures,
                    autoHeight: true
                }, this.gridNode);
                this.gridWidget.startup();
                domClass.add(this.gridWidget.gridNode, "multiplicity-"+this.relation.maxMultiplicity);
                domClass.add(this.gridWidget.gridNode, "relation-"+this.relation.thisEndName+"-"+this.relation.name);
                this.setDefaultButtonStates();
            }));

            this.own(
                topic.subscribe('ui/_include/widget/GridWidget/dnd-start', lang.hitch(this, function(error) {
                    this.showNotification({
                        type: "process",
                        message: Dict.translate("Saving data")
                    });
                })),
                topic.subscribe('ui/_include/widget/GridWidget/dnd-end', lang.hitch(this, function(error) {
                    this.showNotification({
                        type: "ok",
                        message: Dict.translate("Finished"),
                        fadeOut: true
                    });
                })),
                topic.subscribe('ui/_include/widget/GridWidget/refresh-complete', lang.hitch(this, function(grid) {
                    if (this.gridWidget && grid == this.gridWidget.grid) {
                        if (this.gridWidget.grid && this.gridWidget.grid.get('total') >= this.relation.maxMultiplicity) {
                            this.createBtn.set("disabled", true);
                            this.linkBtn.set("disabled", true);
                        }
                        else {
                            this.setDefaultButtonStates();
                        }
                    }
                }))
            );
        },

        getGridActions: function() {
            var actions = [];

            var editAction = new Edit({
                page: this.page,
                route: this.route
            });
            actions.push(editAction);

            var oid = this.entity.get('oid');

            if (this.permissions[this.type+'??copy'] === true && this.permissions[this.type+'??create'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                var copyAction = new Copy({
                    targetoid: this.entity.get('oid'),
                    init: lang.hitch(this, function() {
                        this.showNotification({
                            type: "process",
                            message: Dict.translate("Copying %0%", [this.typeClass.getDisplayValue(copyAction.entity)])
                        });
                    }),
                    callback: lang.hitch(this, function(response) {
                        // success
                        this.gridWidget.refresh();
                        this.showNotification({
                            type: "ok",
                            message: Dict.translate("%0% was successfully copied", [this.typeClass.getDisplayValue(response)]),
                            fadeOut: true
                        });
                    }),
                    errback: lang.hitch(this, function(error) {
                        // error
                        this.showBackendError(error);
                    })
                });
                actions.push(copyAction);
            }

            if (this.relation.aggregationKind === "composite") {
                if (this.permissions[this.type+'??delete'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                    var deleteAction = new Delete({
                        callback: lang.hitch(this, function(response) {
                            // success
                            this.gridWidget.refresh();
                            this.showNotification({
                                type: "ok",
                                message: Dict.translate("%0% was successfully deleted", [this.typeClass.getDisplayValue(response)]),
                                fadeOut: true
                            });
                        }),
                        errback: lang.hitch(this, function(error) {
                            // error
                            this.showBackendError(error);
                        })
                    });
                    actions.push(deleteAction);
                }
            }
            else {
                if (this.permissions[this.type+'??disassociate'] === true && this.permissions[oid+'.'+this.relation.name+'??update'] === true) {
                    var unlinkAction = new Unlink({
                        source: this.entity,
                        relation: this.relation,
                        callback: lang.hitch(this, function(response) {
                            // success
                            this.gridWidget.refresh();
                            this.showNotification({
                                type: "ok",
                                message: Dict.translate("%0% was successfully unlinked", [this.typeClass.getDisplayValue(response)]),
                                fadeOut: true
                            });
                        }),
                        errback: lang.hitch(this, function(error) {
                            // error
                            this.showBackendError(error);
                        })
                    });
                    actions.push(unlinkAction);
                }
            }

            if (this.permissions['??setPermissions'] === true) {
                var permissionsAction = new Permissions();
                actions.push(permissionsAction);
            }

            return actions;
        },

        getGridStore: function() {
            return RelationStore.getStore(this.entity.get('oid'), this.relation.name);
        },

        setBtnState: function(btnName, isEnabled) {
            var btn = this[btnName+"Btn"];
            if (btn) {
                btn.set("disabled", !isEnabled);
            }
        },

        setDefaultButtonStates: function() {
            var oid = this.entity.get('oid');
            var type = Model.getTypeFromOid(oid);

            this.setBtnState("create", this.relation.aggregationKind !== "none" && this.permissions[this.type+'??create'] === true &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true);
            this.setBtnState("link", this.relation.aggregationKind !== "composite" && this.permissions[this.type+'??associate'] === true &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true);

            var canImport = this.relation.aggregationKind === "composite" && !type.isManyToManyRelation(this.relation.name) &&
                this.permissions[oid+'.'+this.relation.name+'??update'] === true && this.permissions[this.type+'??importCSV'] === true;
            if (!canImport) {
                domClass.add(this.importBtn.domNode, "hidden");
            }

            var canExport = this.permissions[this.type+'??exportCSV'] === true;
            if (!canExport) {
                domClass.add(this.exportBtn.domNode, "hidden");
            }
        },

        _import: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new Import({
                type: this.type
            }).execute().then(
                lang.hitch(this, function(result) {
                    this.gridWidget.refresh();
                    this.importBtn.reset();
                }),
                lang.hitch(this, function(error) {
                    this.showBackendError(error);
                    this.importBtn.reset();
                }),
                lang.hitch(this, function(status) {
                    this.importBtn.setProcessing();
                    var progress = status.stepNumber/status.numberOfSteps;
                    this.importBtn.setProgress(progress);
                })
            );
        },

        _export: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            // get filter query
            var gridFilter = this.gridWidget ? this.gridWidget.getFilter() : undefined;
            var query = gridFilter ? this.getGridStore()._renderFilterParams(gridFilter)[0] : null;
            var oid = this.entity.get('oid');

            new Export({
                type: Model.getTypeNameFromOid(oid),
                query: query,
                parentId: Model.getIdFromOid(oid),
                relation: this.relation.name
            }).execute();
        },

        _create: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new CreateInRelation({
                page: this.page,
                route: this.route,
                source: this.entity,
                relation: this.relation,
                init: lang.hitch(this, function() {
                    this.hideNotification();
                })
            }).execute();
        },

        _link: function(e) {
            // prevent the page from navigating after submit
            e.preventDefault();

            new Link({
                source: this.entity,
                relation: this.relation,
                init: lang.hitch(this, function() {
                    this.hideNotification();
                }),
                beforeCallback: lang.hitch(this, function(oids) {
                    this.showNotification({
                        type: "process",
                        message: Dict.translate("Linking objects")
                    });
                })
            }).execute().then(lang.hitch(this, function(response) {
                var message = Dict.translate("Objects were successfully linked");
                this.showNotification({
                    type: "ok",
                    message: message,
                    fadeOut: true
                });
                // success
                this.gridWidget.refresh();
            }), lang.hitch(this, function(error) {
                // error
                this.showBackendError(error);
                this.gridWidget.refresh();
            }));
        }
    });
});<|endoftext|>


<commit_before>function getTotalPrice() {
    var price = window.tripStatus.reduce(function (prev, curr) {
        return prev + curr.price * curr.nights;
    }, 0);

    return price;
}

function postAjaxForPriceSave() {
    var price = getTotalPrice();
    var date = $('#startDate input').val();

    $.ajax({
        type: "POST",
        url: 'http://tripvago.ga/kartoffel/api/price?start_date=' + date + '&current_price=' + price,
        data: JSON.stringify(window.tripStatus),
        success: successPriceSave,
        dataType: 'json'
    });
}

function successPriceSave(retText) {
    $('#savePriceText').html(retText);
}

$('body').on('kartoffel:data:loaded', function () {
    postAjaxForPriceSave();
});
<commit_msg>add close event for price suggest list<commit_after>function getTotalPrice() {
    var price = window.tripStatus.reduce(function (prev, curr) {
        return prev + curr.price * curr.nights;
    }, 0);

    return price;
}

function postAjaxForPriceSave() {
    var price = getTotalPrice();
    var date = $('#startDate input').val();

    $.ajax({
        type: "POST",
        url: 'http://tripvago.ga/kartoffel/api/price?start_date=' + date + '&current_price=' + price,
        data: JSON.stringify(window.tripStatus),
        success: successPriceSave,
        dataType: 'json'
    });
}

function successPriceSave(retText) {
    var element = $('#savePriceText');
    element.html(retText);

    element.closest("price-suggest").addClass("active");
}

$('body').on('kartoffel:data:loaded', function () {
    postAjaxForPriceSave();
});

$('.price-suggest a.close').click(function() {
    $(this).closest("price-suggest").removeClass("active");
});
<|endoftext|>


<commit_before>// Widgets.Temperature
// -------------------

Widgets.Temperature = Widgets.Device.extend({

    defaults: {
        kind: 'temperature',
        buffer: {
            pairing: true,
            shadowing: true
        }
    },

    onInitUI: function () {
        Widgets.Device.prototype.onInitUI.apply(this, arguments);

        this._$aside.css({
            'line-height': parseInt(this.computed('svg.height')) + "px"
        });
    },

    onBeforeInitD3: function () {
        Widgets.Device.prototype.onBeforeInitD3.apply(this, arguments);

        // Setup d3 functions
        this.valueFn = function (d) {
            try {
                if (d.timestamp) {
                    return parseInt(d.data.event.state.value);
                } else {
                    return parseInt(d.event.state.value);
                }
            } catch (e) {
                return 0;
            }
        };
    },

    onInitD3: function () {
        Widgets.Device.prototype.onInitD3.apply(this, arguments);

        this.y = d3.scale.linear()
            .range([0, this.computed('svg.height') - this.options.theme.device.state.border.width]);

        this.initD3Chart();
    },

    onDestroyD3: function() {
        Widgets.Device.prototype.onDestroyD3.apply(this, arguments);

        delete this.y;
        this.destroyD3Chart();
    },

    onRender: function () {
        Widgets.Device.prototype.onRender.apply(this, arguments);

        var self = this;

        this.y.domain(d3.extent(
            self.buffer.select(function (d) {
                return ensure(d.data, 'event') && d.data.event.type == 'update'}
            ), self.valueFn)
        );

        this.renderD3Chart();
    },

    onRulerFocusUpdate: function (coordinate, direction, timestamp, focusedFrame, lastFocusedFrame) {
        Widgets.Device.prototype.onRulerFocusUpdate.apply(this, arguments);

        if (ensure(focusedFrame, 'data.event.type', 'update') && ensure(focusedFrame, 'data.event.picto')) {
            this._$picto.attr({class: 'picto'}).text(this.valueFn(focusedFrame.data)+'°');
        } else {
            // fallback
            this._$picto.attr({class: 'picto picto-temperature_type'}).text('');
        }

        this.updateD3ChartFocus(focusedFrame, lastFocusedFrame);
    }
});

_.extend(Widgets.Temperature.prototype, Widgets.Mixins.Chart, Widgets.Mixins.Focus);
<commit_msg>use float instead of int for temperature, fixes #44<commit_after>// Widgets.Temperature
// -------------------

Widgets.Temperature = Widgets.Device.extend({

    defaults: {
        kind: 'temperature',
        buffer: {
            pairing: true,
            shadowing: true
        }
    },

    onInitUI: function () {
        Widgets.Device.prototype.onInitUI.apply(this, arguments);

        this._$aside.css({
            'line-height': parseInt(this.computed('svg.height')) + "px"
        });
    },

    onBeforeInitD3: function () {
        Widgets.Device.prototype.onBeforeInitD3.apply(this, arguments);

        // Setup d3 functions
        this.valueFn = function (d) {
            try {
                if (d.timestamp) {
                    return parseFloat(d.data.event.state.value);
                } else {
                    return parseFloat(d.event.state.value);
                }
            } catch (e) {
                return 0.0;
            }
        };
    },

    onInitD3: function () {
        Widgets.Device.prototype.onInitD3.apply(this, arguments);

        this.y = d3.scale.linear()
            .range([0, this.computed('svg.height') - this.options.theme.device.state.border.width]);

        this.initD3Chart();
    },

    onDestroyD3: function() {
        Widgets.Device.prototype.onDestroyD3.apply(this, arguments);

        delete this.y;
        this.destroyD3Chart();
    },

    onRender: function () {
        Widgets.Device.prototype.onRender.apply(this, arguments);

        var self = this;

        this.y.domain(d3.extent(
            self.buffer.select(function (d) {
                return ensure(d.data, 'event') && d.data.event.type == 'update'}
            ), self.valueFn)
        );

        this.renderD3Chart();
    },

    onRulerFocusUpdate: function (coordinate, direction, timestamp, focusedFrame, lastFocusedFrame) {
        Widgets.Device.prototype.onRulerFocusUpdate.apply(this, arguments);

        if (ensure(focusedFrame, 'data.event.type', 'update') && ensure(focusedFrame, 'data.event.picto')) {
            this._$picto.attr({class: 'picto'}).text(parseInt(this.valueFn(focusedFrame.data))+'°');
        } else {
            // fallback
            this._$picto.attr({class: 'picto picto-temperature_type'}).text('');
        }

        this.updateD3ChartFocus(focusedFrame, lastFocusedFrame);
    }
});

_.extend(Widgets.Temperature.prototype, Widgets.Mixins.Chart, Widgets.Mixins.Focus);
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';

// should be stateless Component
export default class Tile extends Component {
  constructor(props) {
    super(props);
  }

  render() {
    var letterClass, tileClass;
    if (this.props.wordId) {
      letterClass = 'letter red';
    } else {
      letterClass = 'letter';
    }
    if (this.props.gridId) {
      tileClass = 'tile';
    } else {
      tileClass = 'tile white bg-black';
    }
    return (

{this.props.wordId}

{this.props.gridId}

{this.props.char}

    );
  }
}
Tile.propTypes = {
};
<commit_msg>reduce tile to stateless<commit_after>import React from 'react';
const Tile = ({wordId,gridId,char}) => {
    var letterClass, tileClass;
    if (wordId) {
      letterClass = 'letter red';
    } else {
      letterClass = 'letter';
    }
    if (gridId) {
      tileClass = 'tile';
    } else {
      tileClass = 'tile white bg-black';
    }
    return (

{wordId}

{gridId}

{char}

    );
}

export default Tile
<|endoftext|>


<commit_before>import React from "react";
import ReactDOM from "react-dom";

import loaded from "./loaded";

import App from "./App";

const error = message => {
  if (console && console.error) {
    console.error(message);
  }
};

function startInterface() {
  const config = window._sjui.config;
  if (!config) {
    error(
      'global value "_sjui" not found, please check the code snippet for your Sajari search interface'
    );
    return;
  }
  if (!config.project) {
    error("project value in config not set, must be set");
  }
  if (!config.collection) {
    error("collection value in config not set, must be set");
  }
  if (!config.pipeline) {
    error("pipeline value in config not set, must be set");
  }

  let renderTarget = null;
  if (config.overlay) {
    // Create a container to render the overlay into
    const overlayContainer = document.createElement("div");
    overlayContainer.id = "sj-overlay-holder";
    document.body.appendChild(overlayContainer);

    renderTarget = overlayContainer;
  } else if (config.attachSearchBox && config.attachSearchResponse) {
    renderTarget = config.attachSearchBox;
  } else {
    error(
      "no render mode found, need to specify either overlay or attachSearchBox and attachSearchResponse in config"
    );
    return;
  }

  ReactDOM.render(<App config={config} />, renderTarget);
}

loaded(window, startInterface);
<commit_msg>site-search-overlay: exit on bad config<commit_after>import React from "react";
import ReactDOM from "react-dom";

import loaded from "./loaded";

import App from "./App";

const error = message => {
  if (console && console.error) {
    console.error(message);
  }
};

function startInterface() {
  const config = window._sjui.config;
  if (!config) {
    error(
      'global value "_sjui" not found, please check the code snippet for your Sajari search interface'
    );
    return;
  }
  if (!config.project) {
    error("project value in config not set, must be set");
    return;
  }
  if (!config.collection) {
    error("collection value in config not set, must be set");
    return;
  }
  if (!config.pipeline) {
    error("pipeline value in config not set, must be set");
    return;
  }

  let renderTarget = null;
  if (config.overlay) {
    // Create a container to render the overlay into
    const overlayContainer = document.createElement("div");
    overlayContainer.id = "sj-overlay-holder";
    document.body.appendChild(overlayContainer);

    renderTarget = overlayContainer;
  } else if (config.attachSearchBox && config.attachSearchResponse) {
    renderTarget = config.attachSearchBox;
  } else {
    error(
      "no render mode found, need to specify either overlay or attachSearchBox and attachSearchResponse in config"
    );
    return;
  }

  ReactDOM.render(<App config={config} />, renderTarget);
}

loaded(window, startInterface);
<|endoftext|>


<commit_before>'use strict';
const l10nFile = __dirname + '/../l10n/commands/drop.yml';
const l10n = require('../src/l10n')(l10nFile);
const CommandUtil = require('../src/command_util').CommandUtil;
const util = require('util');

exports.command = (rooms, items, players, npcs, Commands) => {
  return (args, player, isDead) => {
    const room = rooms.getAt(player.getLocation());

    args = args.toLowerCase();

    if (args === 'all') {
      dropAll();
      return;
    }

    let item = CommandUtil.findItemInInventory(args, player, true);

    if (!item) {
      return player.sayL10n(l10n, 'ITEM_NOT_FOUND');
    }

    if (item.isEquipped()) {
      item = CommandUtil.findItemInInventory('2.' + args, player, true) || item;
      if (item.isEquipped()) {
        return player.sayL10n(l10n, 'ITEM_WORN');
      }
    }

    drop(item);

    function dropAll() {
      const items = player.getInventory()
        .filter(item => !item.isEquipped());
      if (!items.length && !isDead) { return player.say('You have nothing to drop.'); }
      items.forEach(item => drop(item));
    }

    function drop(item) {
      let playerName = player.getName();

      if (item.isEquipped()) {
        return player.sayL10n(l10n, 'ITEM_WORN');
      }

      players.eachIf(
        p => CommandUtil.inSameRoom(p, player),
        p => p.sayL10n(l10n, 'OTHER_DROPS', playerName, item.getShortDesc(p.getLocale()))
      );

      let itemName = item.getShortDesc('en');
      if (!isDead) { player.sayL10n(l10n, 'ITEM_DROP', itemName, false); }
      util.log(playerName + " drops " + itemName + " at " + room.getLocation() + ".");

      room.getNpcs().forEach( id => {
        let npc = npcs.get(id);
        npc.emit('playerDropItem', room, rooms, player, players, npc, npcs, item);
      });

      player.removeItem(item);
      room.addItem(item.getUuid());
      item.setInventory(null);
      item.setRoom(room.getLocation());
    }
  };
};
<commit_msg>Only emit if the player is not dropping items due to death.<commit_after>'use strict';
const l10nFile = __dirname + '/../l10n/commands/drop.yml';
const l10n = require('../src/l10n')(l10nFile);
const CommandUtil = require('../src/command_util').CommandUtil;
const util = require('util');

exports.command = (rooms, items, players, npcs, Commands) => {
  return (args, player, isDead) => {
    const room = rooms.getAt(player.getLocation());

    args = args.toLowerCase();

    if (args === 'all') {
      dropAll();
      return;
    }

    let item = CommandUtil.findItemInInventory(args, player, true);

    if (!item) {
      return player.sayL10n(l10n, 'ITEM_NOT_FOUND');
    }

    if (item.isEquipped()) {
      item = CommandUtil.findItemInInventory('2.' + args, player, true) || item;
      if (item.isEquipped()) {
        return player.sayL10n(l10n, 'ITEM_WORN');
      }
    }

    drop(item);

    function dropAll() {
      const items = player.getInventory()
        .filter(item => !item.isEquipped());
      if (!items.length && !isDead) { return player.say('You have nothing to drop.'); }
      items.forEach(item => drop(item));
    }

    function drop(item) {
      let playerName = player.getName();

      if (item.isEquipped()) {
        return player.sayL10n(l10n, 'ITEM_WORN');
      }

      players.eachIf(
        p => CommandUtil.inSameRoom(p, player),
        p => p.sayL10n(l10n, 'OTHER_DROPS', playerName, item.getShortDesc(p.getLocale()))
      );

      let itemName = item.getShortDesc('en');
      if (!isDead) {
        player.sayL10n(l10n, 'ITEM_DROP', itemName, false);
        room.getNpcs().forEach( id => {
          let npc = npcs.get(id);
          npc.emit('playerDropItem', room, rooms, player, players, npc, npcs, item);
        });
      }
      util.log(playerName + " drops " + itemName + " at " + room.getLocation() + ".");

      player.removeItem(item);
      room.addItem(item.getUuid());
      item.setInventory(null);
      item.setRoom(room.getLocation());
    }
  };
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

var os = require("os");
var network = require("network");
var yaml = require("yamljs");
var promise = require("deferred");
var watcher = require("watchjs");

var logger = require("./log.js");
var kvstore = require("./kvstore.js");

var ClusterNode = function (config) {
    // load the config file
    this.config = config;
    logger.info("ClusterNode initialised with config", config);
    // open kvstore
    this.store = new kvstore(config);
}

ClusterNode.prototype.updateTargets = function(targets) {
    // write state of each target from our init to kvstore
    for (var target in targets) {
        var key = "/cluster/"+this.config.cluster+"/hoststate/"+this.config.myhostname+"/"+target;
        if (this.store.get(key) != targets[target].toString()) {
            this.store.set(key, targets[target]);
        }
    }
}

ClusterNode.prototype.getAddress = function () {
    var deferred = promise();
    network.get_interfaces_list(function (err, list) {
        list.forEach(function (interface) {
            logger.debug("found interface", interface.name);
            if (interface.name == "ethwe") {
                logger.info("selecting interface", interface);
                deferred.resolve(interface.ip_address);
            }
        });
        network.get_active_interface(function (err, interface) {
            logger.info("selecting interface", interface);
            deferred.resolve(interface.ip_address);
        })
    });
    return deferred.promise();
}

ClusterNode.prototype.waitForDependencies = function () {
    var me = this;
    var deferred = promise();
    logger.info("ClusterNode is waiting for cluster service dependencies");
    var poll_ms = 2000;
    // create a status object for our depends
    var depends = {};
    for (var i = this.config.depends.length - 1; i >= 0; i--) {
        depends[this.config.depends[i]] = false;
    };
    // define function to check the depends object
    var check_depends = function () {
        logger.debug(depends);
        var ready = true;
        for (var depend in depends) {
            if (depends[depend] == false) {
                var value = me.store.get("/cluster/"+me.config.cluster+"/services/"+depend);
                if (value) {
                    logger.debug("found service", depend, "on", value);
                    depends[depend] = true;
                } else {
                    ready = false;
                }
            }
        }
        if (ready) {
            deferred.resolve();
        } else {
            setTimeout(check_depends, poll_ms);
        }
    };
    // poll for status changes
    setTimeout(check_depends, poll_ms);
    return deferred.promise();
}

ClusterNode.prototype.waitForProviders = function (targets) {
    var me = this;
    var deferred = promise();
    logger.info("ClusterNode is waiting for provider services to start");
    watcher.watch(targets, this.config.providers, function () {
        logger.debug("cluster provider targets dependency state changed");
        var ready = true;
        for (var target in me.config.providers) {
            if (targets[target] == false) {
                ready = false;
            }
        }
        if (ready) {
            logger.info("ClusterNode provider targets dependencies satisfied");
            // register our service state
            me.store.register("/cluster/"+me.config.cluster+"/services/"+me.config.service, me.config.myhostname, 60);
            deferred.resolve();
        } else {
            logger.debug("cluster provider targets dependencies are not satisfied");
        }
    });
    return deferred.promise();
}

ClusterNode.prototype.runServiceHooks = function () {

}

module.exports = {
    ClusterNode: function (service, config, targets) {
        var deferred = promise();
        var clusternode = new ClusterNode(config.cluster);
        var store = new kvstore(config.cluster);
        // register our targets and watch for changes
        //clusternode.updateTargets(targets);
        //watcher.watch(targets, function () {
        //    clusternode.updateTargets(targets);
        //});
        // wait for dependencies
        // when dependencies are satisfied, trigger service manager to continue
        if(config.cluster.depends) {
            clusternode.waitForDependencies().then(function () {
                logger.info("ClusterNode cluster service dependencies satisfied");
                deferred.resolve();
            });
        } else {
            logger.debug("there are no cluster service dependencies");
            deferred.resolve();
        }
        // wait for provider targets to fulfil the cluster service we provide to trigger
        if (config.cluster.service) {
            clusternode.waitForProviders(targets).then(function () {
                logger.debug("service is registered:", config.cluster.service);
            });
        }
        // return promise
        return deferred.promise();
    }
};<commit_msg>implement run service hooks (first-run)<commit_after>#!/usr/bin/env node

var os = require("os");
var fs = require("fs");
var network = require("network");
var yaml = require("yamljs");
var promise = require("deferred");
var watcher = require("watchjs");
var child_process = require('child_process');

var logger = require("./log.js");
var kvstore = require("./kvstore.js");

var ClusterNode = function (config) {
    // load the config file
    this.config = config;
    logger.info("ClusterNode initialised with config", config);
    // open kvstore
    this.store = new kvstore(config);
    // our service dependencies
    this.depends = {};
    this.depends_hooks = {};
}

ClusterNode.prototype.updateTargets = function(targets) {
    // write state of each target from our init to kvstore
    for (var target in targets) {
        var key = "/cluster/"+this.config.cluster+"/hoststate/"+this.config.myhostname+"/"+target;
        if (this.store.get(key) != targets[target].toString()) {
            this.store.set(key, targets[target]);
        }
    }
}

ClusterNode.prototype.getAddress = function () {
    var deferred = promise();
    network.get_interfaces_list(function (err, list) {
        list.forEach(function (interface) {
            logger.debug("found interface", interface.name);
            if (interface.name == "ethwe") {
                logger.info("selecting interface", interface);
                deferred.resolve(interface.ip_address);
            }
        });
        network.get_active_interface(function (err, interface) {
            logger.info("selecting interface", interface);
            deferred.resolve(interface.ip_address);
        })
    });
    return deferred.promise();
}

ClusterNode.prototype.waitForDependencies = function () {
    var me = this;
    var deferred = promise();
    logger.info("ClusterNode is waiting for cluster service dependencies");
    var poll_ms = 2000;
    // create a status object for our depends
    this.depends = {};
    for (var i = this.config.depends.length - 1; i >= 0; i--) {
        this.depends[this.config.depends[i]] = false;
        this.depends_hooks[this.config.depends[i]] = false;
    };
    // define function to check the depends object
    var check_depends = function () {
        logger.debug(me.depends);
        var ready = true;
        for (var depend in me.depends) {
            if (me.depends[depend] == false) {
                var value = me.store.get("/cluster/"+me.config.cluster+"/services/"+depend);
                if (value) {
                    logger.debug("found service", depend, "on", value);
                    // save the host providing this service
                    me.depends[depend] = value;
                } else {
                    ready = false;
                }
            }
        }
        if (ready) {
            deferred.resolve();
        } else {
            setTimeout(check_depends, poll_ms);
        }
    };
    // poll for status changes
    setTimeout(check_depends, poll_ms);
    return deferred.promise();
}

ClusterNode.prototype.waitForProviders = function (targets) {
    var me = this;
    var deferred = promise();
    logger.info("ClusterNode is waiting for provider services to start");
    watcher.watch(targets, this.config.providers, function () {
        logger.debug("cluster provider targets dependency state changed");
        var ready = true;
        for (var target in me.config.providers) {
            if (targets[target] == false) {
                ready = false;
            }
        }
        if (ready) {
            logger.info("ClusterNode provider targets dependencies satisfied");
            // register our service state
            me.store.register("/cluster/"+me.config.cluster+"/services/"+me.config.service, me.config.myhostname, 60);
            deferred.resolve();
        } else {
            logger.debug("cluster provider targets dependencies are not satisfied");
        }
    });
    return deferred.promise();
}

ClusterNode.prototype.runServiceHooks = function () {
    var me = this;
    var deferred = promise();
    var hook_dir = "/etc/vcc/service-hooks.d/";
    // define a function to execute each hook
    var run_hook = function (service, host) {
        // check if we have hook for this service
        var script = hook_dir+service+".sh";
        logger.debug("looking for service hook for", service);
        fs.stat(script, function(err, stat) {
            if(err == null) {
                // run the hook
                logger.debug('running service hook for', service, 'with target', host);
                var proc = child_process.spawn("/bin/sh", [script, host]);
                proc.on('exit', function (code, signal) {
                    if (code > 0) {
                        logger.warn("hook", script, "exited with code", code);
                    } else {
                        logger.debug("hook", script, "exited with code", code);
                    }
                    // when the hook is complete, set to true and the watcher will wait for all
                    me.depends_hooks[service] = true;
                });
            } else if(err.code == 'ENOENT') {
                // no hook installed but thats okay
                me.depends_hooks[service] = true;
                logger.warn('no service hook installed for', service);
            } else {
                // something went wrong, should we continue?
                me.depends_hooks[service] = true;
                logger.error('unhandled error hook stat', service);
            }
        });
    }
    // define a function to wait for all hooks to finish execution
    var check_hooks = function () {
        var ready = true;
        for (var service in me.depends_hooks) {
            if (me.depends_hooks[service] == false) {
                ready = false
            }
        }
        if (ready) {
            logger.debug("service hooks are finished");
            deferred.resolve();
        } else {
            logger.debug("service hooks are not finished");
        }
    }
    // register watch for hooks finished
    watcher.watch(this.depends_hooks, check_hooks);
    // for each depends, run the hook
    for (var service in this.depends) {
        run_hook(service, this.depends[service]);
    }
    return deferred.promise();
}

module.exports = {
    ClusterNode: function (service, config, targets) {
        var deferred = promise();
        var clusternode = new ClusterNode(config.cluster);
        var store = new kvstore(config.cluster);
        // register our targets and watch for changes
        //clusternode.updateTargets(targets);
        //watcher.watch(targets, function () {
        //    clusternode.updateTargets(targets);
        //});
        // wait for dependencies
        // when dependencies are satisfied, trigger service manager to continue
        if(config.cluster.depends) {
            clusternode.waitForDependencies().then(function () {
                logger.info("ClusterNode cluster service dependencies satisfied");
                logger.info("Running cluster service hooks (first-run)");
                clusternode.runServiceHooks().then(function () {
                    deferred.resolve();
                });
            });
        } else {
            logger.debug("there are no cluster service dependencies");
            deferred.resolve();
        }
        // wait for provider targets to fulfil the cluster service we provide to trigger
        if (config.cluster.service) {
            clusternode.waitForProviders(targets).then(function () {
                logger.debug("service is registered:", config.cluster.service);
            });
        }
        // return promise
        return deferred.promise();
    }
};<|endoftext|>


<commit_before>// Copyright (c) Jupyter-Contrib Team.
// Distributed under the terms of the Modified BSD License.

define(function(require, exports, module) {
    'use strict';

    var $ = require('jquery');
    var Jupyter = require('base/js/namespace');
    var events = require('base/js/events');
    var utils = require('base/js/utils');
    var ConfigSection = require('services/config').ConfigSection;
    var CodeCell = require('notebook/js/codecell').CodeCell;

    // this wrapper function allows config & hotkeys to be per-plugin
    function KernelExecOnCells(mod_name, cfg) {

        this.mod_name = mod_name;
        this.cfg = cfg;
        this.mod_log_prefix = '[' + this.mod_name + ']';
        this.mod_edit_shortcuts = {};
        this.mod_cmd_shortcuts = {};
        this.default_kernel_config = {
            library: '',
            prefix: '',
            postfix: '',
            replacements_json_to_kernel: [],
            trim_formatted_text: true
        };
        // gives default settings
        var default_cfg = {
            add_toolbar_button: true,
       hotkeys: {
	    process_selected: 'Ctrl-L',
	    process_all: 'Ctrl-Shift-L',
	},
            register_hotkey: true,
            show_alerts_for_errors: true,
            button_icon: 'fa-legal',
            button_label: mod_name,
            kbd_shortcut_text: mod_name,
            kernel_config_map: {},
        actions: null, // to be filled by register_actions
        };
        // apply defaults to cfg. Don't use jquery extend, as we want cfg to still
        // be same object, in case plugin alters it later
        for (var key in default_cfg) {
            if (!this.cfg.hasOwnProperty(key)) {
                this.cfg[key] = default_cfg[key];
            }
        }
        // set default json string, will later be updated from config
        // before it is parsed into an object
        this.cfg.kernel_config_map_json = JSON.stringify(this.cfg.kernel_config_map);

    } // end per-plugin wrapper define_plugin_functions

    // Prototypes
    // ----------

        /**
         * return a Promise which will resolve/reject based on the kernel message
         * type.
         * The returned promise will be
         *   - resolved if the message was not an error
         *   - rejected using the message's error text if msg.msg_type is "error"
         */
        KernelExecOnCells.prototype.convert_error_msg_to_broken_promise = function(msg) {
            var that = this;
            return new Promise(function(resolve, reject) {
                if (msg.msg_type == 'error') {
                    return reject(that.mod_log_prefix + '\n Error: ' + msg.content.ename + '\n' + msg.content.evalue);
                }
                return resolve(msg);
            });
        }

        KernelExecOnCells.prototype.get_kernel_config = function() {
            var kernelLanguage = Jupyter.notebook.metadata.kernelspec.language.toLowerCase();
            var kernel_config = this.cfg.kernel_config_map[kernelLanguage];
            // true => deep
            return $.extend(true, {}, this.default_kernel_config, kernel_config);
        }

        KernelExecOnCells.prototype.transform_json_string_to_kernel_string = function(str, kernel_config) {
            for (var ii = 0; ii < kernel_config.replacements_json_to_kernel.length; ii++) {
                var from = kernel_config.replacements_json_to_kernel[ii][0];
                var to = kernel_config.replacements_json_to_kernel[ii][1];
                str = str.replace(from, to);
            }
            return str;
        }

        /**
         * construct functions as callbacks for the autoformat cell promise. This
         * is necessary because javascript lacks loop scoping, so if we don't use
         * this IIFE pattern, cell_index & cell are passed by reference, and every
         * callback ends up using the same value
         */
        KernelExecOnCells.prototype.construct_cell_callbacks = function(cell_index, cell) {
            var that = this;
            var on_success = function(formatted_text) {
                cell.set_text(formatted_text);
            };
            var on_failure = function(reason) {
                console.warn(
                    that.mod_log_prefix,
                    'error processing cell', cell_index + ':\n',
                    reason
                );
                if (that.cfg.show_alerts_for_errors) {
                    alert(reason);
                }
            };
            return [on_success, on_failure];
        }

        KernelExecOnCells.prototype.autoformat_cells = function(indices) {

            if (indices === undefined) {
                indices = Jupyter.notebook.get_selected_cells_indices();
            }
            var kernel_config = this.get_kernel_config();
            for (var ii = 0; ii < indices.length; ii++) {
                var cell_index = indices[ii];
                var cell = Jupyter.notebook.get_cell(cell_index);
                if (!(cell instanceof CodeCell)) {
                    continue;
                }
                // IIFE because otherwise cell_index & cell are passed by reference
                var callbacks = this.construct_cell_callbacks(cell_index, cell);
                this.autoformat_text(cell.get_text(), kernel_config).then(callbacks[0], callbacks[1]);
            }
        }

        KernelExecOnCells.prototype.autoformat_text = function(text, kernel_config) {
            var that = this;
            return new Promise(function(resolve, reject) {
                kernel_config = kernel_config || that.get_kernel_config();
                var kernel_str = that.transform_json_string_to_kernel_string(
                    JSON.stringify(text), kernel_config);
                Jupyter.notebook.kernel.execute(
                    kernel_config.prefix + kernel_str + kernel_config.postfix, {
                        iopub: {
                            output: function(msg) {
                                return resolve(that.convert_error_msg_to_broken_promise(msg).then(
                                    function on_success(msg) {
                                        var formatted_text;
                                        // print goes to stream text => msg.content.text
                                        try {
                                            formatted_text = String(JSON.parse(msg.content.text));
                                        } catch (err) {
                                            return Promise.reject(err);
                                        }
                                        if (kernel_config.trim_formatted_text) {
                                            formatted_text = formatted_text.trim();
                                        }
                                        return formatted_text;
                                    }
                                ));
                            }
                        }
                    }, { silent: false }
                );
            });
        }

        KernelExecOnCells.prototype.add_toolbar_button = function() {
            var that = this;
            if ($('#' + this.mod_name + '_button').length < 1) {
                var button_group_id = this.mod_name + '_button';
                Jupyter.toolbar.add_buttons_group(
                    [this.cfg.actions.process_selected.name], button_group_id);
            }
        }

        KernelExecOnCells.prototype.add_keyboard_shortcuts = function() {
            var new_shortcuts = {};
            new_shortcuts[this.cfg.hotkeys.process_selected] = this.cfg.actions.process_selected.name;
            new_shortcuts[this.cfg.hotkeys.process_all] = this.cfg.actions.process_all.name;
            Jupyter.keyboard_manager.edit_shortcuts.add_shortcuts(new_shortcuts);
            Jupyter.keyboard_manager.command_shortcuts.add_shortcuts(new_shortcuts);
        }

        KernelExecOnCells.prototype.register_actions = function() {
            /**
             *  it's important that the actions created by registering keyboard
             *  shortcuts get different names, as otherwise a default action is
             *  created, whose name is a string representation of the handler
             *  function.
             *  Since this library uses the same handler function for all plugins,
             *  just with different contexts (different values of cfg), their
             *  string representations are the same, and the last one to be
             *  registered overwrites all previous versions.
             *  This is essentially an issue with notebook, but it encourages us to
             *  use actions, which is where notebook is going anyway.
             */
            var actions = this.cfg.actions = {};
            var that = this;
            actions.process_selected = {
                help: that.cfg.kbd_shortcut_text + ' selected cell(s)',
                help_index: 'yf',
                icon: that.cfg.button_icon,
                handler: function(evt) { that.autoformat_cells(); },
            };
            actions.process_all = {
                help: that.cfg.kbd_shortcut_text + " the whole notebook",
                help_index: 'yf',
                icon: that.cfg.button_icon,
                handler: function(evt) {
                    var indices = [],
                        N = Jupyter.notebook.ncells();
                    for (var i = 0; i < N; i++) {
                        indices.push(i);
                    }
                    that.autoformat_cells(indices);
                },
            };

            actions.process_selected.name = Jupyter.keyboard_manager.actions.register(
                actions.process_selected, 'process_selected_cells', that.mod_name);
            actions.process_all.name = Jupyter.keyboard_manager.actions.register(
                actions.process_all, 'process_all_cells', that.mod_name);
        }

        KernelExecOnCells.prototype.setup_for_new_kernel = function() {
            var kernelLanguage = Jupyter.notebook.metadata.kernelspec.language.toLowerCase();
            var kernel_config = this.cfg.kernel_config_map[kernelLanguage];
            if (kernel_config === undefined) {
                $('#' + this.mod_name + '_button').remove();
                alert(this.mod_log_prefix + " Sorry, can't use kernel language " + kernelLanguage + ".\n" +
                    "Configurations are currently only defined for the following languages:\n" +
                    Object.keys(this.cfg.kernel_config_map).join(', ') + "\n" +
                    "See readme for more details.");
            // also remove keyboard shortcuts
            if (this.cfg.register_hotkey) {
                Jupyter.keyboard_manager.edit_shortcuts.remove_shortcut(this.cfg.hotkeys.process_selected);
                Jupyter.keyboard_manager.edit_shortcuts.remove_shortcut(this.cfg.hotkeys.process_all);
                Jupyter.keyboard_manager.command_shortcuts.remove_shortcut(this.cfg.hotkeys.process_all);
            }

            } else {
                if (this.cfg.add_toolbar_button) {
                    this.add_toolbar_button();
                }
                if (this.cfg.register_hotkey) {
                   this.add_keyboard_shortcuts();
                }
                Jupyter.notebook.kernel.execute(
                    kernel_config.library, { iopub: { output: this.convert_error_msg_to_broken_promise } }, { silent: false }
                );
            }
        }

        KernelExecOnCells.prototype.initialize_plugin = function() {
            var that = this;
            var base_url = utils.get_body_data("baseUrl");
            var conf_section = new ConfigSection('notebook', { base_url: base_url });
            // first, load config
            conf_section.load()
                // now update default config with that loaded from server
                .then(function on_success(config_data) {
                    $.extend(true, that.cfg, config_data[that.mod_name]);
                }, function on_error(err) {
                    console.warn(that.mod_log_prefix, 'error loading config:', err);
                })
                // next parse json config values
                .then(function on_success() {
                    var parsed_kernel_cfg = JSON.parse(that.cfg.kernel_config_map_json);
                    $.extend(that.cfg.kernel_config_map, parsed_kernel_cfg);
                })
                // if we failed to parse the json values in the config
                // using catch pattern, we attempt to continue anyway using defaults
                .catch(function on_error(err) {
                    console.warn(
                        that.mod_log_prefix, 'error parsing config variable',
                        that.mod_name + '.kernel_config_map_json to a json object:',
                        err
                    );
                })
                // now do things which required the config to be loaded
                .then(function on_success() {
	            that.register_actions(); // register actions
                    // kernel may already have been loaded before we get here, in which
                    // case we've missed the kernel_ready.Kernel event, so try ctx
                    if (typeof Jupyter.notebook.kernel !== "undefined" && Jupyter.notebook.kernel !== null) {
                        that.setup_for_new_kernel();
                    }

                    // on kernel_ready.Kernel, a new kernel has been started
                    events.on("kernel_ready.Kernel", function(evt, data) {
                        console.log(that.mod_log_prefix, 'restarting for new kernel_ready.Kernel event');
                        that.setup_for_new_kernel();
                    });
                }).catch(function on_error(err) {
                    console.error(that.mod_log_prefix, 'error loading:', err);
                });
        }

    exports.define_plugin = KernelExecOnCells;
    return {define_plugin: KernelExecOnCells}
});
<commit_msg>[lib] show a specific alert for missing kernel dependencies<commit_after>// Copyright (c) Jupyter-Contrib Team.
// Distributed under the terms of the Modified BSD License.

define(function(require, exports, module) {
    'use strict';

    var $ = require('jquery');
    var Jupyter = require('base/js/namespace');
    var events = require('base/js/events');
    var utils = require('base/js/utils');
    var ConfigSection = require('services/config').ConfigSection;
    var CodeCell = require('notebook/js/codecell').CodeCell;

    // this wrapper function allows config & hotkeys to be per-plugin
    function KernelExecOnCells(mod_name, cfg) {

        this.mod_name = mod_name;
        this.cfg = cfg;
        this.mod_log_prefix = '[' + this.mod_name + ']';
        this.mod_edit_shortcuts = {};
        this.mod_cmd_shortcuts = {};
        this.default_kernel_config = {
            library: '',
            prefix: '',
            postfix: '',
            replacements_json_to_kernel: [],
            trim_formatted_text: true
        };
        // gives default settings
        var default_cfg = {
            add_toolbar_button: true,
       hotkeys: {
	    process_selected: 'Ctrl-L',
	    process_all: 'Ctrl-Shift-L',
	},
            register_hotkey: true,
            show_alerts_for_errors: true,
            button_icon: 'fa-legal',
            button_label: mod_name,
            kbd_shortcut_text: mod_name,
            kernel_config_map: {},
        actions: null, // to be filled by register_actions
        };
        // apply defaults to cfg. Don't use jquery extend, as we want cfg to still
        // be same object, in case plugin alters it later
        for (var key in default_cfg) {
            if (!this.cfg.hasOwnProperty(key)) {
                this.cfg[key] = default_cfg[key];
            }
        }
        // set default json string, will later be updated from config
        // before it is parsed into an object
        this.cfg.kernel_config_map_json = JSON.stringify(this.cfg.kernel_config_map);

    } // end per-plugin wrapper define_plugin_functions

    // Prototypes
    // ----------

        /**
         * return a Promise which will resolve/reject based on the kernel message
         * type.
         * The returned promise will be
         *   - resolved if the message was not an error
         *   - rejected using the message's error text if msg.msg_type is "error"
         */
        KernelExecOnCells.prototype.convert_error_msg_to_broken_promise = function(msg) {
            var that = this;
            return new Promise(function(resolve, reject) {
                if (msg.msg_type == 'error') {
                    return reject(that.mod_log_prefix + '\n Error: ' + msg.content.ename + '\n' + msg.content.evalue);
                }
                return resolve(msg);
            });
        }

        KernelExecOnCells.prototype.convert_loading_library_error_msg_to_broken_promise = function(msg) {
            var that = this;
            return new Promise(function(resolve, reject) {
                if (msg.msg_type == 'error') {
                    return reject(that.mod_log_prefix + '\n Error loading library for ' +
                        Jupyter.notebook.metadata.kernelspec.language + ':\n' +
                        msg.content.ename  + msg.content.evalue +
                        '\n\nCheck that the appropriate library/module is correctly installed (read ' +
                        that.mod_name + '\'s documentation for details)');
                }
                return resolve(msg);
            });
        }

        KernelExecOnCells.prototype.get_kernel_config = function() {
            var kernelLanguage = Jupyter.notebook.metadata.kernelspec.language.toLowerCase();
            var kernel_config = this.cfg.kernel_config_map[kernelLanguage];
            // true => deep
            return $.extend(true, {}, this.default_kernel_config, kernel_config);
        }

        KernelExecOnCells.prototype.transform_json_string_to_kernel_string = function(str, kernel_config) {
            for (var ii = 0; ii < kernel_config.replacements_json_to_kernel.length; ii++) {
                var from = kernel_config.replacements_json_to_kernel[ii][0];
                var to = kernel_config.replacements_json_to_kernel[ii][1];
                str = str.replace(from, to);
            }
            return str;
        }

        /**
         * construct functions as callbacks for the autoformat cell promise. This
         * is necessary because javascript lacks loop scoping, so if we don't use
         * this IIFE pattern, cell_index & cell are passed by reference, and every
         * callback ends up using the same value
         */
        KernelExecOnCells.prototype.construct_cell_callbacks = function(cell_index, cell) {
            var that = this;
            var on_success = function(formatted_text) {
                cell.set_text(formatted_text);
            };
            var on_failure = function(reason) {
                console.warn(
                    that.mod_log_prefix,
                    'error processing cell', cell_index + ':\n',
                    reason
                );
                if (that.cfg.show_alerts_for_errors) {
                    alert(reason);
                }
            };
            return [on_success, on_failure];
        }

        KernelExecOnCells.prototype.autoformat_cells = function(indices) {

            if (indices === undefined) {
                indices = Jupyter.notebook.get_selected_cells_indices();
            }
            var kernel_config = this.get_kernel_config();
            for (var ii = 0; ii < indices.length; ii++) {
                var cell_index = indices[ii];
                var cell = Jupyter.notebook.get_cell(cell_index);
                if (!(cell instanceof CodeCell)) {
                    continue;
                }
                // IIFE because otherwise cell_index & cell are passed by reference
                var callbacks = this.construct_cell_callbacks(cell_index, cell);
                this.autoformat_text(cell.get_text(), kernel_config).then(callbacks[0], callbacks[1]);
            }
        }

        KernelExecOnCells.prototype.autoformat_text = function(text, kernel_config) {
            var that = this;
            return new Promise(function(resolve, reject) {
                kernel_config = kernel_config || that.get_kernel_config();
                var kernel_str = that.transform_json_string_to_kernel_string(
                    JSON.stringify(text), kernel_config);
                Jupyter.notebook.kernel.execute(
                    kernel_config.prefix + kernel_str + kernel_config.postfix, {
                        iopub: {
                            output: function(msg) {
                                return resolve(that.convert_error_msg_to_broken_promise(msg).then(
                                    function on_success(msg) {
                                        // print goes to stream text => msg.content.text
                                        // but for some kernels (eg nodejs) can be called as result of exec
                                        if (msg.content.text !== undefined){
                                        var formatted_text;
                                        try {
                                            formatted_text = String(JSON.parse(msg.content.text));
                                        } catch (err) {
                                            return Promise.reject(err);
                                        }
                                        if (kernel_config.trim_formatted_text) {
                                            formatted_text = formatted_text.trim();
                                        }
                                        return formatted_text;
                                    }}
                                ));
                            }
                        }
                    }, { silent: false }
                );
            });
        }

        KernelExecOnCells.prototype.add_toolbar_button = function() {
            var that = this;
            if ($('#' + this.mod_name + '_button').length < 1) {
                var button_group_id = this.mod_name + '_button';
                Jupyter.toolbar.add_buttons_group(
                    [this.cfg.actions.process_selected.name], button_group_id);
            }
        }

        KernelExecOnCells.prototype.add_keyboard_shortcuts = function() {
            var new_shortcuts = {};
            new_shortcuts[this.cfg.hotkeys.process_selected] = this.cfg.actions.process_selected.name;
            new_shortcuts[this.cfg.hotkeys.process_all] = this.cfg.actions.process_all.name;
            Jupyter.keyboard_manager.edit_shortcuts.add_shortcuts(new_shortcuts);
            Jupyter.keyboard_manager.command_shortcuts.add_shortcuts(new_shortcuts);
        }

        KernelExecOnCells.prototype.register_actions = function() {
            /**
             *  it's important that the actions created by registering keyboard
             *  shortcuts get different names, as otherwise a default action is
             *  created, whose name is a string representation of the handler
             *  function.
             *  Since this library uses the same handler function for all plugins,
             *  just with different contexts (different values of cfg), their
             *  string representations are the same, and the last one to be
             *  registered overwrites all previous versions.
             *  This is essentially an issue with notebook, but it encourages us to
             *  use actions, which is where notebook is going anyway.
             */
            var actions = this.cfg.actions = {};
            var that = this;
            actions.process_selected = {
                help: that.cfg.kbd_shortcut_text + ' selected cell(s)',
                help_index: 'yf',
                icon: that.cfg.button_icon,
                handler: function(evt) { that.autoformat_cells(); },
            };
            actions.process_all = {
                help: that.cfg.kbd_shortcut_text + " the whole notebook",
                help_index: 'yf',
                icon: that.cfg.button_icon,
                handler: function(evt) {
                    var indices = [],
                        N = Jupyter.notebook.ncells();
                    for (var i = 0; i < N; i++) {
                        indices.push(i);
                    }
                    that.autoformat_cells(indices);
                },
            };

            actions.process_selected.name = Jupyter.keyboard_manager.actions.register(
                actions.process_selected, 'process_selected_cells', that.mod_name);
            actions.process_all.name = Jupyter.keyboard_manager.actions.register(
                actions.process_all, 'process_all_cells', that.mod_name);
        }

        KernelExecOnCells.prototype.setup_for_new_kernel = function() {
            var that = this;
            var kernelLanguage = Jupyter.notebook.metadata.kernelspec.language.toLowerCase();
            var kernel_config = this.cfg.kernel_config_map[kernelLanguage];
            if (kernel_config === undefined) {
                $('#' + this.mod_name + '_button').remove();
                alert(this.mod_log_prefix + " Sorry, can't use kernel language " + kernelLanguage + ".\n" +
                    "Configurations are currently only defined for the following languages:\n" +
                    Object.keys(this.cfg.kernel_config_map).join(', ') + "\n" +
                    "See readme for more details.");
            // also remove keyboard shortcuts
            if (this.cfg.register_hotkey) {
                try {
                    Jupyter.keyboard_manager.edit_shortcuts.remove_shortcut(this.cfg.hotkeys.process_selected);
                    Jupyter.keyboard_manager.edit_shortcuts.remove_shortcut(this.cfg.hotkeys.process_all);
                    Jupyter.keyboard_manager.command_shortcuts.remove_shortcut(this.cfg.hotkeys.process_all);
                } catch (err) {}
            }

            } else { // kernel language is supported
                if (this.cfg.add_toolbar_button) {
                    this.add_toolbar_button();
                }
                if (this.cfg.register_hotkey) {
                   this.add_keyboard_shortcuts();
                }
                Jupyter.notebook.kernel.execute(
                    kernel_config.library, {
                        iopub: {
                            output: function(msg) {
                                return that.convert_loading_library_error_msg_to_broken_promise(msg)
                                    .catch(
                                        function on_failure(err) {
                                            if (that.cfg.show_alerts_for_errors) {
                                                alert(err);
                                            }
                                            else{
                                                console.error(err)
                                            }
                                        }

                                    );
                            }
                        }
                    }, { silent: false }
                );

            }
        }

        KernelExecOnCells.prototype.initialize_plugin = function() {
            var that = this;
            var base_url = utils.get_body_data("baseUrl");
            var conf_section = new ConfigSection('notebook', { base_url: base_url });
            // first, load config
            conf_section.load()
                // now update default config with that loaded from server
                .then(function on_success(config_data) {
                    $.extend(true, that.cfg, config_data[that.mod_name]);
                }, function on_error(err) {
                    console.warn(that.mod_log_prefix, 'error loading config:', err);
                })
                // next parse json config values
                .then(function on_success() {
                    var parsed_kernel_cfg = JSON.parse(that.cfg.kernel_config_map_json);
                    $.extend(that.cfg.kernel_config_map, parsed_kernel_cfg);
                })
                // if we failed to parse the json values in the config
                // using catch pattern, we attempt to continue anyway using defaults
                .catch(function on_error(err) {
                    console.warn(
                        that.mod_log_prefix, 'error parsing config variable',
                        that.mod_name + '.kernel_config_map_json to a json object:',
                        err
                    );
                })
                // now do things which required the config to be loaded
                .then(function on_success() {
	            that.register_actions(); // register actions
                    // kernel may already have been loaded before we get here, in which
                    // case we've missed the kernel_ready.Kernel event, so try ctx
                    if (typeof Jupyter.notebook.kernel !== "undefined" && Jupyter.notebook.kernel !== null) {
                        that.setup_for_new_kernel();
                    }

                    // on kernel_ready.Kernel, a new kernel has been started
                    events.on("kernel_ready.Kernel", function(evt, data) {
                        console.log(that.mod_log_prefix, 'restarting for new kernel_ready.Kernel event');
                        that.setup_for_new_kernel();
                    });
                }).catch(function on_error(err) {
                    console.error(that.mod_log_prefix, 'error loading:', err);
                });
        }

    exports.define_plugin = KernelExecOnCells;
    return {define_plugin: KernelExecOnCells}
});
<|endoftext|>


<commit_before>/*-----------------------------
Advanced Path Animation v1.1.0
(c) 2015 <http://vrnehot.ru>
------------------------------*/
function APA(opt){
	this.offset = 0; // 0..1
	this.duration = 1000;
	this.loop = Infinity;
	this.easing = null; // function(p){ return p; }
	this.fps = 60;
	this.path = ''; // svg path element || svg path string
	this.checkpoints = {}; // { "p":function(){} } where p = 0..1

	if(opt){ for(var i in opt)if(opt.hasOwnProperty(i)){ this[i] = opt[i]; } }
	this.init();
}
APA.prototype.init = function(){
	if( this.path ) this.updatePath(this.path);
	this.setFPS(this.fps || 60);
	this.timer = null;
}
APA.prototype.start = function(opt){
	this.stop();
	this.progress = this.offset || 0;

	if(!this.duration) return false;
	this.lastFrame = new Date();
	this.running = true;
	this.elapsed = 0;

	this.update();
}
APA.prototype.update = function(){
	var z = this, now = new Date(), tslf = now - this.lastFrame, lp; this.lastFrame = now;
	this.timer = setTimeout(function(){ z.update(); },this.dbf);
	if(!this.running) return;

	this.elapsed += tslf;
	lp = this.progress;
	this.progress = this.elapsed / this.duration;

	if( typeof this.easing == 'function' ) this.progress = this.easing(this.progress);
	if(this.reverse) this.progress = this.offset - this.progress;
	else this.progress += this.offset;

	if(this.progress < 0 || this.progress > 1){
		if(this.loop){
			if(typeof this.lap == 'function'){ this.lap.call( this ); }
			if(this.loop != Infinity){ this.loop--; }
		}
		else{
			this.stop();
			if(typeof this.finish == 'function'){ return this.finish.call( this ); }
		}
	}
	this.angle = this.angleAt(this.progress);
	this.point = this.pointAt(this.progress);
	this.checkPoints(Math.min(this.progress,lp),Math.max(this.progress,lp));
	if(typeof this.step == 'function'){ this.step.call(this); }
}
APA.prototype.pause = function(){ this.running = false; }
APA.prototype.resume = function(){ this.running = true; }
APA.prototype.toggle = function(){ this.running = !this.running; }
APA.prototype.stop = function(){
	clearTimeout( this.timer );
	this.timer = null;
	this.running = false;
}
APA.prototype.checkPoints = function(min,max){
	var point;
	for(var i in this.checkpoints)if(this.checkpoints.hasOwnProperty(i)){
		point = this.checkpoints[i];
		if(i > min && i < max && typeof point == 'function'){ point.call( this ); }
	}
}
APA.prototype.pointAt = function(progress){ return this.path.getPointAtLength( this.len * progress ); }
APA.prototype.angleAt = function(progress){
	var p = [this.pointAt(progress - 0.01),this.pointAt(progress + 0.01)];
	return Math.atan2(p[1].y-p[0].y,p[1].x-p[0].x)*180 / Math.PI;
}

APA.prototype.updatePath = function(path){
	if(typeof path == 'string'){
		this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
		this.path.setAttribute('d', path);
	}
	else{ this.path = path; }
	this.len = this.path.getTotalLength();
}
APA.prototype.addCheckpoint = function(p,fn){ this.checkpoints[p] = fn; }
APA.prototype.setFPS = function(fps){
	fps = parseInt(fps);
	if(fps){ this.fps = fps; this.dbf = 1000 / fps; }
}
<commit_msg>Update apa.js<commit_after>/*-----------------------------
Advanced Path Animation v1.1.0
(c) 2015 <http://vrnehot.ru>
------------------------------*/
function APA(opt){
	this.offset = 0; // 0..1
	this.duration = 1000;
	this.loop = Infinity;
	this.easing = null; // function(p){ return p; }
	this.fps = 60;
	this.path = ''; // svg path element || svg path string
	this.checkpoints = {}; // { "p":function(){} } where p = 0..1
	this.speed = 1;

	if(opt){ for(var i in opt)if(opt.hasOwnProperty(i)){ this[i] = opt[i]; } }
	this.init();
}
APA.prototype.init = function(){
	if( this.path ) this.updatePath(this.path);
	this.setFPS(this.fps || 60);
	this.timer = null;
}
APA.prototype.start = function(opt){
	this.stop();
	this.progress = this.offset || 0;

	if(!this.duration) return false;
	this.lastFrame = new Date();
	this.running = true;
	this.elapsed = 0;

	this.update();
}
APA.prototype.update = function(){
	var z = this, now = new Date(), tslf = now - this.lastFrame, lp; this.lastFrame = now;
	this.timer = setTimeout(function(){ z.update(); },this.dbf);
	if(!this.running) return;

	this.elapsed += (tslf * this.speed);
	lp = this.progress;
	this.progress = this.elapsed / this.duration;

	if( typeof this.easing == 'function' ) this.progress = this.easing(this.progress);
	if(this.reverse) this.progress = this.offset - this.progress;
	else this.progress += this.offset;

	if(this.progress < 0 || this.progress > 1){
		if(this.loop){
			if(typeof this.lap == 'function'){ this.lap.call( this ); }
			if(this.loop != Infinity){ this.loop--; }
		}
		else{
			this.stop();
			if(typeof this.finish == 'function'){ return this.finish.call( this ); }
		}
	}
	this.angle = this.angleAt(this.progress);
	this.point = this.pointAt(this.progress);
	this.checkPoints(Math.min(this.progress,lp),Math.max(this.progress,lp));
	if(typeof this.step == 'function'){ this.step.call(this); }
}
APA.prototype.pause = function(){ this.running = false; }
APA.prototype.resume = function(){ this.running = true; }
APA.prototype.toggle = function(){ this.running = !this.running; }
APA.prototype.stop = function(){
	clearTimeout( this.timer );
	this.timer = null;
	this.running = false;
}
APA.prototype.checkPoints = function(min,max){
	var point;
	for(var i in this.checkpoints)if(this.checkpoints.hasOwnProperty(i)){
		point = this.checkpoints[i];
		if(i > min && i < max && typeof point == 'function'){ point.call( this ); }
	}
}
APA.prototype.pointAt = function(progress){ return this.path.getPointAtLength( this.len * progress ); }
APA.prototype.angleAt = function(progress){
	var p = [this.pointAt(progress - 0.01),this.pointAt(progress + 0.01)];
	return Math.atan2(p[1].y-p[0].y,p[1].x-p[0].x)*180 / Math.PI;
}

APA.prototype.updatePath = function(path){
	if(typeof path == 'string'){
		this.path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
		this.path.setAttribute('d', path);
	}
	else{ this.path = path; }
	this.len = this.path.getTotalLength();
}
APA.prototype.addCheckpoint = function(p,fn){ this.checkpoints[p] = fn; }
APA.prototype.setFPS = function(fps){
	fps = parseInt(fps);
	if(fps){ this.fps = fps; this.dbf = 1000 / fps; }
}
APA.prototype.setSpeed = function(spd){
	this.speed = parseFloat(spd);
}
<|endoftext|>