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

/**
 * The KanbanController is the class that coordinates the kanban model and the
 * kanban renderer.  It also makes sure that update from the search view are
 * properly interpreted.
 */

var BasicController = require('web.BasicController');
var Context = require('web.Context');
var core = require('web.core');
var view_dialogs = require('web.view_dialogs');

var _t = core._t;
var qweb = core.qweb;

var KanbanController = BasicController.extend({
    custom_events: _.extend({}, BasicController.prototype.custom_events, {
        quick_create_add_column: '_onAddColumn',
        quick_create_record: '_onQuickCreateRecord',
        resequence_columns: '_onResequenceColumn',
        button_clicked: '_onButtonClicked',
        kanban_record_delete: '_onRecordDelete',
        kanban_record_update: '_onUpdateRecord',
        kanban_column_delete: '_onDeleteColumn',
        kanban_column_add_record: '_onAddRecordToColumn',
        kanban_column_resequence: '_onColumnResequence',
        kanban_column_archive_records: '_onArchiveRecords',
        kanban_load_more: '_onLoadMore',
        column_toggle_fold: '_onToggleColumn',
    }),
    /**
     * @override
     * @param {Object} params
     */
    init: function (parent, model, renderer, params) {
        this._super.apply(this, arguments);

        this.on_create = params.on_create;
        this.hasButtons = params.hasButtons;

        // true iff grouped by an m2o field and group_create action enabled
        this.create_column_enabled = false;
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @param {jQueryElement} $node
     */
    renderButtons: function ($node) {
        var self = this;
        if (this.hasButtons && this.is_action_enabled('create')) {
            this.$buttons = $(qweb.render('KanbanView.buttons', {widget: this}));
            this.$buttons.on('click', 'button.o-kanban-button-new', function () {
                var data = self.model.get(self.handle, {raw: true});
                if (data.groupedBy.length > 0 && data.count > 0 && self.on_create === 'quick_create') {
                    // Activate the quick create in the first column
                    self.renderer.addQuickCreate();
                } else if (self.on_create && self.on_create !== 'quick_create') {
                    // Execute the given action
                    self.do_action(self.on_create, {
                        on_close: self.reload.bind(self),
                        additional_context: data.context,
                    });
                } else {
                    // Open the form view
                    self.trigger_up('switch_view', {
                        view_type: 'form',
                        res_id: undefined
                    });
                }
            });
            this._updateButtons();
            this.$buttons.appendTo($node);
        }
    },
    /**
     * Override update method to recompute create_column_enabled.
     *
     * @returns {Deferred}
     */
    update: function () {
        var state = this.model.get(this.handle, {raw: true});
        var group_by_field = state.fields[state.groupedBy[0]];
        var grouped_by_m2o = group_by_field && (group_by_field.type === 'many2one');
        var groupCreate = this.is_action_enabled('group_create');
        this.create_column_enabled = grouped_by_m2o && groupCreate;
        return this._super.apply(this, arguments);
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @override method comes from field manager mixin
     * @param {string} id local id from the basic record data
     */
    _confirmSave: function (id) {
        this.renderer.updateRecord(this.model.get(id));
    },
    /**
     * The nocontent helper should be displayed in kanban:
     *   - ungrouped: if there is no records
     *   - grouped: if there is no groups and no column quick create
     *
     * @override
     * @private
     */
    _hasContent: function (state) {
        return this._super.apply(this, arguments) ||
               this.create_column_enabled ||
               (state.groupedBy.length && state.data.length);
    },
    /**
     * This method calls the server to ask for a resequence.  Note that this
     * does not rerender the user interface, because in most case, the
     * resequencing operation has already been displayed by the renderer.
     *
     * @param {string} column_id
     * @param {string[]} ids
     * @returns {Deferred}
     */
    _resequenceRecords: function (column_id, ids) {
        return this.model.resequence(this.modelName, ids, column_id);
    },
    /**
     * In grouped mode, set 'Create' button as btn-default if there is no column
     * (except if we can't create new columns)
     *
     * @override from abstract controller
     */
    _updateButtons: function () {
        if (this.$buttons) {
            var data = this.model.get(this.handle, {raw: true});
            var create_muted = data.count === 0 && this.create_column_enabled;
            this.$buttons.find('.o-kanban-button-new')
                .toggleClass('btn-primary', !create_muted)
                .toggleClass('btn-default', create_muted);
        }
    },
    /**
     * @override
     * @param {Object} state
     * @returns {Deferred}
     */
    _update: function (state) {
        var hasNoContent = !this._hasContent(state);
        this.$el.toggleClass('o_kanban_nocontent', hasNoContent);
        this._toggleNoContentHelper(hasNoContent);
        return this._super.apply(this, arguments);
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * This handler is called when an event (from the quick create add column)
     * event bubbles up. When that happens, we need to ask the model to create
     * a group and to update the renderer
     *
     * @param {OdooEvent} event
     */
    _onAddColumn: function (event) {
        var self = this;
        this.model.createGroup(event.data.value, this.handle).then(function () {
            self.update({}, {reload: false});
            self.trigger_up('scrollTo', {selector: '.o_column_quick_create'});
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onAddRecordToColumn: function (event) {
        var self = this;
        var record = event.data.record;
        var column = event.target;
        this.alive(this.model.moveRecord(record.db_id, column.db_id, this.handle))
            .then(function (column_db_ids) {
                return self._resequenceRecords(column.db_id, event.data.ids)
                    .then(function () {
                        _.each(column_db_ids, function (db_id) {
                            var data = self.model.get(db_id);
                            self.renderer.updateColumn(db_id, data);
                        });
                });
            }).fail(this.reload.bind(this));
    },
    /**
     * The interface allows in some case the user to archive a column. This is
     * what this handler is for.
     *
     * @param {OdooEvent} event
     */
    _onArchiveRecords: function (event) {
        var self = this;
        var active_value = !event.data.archive;
        var column = event.target;
        var record_ids = [];
        _.each(column.records, function (kanban_record) {
            if (kanban_record.record.active.value !== active_value) {
                record_ids.push(kanban_record.db_id);
            }
        });
        if (record_ids.length) {
            this.model
                .toggleActive(record_ids, active_value, column.db_id)
                .then(function (db_id) {
                    var data = self.model.get(db_id);
                    self.renderer.updateColumn(db_id, data);
                });
        }
    },
    /**
     * @param {OdooEvent} event
     */
    _onColumnResequence: function (event) {
        this._resequenceRecords(event.target.db_id, event.data.ids);
    },
    /**
     * @param {OdooEvent} event
     */
    _onDeleteColumn: function (event) {
        var self = this;
        var column = event.target;
        var state = this.model.get(this.handle, {raw: true});
        var relatedModelName = state.fields[state.groupedBy[0]].relation;
        this.model
            .deleteRecords([column.db_id], relatedModelName, this.handle)
            .done(function () {
                if (column.isEmpty()) {
                    self.renderer.removeWidget(column);
                    self._updateButtons();
                } else {
                    self.reload();
                }
            });
    },
    /**
     * This method comes from the field manager mixin.  Since there is no
     * onchange for kanban, we force a save for every change
     *
     * @override
     * @param {OdooEvent} event
     */
    _onFieldChanged: function (event) {
        event.data.force_save = true;
        this._super.apply(this, arguments);
    },
    /**
     * @param {OdooRevent} event
     */
    _onLoadMore: function (event) {
        var self = this;
        var column = event.target;
        this.model.load_more(column.db_id).then(function (db_id) {
            var data = self.model.get(db_id);
            self.renderer.updateColumn(db_id, data);
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onButtonClicked: function (event) {
        var self = this;
        var attrs = event.data.attrs;
        var record = event.data.record;
        if (attrs.context) {
            attrs.context = new Context(attrs.context)
                .set_eval_context({
                    active_id: record.res_id,
                    active_ids: [record.res_id],
                    active_model: record.model,
                });
        }
        this.trigger_up('execute_action', {
            action_data: attrs,
            model: record.model,
            record_id: record.res_id,
            on_close: function () {
                self.model.reload(record.id).then(function (db_id) {
                    var data = self.model.get(db_id);
                    var kanban_record = event.target;
                    kanban_record.update(data);
                });
            },
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onQuickCreateRecord: function (event) {
        var self = this;
        var column = event.target;
        var name = event.data.value;
        var state = this.model.get(this.handle, {raw: true});
        var columnState = this.model.get(column.db_id, {raw: true});
        var context = columnState.getContext();
        context['default_' + state.groupedBy[0]] = columnState.res_id;

        this._rpc({
                model: state.model,
                method: 'name_create',
                args: [name],
                context: context,
            })
            .then(add_record)
            .fail(function (error, event) {
                event.preventDefault();
                new view_dialogs.FormViewDialog(self, {
                    res_model: state.model,
                    context: _.extend({default_name: name}, context),
                    title: _t("Create"),
                    disable_multiple_selection: true,
                    on_saved: function(record) {
                        add_record([record.res_id]);
                    },
                }).open();
            });

        function add_record(records) {
            return self.model
                .addRecordToGroup(columnState.id, records[0])
                .then(function (db_id) {
                    column.addRecord(self.model.get(db_id), {position: 'before'});
                });
        }
    },
    /**
     * @param {OdooEvent} event
     */
    _onRecordDelete: function (event) {
        this._deleteRecords([event.data.id]);
    },
    /**
     * @param {OdooEvent} event
     */
    _onResequenceColumn: function (event) {
        var state = this.model.get(this.handle, {raw: true});
        var model = state.fields[state.groupedBy[0]].relation;
        this.model.resequence(model, event.data.ids, this.handle);
    },
    /**
     * @param {OdooEvent} event
     */
    _onToggleColumn: function (event) {
        var self = this;
        var column = event.target;
        this.model.toggleGroup(column.db_id).then(function (db_id) {
            var data = self.model.get(db_id);
            self.renderer.updateColumn(db_id, data);
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onUpdateRecord: function (event) {
        var record = event.target;
        this.alive(this.model.save(record.db_id, event.data))
            .then(record.update.bind(record));
    },
});

return KanbanController;

});
<commit_msg>[REF] web: make clicking on create button overridable in kanban<commit_after>odoo.define('web.KanbanController', function (require) {
"use strict";

/**
 * The KanbanController is the class that coordinates the kanban model and the
 * kanban renderer.  It also makes sure that update from the search view are
 * properly interpreted.
 */

var BasicController = require('web.BasicController');
var Context = require('web.Context');
var core = require('web.core');
var view_dialogs = require('web.view_dialogs');

var _t = core._t;
var qweb = core.qweb;

var KanbanController = BasicController.extend({
    custom_events: _.extend({}, BasicController.prototype.custom_events, {
        quick_create_add_column: '_onAddColumn',
        quick_create_record: '_onQuickCreateRecord',
        resequence_columns: '_onResequenceColumn',
        button_clicked: '_onButtonClicked',
        kanban_record_delete: '_onRecordDelete',
        kanban_record_update: '_onUpdateRecord',
        kanban_column_delete: '_onDeleteColumn',
        kanban_column_add_record: '_onAddRecordToColumn',
        kanban_column_resequence: '_onColumnResequence',
        kanban_column_archive_records: '_onArchiveRecords',
        kanban_load_more: '_onLoadMore',
        column_toggle_fold: '_onToggleColumn',
    }),
    /**
     * @override
     * @param {Object} params
     */
    init: function (parent, model, renderer, params) {
        this._super.apply(this, arguments);

        this.on_create = params.on_create;
        this.hasButtons = params.hasButtons;

        // true iff grouped by an m2o field and group_create action enabled
        this.create_column_enabled = false;
    },

    //--------------------------------------------------------------------------
    // Public
    //--------------------------------------------------------------------------

    /**
     * @param {jQueryElement} $node
     */
    renderButtons: function ($node) {
        if (this.hasButtons && this.is_action_enabled('create')) {
            this.$buttons = $(qweb.render('KanbanView.buttons', {widget: this}));
            this.$buttons.on('click', 'button.o-kanban-button-new', this._onButtonNew.bind(this));
            this._updateButtons();
            this.$buttons.appendTo($node);
        }
    },
    /**
     * Override update method to recompute create_column_enabled.
     *
     * @returns {Deferred}
     */
    update: function () {
        var state = this.model.get(this.handle, {raw: true});
        var group_by_field = state.fields[state.groupedBy[0]];
        var grouped_by_m2o = group_by_field && (group_by_field.type === 'many2one');
        var groupCreate = this.is_action_enabled('group_create');
        this.create_column_enabled = grouped_by_m2o && groupCreate;
        return this._super.apply(this, arguments);
    },

    //--------------------------------------------------------------------------
    // Private
    //--------------------------------------------------------------------------

    /**
     * @override method comes from field manager mixin
     * @param {string} id local id from the basic record data
     */
    _confirmSave: function (id) {
        this.renderer.updateRecord(this.model.get(id));
    },
    /**
     * The nocontent helper should be displayed in kanban:
     *   - ungrouped: if there is no records
     *   - grouped: if there is no groups and no column quick create
     *
     * @override
     * @private
     */
    _hasContent: function (state) {
        return this._super.apply(this, arguments) ||
               this.create_column_enabled ||
               (state.groupedBy.length && state.data.length);
    },
    /**
     * This method calls the server to ask for a resequence.  Note that this
     * does not rerender the user interface, because in most case, the
     * resequencing operation has already been displayed by the renderer.
     *
     * @param {string} column_id
     * @param {string[]} ids
     * @returns {Deferred}
     */
    _resequenceRecords: function (column_id, ids) {
        return this.model.resequence(this.modelName, ids, column_id);
    },
    /**
     * In grouped mode, set 'Create' button as btn-default if there is no column
     * (except if we can't create new columns)
     *
     * @override from abstract controller
     */
    _updateButtons: function () {
        if (this.$buttons) {
            var data = this.model.get(this.handle, {raw: true});
            var create_muted = data.count === 0 && this.create_column_enabled;
            this.$buttons.find('.o-kanban-button-new')
                .toggleClass('btn-primary', !create_muted)
                .toggleClass('btn-default', create_muted);
        }
    },
    /**
     * @override
     * @param {Object} state
     * @returns {Deferred}
     */
    _update: function (state) {
        var hasNoContent = !this._hasContent(state);
        this.$el.toggleClass('o_kanban_nocontent', hasNoContent);
        this._toggleNoContentHelper(hasNoContent);
        return this._super.apply(this, arguments);
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * This handler is called when an event (from the quick create add column)
     * event bubbles up. When that happens, we need to ask the model to create
     * a group and to update the renderer
     *
     * @param {OdooEvent} event
     */
    _onAddColumn: function (event) {
        var self = this;
        this.model.createGroup(event.data.value, this.handle).then(function () {
            self.update({}, {reload: false});
            self.trigger_up('scrollTo', {selector: '.o_column_quick_create'});
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onAddRecordToColumn: function (event) {
        var self = this;
        var record = event.data.record;
        var column = event.target;
        this.alive(this.model.moveRecord(record.db_id, column.db_id, this.handle))
            .then(function (column_db_ids) {
                return self._resequenceRecords(column.db_id, event.data.ids)
                    .then(function () {
                        _.each(column_db_ids, function (db_id) {
                            var data = self.model.get(db_id);
                            self.renderer.updateColumn(db_id, data);
                        });
                });
            }).fail(this.reload.bind(this));
    },
    /**
     * The interface allows in some case the user to archive a column. This is
     * what this handler is for.
     *
     * @param {OdooEvent} event
     */
    _onArchiveRecords: function (event) {
        var self = this;
        var active_value = !event.data.archive;
        var column = event.target;
        var record_ids = [];
        _.each(column.records, function (kanban_record) {
            if (kanban_record.record.active.value !== active_value) {
                record_ids.push(kanban_record.db_id);
            }
        });
        if (record_ids.length) {
            this.model
                .toggleActive(record_ids, active_value, column.db_id)
                .then(function (db_id) {
                    var data = self.model.get(db_id);
                    self.renderer.updateColumn(db_id, data);
                });
        }
    },
    /**
     * @param {OdooEvent} event
     */
    _onColumnResequence: function (event) {
        this._resequenceRecords(event.target.db_id, event.data.ids);
    },
    /**
     * @param {OdooEvent} event
     */
    _onDeleteColumn: function (event) {
        var self = this;
        var column = event.target;
        var state = this.model.get(this.handle, {raw: true});
        var relatedModelName = state.fields[state.groupedBy[0]].relation;
        this.model
            .deleteRecords([column.db_id], relatedModelName, this.handle)
            .done(function () {
                if (column.isEmpty()) {
                    self.renderer.removeWidget(column);
                    self._updateButtons();
                } else {
                    self.reload();
                }
            });
    },
    /**
     * This method comes from the field manager mixin.  Since there is no
     * onchange for kanban, we force a save for every change
     *
     * @override
     * @param {OdooEvent} event
     */
    _onFieldChanged: function (event) {
        event.data.force_save = true;
        this._super.apply(this, arguments);
    },
    /**
     * @param {OdooRevent} event
     */
    _onLoadMore: function (event) {
        var self = this;
        var column = event.target;
        this.model.load_more(column.db_id).then(function (db_id) {
            var data = self.model.get(db_id);
            self.renderer.updateColumn(db_id, data);
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onButtonClicked: function (event) {
        var self = this;
        var attrs = event.data.attrs;
        var record = event.data.record;
        if (attrs.context) {
            attrs.context = new Context(attrs.context)
                .set_eval_context({
                    active_id: record.res_id,
                    active_ids: [record.res_id],
                    active_model: record.model,
                });
        }
        this.trigger_up('execute_action', {
            action_data: attrs,
            model: record.model,
            record_id: record.res_id,
            on_close: function () {
                self.model.reload(record.id).then(function (db_id) {
                    var data = self.model.get(db_id);
                    var kanban_record = event.target;
                    kanban_record.update(data);
                });
            },
        });
    },
    /**
     * @private
     */
    _onButtonNew: function () {
        var data = this.model.get(this.handle, {raw: true});
        if (data.groupedBy.length > 0 && data.count > 0 && this.on_create === 'quick_create') {
            // Activate the quick create in the first column
            this.renderer.addQuickCreate();
        } else if (this.on_create && this.on_create !== 'quick_create') {
            // Execute the given action
            this.do_action(this.on_create, {
                on_close: this.reload.bind(this),
                additional_context: data.context,
            });
        } else {
            // Open the form view
            this.trigger_up('switch_view', {
                view_type: 'form',
                res_id: undefined
            });
        }
    },
    /**
     * @param {OdooEvent} event
     */
    _onQuickCreateRecord: function (event) {
        var self = this;
        var column = event.target;
        var name = event.data.value;
        var state = this.model.get(this.handle, {raw: true});
        var columnState = this.model.get(column.db_id, {raw: true});
        var context = columnState.getContext();
        context['default_' + state.groupedBy[0]] = columnState.res_id;

        this._rpc({
                model: state.model,
                method: 'name_create',
                args: [name],
                context: context,
            })
            .then(add_record)
            .fail(function (error, event) {
                event.preventDefault();
                new view_dialogs.FormViewDialog(self, {
                    res_model: state.model,
                    context: _.extend({default_name: name}, context),
                    title: _t("Create"),
                    disable_multiple_selection: true,
                    on_saved: function(record) {
                        add_record([record.res_id]);
                    },
                }).open();
            });

        function add_record(records) {
            return self.model
                .addRecordToGroup(columnState.id, records[0])
                .then(function (db_id) {
                    column.addRecord(self.model.get(db_id), {position: 'before'});
                });
        }
    },
    /**
     * @param {OdooEvent} event
     */
    _onRecordDelete: function (event) {
        this._deleteRecords([event.data.id]);
    },
    /**
     * @param {OdooEvent} event
     */
    _onResequenceColumn: function (event) {
        var state = this.model.get(this.handle, {raw: true});
        var model = state.fields[state.groupedBy[0]].relation;
        this.model.resequence(model, event.data.ids, this.handle);
    },
    /**
     * @param {OdooEvent} event
     */
    _onToggleColumn: function (event) {
        var self = this;
        var column = event.target;
        this.model.toggleGroup(column.db_id).then(function (db_id) {
            var data = self.model.get(db_id);
            self.renderer.updateColumn(db_id, data);
        });
    },
    /**
     * @param {OdooEvent} event
     */
    _onUpdateRecord: function (event) {
        var record = event.target;
        this.alive(this.model.save(record.db_id, event.data))
            .then(record.update.bind(record));
    },
});

return KanbanController;

});
<|endoftext|>


<commit_before>/*---------------------------------------------------------
 * OpenERP web_graph
 *---------------------------------------------------------*/

openerp.web_graph = function (openerp) {
var COLOR_PALETTE = [
    '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b',
    '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00',
    '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#9abe00',
    '#ffc900', '#510090', '#0000c9', '#009b00', '#75507b', '#3465a4',
    '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9',
    '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900',
    '#cc0000', '#d400a8'];

var QWeb = openerp.web.qweb;
openerp.web.views.add('graph', 'openerp.web_graph.GraphView');
openerp.web_graph.GraphView = openerp.web.View.extend({

    init: function(parent, dataset, view_id, options) {
        this._super(parent);
        this.set_default_options(options);
        this.dataset = dataset;
        this.view_id = view_id;

        this.first_field = null;
        this.abscissa = null;
        this.ordinate = null;
        this.columns = [];
        this.group_field = null;
        this.is_loaded = $.Deferred();

        this.renderer = null;
    },
    stop: function () {
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this._super();
    },
    do_show: function () {
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        var self = this;
        this._super();
        var loaded;
        if (this.embedded_view) {
            loaded = $.when([self.embedded_view]);
        } else {
            loaded = this.rpc('/web/view/load', {
                    model: this.dataset.model,
                    view_id: this.view_id,
                    view_type: 'graph'
            });
        }
        return $.when(
            this.dataset.call_and_eval('fields_get', [false, {}], null, 1),
            loaded)
            .then(function (fields_result, view_result) {
                self.fields = fields_result[0];
                self.fields_view = view_result[0];
                self.on_loaded(self.fields_view);
            });
    },
    /**
     * Returns all object fields involved in the graph view
     */
    list_fields: function () {
        var fs = [this.abscissa];
        fs.push.apply(fs, _(this.columns).pluck('name'));
        if (this.group_field) {
            fs.push(this.group_field);
        }
        return fs;
    },
    on_loaded: function() {
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.orientation = this.fields_view.arch.attrs.orientation || 'vertical';

        _.each(this.fields_view.arch.children, function (field) {
            var attrs = field.attrs;
            if (attrs.group) {
                this.group_field = attrs.name;
            } else if(!this.abscissa) {
                this.first_field = this.abscissa = attrs.name;
            } else {
                this.columns.push({
                    name: attrs.name,
                    operator: attrs.operator || '+'
                });
            }
        }, this);
        this.ordinate = this.columns[0].name;
        this.is_loaded.resolve();
    },
    schedule_chart: function(results) {
        var self = this;
        this.$element.html(QWeb.render("GraphView", {
            "fields_view": this.fields_view,
            "chart": this.chart,
            'element_id': this.element_id
        }));

        var fields = _(this.columns).pluck('name').concat([this.abscissa]);
        if (this.group_field) { fields.push(this.group_field); }
        // transform search result into usable records (convert from OpenERP
        // value shapes to usable atomic types
        var records = _(results).map(function (result) {
            var point = {};
            _(result).each(function (value, field) {
                if (!_(fields).contains(field)) { return; }
                if (value === false) { point[field] = false; return; }
                switch (self.fields[field].type) {
                case 'selection':
                    point[field] = _(self.fields[field].selection).detect(function (choice) {
                        return choice[0] === value;
                    })[1];
                    break;
                case 'many2one':
                    point[field] = value[1];
                    break;
                case 'integer': case 'float': case 'char':
                case 'date': case 'datetime':
                    point[field] = value;
                    break;
                default:
                    throw new Error(
                        "Unknown field type " + self.fields[field].type
                        + "for field " + field + " (" + value + ")");
                }
            });
            return point;
        });
        // aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
        // leave split on group field => max m*n records where m is the # of
        // values for the abscissa and n is the # of values for the group field
        var graph_data = [];
        _(records).each(function (record) {
            var abscissa = record[self.abscissa],
                group = record[self.group_field];
            var r = _(graph_data).detect(function (potential) {
                return potential[self.abscissa] === abscissa
                        && (!self.group_field
                            || potential[self.group_field] === group);
            });
            var datapoint = r || {};

            datapoint[self.abscissa] = abscissa;
            if (self.group_field) { datapoint[self.group_field] = group; }
            _(self.columns).each(function (column) {
                var val = record[column.name],
                    aggregate = datapoint[column.name];
                switch(column.operator) {
                case '+':
                    datapoint[column.name] = (aggregate || 0) + val;
                    return;
                case '*':
                    datapoint[column.name] = (aggregate || 1) * val;
                    return;
                case 'min':
                    datapoint[column.name] = (aggregate || Infinity) > val
                                           ? val
                                           : aggregate;
                    return;
                case 'max':
                    datapoint[column.name] = (aggregate || -Infinity) < val
                                           ? val
                                           : aggregate;
                }
            });

            if (!r) { graph_data.push(datapoint); }
        });
        graph_data = _(graph_data).sortBy(function (point) {
            return point[self.abscissa] + '[[--]]' + point[self.group_field];
        });

        if (this.chart == 'bar') {
            return this.schedule_bar(graph_data);
        } else if (this.chart == "pie") {
            return this.schedule_pie(graph_data);
        }
    },

    schedule_bar: function(results) {
        var self = this;
        var group_list, view_chart;
        if (!this.group_field) {
            view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
            group_list = _(this.columns).map(function (column, index) {
                return {
                    group: column.name,
                    text: self.fields[column.name].string,
                    color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
                }
            });
        } else {
            // dhtmlx handles clustered bar charts (> 1 column per abscissa
            // value) and stacked bar charts (basically the same but with the
            // columns on top of one another instead of side by side), but it
            // does not handle clustered stacked bar charts
            if (this.columns.length > 1) {
                this.$element.text(
                    'OpenERP Web does not support combining grouping and '
                  + 'multiple columns in graph at this time.');
                throw new Error(
                    'dhtmlx can not handle columns counts of that magnitude');
            }
            // transform series for clustered charts into series for stacked
            // charts
            view_chart = (this.orientation === 'horizontal')
                    ? 'stackedBarH' : 'stackedBar';
            group_list = _(results).chain()
                    .pluck(this.group_field)
                    .uniq()
                    .map(function (value, index) {
                        return {
                            group: self.ordinate + '_' +
                                    value.toLowerCase().replace(/[\s\/]+/g,'_'),
                            text: value,
                            color: COLOR_PALETTE[index % COLOR_PALETTE.length]
                        };
                    }).value();

            results = _(results).chain()
                .groupBy(function (record) { return record[self.abscissa]; })
                .map(function (records) {
                    var r = {};
                    // second argument is coerced to a str, no good for boolean
                    r[self.abscissa] = records[0][self.abscissa];
                    _(records).each(function (record) {
                        var key = _.str.sprintf('%s_%s',
                            self.ordinate,
                            record[self.group_field].toLowerCase().replace(/[\s\/]+/g,'_'));
                        r[key] = record[self.ordinate];
                    });
                    return r;
                })
                .value();
        }
        var abscissa_description = {
            title: "" + this.fields[this.abscissa].string + "",
            template: function (obj) {
                return obj[self.abscissa] || 'Undefined';
            }
        };
        var ordinate_description = {
            lines: true,
            title: "" + this.fields[this.ordinate].string + ""
        };

        var x_axis, y_axis;
        if (self.orientation == 'horizontal') {
            x_axis = ordinate_description;
            y_axis = abscissa_description;
        } else {
            x_axis = abscissa_description;
            y_axis = ordinate_description;
        }
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var bar_chart = new dhtmlXChart({
                view: view_chart,
                container: self.element_id+"-barchart",
                value:"#"+group_list[0].group+"#",
                gradient: "3d",
                border: false,
                width: 1024,
                tooltip:{
                    template: _.str.sprintf("#%s#, %s=#%s#",
                        self.abscissa, group_list[0].text, group_list[0].group)
                },
                radius: 0,
                color:group_list[0].color,
                origin:0,
                xAxis: x_axis,
                yAxis: y_axis,
                padding: {
                    left: 75
                },
                legend: {
                    values: group_list,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker: {
                        type:"round",
                        width:12
                    }
                }
            });
            for (var m = 1; m<group_list.length;m++){
                var column = group_list[m];
                if (column.group === self.group_field) { continue; }
                bar_chart.addSeries({
                    value: "#"+column.group+"#",
                    tooltip:{
                        template: _.str.sprintf("#%s#, %s=#%s#",
                            self.abscissa, column.text, column.group)
                    },
                    color: column.color
                });
            }
            bar_chart.parse(results, "json");
            self.$element.find("#"+self.element_id+"-barchart").height(
                self.$element.find("#"+self.element_id+"-barchart").height()+50);
            bar_chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(bar_chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    schedule_pie: function(result) {
        var self = this;
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var chart =  new dhtmlXChart({
                view:"pie3D",
                container:self.element_id+"-piechart",
                value:"#"+self.ordinate+"#",
                pieInnerText:function(obj) {
                    var sum = chart.sum("#"+self.ordinate+"#");
                    var val = obj[self.ordinate] / sum * 100 ;
                    return val.toFixed(1) + "%";
                },
                tooltip:{
                    template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
                },
                gradient:"3d",
                height: 20,
                radius: 200,
                legend: {
                    width: 300,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker:{
                        type:"round",
                        width:12
                    },
                    template:function(obj){
                        return obj[self.abscissa] || 'Undefined';
                    }
                }
            });
            chart.parse(result,"json");
            chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    open_list_view : function (id){
        var self = this;
        // unconditionally nuke tooltips before switching view
        $(".dhx_tooltip").remove('div');
        id = id[this.abscissa];
        if (typeof id == 'object'){
            id = id[0];
        }

        var record_id = this.abscissa;
        var modes = ["list", "form", "graph"];
        var views = [];
        _.each(modes, function(mode) {
            var view = [false, mode];
            if (self.fields.views && self.fields.views[mode]) {
                view.push(self.fields.views[mode]);
            }
            views.push(view);
        });
        this.do_action({
            "res_model" : this.dataset.model,
            "domain" : [[record_id, '=', id], ['id','in',this.dataset.ids]],
            "views" : views,
            "type" : "ir.actions.act_window",
            "view_type" : "list",
            "view_mode" : "list"
        });
    },

    do_search: function(domain, context, group_by) {
        var self = this;
        return $.when(this.is_loaded).pipe(function() {
            // TODO: handle non-empty group_by with read_group?
            if (!_(group_by).isEmpty()) {
                self.abscissa = group_by[0];
            } else {
                self.abscissa = self.first_field;
            }
            return self.dataset.read_slice(self.list_fields(), {}, $.proxy(self, 'schedule_chart'));
        });
    }
});
};
// vim:et fdc=0 fdl=0:
<commit_msg>[ADD] line and area chart types support<commit_after>/*---------------------------------------------------------
 * OpenERP web_graph
 *---------------------------------------------------------*/

openerp.web_graph = function (openerp) {
var COLOR_PALETTE = [
    '#cc99ff', '#ccccff', '#48D1CC', '#CFD784', '#8B7B8B', '#75507b',
    '#b0008c', '#ff0000', '#ff8e00', '#9000ff', '#0078ff', '#00ff00',
    '#e6ff00', '#ffff00', '#905000', '#9b0000', '#840067', '#9abe00',
    '#ffc900', '#510090', '#0000c9', '#009b00', '#75507b', '#3465a4',
    '#73d216', '#c17d11', '#edd400', '#fcaf3e', '#ef2929', '#ff00c9',
    '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f', '#f57900',
    '#cc0000', '#d400a8'];

var QWeb = openerp.web.qweb;
openerp.web.views.add('graph', 'openerp.web_graph.GraphView');
openerp.web_graph.GraphView = openerp.web.View.extend({

    init: function(parent, dataset, view_id, options) {
        this._super(parent);
        this.set_default_options(options);
        this.dataset = dataset;
        this.view_id = view_id;

        this.first_field = null;
        this.abscissa = null;
        this.ordinate = null;
        this.columns = [];
        this.group_field = null;
        this.is_loaded = $.Deferred();

        this.renderer = null;
    },
    stop: function () {
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this._super();
    },
    do_show: function () {
        this.$element.show();
    },
    do_hide: function () {
        this.$element.hide();
    },
    start: function() {
        var self = this;
        this._super();
        var loaded;
        if (this.embedded_view) {
            loaded = $.when([self.embedded_view]);
        } else {
            loaded = this.rpc('/web/view/load', {
                    model: this.dataset.model,
                    view_id: this.view_id,
                    view_type: 'graph'
            });
        }
        return $.when(
            this.dataset.call_and_eval('fields_get', [false, {}], null, 1),
            loaded)
            .then(function (fields_result, view_result) {
                self.fields = fields_result[0];
                self.fields_view = view_result[0];
                self.on_loaded(self.fields_view);
            });
    },
    /**
     * Returns all object fields involved in the graph view
     */
    list_fields: function () {
        var fs = [this.abscissa];
        fs.push.apply(fs, _(this.columns).pluck('name'));
        if (this.group_field) {
            fs.push(this.group_field);
        }
        return fs;
    },
    on_loaded: function() {
        this.chart = this.fields_view.arch.attrs.type || 'pie';
        this.orientation = this.fields_view.arch.attrs.orientation || 'vertical';

        _.each(this.fields_view.arch.children, function (field) {
            var attrs = field.attrs;
            if (attrs.group) {
                this.group_field = attrs.name;
            } else if(!this.abscissa) {
                this.first_field = this.abscissa = attrs.name;
            } else {
                this.columns.push({
                    name: attrs.name,
                    operator: attrs.operator || '+'
                });
            }
        }, this);
        this.ordinate = this.columns[0].name;
        this.is_loaded.resolve();
    },
    schedule_chart: function(results) {
        var self = this;
        this.$element.html(QWeb.render("GraphView", {
            "fields_view": this.fields_view,
            "chart": this.chart,
            'element_id': this.element_id
        }));

        var fields = _(this.columns).pluck('name').concat([this.abscissa]);
        if (this.group_field) { fields.push(this.group_field); }
        // transform search result into usable records (convert from OpenERP
        // value shapes to usable atomic types
        var records = _(results).map(function (result) {
            var point = {};
            _(result).each(function (value, field) {
                if (!_(fields).contains(field)) { return; }
                if (value === false) { point[field] = false; return; }
                switch (self.fields[field].type) {
                case 'selection':
                    point[field] = _(self.fields[field].selection).detect(function (choice) {
                        return choice[0] === value;
                    })[1];
                    break;
                case 'many2one':
                    point[field] = value[1];
                    break;
                case 'integer': case 'float': case 'char':
                case 'date': case 'datetime':
                    point[field] = value;
                    break;
                default:
                    throw new Error(
                        "Unknown field type " + self.fields[field].type
                        + "for field " + field + " (" + value + ")");
                }
            });
            return point;
        });
        // aggregate data, because dhtmlx is crap. Aggregate on abscissa field,
        // leave split on group field => max m*n records where m is the # of
        // values for the abscissa and n is the # of values for the group field
        var graph_data = [];
        _(records).each(function (record) {
            var abscissa = record[self.abscissa],
                group = record[self.group_field];
            var r = _(graph_data).detect(function (potential) {
                return potential[self.abscissa] === abscissa
                        && (!self.group_field
                            || potential[self.group_field] === group);
            });
            var datapoint = r || {};

            datapoint[self.abscissa] = abscissa;
            if (self.group_field) { datapoint[self.group_field] = group; }
            _(self.columns).each(function (column) {
                var val = record[column.name],
                    aggregate = datapoint[column.name];
                switch(column.operator) {
                case '+':
                    datapoint[column.name] = (aggregate || 0) + val;
                    return;
                case '*':
                    datapoint[column.name] = (aggregate || 1) * val;
                    return;
                case 'min':
                    datapoint[column.name] = (aggregate || Infinity) > val
                                           ? val
                                           : aggregate;
                    return;
                case 'max':
                    datapoint[column.name] = (aggregate || -Infinity) < val
                                           ? val
                                           : aggregate;
                }
            });

            if (!r) { graph_data.push(datapoint); }
        });
        graph_data = _(graph_data).sortBy(function (point) {
            return point[self.abscissa] + '[[--]]' + point[self.group_field];
        });
        if (this.chart == 'bar' || 'line' || 'area') {
            return this.schedule_bar_line_area(graph_data);
        } else if (this.chart == "pie") {
            return this.schedule_pie(graph_data);
        }
    },
    schedule_bar_line_area: function(results) {
        var self = this;
        var group_list,
        view_chart = (self.chart == 'line')?'line':(self.chart == 'area')?'area':'';
        if (!this.group_field) {
            if (self.chart == 'bar'){
                view_chart = (this.orientation === 'horizontal') ? 'barH' : 'bar';
            }
            group_list = _(this.columns).map(function (column, index) {
                return {
                    group: column.name,
                    text: self.fields[column.name].string,
                    color: COLOR_PALETTE[index % (COLOR_PALETTE.length)]
                }
            });
        } else {
            // dhtmlx handles clustered bar charts (> 1 column per abscissa
            // value) and stacked bar charts (basically the same but with the
            // columns on top of one another instead of side by side), but it
            // does not handle clustered stacked bar charts
            if (self.chart == 'bar' && (this.columns.length > 1)) {
                this.$element.text(
                    'OpenERP Web does not support combining grouping and '
                  + 'multiple columns in graph at this time.');
                throw new Error(
                    'dhtmlx can not handle columns counts of that magnitude');
            }
            // transform series for clustered charts into series for stacked
            // charts
            if (self.chart == 'bar'){
                view_chart = (this.orientation === 'horizontal')
                        ? 'stackedBarH' : 'stackedBar';
            }
            group_list = _(results).chain()
                    .pluck(this.group_field)
                    .uniq()
                    .map(function (value, index) {
                        return {
                            group: self.ordinate + '_' +
                                    value.toLowerCase().replace(/[\s\/]+/g,'_'),
                            text: value,
                            color: COLOR_PALETTE[index % COLOR_PALETTE.length]
                        };
                    }).value();

            results = _(results).chain()
                .groupBy(function (record) { return record[self.abscissa]; })
                .map(function (records) {
                    var r = {};
                    // second argument is coerced to a str, no good for boolean
                    r[self.abscissa] = records[0][self.abscissa];
                    _(records).each(function (record) {
                        var key = _.str.sprintf('%s_%s',
                            self.ordinate,
                            record[self.group_field].toLowerCase().replace(/[\s\/]+/g,'_'));
                        r[key] = record[self.ordinate];
                    });
                    return r;
                })
                .value();
        }
        var abscissa_description = {
            title: "" + this.fields[this.abscissa].string + "",
            template: function (obj) {
                return obj[self.abscissa] || 'Undefined';
            }
        };
        var ordinate_description = {
            lines: true,
            title: "" + this.fields[this.ordinate].string + ""
        };

        var x_axis, y_axis;
        if (self.chart == 'bar' && self.orientation == 'horizontal') {
            x_axis = ordinate_description;
            y_axis = abscissa_description;
        } else {
            x_axis = abscissa_description;
            y_axis = ordinate_description;
        }
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var charts = new dhtmlXChart({
                view: view_chart,
                container: self.element_id+"-"+self.chart+"chart",
                value:"#"+group_list[0].group+"#",
                gradient: (self.chart == "bar") ? "3d" : "light",
                alpha: (self.chart == "area") ? 0.6 : 1,
                border: false,
                width: 1024,
                tooltip:{
                    template: _.str.sprintf("#%s#, %s=#%s#",
                        self.abscissa, group_list[0].text, group_list[0].group)
                },
                radius: 0,
                color: (self.chart != "line") ? group_list[0].color : "",
                item: (self.chart == "line") ? {
                            borderColor: group_list[0].color,
                            color: "#000000"
                        } : "",
                line: (self.chart == "line") ? {
                            color: group_list[0].color,
                            width: 3
                        } : "",
                origin:0,
                xAxis: x_axis,
                yAxis: y_axis,
                padding: {
                    left: 75
                },
                legend: {
                    values: group_list,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker: {
                        type:"round",
                        width:12
                    }
                }
            });

            for (var m = 1; m<group_list.length;m++){
                var column = group_list[m];
                if (column.group === self.group_field) { continue; }
                charts.addSeries({
                    value: "#"+column.group+"#",
                    tooltip:{
                        template: _.str.sprintf("#%s#, %s=#%s#",
                            self.abscissa, column.text, column.group)
                    },
                    color: (self.chart != "line") ? column.color : "",
                    item: (self.chart == "line") ? {
                            borderColor: column.color,
                            color: "#000000"
                        } : "",
                    line: (self.chart == "line") ? {
                            color: column.color,
                            width: 3
                        } : ""
                });
            }
            charts.parse(results, "json");
            self.$element.find("#"+self.element_id+"-"+self.chart+"chart").height(
                self.$element.find("#"+self.element_id+"-"+self.chart+"chart").height()+50);
            charts.attachEvent("onItemClick", function(id) {
                self.open_list_view(charts.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    schedule_pie: function(result) {
        var self = this;
        var renderer = function () {
            if (self.$element.is(':hidden')) {
                self.renderer = setTimeout(renderer, 100);
                return;
            }
            self.renderer = null;
            var chart =  new dhtmlXChart({
                view:"pie3D",
                container:self.element_id+"-piechart",
                value:"#"+self.ordinate+"#",
                pieInnerText:function(obj) {
                    var sum = chart.sum("#"+self.ordinate+"#");
                    var val = obj[self.ordinate] / sum * 100 ;
                    return val.toFixed(1) + "%";
                },
                tooltip:{
                    template:"#"+self.abscissa+"#"+"="+"#"+self.ordinate+"#"
                },
                gradient:"3d",
                height: 20,
                radius: 200,
                legend: {
                    width: 300,
                    align:"left",
                    valign:"top",
                    layout: "x",
                    marker:{
                        type:"round",
                        width:12
                    },
                    template:function(obj){
                        return obj[self.abscissa] || 'Undefined';
                    }
                }
            });
            chart.parse(result,"json");
            chart.attachEvent("onItemClick", function(id) {
                self.open_list_view(chart.get(id));
            });
        };
        if (this.renderer) {
            clearTimeout(this.renderer);
        }
        this.renderer = setTimeout(renderer, 0);
    },
    open_list_view : function (id){
        var self = this;
        // unconditionally nuke tooltips before switching view
        $(".dhx_tooltip").remove('div');
        id = id[this.abscissa];
        if (typeof id == 'object'){
            id = id[0];
        }

        var record_id = this.abscissa;
        var modes = ["list", "form", "graph"];
        var views = [];
        _.each(modes, function(mode) {
            var view = [false, mode];
            if (self.fields.views && self.fields.views[mode]) {
                view.push(self.fields.views[mode]);
            }
            views.push(view);
        });
        this.do_action({
            "res_model" : this.dataset.model,
            "domain" : [[record_id, '=', id], ['id','in',this.dataset.ids]],
            "views" : views,
            "type" : "ir.actions.act_window",
            "view_type" : "list",
            "view_mode" : "list"
        });
    },

    do_search: function(domain, context, group_by) {
        var self = this;
        return $.when(this.is_loaded).pipe(function() {
            // TODO: handle non-empty group_by with read_group?
            if (!_(group_by).isEmpty()) {
                self.abscissa = group_by[0];
            } else {
                self.abscissa = self.first_field;
            }
            return self.dataset.read_slice(self.list_fields(), {}, $.proxy(self, 'schedule_chart'));
        });
    }
});
};
// vim:et fdc=0 fdl=0:
<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    Sequelize = require('sequelize'),
    lodash = require('lodash'),
    env = process.env.NODE_ENV || 'development',
    config = require(__dirname + '/../config/config.json')[env],
    sequelize = new Sequelize(config.database, config.username, config.password, config),
    db = {}

fs
    .readdirSync(__dirname)
    .filter(function(file) {
        return (file.indexOf('.') !== 0) && (file !== 'index.js')
    })
    .forEach(function(file) {
        var model = sequelize.import(path.join(__dirname, file))
        db[model.name] = model
    })

Object.keys(db).forEach(function(modelName) {
    if ('associate' in db[modelName]) {
        db[modelName].associate(db)
    }
})

module.exports = lodash.extend({
    sequelize: sequelize,
    Sequelize: Sequelize
}, db)
<commit_msg>s/lodash/_<commit_after>var fs = require('fs'),
    path = require('path'),
    Sequelize = require('sequelize'),
    _ = require('lodash'),
    env = process.env.NODE_ENV || 'development',
    config = require(__dirname + '/../config/config.json')[env],
    sequelize = new Sequelize(config.database, config.username, config.password, config),
    db = {}

fs
    .readdirSync(__dirname)
    .filter(function(file) {
        return (file.indexOf('.') !== 0) && (file !== 'index.js')
    })
    .forEach(function(file) {
        var model = sequelize.import(path.join(__dirname, file))
        db[model.name] = model
    })

Object.keys(db).forEach(function(modelName) {
    if ('associate' in db[modelName]) {
        db[modelName].associate(db)
    }
})

module.exports = _.extend({
    sequelize: sequelize,
    Sequelize: Sequelize
}, db)
<|endoftext|>


<commit_before>const isReply = tweet => {
  const RT = /^RT/i

  if (
    RT.test(tweet.text) ||
    tweet.is_quote_status ||
    tweet.retweeted_status ||
    tweet.in_reply_to_status_id ||
    tweet.in_reply_to_status_id_str ||
    tweet.in_reply_to_user_id ||
    tweet.in_reply_to_user_id_str ||
    tweet.in_reply_to_screen_name
  ) {
    return true
  }

  return false
}

module.exports = isReply
<commit_msg>Delete isReply.js<commit_after><|endoftext|>


<commit_before>---
layout: nil
---
var relative = null;
if (location.protocol==='file:') {
	relative = Array($('link[rel="canonical"]').attr('href').match(/\//g).length-2).join('../');
	if (relative == '') relative = './';
}
function to_relative(link, index) {
	if (!relative) return link;
	var hash = link ? link.match(/(#.*)$/) : null;
	if (hash) {
		link = link.replace(/#.*$/, '');
		if (link == '') link = './';
	}
	return link?(link.replace(/^\//, relative)+(index?(link.substr(-1)=='/'?'index.html':''):'')+(hash?hash[1]:'')):null;
}
$(function(){
	if (relative) {
		$('a').attr('href', function(a,b){return to_relative(b, true);});
		$('img').attr('src', function(a,b){return to_relative(b, false);});
	}
	if ($("img.lazy").length > 0) {
		$("img.lazy").lazyload({ threshold : 400 });
	}
	$().UItoTop({ scrollSpeed:100 });
    if ($("#etalage").length == 1) {
		$("#etalage").etalage({
			thumb_image_width: 250,
			thumb_image_height: 250,
			source_image_width: 600,
			source_image_height: 600,
			zoom_area_width: 300,
			zoom_area_height: 300,
			zoom_area_distance: 5,
			small_thumbs: 4,
			smallthumb_inactive_opacity: 0.3,
			smallthumbs_position: "bottom",
			speed: 200,
			show_icon: false,
			autoplay: false,
			keyboard: false,
			zoom_easing: false
		});
		$('#etalage').bind('contextmenu', function(){return false;});
	}
	$(".tabbtn").bind("click", function () {
		var a = $(this).parent().index();
		$(".tabbtn").removeClass("sel");
		$(this).addClass("sel");
		$(".maincontent div.mc").hide();
		$(".maincontent div").eq(a).show()
	});
	if ($('#leftprolist').length == 1) {
		var menuOffset = $('#leftprolist')[0].offsetTop;
		$(document).bind('ready scroll',function() {
			var docScroll = $(document).scrollTop();
			if(docScroll >= menuOffset) {
				$('#leftprolist').addClass('fixed').css({left:$('#header').position().left-document.body.scrollLeft});
			} else {
				$('#leftprolist').removeClass('fixed');
			}
		});
	}
	$(window).bind('resize',function(){
		if ($('#leftprolist').hasClass('fixed')) $('#leftprolist').css({left:$('#header').position().left-document.body.scrollLeft});
	});
	if ($("#main_case").length == 1) {
		$.getJSON('/products/catelog.json', function(CAT){
			window.CAT = CAT;
			$('#main_case a').each(function(){
				$(this).hover(function(){
					$('#main_case a').removeClass('current');
					$('#showcase .showcase_details').addClass('hidden');
					$('.indicator', this).addClass('indicator_wht');
					var cat=$(this).data('category');
					var sublist=$('#showcase .subcaselist'), sublist2=$('#showcase .subcaselist2'), i;
					sublist.attr('data-belongsto', cat).empty();
					sublist2.attr('data-belongsto', cat).empty();
					for (i=0; i<CAT[cat].length; i++) {
						if (i==27) {
							CAT[cat][i].name="More...";
						} else if (i>27) {
							break;
						}
						$('
').append($(''+CAT[cat][i].name+'').hover(function(){
							$('#showcase').find('.subcaselist, .subcaselist2').removeClass('hidden');
							$('#main_case a').removeClass('current');
							var fpar=$('#main_case a[data-category="'+$(this).parents('.showcase_list').attr('data-belongsto')+'"]');
							fpar.addClass('current');
							$('#showcase .showcase_details').empty().append('
'+fpar.text()+'
'+$(this).text()+'
').removeClass('hidden');
						}, function(){
							$('#main_case a').removeClass('current');
							$('#showcase').find('.subcaselist, .subcaselist2').addClass('hidden');
							$('#showcase .showcase_details').addClass('hidden');
						})).appendTo(
							i<14 ? sublist : sublist2
						);
					}
					sublist.removeClass('hidden').css({left: $('#main_case').position().left+$('#main_case').width()});
					if (sublist2.children().length==0) {
						sublist2.addClass('hidden');
					} else {
						sublist2.removeClass('hidden').css({left: sublist.position().left+sublist.width()});
					}
					var top=$(this).position().top;
					if (top+sublist.height() > $('#showcase').height()) top = $('#showcase').height() - sublist.height() - 20;
					if (top < 20) top=20;
					sublist.css({top: top});
				}, function(){
					$('#showcase').find('.subcaselist, .subcaselist2').addClass('hidden');
					$('.indicator', this).removeClass('indicator_wht');
				});
				$(this).append('

');
			});
		});
	}
	if ($('.anchors').length>0) {
		$('.clicktotop').bind('click',function(){window.location.hash='';});
		$(document).bind('ready scroll',function() {
			var anchors=$('.anchors'), i, top=f_scrollTop();
			$('#leftprolist a').removeClass('current');
			if (top < 120) return;
			for (i=0; i<anchors.length; i++) {
				var t1=anchors.eq(i).position()?anchors.eq(i).position().top:0;
				var t2=anchors.eq(i+1).position()?anchors.eq(i+1).position().top:_8HTML_.getDocHeight();
				t1-=150;
				t2-=150;
				if (top > t1 && top < t2) {
					$('#leftprolist a[data-category="'+anchors.eq(i).attr('name')+'"]').addClass('current');
				}
			}
		});
	}
});
function f_scrollTop() {
	return f_filterResults (
		window.pageYOffset ? window.pageYOffset : 0,
		document.documentElement ? document.documentElement.scrollTop : 0,
		document.body ? document.body.scrollTop : 0
	);
}
function f_filterResults(n_win, n_docel, n_body) {
	var n_result = n_win ? n_win : 0;
	if (n_docel && (!n_result || (n_result > n_docel)))
		n_result = n_docel;
	return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}
var _8HTML_=window._8HTML_||{};
/*fix footer*/
_8HTML_.getDocHeight=function(){var a=document;return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))};
_8HTML_.fixfooter=function(){$("#wrapper").height()<_8HTML_.getDocHeight()&&$("#footer").css("margin-top",parseInt($("#footer").css("margin-top"))+_8HTML_.getDocHeight()-$("#wrapper").height())}
$(_8HTML_.fixfooter);
/* totop */
(function(a){a.fn.UItoTop=function(e){var b=a.extend({text:"To Top",min:200,inDelay:600,outDelay:400,containerID:"toTop",containerHoverID:"toTopHover",scrollSpeed:1200,easingType:"linear"},e),d="#"+b.containerID,c="#"+b.containerHoverID;a("body").append($(''+b.text+'').click(function(){window.scrollTo(0, 0);}));a(d).hide().on("click.UItoTop",function(){a("html, body").animate({scrollTop:0},b.scrollSpeed,b.easingType);a("#"+b.containerHoverID,this).stop().animate({opacity:0},b.inDelay,b.easingType);return!1}).prepend('').hover(function(){a(c,this).stop().animate({opacity:1},600,"linear")},function(){a(c,this).stop().animate({opacity:0},700,"linear")});a(window).scroll(function(){var c=a(window).scrollTop();"undefined"===typeof document.body.style.maxHeight&&a(d).css({position:"absolute",top:c+a(window).height()-50});c>b.min?a(d).fadeIn(b.inDelay):a(d).fadeOut(b.Outdelay)})}})(jQuery);
<commit_msg>fix small scroll bug;<commit_after>---
layout: nil
---
var relative = null;
if (location.protocol==='file:') {
	relative = Array($('link[rel="canonical"]').attr('href').match(/\//g).length-2).join('../');
	if (relative == '') relative = './';
}
function to_relative(link, index) {
	if (!relative) return link;
	var hash = link ? link.match(/(#.*)$/) : null;
	if (hash) {
		link = link.replace(/#.*$/, '');
		if (link == '') link = './';
	}
	return link?(link.replace(/^\//, relative)+(index?(link.substr(-1)=='/'?'index.html':''):'')+(hash?hash[1]:'')):null;
}
$(function(){
	if (relative) {
		$('a').attr('href', function(a,b){return to_relative(b, true);});
		$('img').attr('src', function(a,b){return to_relative(b, false);});
	}
	if ($("img.lazy").length > 0) {
		$("img.lazy").lazyload({ threshold : 400 });
	}
	$().UItoTop({ scrollSpeed:100 });
    if ($("#etalage").length == 1) {
		$("#etalage").etalage({
			thumb_image_width: 250,
			thumb_image_height: 250,
			source_image_width: 600,
			source_image_height: 600,
			zoom_area_width: 300,
			zoom_area_height: 300,
			zoom_area_distance: 5,
			small_thumbs: 4,
			smallthumb_inactive_opacity: 0.3,
			smallthumbs_position: "bottom",
			speed: 200,
			show_icon: false,
			autoplay: false,
			keyboard: false,
			zoom_easing: false
		});
		$('#etalage').bind('contextmenu', function(){return false;});
	}
	$(".tabbtn").bind("click", function () {
		var a = $(this).parent().index();
		$(".tabbtn").removeClass("sel");
		$(this).addClass("sel");
		$(".maincontent div.mc").hide();
		$(".maincontent div").eq(a).show()
	});
	if ($('#leftprolist').length == 1) {
		var menuOffset = $('#leftprolist')[0].offsetTop;
		$(document).bind('ready scroll',function() {
			var docScroll = $(document).scrollTop();
			if(docScroll >= menuOffset) {
				$('#leftprolist').addClass('fixed').css({left:$('#header').position().left-document.body.scrollLeft});
			} else {
				$('#leftprolist').removeClass('fixed');
			}
		});
	}
	$(window).bind('resize',function(){
		if ($('#leftprolist').hasClass('fixed')) $('#leftprolist').css({left:$('#header').position().left-document.body.scrollLeft});
	});
	if ($("#main_case").length == 1) {
		$.getJSON('/products/catelog.json', function(CAT){
			window.CAT = CAT;
			$('#main_case a').each(function(){
				$(this).hover(function(){
					$('#main_case a').removeClass('current');
					$('#showcase .showcase_details').addClass('hidden');
					$('.indicator', this).addClass('indicator_wht');
					var cat=$(this).data('category');
					var sublist=$('#showcase .subcaselist'), sublist2=$('#showcase .subcaselist2'), i;
					sublist.attr('data-belongsto', cat).empty();
					sublist2.attr('data-belongsto', cat).empty();
					for (i=0; i<CAT[cat].length; i++) {
						if (i==27) {
							CAT[cat][i].name="More...";
						} else if (i>27) {
							break;
						}
						$('
').append($(''+CAT[cat][i].name+'').hover(function(){
							$('#showcase').find('.subcaselist, .subcaselist2').removeClass('hidden');
							$('#main_case a').removeClass('current');
							var fpar=$('#main_case a[data-category="'+$(this).parents('.showcase_list').attr('data-belongsto')+'"]');
							fpar.addClass('current');
							$('#showcase .showcase_details').empty().append('
'+fpar.text()+'
'+$(this).text()+'
').removeClass('hidden');
						}, function(){
							$('#main_case a').removeClass('current');
							$('#showcase').find('.subcaselist, .subcaselist2').addClass('hidden');
							$('#showcase .showcase_details').addClass('hidden');
						})).appendTo(
							i<14 ? sublist : sublist2
						);
					}
					sublist.removeClass('hidden').css({left: $('#main_case').position().left+$('#main_case').width()});
					if (sublist2.children().length==0) {
						sublist2.addClass('hidden');
					} else {
						sublist2.removeClass('hidden').css({left: sublist.position().left+sublist.width()});
					}
					var top=$(this).position().top;
					if (top+sublist.height() > $('#showcase').height()) top = $('#showcase').height() - sublist.height() - 20;
					if (top < 20) top=20;
					sublist.css({top: top});
				}, function(){
					$('#showcase').find('.subcaselist, .subcaselist2').addClass('hidden');
					$('.indicator', this).removeClass('indicator_wht');
				});
				$(this).append('

');
			});
		});
	}
	if ($('.anchors').length>0) {
		$('.clicktotop').bind('click',function(){window.location.hash='';window.scrollTo(0, 0);});
		$(document).bind('ready scroll',function() {
			var anchors=$('.anchors'), i, top=f_scrollTop();
			$('#leftprolist a').removeClass('current');
			if (top < 120) return;
			for (i=0; i<anchors.length; i++) {
				var t1=anchors.eq(i).position()?anchors.eq(i).position().top:0;
				var t2=anchors.eq(i+1).position()?anchors.eq(i+1).position().top:_8HTML_.getDocHeight();
				t1-=150;
				t2-=150;
				if (top > t1 && top < t2) {
					$('#leftprolist a[data-category="'+anchors.eq(i).attr('name')+'"]').addClass('current');
				}
			}
		});
	}
});
function f_scrollTop() {
	return f_filterResults (
		window.pageYOffset ? window.pageYOffset : 0,
		document.documentElement ? document.documentElement.scrollTop : 0,
		document.body ? document.body.scrollTop : 0
	);
}
function f_filterResults(n_win, n_docel, n_body) {
	var n_result = n_win ? n_win : 0;
	if (n_docel && (!n_result || (n_result > n_docel)))
		n_result = n_docel;
	return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}
var _8HTML_=window._8HTML_||{};
/*fix footer*/
_8HTML_.getDocHeight=function(){var a=document;return Math.max(Math.max(a.body.scrollHeight,a.documentElement.scrollHeight),Math.max(a.body.offsetHeight,a.documentElement.offsetHeight),Math.max(a.body.clientHeight,a.documentElement.clientHeight))};
_8HTML_.fixfooter=function(){$("#wrapper").height()<_8HTML_.getDocHeight()&&$("#footer").css("margin-top",parseInt($("#footer").css("margin-top"))+_8HTML_.getDocHeight()-$("#wrapper").height())}
$(_8HTML_.fixfooter);
/* totop */
(function(a){a.fn.UItoTop=function(e){var b=a.extend({text:"To Top",min:200,inDelay:600,outDelay:400,containerID:"toTop",containerHoverID:"toTopHover",scrollSpeed:1200,easingType:"linear"},e),d="#"+b.containerID,c="#"+b.containerHoverID;a("body").append($(''+b.text+'').click(function(){window.scrollTo(0, 0);}));a(d).hide().on("click.UItoTop",function(){a("html, body").animate({scrollTop:0},b.scrollSpeed,b.easingType);a("#"+b.containerHoverID,this).stop().animate({opacity:0},b.inDelay,b.easingType);return!1}).prepend('').hover(function(){a(c,this).stop().animate({opacity:1},600,"linear")},function(){a(c,this).stop().animate({opacity:0},700,"linear")});a(window).scroll(function(){var c=a(window).scrollTop();"undefined"===typeof document.body.style.maxHeight&&a(d).css({position:"absolute",top:c+a(window).height()-50});c>b.min?a(d).fadeIn(b.inDelay):a(d).fadeOut(b.Outdelay)})}})(jQuery);
<|endoftext|>


<commit_before>define(['mac/roman'], function(macintoshRoman) {

  'use strict';

  return function(resource) {
    var pictDV = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    var fileSizeBytes = pictDV.getUint16(0, false);
    var top = pictDV.getInt16(2, false);
    var left = pictDV.getInt16(4, false);
    var bottom = pictDV.getInt16(6, false);
    var right = pictDV.getInt16(8, false);
    var canvas = document.createElement('CANVAS');
    canvas.width = right - left;
    canvas.height = bottom - top;
    var ctx = canvas.getContext('2d');
    var fontFamily = 'sans-serif';
    if (resource.data[10] === 0x11 && resource.data[11] === 0x01) {
      // version 1
      console.log('PICTv1', left, top, right, bottom);
      function region() {
        var len = pictDV.getUint16(pictPos);
        var region = {
          left: pictDV.getInt16(pictPos + 2),
          top: pictDV.getInt16(pictPos + 4),
          right: pictDV.getInt16(pictPos + 6),
          bottom: pictDV.getInt16(pictPos + 8),
        };
        if (len > 10) {
          region.extra = resource.data.subarray(pictPos + 10, pictPos + len);
        }
        pictPos += len;
        return region;
      }
      var currentX = 0, currentY = 0;
      pictV1loop:
      for (var pictPos = 12; resource.data[pictPos] !== 0xff; ) {
        switch(resource.data[pictPos++]) {
          case 0x00: break; // no-op
          case 0x01: // clipping region
            var clipRegion = region();
            console.log('clip', clipRegion);
            break;
          case 0x02: // background pattern
            pictPos += 8;
            break;
          case 0x03: // font number for text
            pictPos += 2;
            break;
          case 0x04: // text font style
            pictPos += 1;
            break;
          case 0x05: // source mode
            pictPos += 2;
            break;
          case 0x06: // extra space
            pictPos += 4;
            break;
          case 0x07: // pen size
            pictPos += 4;
            break;
          case 0x08: // pen mode
            pictPos += 2;
            break;
          case 0x09: // pen pattern
            pictPos += 8;
            break;
          case 0x0A: // fill pattern
            pictPos += 8;
            break;
          case 0x0B: // oval size
            pictPos += 4;
            break;
          case 0x0C: // origin
            currentY = pictDV.getInt16(pictPos);
            currentX = pictDV.getInt16(pictPos + 2);
            pictPos += 4;
            break;
          case 0x0D: // text size
            ctx.font = fontFamily + ' ' + pictDV.getUint16(pictPos) + 'px';
            pictPos += 2;
            break;
          case 0x0E: // foreground color
            pictPos += 4;
            break;
          case 0x0F: // background color
            pictPos += 4;
            break;
          case 0x10: // txratio
            pictPos += 8;
            break;
          case 0x11: // version
            pictPos += 1;
            break;
          case 0x20: // line
            pictPos += 8;
            break;
          case 0x21: // line from
            pictPos += 4;
            break;
          case 0x22: // short line
            pictPos += 6;
            break;
          case 0x23: // short line from
            pictPos += 2;
            break;
          case 0x28: // long text
            var y = pictDV.getInt16(pictPos, false);
            var x = pictDV.getInt16(pictPos + 2, false);
            pictPos += 4;
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX = x;
            currentY = y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x29: // DHtext
            var x = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX += x;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x2A: // DVtext
            var y = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentY += y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x2B: // DHDVtext
            var x = resource.data[pictPos++];
            var y = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX += x;
            currentY += y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x30: // frame rect
            pictPos += 8;
            break;
          case 0x31: // paint rect
            pictPos += 8;
            break;
          case 0x32: // erase rect
            pictPos += 8;
            break;
          case 0x33: // invert rect
            pictPos += 8;
            break;
          case 0x34: // fill rect
            pictPos += 8;
            break;
          case 0x38: // frame same rect
            break;
          case 0x39: // paint same rect
            break;
          case 0x3A: // erase same rect
            break;
          case 0x3B: // invert same rect
            break;
          case 0x3C: // fill same rect
            break;

          case 0x40: // frame rrect
            pictPos += 8;
            break;
          case 0x41: // paint rrect
            pictPos += 8;
            break;
          case 0x42: // erase rrect
            pictPos += 8;
            break;
          case 0x43: // invert rrect
            pictPos += 8;
            break;
          case 0x44: // fill rrect
            pictPos += 8;
            break;
          case 0x48: // frame same rrect
            break;
          case 0x49: // paint same rrect
            break;
          case 0x4A: // erase same rrect
            break;
          case 0x4B: // invert same rrect
            break;
          case 0x4C: // fill same rrect
            break;

          case 0x50: // frame oval
            pictPos += 8;
            break;
          case 0x51: // paint oval
            pictPos += 8;
            break;
          case 0x52: // erase oval
            pictPos += 8;
            break;
          case 0x53: // invert oval
            pictPos += 8;
            break;
          case 0x54: // fill oval
            pictPos += 8;
            break;
          case 0x58: // frame same oval
            break;
          case 0x59: // paint same oval
            break;
          case 0x5A: // erase same oval
            break;
          case 0x5B: // invert same oval
            break;
          case 0x5C: // fill same oval
            break;

          case 0x60: // frame arc
            pictPos += 12;
            break;
          case 0x61: // paint arc
            pictPos += 12;
            break;
          case 0x62: // erase arc
            pictPos += 12;
            break;
          case 0x63: // invert arc
            pictPos += 12;
            break;
          case 0x64: // fill arc
            pictPos += 12;
            break;
          case 0x68: // frame same arc
            pictPos += 4;
            break;
          case 0x69: // paint same arc
            pictPos += 4;
            break;
          case 0x6A: // erase same arc
            pictPos += 4;
            break;
          case 0x6B: // invert same arc
            pictPos += 4;
            break;
          case 0x6C: // fill same arc
            pictPos += 4;
            break;

          // case 0x70: // frame poly
          // case 0x71: // paint poly
          // case 0x72: // erase poly
          // case 0x73: // invert poly
          // case 0x74: // fill poly
          case 0x78: // frame same poly
            break;
          case 0x79: // paint same poly
            break;
          case 0x7A: // erase same poly
            break;
          case 0x7B: // invert same poly
            break;
          case 0x7C: // fill same poly
            break;

          case 0x80: // frame region
            var frameRegion = region();
            console.log('frame', frameRegion);
            break;
          case 0x81: // paint region
            var paintRegion = region();
            console.log('paint', paintRegion);
            break;
          case 0x82: // erase region
            var eraseRegion = region();
            console.log('erase', eraseRegion);
            break;
          case 0x83: // invert region
            var invertRegion = region();
            console.log('invert', invertRegion);
            break;
          case 0x84: // fill region
            var fillRegion = region();
            console.log('fill', fillRegion);
            break;
          case 0x88: // frame same region
            break;
          case 0x89: // paint same region
            break;
          case 0x8A: // erase same region
            break;
          case 0x8B: // invert same region
            break;
          case 0x8C: // fill same region
            break;

          //case 0x90: // copy bits to clipped rect
          //case 0x91: // copy bits to clipped region

          case 0x98: // copy packed bits to clipped rect
            var pixmap = {
              baseAddr: pictDV.getUint16(pictPos, false),
              rowBytes: pictDV.getUint16(pictPos + 2, false),
              bounds: {
                top: pictDV.getInt16(pictPos + 2 + 2, false),
                left: pictDV.getInt16(pictPos + 2 + 4, false),
                bottom: pictDV.getInt16(pictPos + 2 + 6, false),
                right: pictDV.getInt16(pictPos + 2 + 8, false),
              },
              recordVersionNumber: pictDV.getInt16(pictPos + 2 + 10, false),
              packType: pictDV.getInt16(pictPos + 2 + 12, false),
              packSize: pictDV.getInt32(pictPos + 2 + 14, false),
              hRes: fixed(pictDV.getInt32(pictPos + 2 + 18, false)),
              vRes: fixed(pictDV.getInt32(pictPos + 2 + 22, false)),
              pixelType: pictDV.getInt16(pictPos + 2 + 26, false),
              pixelSize: pictDV.getInt16(pictPos + 2 + 28, false),
              componentsPerPixel: pictDV.getInt16(pictPos + 2 + 30, false),
              bitsPerComponent: pictDV.getInt16(pictPos + 2 + 32, false),
              planeBytes: pictDV.getInt32(pictPos + 2 + 34, false),
              colorTableHandle: pictDV.getInt16(pictPos + 2 + 38, false),
              reserved: pictDV.getInt32(pictPos + 2 + 40, false),
            };
            console.log(resource.data.subarray(pictPos, pictPos + 2 + 44));
            pictPos += 2 + 44;
            console.log(pixmap);
            break pictV1loop;

          //case 0x99: // copy packed bits to clipped region

          case 0xA0: // short comment
            var kind = pictDV.getUint16(pictPos, false);
            console.log('comment', kind);
            pictPos += 2;
            break;
          case 0xA1: // long comment
            var kind = pictDV.getUint16(pictPos, false);
            var len = pictDV.getUint16(pictPos + 2, false);
            var commentData = resource.data.subarray(pictPos + 4, pictPos + 4 + len);
            console.log('comment', kind, commentData);
            pictPos += 4 + len;
            break;

          //case 0xFF: // end of picture (checked by outer loop)

          default:
            console.error('unhandled PICTv1 opcode: 0x' + resource.data[pictPos - 1].toString(16));
            break pictV1loop;
        }
      }
    }
    else if (resource.data[10] === 0x00 && resource.data[11] === 0x11
        && resource.data[12] === 0x02 && resource.data[13] === 0xff) {
      // version 2
      console.log('PICTv2', left, top, right, bottom);
    }
    else {
      console.error('unknown PICT format version');
      return;
    }
    resource.image = {width:right - left, height:bottom-top, url:canvas.toDataURL()};
    if (left) resource.image.offsetX = left;
    if (top) resource.image.offsetY = top;
  };

});
<commit_msg>Use fixedPoint module<commit_after>define(['mac/roman', 'mac/fixedPoint'], function(macintoshRoman, fixedPoint) {

  'use strict';

  return function(resource) {
    var pictDV = new DataView(resource.data.buffer, resource.data.byteOffset, resource.data.byteLength);
    var fileSizeBytes = pictDV.getUint16(0, false);
    var top = pictDV.getInt16(2, false);
    var left = pictDV.getInt16(4, false);
    var bottom = pictDV.getInt16(6, false);
    var right = pictDV.getInt16(8, false);
    var canvas = document.createElement('CANVAS');
    canvas.width = right - left;
    canvas.height = bottom - top;
    var ctx = canvas.getContext('2d');
    var fontFamily = 'sans-serif';
    if (resource.data[10] === 0x11 && resource.data[11] === 0x01) {
      // version 1
      console.log('PICTv1', left, top, right, bottom);
      function region() {
        var len = pictDV.getUint16(pictPos);
        var region = {
          left: pictDV.getInt16(pictPos + 2),
          top: pictDV.getInt16(pictPos + 4),
          right: pictDV.getInt16(pictPos + 6),
          bottom: pictDV.getInt16(pictPos + 8),
        };
        if (len > 10) {
          region.extra = resource.data.subarray(pictPos + 10, pictPos + len);
        }
        pictPos += len;
        return region;
      }
      var currentX = 0, currentY = 0;
      pictV1loop:
      for (var pictPos = 12; resource.data[pictPos] !== 0xff; ) {
        switch(resource.data[pictPos++]) {
          case 0x00: break; // no-op
          case 0x01: // clipping region
            var clipRegion = region();
            console.log('clip', clipRegion);
            break;
          case 0x02: // background pattern
            pictPos += 8;
            break;
          case 0x03: // font number for text
            pictPos += 2;
            break;
          case 0x04: // text font style
            pictPos += 1;
            break;
          case 0x05: // source mode
            pictPos += 2;
            break;
          case 0x06: // extra space
            pictPos += 4;
            break;
          case 0x07: // pen size
            pictPos += 4;
            break;
          case 0x08: // pen mode
            pictPos += 2;
            break;
          case 0x09: // pen pattern
            pictPos += 8;
            break;
          case 0x0A: // fill pattern
            pictPos += 8;
            break;
          case 0x0B: // oval size
            pictPos += 4;
            break;
          case 0x0C: // origin
            currentY = pictDV.getInt16(pictPos);
            currentX = pictDV.getInt16(pictPos + 2);
            pictPos += 4;
            break;
          case 0x0D: // text size
            ctx.font = fontFamily + ' ' + pictDV.getUint16(pictPos) + 'px';
            pictPos += 2;
            break;
          case 0x0E: // foreground color
            pictPos += 4;
            break;
          case 0x0F: // background color
            pictPos += 4;
            break;
          case 0x10: // txratio
            pictPos += 8;
            break;
          case 0x11: // version
            pictPos += 1;
            break;
          case 0x20: // line
            pictPos += 8;
            break;
          case 0x21: // line from
            pictPos += 4;
            break;
          case 0x22: // short line
            pictPos += 6;
            break;
          case 0x23: // short line from
            pictPos += 2;
            break;
          case 0x28: // long text
            var y = pictDV.getInt16(pictPos, false);
            var x = pictDV.getInt16(pictPos + 2, false);
            pictPos += 4;
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX = x;
            currentY = y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x29: // DHtext
            var x = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX += x;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x2A: // DVtext
            var y = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentY += y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x2B: // DHDVtext
            var x = resource.data[pictPos++];
            var y = resource.data[pictPos++];
            var text = macintoshRoman(resource.data, pictPos+1, resource.data[pictPos]);
            pictPos += 1 + text.length;
            currentX += x;
            currentY += y;
            ctx.fillText(text, currentX, currentY);
            break;
          case 0x30: // frame rect
            pictPos += 8;
            break;
          case 0x31: // paint rect
            pictPos += 8;
            break;
          case 0x32: // erase rect
            pictPos += 8;
            break;
          case 0x33: // invert rect
            pictPos += 8;
            break;
          case 0x34: // fill rect
            pictPos += 8;
            break;
          case 0x38: // frame same rect
            break;
          case 0x39: // paint same rect
            break;
          case 0x3A: // erase same rect
            break;
          case 0x3B: // invert same rect
            break;
          case 0x3C: // fill same rect
            break;

          case 0x40: // frame rrect
            pictPos += 8;
            break;
          case 0x41: // paint rrect
            pictPos += 8;
            break;
          case 0x42: // erase rrect
            pictPos += 8;
            break;
          case 0x43: // invert rrect
            pictPos += 8;
            break;
          case 0x44: // fill rrect
            pictPos += 8;
            break;
          case 0x48: // frame same rrect
            break;
          case 0x49: // paint same rrect
            break;
          case 0x4A: // erase same rrect
            break;
          case 0x4B: // invert same rrect
            break;
          case 0x4C: // fill same rrect
            break;

          case 0x50: // frame oval
            pictPos += 8;
            break;
          case 0x51: // paint oval
            pictPos += 8;
            break;
          case 0x52: // erase oval
            pictPos += 8;
            break;
          case 0x53: // invert oval
            pictPos += 8;
            break;
          case 0x54: // fill oval
            pictPos += 8;
            break;
          case 0x58: // frame same oval
            break;
          case 0x59: // paint same oval
            break;
          case 0x5A: // erase same oval
            break;
          case 0x5B: // invert same oval
            break;
          case 0x5C: // fill same oval
            break;

          case 0x60: // frame arc
            pictPos += 12;
            break;
          case 0x61: // paint arc
            pictPos += 12;
            break;
          case 0x62: // erase arc
            pictPos += 12;
            break;
          case 0x63: // invert arc
            pictPos += 12;
            break;
          case 0x64: // fill arc
            pictPos += 12;
            break;
          case 0x68: // frame same arc
            pictPos += 4;
            break;
          case 0x69: // paint same arc
            pictPos += 4;
            break;
          case 0x6A: // erase same arc
            pictPos += 4;
            break;
          case 0x6B: // invert same arc
            pictPos += 4;
            break;
          case 0x6C: // fill same arc
            pictPos += 4;
            break;

          // case 0x70: // frame poly
          // case 0x71: // paint poly
          // case 0x72: // erase poly
          // case 0x73: // invert poly
          // case 0x74: // fill poly
          case 0x78: // frame same poly
            break;
          case 0x79: // paint same poly
            break;
          case 0x7A: // erase same poly
            break;
          case 0x7B: // invert same poly
            break;
          case 0x7C: // fill same poly
            break;

          case 0x80: // frame region
            var frameRegion = region();
            console.log('frame', frameRegion);
            break;
          case 0x81: // paint region
            var paintRegion = region();
            console.log('paint', paintRegion);
            break;
          case 0x82: // erase region
            var eraseRegion = region();
            console.log('erase', eraseRegion);
            break;
          case 0x83: // invert region
            var invertRegion = region();
            console.log('invert', invertRegion);
            break;
          case 0x84: // fill region
            var fillRegion = region();
            console.log('fill', fillRegion);
            break;
          case 0x88: // frame same region
            break;
          case 0x89: // paint same region
            break;
          case 0x8A: // erase same region
            break;
          case 0x8B: // invert same region
            break;
          case 0x8C: // fill same region
            break;

          //case 0x90: // copy bits to clipped rect
          //case 0x91: // copy bits to clipped region

          case 0x98: // copy packed bits to clipped rect
            var pixmap = {
              baseAddr: pictDV.getUint16(pictPos, false),
              rowBytes: pictDV.getUint16(pictPos + 2, false),
              bounds: {
                top: pictDV.getInt16(pictPos + 2 + 2, false),
                left: pictDV.getInt16(pictPos + 2 + 4, false),
                bottom: pictDV.getInt16(pictPos + 2 + 6, false),
                right: pictDV.getInt16(pictPos + 2 + 8, false),
              },
              recordVersionNumber: pictDV.getInt16(pictPos + 2 + 10, false),
              packType: pictDV.getInt16(pictPos + 2 + 12, false),
              packSize: pictDV.getInt32(pictPos + 2 + 14, false),
              hRes: fixedPoint.fromInt32(pictDV.getInt32(pictPos + 2 + 18, false)),
              vRes: fixedPoint.fromInt32(pictDV.getInt32(pictPos + 2 + 22, false)),
              pixelType: pictDV.getInt16(pictPos + 2 + 26, false),
              pixelSize: pictDV.getInt16(pictPos + 2 + 28, false),
              componentsPerPixel: pictDV.getInt16(pictPos + 2 + 30, false),
              bitsPerComponent: pictDV.getInt16(pictPos + 2 + 32, false),
              planeBytes: pictDV.getInt32(pictPos + 2 + 34, false),
              colorTableHandle: pictDV.getInt16(pictPos + 2 + 38, false),
              reserved: pictDV.getInt32(pictPos + 2 + 40, false),
            };
            console.log(resource.data.subarray(pictPos, pictPos + 2 + 44));
            pictPos += 2 + 44;
            console.log(pixmap);
            break pictV1loop;

          //case 0x99: // copy packed bits to clipped region

          case 0xA0: // short comment
            var kind = pictDV.getUint16(pictPos, false);
            console.log('comment', kind);
            pictPos += 2;
            break;
          case 0xA1: // long comment
            var kind = pictDV.getUint16(pictPos, false);
            var len = pictDV.getUint16(pictPos + 2, false);
            var commentData = resource.data.subarray(pictPos + 4, pictPos + 4 + len);
            console.log('comment', kind, commentData);
            pictPos += 4 + len;
            break;

          //case 0xFF: // end of picture (checked by outer loop)

          default:
            console.error('unhandled PICTv1 opcode: 0x' + resource.data[pictPos - 1].toString(16));
            break pictV1loop;
        }
      }
    }
    else if (resource.data[10] === 0x00 && resource.data[11] === 0x11
        && resource.data[12] === 0x02 && resource.data[13] === 0xff) {
      // version 2
      console.log('PICTv2', left, top, right, bottom);
    }
    else {
      console.error('unknown PICT format version');
      return;
    }
    resource.image = {width:right - left, height:bottom-top, url:canvas.toDataURL()};
    if (left) resource.image.offsetX = left;
    if (top) resource.image.offsetY = top;
  };

});
<|endoftext|>