<commit_before>/*global gettext, pgettext, get_format, quickElement, removeChildren, addEvent*/
/*
calendar.js - Calendar functions by Adrian Holovaty
depends on core.js for utility functions like removeChildren or quickElement
*/

(function() {
    'use strict';
    // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
    var CalendarNamespace = {
        monthsOfYear: [
            gettext('January'),
            gettext('February'),
            gettext('March'),
            gettext('April'),
            gettext('May'),
            gettext('June'),
            gettext('July'),
            gettext('August'),
            gettext('September'),
            gettext('October'),
            gettext('November'),
            gettext('December')
        ],
        daysOfWeek: [
            pgettext('one letter Sunday', 'S'),
            pgettext('one letter Monday', 'M'),
            pgettext('one letter Tuesday', 'T'),
            pgettext('one letter Wednesday', 'W'),
            pgettext('one letter Thursday', 'T'),
            pgettext('one letter Friday', 'F'),
            pgettext('one letter Saturday', 'S')
        ],
        firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
        isLeapYear: function(year) {
            return (((year % 4) === 0) && ((year % 100) !== 0 ) || ((year % 400) === 0));
        },
        getDaysInMonth: function(month, year) {
            var days;
            if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) {
                days = 31;
            }
            else if (month === 4 || month === 6 || month === 9 || month === 11) {
                days = 30;
            }
            else if (month === 2 && CalendarNamespace.isLeapYear(year)) {
                days = 29;
            }
            else {
                days = 28;
            }
            return days;
        },
        draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
            var today = new Date();
            var todayDay = today.getDate();
            var todayMonth = today.getMonth() + 1;
            var todayYear = today.getFullYear();
            var todayClass = '';

            // Use UTC functions here because the date field does not contain time
            // and using the UTC function variants prevent the local time offset
            // from altering the date, specifically the day field.  For example:
            //
            // ```
            // var x = new Date('2013-10-02');
            // var day = x.getDate();
            // ```
            //
            // The day variable above will be 1 instead of 2 in, say, US Pacific time
            // zone.
            var isSelectedMonth = false;
            if (typeof selected !== 'undefined') {
                isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month);
            }

            month = parseInt(month);
            year = parseInt(year);
            var calDiv = document.getElementById(div_id);
            removeChildren(calDiv);
            var calTable = document.createElement('table');
            quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year);
            var tableBody = quickElement('tbody', calTable);

            // Draw days-of-week header
            var tableRow = quickElement('tr', tableBody);
            for (var i = 0; i < 7; i++) {
                quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
            }

            var startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
            var days = CalendarNamespace.getDaysInMonth(month, year);

            var nonDayCell;

            // Draw blanks before first of month
            tableRow = quickElement('tr', tableBody);
            for (i = 0; i < startingPos; i++) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            function calendarMonth(y, m) {
                function onClick(e) {
                    e.preventDefault();
                    callback(y, m, django.jQuery(this).text());
                }
                return onClick;
            }

            // Draw days of month
            var currentDay = 1;
            for (i = startingPos; currentDay <= days; i++) {
                if (i % 7 === 0 && currentDay !== 1) {
                    tableRow = quickElement('tr', tableBody);
                }
                if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) {
                    todayClass = 'today';
                } else {
                    todayClass = '';
                }

                // use UTC function; see above for explanation.
                if (isSelectedMonth && currentDay === selected.getUTCDate()) {
                    if (todayClass !== '') {
                        todayClass += " ";
                    }
                    todayClass += "selected";
                }

                var cell = quickElement('td', tableRow, '', 'class', todayClass);
                var link = quickElement('a', cell, currentDay, 'href', '#');
                addEvent(link, 'click', calendarMonth(year, month));
                currentDay++;
            }

            // Draw blanks after end of month (optional, but makes for valid code)
            while (tableRow.childNodes.length < 7) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            calDiv.appendChild(calTable);
        }
    };

    // Calendar -- A calendar instance
    function Calendar(div_id, callback, selected) {
        // div_id (string) is the ID of the element in which the calendar will
        //     be displayed
        // callback (string) is the name of a JavaScript function that will be
        //     called with the parameters (year, month, day) when a day in the
        //     calendar is clicked
        this.div_id = div_id;
        this.callback = callback;
        this.today = new Date();
        this.currentMonth = this.today.getMonth() + 1;
        this.currentYear = this.today.getFullYear();
        if (typeof selected !== 'undefined') {
            this.selected = selected;
        }
    }
    Calendar.prototype = {
        drawCurrent: function() {
            CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
        },
        drawDate: function(month, year, selected) {
            this.currentMonth = month;
            this.currentYear = year;

            if(selected) {
                this.selected = selected;
            }

            this.drawCurrent();
        },
        drawPreviousMonth: function() {
            if (this.currentMonth === 1) {
                this.currentMonth = 12;
                this.currentYear--;
            }
            else {
                this.currentMonth--;
            }
            this.drawCurrent();
        },
        drawNextMonth: function() {
            if (this.currentMonth === 12) {
                this.currentMonth = 1;
                this.currentYear++;
            }
            else {
                this.currentMonth++;
            }
            this.drawCurrent();
        },
        drawPreviousYear: function() {
            this.currentYear--;
            this.drawCurrent();
        },
        drawNextYear: function() {
            this.currentYear++;
            this.drawCurrent();
        }
    };
    window.Calendar = Calendar;
    window.CalendarNamespace = CalendarNamespace;
})();
<commit_msg>Removed lone usage of jQuery in admin's calendar.js<commit_after>/*global gettext, pgettext, get_format, quickElement, removeChildren, addEvent*/
/*
calendar.js - Calendar functions by Adrian Holovaty
depends on core.js for utility functions like removeChildren or quickElement
*/

(function() {
    'use strict';
    // CalendarNamespace -- Provides a collection of HTML calendar-related helper functions
    var CalendarNamespace = {
        monthsOfYear: [
            gettext('January'),
            gettext('February'),
            gettext('March'),
            gettext('April'),
            gettext('May'),
            gettext('June'),
            gettext('July'),
            gettext('August'),
            gettext('September'),
            gettext('October'),
            gettext('November'),
            gettext('December')
        ],
        daysOfWeek: [
            pgettext('one letter Sunday', 'S'),
            pgettext('one letter Monday', 'M'),
            pgettext('one letter Tuesday', 'T'),
            pgettext('one letter Wednesday', 'W'),
            pgettext('one letter Thursday', 'T'),
            pgettext('one letter Friday', 'F'),
            pgettext('one letter Saturday', 'S')
        ],
        firstDayOfWeek: parseInt(get_format('FIRST_DAY_OF_WEEK')),
        isLeapYear: function(year) {
            return (((year % 4) === 0) && ((year % 100) !== 0 ) || ((year % 400) === 0));
        },
        getDaysInMonth: function(month, year) {
            var days;
            if (month === 1 || month === 3 || month === 5 || month === 7 || month === 8 || month === 10 || month === 12) {
                days = 31;
            }
            else if (month === 4 || month === 6 || month === 9 || month === 11) {
                days = 30;
            }
            else if (month === 2 && CalendarNamespace.isLeapYear(year)) {
                days = 29;
            }
            else {
                days = 28;
            }
            return days;
        },
        draw: function(month, year, div_id, callback, selected) { // month = 1-12, year = 1-9999
            var today = new Date();
            var todayDay = today.getDate();
            var todayMonth = today.getMonth() + 1;
            var todayYear = today.getFullYear();
            var todayClass = '';

            // Use UTC functions here because the date field does not contain time
            // and using the UTC function variants prevent the local time offset
            // from altering the date, specifically the day field.  For example:
            //
            // ```
            // var x = new Date('2013-10-02');
            // var day = x.getDate();
            // ```
            //
            // The day variable above will be 1 instead of 2 in, say, US Pacific time
            // zone.
            var isSelectedMonth = false;
            if (typeof selected !== 'undefined') {
                isSelectedMonth = (selected.getUTCFullYear() === year && (selected.getUTCMonth() + 1) === month);
            }

            month = parseInt(month);
            year = parseInt(year);
            var calDiv = document.getElementById(div_id);
            removeChildren(calDiv);
            var calTable = document.createElement('table');
            quickElement('caption', calTable, CalendarNamespace.monthsOfYear[month - 1] + ' ' + year);
            var tableBody = quickElement('tbody', calTable);

            // Draw days-of-week header
            var tableRow = quickElement('tr', tableBody);
            for (var i = 0; i < 7; i++) {
                quickElement('th', tableRow, CalendarNamespace.daysOfWeek[(i + CalendarNamespace.firstDayOfWeek) % 7]);
            }

            var startingPos = new Date(year, month - 1, 1 - CalendarNamespace.firstDayOfWeek).getDay();
            var days = CalendarNamespace.getDaysInMonth(month, year);

            var nonDayCell;

            // Draw blanks before first of month
            tableRow = quickElement('tr', tableBody);
            for (i = 0; i < startingPos; i++) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            function calendarMonth(y, m) {
                function onClick(e) {
                    e.preventDefault();
                    callback(y, m, this.textContent);
                }
                return onClick;
            }

            // Draw days of month
            var currentDay = 1;
            for (i = startingPos; currentDay <= days; i++) {
                if (i % 7 === 0 && currentDay !== 1) {
                    tableRow = quickElement('tr', tableBody);
                }
                if ((currentDay === todayDay) && (month === todayMonth) && (year === todayYear)) {
                    todayClass = 'today';
                } else {
                    todayClass = '';
                }

                // use UTC function; see above for explanation.
                if (isSelectedMonth && currentDay === selected.getUTCDate()) {
                    if (todayClass !== '') {
                        todayClass += " ";
                    }
                    todayClass += "selected";
                }

                var cell = quickElement('td', tableRow, '', 'class', todayClass);
                var link = quickElement('a', cell, currentDay, 'href', '#');
                addEvent(link, 'click', calendarMonth(year, month));
                currentDay++;
            }

            // Draw blanks after end of month (optional, but makes for valid code)
            while (tableRow.childNodes.length < 7) {
                nonDayCell = quickElement('td', tableRow, ' ');
                nonDayCell.className = "nonday";
            }

            calDiv.appendChild(calTable);
        }
    };

    // Calendar -- A calendar instance
    function Calendar(div_id, callback, selected) {
        // div_id (string) is the ID of the element in which the calendar will
        //     be displayed
        // callback (string) is the name of a JavaScript function that will be
        //     called with the parameters (year, month, day) when a day in the
        //     calendar is clicked
        this.div_id = div_id;
        this.callback = callback;
        this.today = new Date();
        this.currentMonth = this.today.getMonth() + 1;
        this.currentYear = this.today.getFullYear();
        if (typeof selected !== 'undefined') {
            this.selected = selected;
        }
    }
    Calendar.prototype = {
        drawCurrent: function() {
            CalendarNamespace.draw(this.currentMonth, this.currentYear, this.div_id, this.callback, this.selected);
        },
        drawDate: function(month, year, selected) {
            this.currentMonth = month;
            this.currentYear = year;

            if(selected) {
                this.selected = selected;
            }

            this.drawCurrent();
        },
        drawPreviousMonth: function() {
            if (this.currentMonth === 1) {
                this.currentMonth = 12;
                this.currentYear--;
            }
            else {
                this.currentMonth--;
            }
            this.drawCurrent();
        },
        drawNextMonth: function() {
            if (this.currentMonth === 12) {
                this.currentMonth = 1;
                this.currentYear++;
            }
            else {
                this.currentMonth++;
            }
            this.drawCurrent();
        },
        drawPreviousYear: function() {
            this.currentYear--;
            this.drawCurrent();
        },
        drawNextYear: function() {
            this.currentYear++;
            this.drawCurrent();
        }
    };
    window.Calendar = Calendar;
    window.CalendarNamespace = CalendarNamespace;
})();
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, Trent Mick. All rights reserved.
 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
 */

var util = require('util'),
    format = util.format;
var p = console.warn;
var child_process = require('child_process'),
    spawn = child_process.spawn,
    exec = child_process.exec;
var os = require('os');
var path = require('path');
var fs = require('fs');

var assert = require('assert-plus');
var WError = require('verror').WError;
var dashdash = require('dashdash');
var sprintf = require('extsprintf').sprintf;

// ---- internal support stuff

function space(n) {
    var s = '';
    for (var i = 0; i < n; i++) {
        s += ' ';
    }
    return s;
}

function objCopy(obj) {
    var copy = {};
    Object.keys(obj).forEach(function (k) {
        copy[k] = obj[k];
    });
    return copy;
}

// ---- Errors

/**
 * Base CmdlnError. Instances (including derived errors) have these attributes:
 *
 * - `message` {String} All errors will have a message.
 * - `code` {String} A CamelCase code string for this type of error. E.g.
 *   'Cmdln' (generic), 'IllegalOption', etc.
 * - `cause` {Error} Optional. An underlying cause error.
 */
function CmdlnError(options) {
    assert.object(options, 'options');
    assert.string(options.message, 'options.message');
    assert.optionalString(options.code, 'options.code');
    if (!options.code) options.code = 'Cmdln';
    assert.optionalObject(options.cause, 'options.cause');
    var self = this;

    var args = [];
    if (options.cause) args.push(options.cause);
    args.push(options.message);
    WError.apply(this, args);

    var extra = Object.keys(options).filter(
        function (k) { return ['cause', 'message'].indexOf(k) === -1; });
    extra.forEach(function (k) {
        self[k] = options[k];
    });
}
util.inherits(CmdlnError, WError);

function IllegalOptionError(cause, option) {
    if (option === undefined) {
        option = cause;
        cause = undefined;
    }
    assert.string(option);
    CmdlnError.call(this, {
        cause: cause,
        message: sprintf('illegal option: "%s"', option),
        code: 'IllegalOption',
        exitStatus: 1
    });
}
util.inherits(IllegalOptionError, CmdlnError);

function UnknownCommandError(cause, command) {
    if (command === undefined) {
        command = cause;
        cause = undefined;
    }
    assert.string(command);
    CmdlnError.call(this, {
        cause: cause,
        message: sprintf('unknown command: "%s"', command),
        code: 'UnknownCommand',
        exitStatus: 1
    });
}
util.inherits(UnknownCommandError, CmdlnError);

// ---- Cmdln object

/**
 * Create a command line tool.
 *
 * @param config {Object} All keys are optional unless otherwise stated
 *      - @param name {String} Tool name. Defaults to lowercase'd constructor
 *        name.
 *      - @param desc {String} Description string to include at the top of
 *        usage information.
 *      - @param helpOpts {Object} Help output formatting options. These
 *        are the same formatting options as for `dashdash.Parser.help`:
 *        indent, maxCol, helpCol, minHelpCol, maxHelpCol (TODO:doc).
 *
 * XXX hooks for adding -h, --help, help ? instead of automatic?
 *      - @param helpOpt {Boolean} Whether to include the '-h|--help' option
 *        on the tool and subcommands. Default is true.
 *      - @param helpCmd {Boolean} Whether to include the `help` subcommand.
 *        Default true.
 *      - XXX take optional bunyan logger
 */
function Cmdln(config) {
    var self = this;
    assert.optionalObject(config, 'config');
    config = config || {};
    assert.optionalString(config.name, 'config.name')
    assert.optionalString(config.desc, 'config.desc')
    assert.optionalObject(config.helpOpts, 'config.helpOpts')

    this.name = config.name || this.constructor.name.toLowerCase();
    this.desc = config.desc;
    this.helpOpts = config.helpOpts || {};
    if (!this.helpOpts.indent)
        this.helpOpts.indent = space(4);
    else if (typeof(this.helpOpts.indent) === 'number')
        this.helpOpts.indent = space(this.helpOpts.indent);
    if (!this.helpOpts.maxCol) this.helpOpts.maxCol = 80;
    if (!this.helpOpts.minHelpCol) this.helpOpts.minHelpCol = 20;
    if (!this.helpOpts.maxHelpCol) this.helpOpts.maxHelpCol = 40;

    // XXX document and expose this.
    //* @param envopts {Array} Array or 2-tuples mapping envvar name to option for
    //*      which it is a fallback.
    //this.envopts = [];

    // Find the tree of constructors (typically just this and the Cmdln
    // super class) on who's prototype to look for "do_*" and "help_*"
    // methods.
    var prototypes = [this.constructor.prototype];
    var ctor = this.constructor;
    while (ctor) {
        prototypes.push(ctor.prototype);
        ctor = ctor.super_; // presuming `util.inherits` usage
    }
    prototypes.reverse();

    // Load subcmds (do_* methods), aliases (`do_*.aliases`) and helpcmds
    // (`help_*` methods).
    this.subcmds = {};
    this.aliases = {};
    this.helpcmds = {};
    prototypes.forEach(function (proto) {
        Object.keys(proto)
            .filter(function (funcname) { return /^do_/.test(funcname); })
            .sort()
            .forEach(function (funcname) {
                var name = funcname.slice(3);
                var func = proto[funcname];
                self.subcmds[name] = func;
                self.aliases[name] = name;
                (func.aliases || []).forEach(function (alias) {
                    self.aliases[alias] = name;
                });
            });
        Object.keys(proto)
            .filter(function (funcname) { return /^help_/.test(funcname); })
            .sort()
            .forEach(function (funcname) {
                var name = funcname.slice(5);
                var func = proto[funcname];
                self.helpcmds[name] = func;
            });
    });
    // p('subcmds: ', this.subcmds)
    // p('aliases: ', this.aliases)
    // p('helpcmds: ', this.helpcmds)
}

/**
 * Return the option parser for the tool.
 */
Cmdln.prototype.getOptParser = function getOptParser() {
    var options = [
        {
            names: ['help', 'h'],
            help: 'Show this help message and exit.',
            type: 'bool'
        }
    ];
    return new dashdash.Parser({options: options, interspersed: false});
};

/**
 * Cmdln mainline.
 *
 * @param argv {Array}
 * @param callback {Function} `function (err)`
 */
Cmdln.prototype.main = function main(argv, callback) {
    var self = this;
    try {
        this.optParser = this.getOptParser();
    } catch (e) {
        //XXX wrap error: WError usage
        callback(e);
    }
    self.handleArgv(argv, function (argvErr, opts, args) {
        if (argvErr) {
            callback(argvErr);
            return;
        }

        //XXX
        //// envopts
        //(self.envopts || []).forEach(function (envopt) {
        //    var envname = envopt[0];
        //    var optname = envopt[1];
        //    if (process.env[envname] && !opts[optname]) {
        //        //XXX trace log this
        //        // console.log('set `opts.%s = "%s" from %s envvar',
        //        //     optname, process.env[envname], envname);
        //        opts[optname] = process.env[envname];
        //    }
        //});

        if (args.length === 0) {
            self.printHelp(function (helpErr) { callback(helpErr); });
            return;
        } else if (opts.help) {
            // We want `cli foo -h` to show help for the 'foo' subcmd.
            if (args[0] !== 'help') {
                self.do_help(args[0], opts, args, callback);
                return;
            }
        }

        // XXX post argv/opt processing hook in which to setup. Perhaps
        //     something simple like `init()`.
        var subcmdArgv = argv.slice(0, 2).concat(args);
        var subcmd = args.shift();
        try {
            self.dispatch(subcmd, subcmdArgv,
                function (dispErr) { callback(dispErr); });
        } catch (ex) {
            callback(ex);
        }
    });
};

/**
 * Process command-line argv.
 *
 * @param argv {Array}
 * @param callback {Function} `function (err, opts, args)`.
 */
Cmdln.prototype.handleArgv = function handleArgv(argv, callback) {
    var opts = this.optParser.parse(argv);
    callback(null, opts, opts._args);
};

/**
 * Print top-level tool help.
 *
 * @param callback {Function} `function (err)`.
 */
Cmdln.prototype.printHelp = function printHelp(callback) {
    assert.func(callback, 'callback');
    var self = this;
    var helpOpts = this.helpOpts;
    var indent = helpOpts.indent;

    var lines = [];
    if (this.desc) {
        lines.push(this.desc);
        if (this.desc.slice(-1) !== '\n') {
            lines.push('');
        }
    }

    lines = lines.concat([
        'Usage:',
        format('%s%s [OPTIONS] COMMAND [ARGS...]', indent, self.name),
        format('%s%s help COMMAND', indent, self.name),
        ''
    ]);
    if (this.optParser.help) {
        lines.push('Options:');
        lines.push(this.optParser.help(helpOpts));
    }

    if (self.envopts && self.envopts.length) {
        var envTemplate = indent + '%-23s  %s';
        lines.push('Environment:');
        self.envopts.forEach(function (envopt) {
            var envname = envopt[0];
            var optname = envopt[1];
            lines.push(sprintf(envTemplate, envname,
                'Fallback for --' + optname));
        });
        lines.push('');
    }

    lines = lines.concat([
        'Commands:'
    ]);
    // Automatic command line from `this.subcmds`.
    //XXX same helpCol as for the opts above, textwrap, etc.
    var cmdTemplate = format('%s%%-%ds  %s',
        indent, helpOpts.minHelpCol - indent.length - 2);
    Object.keys(this.subcmds).forEach(function (name) {
        var func = self.subcmds[name];
        if (func.hidden) {
            return;
        }
        var names = name;
        if (func.aliases) {
            names += sprintf(' (%s)', func.aliases.join(', '));
        }
        var desc = (func.help || '').split('\n', 1)[0];
        desc = desc.replace(/{{name}}/g, self.name);
        var line = sprintf(cmdTemplate, names, desc);
        lines.push(line);
    });

    console.log(lines.join('\n'));
    callback();
};

/**
 * Dispatch to the appropriate "do_SUBCMD" function.
 */
Cmdln.prototype.dispatch = function dispatch(subcmd, argv, callback) {
    var name = this.aliases[subcmd];
    if (!name) {
        callback(new UnknownCommandError(subcmd));
        return;
    }
    var func = this.subcmds[name];

    //p("XXX dispatch", subcmd, argv, func.options)
    var opts = null;
    var args = argv.slice(3);
    if (func.options) {
        try {
            var parser = new dashdash.Parser({options: func.options});
        } catch (e) {
            //XXX Translate error
            callback(e);
        }
        try {
            opts = parser.parse(argv, 3);
        } catch (e) {
            //XXX Translate error: IllegalOptionError, etc.
            callback(e);
        }
        args = opts._args;
    }
    //p("XXX dispatch: opts=", opts, "args=", args)

    func.call(this, subcmd, opts, args, callback);
};

Cmdln.prototype.do_help = function do_help(subcmd, opts, args, callback) {
    var self = this;
    if (args.length === 0) {
        this.printHelp(callback);
        return;
    }
    var alias = args[0];
    var name = this.aliases[alias];
    if (!name) {
        callback(new UnknownCommandError(alias));
        return;
    }

    // If there is a `.help_NAME`, use that.
    var helpfunc = this.helpcmds[name];
    if (helpfunc) {
        helpfunc.call(this, alias, callback);
        return;
    }

    var func = this.subcmds[name];
    if (func.help) {
        var help = func.help;
        help = help.replace('{{name}}', name);
        if (~help.indexOf('{{options}}')) {
            var parser = new dashdash.Parser({options: func.options});
            help = help.replace('{{options}}',
                'Options:\n' + parser.help(this.helpOpts));
        }
        console.log(help.trimRight());
        callback();
    } else {
        callback(new CmdlnError({message: format('no help for "%s"', alias)}));
    }
};
Cmdln.prototype.do_help.aliases = ['?'];
Cmdln.prototype.do_help.help = 'Give detailed help on a specific sub-command.';

Cmdln.prototype.help_help = function help_help(subcmd, callback) {
    this.printHelp(callback);
};

// ---- exports

module.exports = {
    Cmdln: Cmdln,
    CmdlnError: CmdlnError,
    IllegalOptionError: IllegalOptionError,
    UnknownCommandError: UnknownCommandError
};
<commit_msg>show commands in help output in the order specified<commit_after>/*
 * Copyright (c) 2013, Trent Mick. All rights reserved.
 * Copyright (c) 2013, Joyent, Inc. All rights reserved.
 */

var util = require('util'),
    format = util.format;
var p = console.warn;
var child_process = require('child_process'),
    spawn = child_process.spawn,
    exec = child_process.exec;
var os = require('os');
var path = require('path');
var fs = require('fs');

var assert = require('assert-plus');
var WError = require('verror').WError;
var dashdash = require('dashdash');
var sprintf = require('extsprintf').sprintf;

// ---- internal support stuff

function space(n) {
    var s = '';
    for (var i = 0; i < n; i++) {
        s += ' ';
    }
    return s;
}

function objCopy(obj) {
    var copy = {};
    Object.keys(obj).forEach(function (k) {
        copy[k] = obj[k];
    });
    return copy;
}

// ---- Errors

/**
 * Base CmdlnError. Instances (including derived errors) have these attributes:
 *
 * - `message` {String} All errors will have a message.
 * - `code` {String} A CamelCase code string for this type of error. E.g.
 *   'Cmdln' (generic), 'IllegalOption', etc.
 * - `cause` {Error} Optional. An underlying cause error.
 */
function CmdlnError(options) {
    assert.object(options, 'options');
    assert.string(options.message, 'options.message');
    assert.optionalString(options.code, 'options.code');
    if (!options.code) options.code = 'Cmdln';
    assert.optionalObject(options.cause, 'options.cause');
    var self = this;

    var args = [];
    if (options.cause) args.push(options.cause);
    args.push(options.message);
    WError.apply(this, args);

    var extra = Object.keys(options).filter(
        function (k) { return ['cause', 'message'].indexOf(k) === -1; });
    extra.forEach(function (k) {
        self[k] = options[k];
    });
}
util.inherits(CmdlnError, WError);

function IllegalOptionError(cause, option) {
    if (option === undefined) {
        option = cause;
        cause = undefined;
    }
    assert.string(option);
    CmdlnError.call(this, {
        cause: cause,
        message: sprintf('illegal option: "%s"', option),
        code: 'IllegalOption',
        exitStatus: 1
    });
}
util.inherits(IllegalOptionError, CmdlnError);

function UnknownCommandError(cause, command) {
    if (command === undefined) {
        command = cause;
        cause = undefined;
    }
    assert.string(command);
    CmdlnError.call(this, {
        cause: cause,
        message: sprintf('unknown command: "%s"', command),
        code: 'UnknownCommand',
        exitStatus: 1
    });
}
util.inherits(UnknownCommandError, CmdlnError);

// ---- Cmdln object

/**
 * Create a command line tool.
 *
 * @param config {Object} All keys are optional unless otherwise stated
 *      - @param name {String} Tool name. Defaults to lowercase'd constructor
 *        name.
 *      - @param desc {String} Description string to include at the top of
 *        usage information.
 *      - @param helpOpts {Object} Help output formatting options. These
 *        are the same formatting options as for `dashdash.Parser.help`:
 *        indent, maxCol, helpCol, minHelpCol, maxHelpCol (TODO:doc).
 *
 * XXX hooks for adding -h, --help, help ? instead of automatic?
 *      - @param helpOpt {Boolean} Whether to include the '-h|--help' option
 *        on the tool and subcommands. Default is true.
 *      - @param helpCmd {Boolean} Whether to include the `help` subcommand.
 *        Default true.
 *      - XXX take optional bunyan logger
 */
function Cmdln(config) {
    var self = this;
    assert.optionalObject(config, 'config');
    config = config || {};
    assert.optionalString(config.name, 'config.name')
    assert.optionalString(config.desc, 'config.desc')
    assert.optionalObject(config.helpOpts, 'config.helpOpts')

    this.name = config.name || this.constructor.name.toLowerCase();
    this.desc = config.desc;
    this.helpOpts = config.helpOpts || {};
    if (!this.helpOpts.indent)
        this.helpOpts.indent = space(4);
    else if (typeof(this.helpOpts.indent) === 'number')
        this.helpOpts.indent = space(this.helpOpts.indent);
    if (!this.helpOpts.maxCol) this.helpOpts.maxCol = 80;
    if (!this.helpOpts.minHelpCol) this.helpOpts.minHelpCol = 20;
    if (!this.helpOpts.maxHelpCol) this.helpOpts.maxHelpCol = 40;

    // XXX document and expose this.
    //* @param envopts {Array} Array or 2-tuples mapping envvar name to option for
    //*      which it is a fallback.
    //this.envopts = [];

    // Find the tree of constructors (typically just this and the Cmdln
    // super class) on who's prototype to look for "do_*" and "help_*"
    // methods.
    var prototypes = [];
    var ctor = this.constructor;
    while (ctor) {
        prototypes.push(ctor.prototype);
        ctor = ctor.super_; // presuming `util.inherits` usage
    }
    prototypes.reverse();

    // Load subcmds (do_* methods), aliases (`do_*.aliases`) and helpcmds
    // (`help_*` methods).
    this.subcmdOrder = [];
    this.subcmds = {};
    this.aliases = {};
    this.helpcmds = {};
    prototypes.forEach(function (proto) {
        Object.keys(proto)
            .filter(function (funcname) { return /^do_/.test(funcname); })
            //.sort()
            .forEach(function (funcname) {
                var name = funcname.slice(3);
                var func = proto[funcname];
                self.subcmds[name] = func;
                self.subcmdOrder.push(name);
                self.aliases[name] = name;
                (func.aliases || []).forEach(function (alias) {
                    self.aliases[alias] = name;
                });
            });
        Object.keys(proto)
            .filter(function (funcname) { return /^help_/.test(funcname); })
            .sort()
            .forEach(function (funcname) {
                var name = funcname.slice(5);
                var func = proto[funcname];
                self.helpcmds[name] = func;
            });
    });
    // p('subcmdOrder:', this.subcmdOrder);
    // p('subcmds: ', this.subcmds);
    // p('aliases: ', this.aliases);
    // p('helpcmds: ', this.helpcmds);
}

/**
 * Return the option parser for the tool.
 */
Cmdln.prototype.getOptParser = function getOptParser() {
    var options = [
        {
            names: ['help', 'h'],
            help: 'Show this help message and exit.',
            type: 'bool'
        }
    ];
    return new dashdash.Parser({options: options, interspersed: false});
};

/**
 * Cmdln mainline.
 *
 * @param argv {Array}
 * @param callback {Function} `function (err)`
 */
Cmdln.prototype.main = function main(argv, callback) {
    var self = this;
    try {
        this.optParser = this.getOptParser();
    } catch (e) {
        //XXX wrap error: WError usage
        callback(e);
    }
    self.handleArgv(argv, function (argvErr, opts, args) {
        if (argvErr) {
            callback(argvErr);
            return;
        }

        //XXX
        //// envopts
        //(self.envopts || []).forEach(function (envopt) {
        //    var envname = envopt[0];
        //    var optname = envopt[1];
        //    if (process.env[envname] && !opts[optname]) {
        //        //XXX trace log this
        //        // console.log('set `opts.%s = "%s" from %s envvar',
        //        //     optname, process.env[envname], envname);
        //        opts[optname] = process.env[envname];
        //    }
        //});

        if (args.length === 0) {
            self.printHelp(function (helpErr) { callback(helpErr); });
            return;
        } else if (opts.help) {
            // We want `cli foo -h` to show help for the 'foo' subcmd.
            if (args[0] !== 'help') {
                self.do_help(args[0], opts, args, callback);
                return;
            }
        }

        // XXX post argv/opt processing hook in which to setup. Perhaps
        //     something simple like `init()`.
        var subcmdArgv = argv.slice(0, 2).concat(args);
        var subcmd = args.shift();
        try {
            self.dispatch(subcmd, subcmdArgv,
                function (dispErr) { callback(dispErr); });
        } catch (ex) {
            callback(ex);
        }
    });
};

/**
 * Process command-line argv.
 *
 * @param argv {Array}
 * @param callback {Function} `function (err, opts, args)`.
 */
Cmdln.prototype.handleArgv = function handleArgv(argv, callback) {
    var opts = this.optParser.parse(argv);
    callback(null, opts, opts._args);
};

/**
 * Print top-level tool help.
 *
 * @param callback {Function} `function (err)`.
 */
Cmdln.prototype.printHelp = function printHelp(callback) {
    assert.func(callback, 'callback');
    var self = this;
    var helpOpts = this.helpOpts;
    var indent = helpOpts.indent;

    var lines = [];
    if (this.desc) {
        lines.push(this.desc);
        if (this.desc.slice(-1) !== '\n') {
            lines.push('');
        }
    }

    lines = lines.concat([
        'Usage:',
        format('%s%s [OPTIONS] COMMAND [ARGS...]', indent, self.name),
        format('%s%s help COMMAND', indent, self.name),
        ''
    ]);
    if (this.optParser.help) {
        lines.push('Options:');
        lines.push(this.optParser.help(helpOpts));
    }

    //XXX
    //if (self.envopts && self.envopts.length) {
    //    var envTemplate = indent + '%-23s  %s';
    //    lines.push('Environment:');
    //    self.envopts.forEach(function (envopt) {
    //        var envname = envopt[0];
    //        var optname = envopt[1];
    //        lines.push(sprintf(envTemplate, envname,
    //            'Fallback for --' + optname));
    //    });
    //    lines.push('');
    //}

    lines = lines.concat([
        'Commands:'
    ]);
    // Automatic command line from `this.subcmds`.
    // TODO: same helpCol as for the opts above, textwrap, etc.
    var cmdTemplate = format('%s%%-%ds  %s',
        indent, helpOpts.minHelpCol - indent.length - 2);
    this.subcmdOrder.forEach(function (name) {
        var func = self.subcmds[name];
        if (func.hidden) {
            return;
        }
        var names = name;
        if (func.aliases) {
            names += sprintf(' (%s)', func.aliases.join(', '));
        }
        var desc = (func.help || '').split('\n', 1)[0];
        desc = desc.replace(/{{name}}/g, self.name);
        var line = sprintf(cmdTemplate, names, desc);
        lines.push(line);
    });

    console.log(lines.join('\n'));
    callback();
};

/**
 * Dispatch to the appropriate "do_SUBCMD" function.
 */
Cmdln.prototype.dispatch = function dispatch(subcmd, argv, callback) {
    var name = this.aliases[subcmd];
    if (!name) {
        callback(new UnknownCommandError(subcmd));
        return;
    }
    var func = this.subcmds[name];

    //p("XXX dispatch", subcmd, argv, func.options)
    var opts = null;
    var args = argv.slice(3);
    if (func.options) {
        try {
            var parser = new dashdash.Parser({options: func.options});
        } catch (e) {
            //XXX Translate error
            callback(e);
        }
        try {
            opts = parser.parse(argv, 3);
        } catch (e) {
            //XXX Translate error: IllegalOptionError, etc.
            callback(e);
        }
        args = opts._args;
    }
    //p("XXX dispatch: opts=", opts, "args=", args)

    func.call(this, subcmd, opts, args, callback);
};

Cmdln.prototype.do_help = function do_help(subcmd, opts, args, callback) {
    var self = this;
    if (args.length === 0) {
        this.printHelp(callback);
        return;
    }
    var alias = args[0];
    var name = this.aliases[alias];
    if (!name) {
        callback(new UnknownCommandError(alias));
        return;
    }

    // If there is a `.help_NAME`, use that.
    var helpfunc = this.helpcmds[name];
    if (helpfunc) {
        helpfunc.call(this, alias, callback);
        return;
    }

    var func = this.subcmds[name];
    if (func.help) {
        var help = func.help;
        help = help.replace('{{name}}', name);
        if (~help.indexOf('{{options}}')) {
            var parser = new dashdash.Parser({options: func.options});
            help = help.replace('{{options}}',
                'Options:\n' + parser.help(this.helpOpts));
        }
        console.log(help.trimRight());
        callback();
    } else {
        callback(new CmdlnError({message: format('no help for "%s"', alias)}));
    }
};
Cmdln.prototype.do_help.aliases = ['?'];
Cmdln.prototype.do_help.help = 'Help on a specific sub-command.';

Cmdln.prototype.help_help = function help_help(subcmd, callback) {
    this.printHelp(callback);
};

// ---- exports

module.exports = {
    Cmdln: Cmdln,
    CmdlnError: CmdlnError,
    IllegalOptionError: IllegalOptionError,
    UnknownCommandError: UnknownCommandError
};
<|endoftext|>


<commit_before>/**
 * @ngdoc object
 * @name anol.layer.DynamicGeoJSON
 *
 * @param {Object} options AnOl Layer options
 * @param {Object} options.olLayer Options for ol.layer.Vector
 * @param {Object} options.olLayer.source Options for ol.source.Vector
 * @param {string} options.olLayer.source.url Url for requesting a GeoJSON
 * @param {string} options.olLayer.source.featureProjection Projection of received GeoJSON
 * @param {Object} options.olLayer.source.additionalParameters Additional parameters added to request
 *
 * @description
 * Inherits from {@link anol.layer.Layer anol.layer.StaticGeoJSON}.
 *
 * Ask *url* with current projection and bbox.
 */
anol.layer.DynamicGeoJSON = function(_options) {
    anol.layer.StaticGeoJSON.call(this, _options);
};
anol.layer.DynamicGeoJSON.prototype = new anol.layer.StaticGeoJSON(false);
$.extend(anol.layer.DynamicGeoJSON.prototype, {
    CLASS_NAME: 'anol.layer.DynamicGeoJSON',
    setOlLayer: function(olLayer) {
        anol.layer.StaticGeoJSON.prototype.setOlLayer.call(this, olLayer);
        this.olSource = olLayer.getSource();
    },
    isCombinable: function(other) {
        var combinable = anol.layer.StaticGeoJSON.prototype.isCombinable.call(this, other);
        if(!combinable) {
            return false;
        }
        if(this.olSourceOptions.url !== other.olSourceOptions.url) {
            return false;
        }
        if(this.olSourceOptions.featureProjection !== other.olSourceOptions.featureProjection) {
            return false;
        }
        return true;
    },
    getCombinedSource: function(other) {
        var anolLayers = this.olSource.get('anolLayers');
        anolLayers.push(other);
        this.olSource.set('anolLayers', anolLayers);
        return this.olSource;
    },
    setVisible: function(visible) {
        anol.layer.StaticGeoJSON.prototype.setVisible.call(this, visible);
        // find better solution than clear, cause it's remove all features from the source, not only
        // features related to current layer. But we need to call clear, otherwise source extent is not
        // resetted and it will not be reloaded with updated url params
        this.olSource.clear(true);
    },
    /**
     * Additional source options
     * - url
     * - featureProjection
     * - additionalParameters
     */
    _createSourceOptions: function(srcOptions) {
        var self = this;
        srcOptions = anol.layer.StaticGeoJSON.prototype._createSourceOptions.call(this,
            srcOptions
        );

        srcOptions.format = new ol.format.GeoJSON();
        srcOptions.strategy = ol.loadingstrategy.bbox;

        var additionalParameters = {};
        srcOptions.loader = function(extent, resolution, projection) {
            angular.forEach(self.olSource.get('anolLayers'), function(layer) {
                if(layer.getVisible()) {
                    additionalParameters = anol.helper.mergeObjects(additionalParameters, layer.olSourceOptions.additionalParameters);
                }
            });
            self.loader(
                srcOptions.url,
                extent,
                resolution,
                projection,
                srcOptions.featureProjection,
                additionalParameters
            );
        };

        return srcOptions;
    },
    loader: function(url, extent, resolution, projection, featureProjection, additionalParameters) {
        var self = this;
        var params = [
            'srs=' + projection.getCode(),
            'bbox=' + extent.join(',')
        ];
        if($.isFunction(additionalParameters)) {
            params.push(additionalParameters());
        } else if(angular.isObject(additionalParameters)) {
            angular.forEach(additionalParameters, function(value, key) {
                params.push(key + '=' + value);
            });
        }

        $.ajax({
            url: url + params.join('&'),
            dataType: 'json'
        })
        .done(function(response) {
            self.responseHandler(response, featureProjection);
        });
    },
    responseHandler: function(response, featureProjection) {
        var self = this;
        var format = new ol.format.GeoJSON();
        var features = format.readFeatures(response, {
            featureProjection: featureProjection
        });
        self.olSource.addFeatures(features);
    },
    createStyle: function(feature, resolution) {
        if(feature !== undefined &&  feature.get('__layer__') !== this.name) {
            return new ol.style.Style();
        }
        return anol.layer.StaticGeoJSON.prototype.createStyle.call(this, feature, resolution);
    }
});
<commit_msg>Fixed combining additionalParameters in dynamic geojson request<commit_after>/**
 * @ngdoc object
 * @name anol.layer.DynamicGeoJSON
 *
 * @param {Object} options AnOl Layer options
 * @param {Object} options.olLayer Options for ol.layer.Vector
 * @param {Object} options.olLayer.source Options for ol.source.Vector
 * @param {string} options.olLayer.source.url Url for requesting a GeoJSON
 * @param {string} options.olLayer.source.featureProjection Projection of received GeoJSON
 * @param {Object} options.olLayer.source.additionalParameters Additional parameters added to request
 *
 * @description
 * Inherits from {@link anol.layer.Layer anol.layer.StaticGeoJSON}.
 *
 * Ask *url* with current projection and bbox.
 */
anol.layer.DynamicGeoJSON = function(_options) {
    anol.layer.StaticGeoJSON.call(this, _options);
};
anol.layer.DynamicGeoJSON.prototype = new anol.layer.StaticGeoJSON(false);
$.extend(anol.layer.DynamicGeoJSON.prototype, {
    CLASS_NAME: 'anol.layer.DynamicGeoJSON',
    setOlLayer: function(olLayer) {
        anol.layer.StaticGeoJSON.prototype.setOlLayer.call(this, olLayer);
        this.olSource = olLayer.getSource();
    },
    isCombinable: function(other) {
        var combinable = anol.layer.StaticGeoJSON.prototype.isCombinable.call(this, other);
        if(!combinable) {
            return false;
        }
        if(this.olSourceOptions.url !== other.olSourceOptions.url) {
            return false;
        }
        if(this.olSourceOptions.featureProjection !== other.olSourceOptions.featureProjection) {
            return false;
        }
        return true;
    },
    getCombinedSource: function(other) {
        var anolLayers = this.olSource.get('anolLayers');
        anolLayers.push(other);
        this.olSource.set('anolLayers', anolLayers);
        return this.olSource;
    },
    setVisible: function(visible) {
        anol.layer.StaticGeoJSON.prototype.setVisible.call(this, visible);
        // find better solution than clear, cause it's remove all features from the source, not only
        // features related to current layer. But we need to call clear, otherwise source extent is not
        // resetted and it will not be reloaded with updated url params
        this.olSource.clear(true);
    },
    /**
     * Additional source options
     * - url
     * - featureProjection
     * - additionalParameters
     */
    _createSourceOptions: function(srcOptions) {
        var self = this;
        srcOptions = anol.layer.StaticGeoJSON.prototype._createSourceOptions.call(this,
            srcOptions
        );

        srcOptions.format = new ol.format.GeoJSON();
        srcOptions.strategy = ol.loadingstrategy.bbox;

        srcOptions.loader = function(extent, resolution, projection) {
            var additionalParameters = {};
            angular.forEach(self.olSource.get('anolLayers'), function(layer) {
                if(layer.getVisible()) {
                    additionalParameters = anol.helper.mergeObjects(additionalParameters, layer.olSourceOptions.additionalParameters);
                }
            });
            self.loader(
                srcOptions.url,
                extent,
                resolution,
                projection,
                srcOptions.featureProjection,
                additionalParameters
            );
        };

        return srcOptions;
    },
    loader: function(url, extent, resolution, projection, featureProjection, additionalParameters) {
        var self = this;
        var params = [
            'srs=' + projection.getCode(),
            'bbox=' + extent.join(',')
        ];
        if($.isFunction(additionalParameters)) {
            params.push(additionalParameters());
        } else if(angular.isObject(additionalParameters)) {
            angular.forEach(additionalParameters, function(value, key) {
                params.push(key + '=' + value);
            });
        }

        $.ajax({
            url: url + params.join('&'),
            dataType: 'json'
        })
        .done(function(response) {
            self.responseHandler(response, featureProjection);
        });
    },
    responseHandler: function(response, featureProjection) {
        var self = this;
        var format = new ol.format.GeoJSON();
        var features = format.readFeatures(response, {
            featureProjection: featureProjection
        });
        self.olSource.addFeatures(features);
    },
    createStyle: function(feature, resolution) {
        if(feature !== undefined &&  feature.get('__layer__') !== this.name) {
            return new ol.style.Style();
        }
        return anol.layer.StaticGeoJSON.prototype.createStyle.call(this, feature, resolution);
    }
});
<|endoftext|>


<commit_before>'use strict';

global.WEB_AUDIO_TEST_API_IGNORE=true;

const assert=require('assert');
const vm=require('vm');
const WebAudioTestAPI=require('web-audio-test-api');
const i18n=require('../src/i18n.js')('en');
const Options=require('../src/options.js');
const Code=require('../src/code.js');

// can't test panner b/c web-audio-test-api uses deprecated panner method
// no, wait, can use it: https://github.com/mohayonao/web-audio-test-api/issues/20
describe("Code",()=>{
	beforeEach(()=>{
		WebAudioTestAPI.use();
	});
	afterEach(()=>{
		WebAudioTestAPI.unuse();
	});
	it("has gain node",()=>{
		const options=new Options({
			sources: [
				{
					source: 'audio',
				}
			],
			filters: [
				{
					filter: 'gain',
					gain: 0.5,
				}
			],
		});
		const code=new Code(options.fix(),i18n);
		const sections=code.extractSections({html:'body',css:'paste',js:'paste'});
		const sandbox={
			AudioContext,
			document: {
				getElementById: id=>new WebAudioTestAPI.HTMLMediaElement(),
			},
		};
		vm.runInNewContext(
			sections.js.get().join("\n"),
			sandbox
		);
		assert.deepEqual(sandbox.ctx.toJSON(),{
			"name": "AudioDestinationNode",
			"inputs": [
				{
					"name": "GainNode",
					"gain": {
						"value": 0.5,
						"inputs": []
					},
					"inputs": [
						{
							"name": "MediaElementAudioSourceNode",
							"inputs": []
						}
					]
				}
			]
		});
	});
});
<commit_msg>test panner node<commit_after>'use strict';

global.WEB_AUDIO_TEST_API_IGNORE=true;

const assert=require('assert');
const vm=require('vm');
const WebAudioTestAPI=require('web-audio-test-api');
const i18n=require('../src/i18n.js')('en');
const Options=require('../src/options.js');
const Code=require('../src/code.js');

WebAudioTestAPI.setState('AudioContext#createStereoPanner','enabled');

describe("Code",()=>{
	function getAudioGraph(optionsData) {
		const options=new Options(optionsData);
		const code=new Code(options.fix(),i18n);
		const sections=code.extractSections({html:'body',css:'paste',js:'paste'});
		const sandbox={
			AudioContext,
			document: {
				getElementById: id=>new WebAudioTestAPI.HTMLMediaElement(),
			},
		};
		vm.runInNewContext(
			sections.js.get().join("\n"),
			sandbox
		);
		return sandbox.ctx.toJSON();
	}
	beforeEach(()=>{
		WebAudioTestAPI.use();
	});
	afterEach(()=>{
		WebAudioTestAPI.unuse();
	});
	it("has gain node",()=>{
		assert.deepEqual(getAudioGraph({
			sources: [
				{
					source: 'audio',
				}
			],
			filters: [
				{
					filter: 'gain',
					gain: 0.5,
				}
			],
		}),{
			"name": "AudioDestinationNode",
			"inputs": [
				{
					"name": "GainNode",
					"gain": {
						"value": 0.5,
						"inputs": []
					},
					"inputs": [
						{
							"name": "MediaElementAudioSourceNode",
							"inputs": []
						}
					]
				}
			]
		});
	});
	it("has panner node",()=>{
		assert.deepEqual(getAudioGraph({
			sources: [
				{
					source: 'audio',
				}
			],
			filters: [
				{
					filter: 'panner',
					pan: 1,
				}
			],
		}),{
			"name": "AudioDestinationNode",
			"inputs": [
				{
					"name": "StereoPannerNode",
					"pan": {
						"value": 1,
						"inputs": []
					},
					"inputs": [
						{
							"name": "MediaElementAudioSourceNode",
							"inputs": []
						}
					]
				}
			]
		});
	});
});
<|endoftext|>


<commit_before>import test from 'tape'
import axios from 'axios'

import { IntTypes, PixelTypes, readImageFile, WorkerPool, stackImages, imageSharedBufferOrCopy, runPipelineBrowser, IOTypes } from 'browser/index.js'

export default function () {
  test('WorkerPool runs and reports progress', async (t) => {
    const verifyImage = (image) => {
      t.is(image.imageType.dimension, 2, 'dimension')
      t.is(image.imageType.componentType, IntTypes.UInt8, 'componentType')
      t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType')
      t.is(image.imageType.components, 1, 'components')
      t.is(image.origin[0], 0.0, 'origin[0]')
      t.is(image.origin[1], 0.0, 'origin[1]')
      t.is(image.spacing[0], 1.0, 'spacing[0]')
      t.is(image.spacing[1], 1.0, 'spacing[1]')
      t.is(image.size[0], 256, 'size[0]')
      t.is(image.size[1], 256, 'size[1]')
      t.is(image.data.byteLength, 65536, 'data.byteLength')
      t.end()
    }

    const progressUpdates = []
    let reportedTotalSplits = null
    function progressLogger (split, totalSplits) {
      progressUpdates.push(split)
      reportedTotalSplits = totalSplits
    }

    const poolSize = 2
    const maxTotalSplits = 4
    const workerPool = new WorkerPool(poolSize, runPipelineBrowser)

    const fileName = 'cthead1.png'
    const testFilePath = 'base/build/ExternalData/test/Input/' + fileName
    const response = await axios.get(testFilePath, { responseType: 'blob' })
    const jsFile = await new window.File([response.data], fileName)
    const { image, webWorker } = await readImageFile(null, jsFile)
    webWorker.terminate()

    const taskArgsArray = []
    for (let index = 0; index < maxTotalSplits; index++) {
      const pipelinePath = 'MedianFilterTest'
      const args = ['cthead1.png.iwi', 'cthead1.png.shrink.iwi', '4', '' + maxTotalSplits, '' + index]
      const desiredOutputs = [
        { path: args[1], type: IOTypes.Image }
      ]
      const data = imageSharedBufferOrCopy(image)
      const inputs = [
        { path: args[0], type: IOTypes.Image, data }
      ]
      taskArgsArray.push([pipelinePath, args, desiredOutputs, inputs])
    }

    const results = await workerPool.runTasks(taskArgsArray, progressLogger).promise
    workerPool.terminateWorkers()

    t.is(reportedTotalSplits, maxTotalSplits, 'reported total splits')
    const imageSplits = results.map(({ outputs }) => outputs[0].data)
    const stackedImage = stackImages(imageSplits)

    verifyImage(stackedImage)
  })
}
<commit_msg>test(WebWorkerPoolTest): Update paths<commit_after>import test from 'tape'
import axios from 'axios'

import { IntTypes, PixelTypes, readImageFile, WorkerPool, stackImages, imageSharedBufferOrCopy, runPipelineBrowser, IOTypes } from 'browser/index.js'

export default function () {
  test('WorkerPool runs and reports progress', async (t) => {
    const verifyImage = (image) => {
      t.is(image.imageType.dimension, 2, 'dimension')
      t.is(image.imageType.componentType, IntTypes.UInt8, 'componentType')
      t.is(image.imageType.pixelType, PixelTypes.Scalar, 'pixelType')
      t.is(image.imageType.components, 1, 'components')
      t.is(image.origin[0], 0.0, 'origin[0]')
      t.is(image.origin[1], 0.0, 'origin[1]')
      t.is(image.spacing[0], 1.0, 'spacing[0]')
      t.is(image.spacing[1], 1.0, 'spacing[1]')
      t.is(image.size[0], 256, 'size[0]')
      t.is(image.size[1], 256, 'size[1]')
      t.is(image.data.byteLength, 65536, 'data.byteLength')
      t.end()
    }

    const progressUpdates = []
    let reportedTotalSplits = null
    function progressLogger (split, totalSplits) {
      progressUpdates.push(split)
      reportedTotalSplits = totalSplits
    }

    const poolSize = 2
    const maxTotalSplits = 4
    const workerPool = new WorkerPool(poolSize, runPipelineBrowser)

    const fileName = 'cthead1.png'
    const testFilePath = 'base/build/ExternalData/test/Input/' + fileName
    const response = await axios.get(testFilePath, { responseType: 'blob' })
    const jsFile = await new window.File([response.data], fileName)
    const { image, webWorker } = await readImageFile(null, jsFile)
    webWorker.terminate()

    const taskArgsArray = []
    for (let index = 0; index < maxTotalSplits; index++) {
      const pipelinePath = 'MedianFilterTest'
      const args = ['./cthead1.png.iwi', './cthead1.png.shrink.iwi', '4', '' + maxTotalSplits, '' + index]
      const desiredOutputs = [
        { path: args[1], type: IOTypes.Image }
      ]
      const data = imageSharedBufferOrCopy(image)
      const inputs = [
        { path: args[0], type: IOTypes.Image, data }
      ]
      taskArgsArray.push([pipelinePath, args, desiredOutputs, inputs])
    }

    const results = await workerPool.runTasks(taskArgsArray, progressLogger).promise
    workerPool.terminateWorkers()

    t.is(reportedTotalSplits, maxTotalSplits, 'reported total splits')
    const imageSplits = results.map(({ outputs }) => outputs[0].data)
    const stackedImage = stackImages(imageSplits)

    verifyImage(stackedImage)
  })
}
<|endoftext|>


<commit_before>/* global d3:false, fc:false, example: false */
(function(d3, fc, example) {

    // Inspired by: http://bost.ocks.org/mike/nations/

    example.yearLabel = function() {
        var event = d3.dispatch('yearChange');

        var labelBox;

        var yearScale = d3.scale.linear()
              .domain([1800, 2009])
              .clamp(true);

        var overlayDataJoin = fc.util.dataJoin()
            .selector('rect.overlay')
            .element('rect')
            .attr('class', 'overlay');

        var labelDataJoin = fc.util.dataJoin()
            .selector('text')
            .element('text');

        function mousemove() { event.yearChange(yearScale.invert(d3.mouse(this)[0])); }

        function yearLabel(selection) {
            selection.each(function(data) {
                var label = labelDataJoin(selection, [data]);

                label.enter()
                    .attr('text-anchor', 'end');

                label.text(data);

                var overlay = overlayDataJoin(selection, [data]);

                overlay.enter()
                    .each(function() {
                        labelBox = label.node().getBBox();
                        yearScale.range([labelBox.x + 10, labelBox.x + labelBox.width - 10]);
                    })
                    .attr({
                        'x': labelBox.x,
                        'y': labelBox.y,
                        'width': labelBox.width,
                        'height': labelBox.height
                    })
                    .on({
                        'mouseover': function() { label.classed('active', true); },
                        'mouseout': function() { label.classed('active', false); },
                        'mousemove': mousemove,
                        'touchmove': mousemove
                    });
            });
        }

        d3.rebind(yearLabel, event, 'on');

        return yearLabel;
    };

    example.chart = function() {
        var event = d3.dispatch('yearChange');

        var radiusScale = d3.scale.sqrt().domain([0, 5e8]).range([0, 40]),
            colourScale = d3.scale.category10();

        function income(d) { return d.income; }
        function lifeExpectancy(d) { return d.lifeExpectancy; }
        function name(d) { return d.name; }
        function population(d) { return d.population; }

        function pointArea(d) { return Math.pow(radiusScale(population(d)), 2) * Math.PI; }
        function sort(a, b) { return population(b) - population(a); }
        function colour(d) { return colourScale(d.region); }

        var point = fc.series.point()
            .xValue(income)
            .yValue(lifeExpectancy)
            .key(name)
            .size(pointArea)
            .decorate(function(selection) {
                selection.enter()
                    .attr('fill', colour)
                    .append('title')
                    .text(name);

                // Ensure the points with the smallest population are on top
                selection.sort(sort);
            });

        var yearLabel = example.yearLabel()
            .on('yearChange', event.yearChange);

        var cartesianChart = fc.chart.cartesian(d3.scale.log(), d3.scale.linear())
            .xDomain([300, 1e5])
            .yDomain([10, 85])
            .margin({top: 25, right: 25, bottom: 20, left: 30})
            .yOrient('left')
            .xTicks(12, d3.format(',d'))
            .plotArea(point)
            .xLabel('income per capita, inflation-adjusted (dollars)')
            .yLabel('life expectancy (years)')
            .decorate(function(selection, data) {
                var xAxisWidth = selection.select('.x-axis')
                    .layout('width');

                selection.select('.x-axis .label')
                    .attr('transform', 'translate(' + xAxisWidth / 2 + ', -26)');

                var yAxisHeight = selection.select('.y-axis')
                    .layout('height');

                selection.select('.y-axis .label')
                    .attr('transform', 'rotate(-90) translate(' + yAxisHeight / 2 + ', 36)');

                var plotAreaContainerEnter = selection.enter()
                    .select('.plot-area-container');

                plotAreaContainerEnter.append('g')
                        .attr('class', 'year label')
                        .layout({
                            position: 'absolute',
                            right: 0,
                            bottom: 24
                        });

                plotAreaContainerEnter.layout();

                // Update the year label when updating the chart
                selection.select('.year.label')
                    .datum(data.year)
                    .call(yearLabel);
            });

        function chart(selection) {
            selection.call(cartesianChart);
        }

        d3.rebind(chart, event, 'on');

        return chart;
    };

    (function() {
        // Finds (and possibly interpolates) the value for the specified year.
        function interpolateValues(values, bisect, year) {
            var i = bisect.left(values, year, 0, values.length - 1),
                a = values[i];
            if (i > 0) {
                var b = values[i - 1],
                    t = (year - a[0]) / (b[0] - a[0]);
                return a[1] * (1 - t) + b[1] * t;
            }
            return a[1];
        }

        // Interpolates the dataset for the given (fractional) year.
        function interpolateData(nations, bisect, year) {
            return nations.map(function(d) {
                return {
                    name: d.name,
                    region: d.region,
                    income: interpolateValues(d.income, bisect, year),
                    population: interpolateValues(d.population, bisect, year),
                    lifeExpectancy: interpolateValues(d.lifeExpectancy, bisect, year)
                };
            });
        }

        d3.json('nations.json', function(nations) {
            // A bisector since many nation's data is sparsely-defined.
            var bisect = d3.bisector(function(d) { return d[0]; });
            var data = interpolateData(nations, bisect, 1800);

            var container = d3.select('#wealth-and-health-of-nations');
            var chart = example.chart();
            chart.on('yearChange', function(year) {
                    enableInteraction();
                    displayYear(year);
                });

            container.transition()
                .duration(30000)
                .ease('linear')
                .tween('year', tweenYear)
                .each('end', enableInteraction);

            var render = fc.util.render(function() {
                    container.datum(data)
                        .call(chart)
                        .layoutSuspended(true);
                });

            // Tweens the entire chart by first tweening the year, and then the data.
            // For the interpolated data, the dots and label are redrawn.
            function tweenYear() {
                var year = d3.interpolateNumber(1800, 2009);
                return function(t) { displayYear(year(t)); };
            }

            function displayYear(year) {
                data = interpolateData(nations, bisect, year);
                data.year = Math.round(year);
                render();
            }

            var interactionEnabled = false;
            function enableInteraction() {
                if (!interactionEnabled) {
                    interactionEnabled = true;

                    // Cancel the current transition, if any.
                    container.transition()
                        .duration(0);
                }
            }
        });
    }());

}(d3, fc, {}));
<commit_msg>Update axis labels to use flexbox layout<commit_after>/* global d3:false, fc:false, example: false */
(function(d3, fc, example) {

    // Inspired by: http://bost.ocks.org/mike/nations/

    example.yearLabel = function() {
        var event = d3.dispatch('yearChange');

        var labelBox;

        var yearScale = d3.scale.linear()
              .domain([1800, 2009])
              .clamp(true);

        var overlayDataJoin = fc.util.dataJoin()
            .selector('rect.overlay')
            .element('rect')
            .attr('class', 'overlay');

        var labelDataJoin = fc.util.dataJoin()
            .selector('text')
            .element('text');

        function mousemove() { event.yearChange(yearScale.invert(d3.mouse(this)[0])); }

        function yearLabel(selection) {
            selection.each(function(data) {
                var label = labelDataJoin(selection, [data]);

                label.enter()
                    .attr('text-anchor', 'end');

                label.text(data);

                var overlay = overlayDataJoin(selection, [data]);

                overlay.enter()
                    .each(function() {
                        labelBox = label.node().getBBox();
                        yearScale.range([labelBox.x + 10, labelBox.x + labelBox.width - 10]);
                    })
                    .attr({
                        'x': labelBox.x,
                        'y': labelBox.y,
                        'width': labelBox.width,
                        'height': labelBox.height
                    })
                    .on({
                        'mouseover': function() { label.classed('active', true); },
                        'mouseout': function() { label.classed('active', false); },
                        'mousemove': mousemove,
                        'touchmove': mousemove
                    });
            });
        }

        d3.rebind(yearLabel, event, 'on');

        return yearLabel;
    };

    example.chart = function() {
        var event = d3.dispatch('yearChange');

        var radiusScale = d3.scale.sqrt().domain([0, 5e8]).range([0, 40]),
            colourScale = d3.scale.category10();

        function income(d) { return d.income; }
        function lifeExpectancy(d) { return d.lifeExpectancy; }
        function name(d) { return d.name; }
        function population(d) { return d.population; }

        function pointArea(d) { return Math.pow(radiusScale(population(d)), 2) * Math.PI; }
        function sort(a, b) { return population(b) - population(a); }
        function colour(d) { return colourScale(d.region); }

        var point = fc.series.point()
            .xValue(income)
            .yValue(lifeExpectancy)
            .key(name)
            .size(pointArea)
            .decorate(function(selection) {
                selection.enter()
                    .attr('fill', colour)
                    .append('title')
                    .text(name);

                // Ensure the points with the smallest population are on top
                selection.sort(sort);
            });

        var yearLabel = example.yearLabel()
            .on('yearChange', event.yearChange);

        var cartesianChart = fc.chart.cartesian(d3.scale.log(), d3.scale.linear())
            .xDomain([300, 1e5])
            .yDomain([10, 85])
            .margin({top: 25, right: 25, bottom: 20, left: 30})
            .yOrient('left')
            .xTicks(12, d3.format(',d'))
            .plotArea(point)
            .xLabel('income per capita, inflation-adjusted (dollars)')
            .yLabel('life expectancy (years)')
            .decorate(function(selection, data) {
                var selectionEnter = selection.enter();

                selectionEnter.select('.x-axis > g')
                    .layout({
                      'position': 'absolute',
                      'right': 0,
                      'bottom': 26
                    });

                selectionEnter.select('.y-axis > g')
                    .layout({
                      'position': 'absolute',
                      'top': 0,
                      'left': 36
                    });

                var plotAreaContainerEnter = selection.enter()
                    .select('.plot-area-container');

                plotAreaContainerEnter.append('g')
                        .attr('class', 'year label')
                        .layout({
                            position: 'absolute',
                            right: 0,
                            bottom: 24
                        });

                selectionEnter.layout();

                // Update the year label when updating the chart
                selection.select('.year.label')
                    .datum(data.year)
                    .call(yearLabel);
            });

        function chart(selection) {
            selection.call(cartesianChart);
        }

        d3.rebind(chart, event, 'on');

        return chart;
    };

    (function() {
        // Finds (and possibly interpolates) the value for the specified year.
        function interpolateValues(values, bisect, year) {
            var i = bisect.left(values, year, 0, values.length - 1),
                a = values[i];
            if (i > 0) {
                var b = values[i - 1],
                    t = (year - a[0]) / (b[0] - a[0]);
                return a[1] * (1 - t) + b[1] * t;
            }
            return a[1];
        }

        // Interpolates the dataset for the given (fractional) year.
        function interpolateData(nations, bisect, year) {
            return nations.map(function(d) {
                return {
                    name: d.name,
                    region: d.region,
                    income: interpolateValues(d.income, bisect, year),
                    population: interpolateValues(d.population, bisect, year),
                    lifeExpectancy: interpolateValues(d.lifeExpectancy, bisect, year)
                };
            });
        }

        d3.json('nations.json', function(nations) {
            // A bisector since many nation's data is sparsely-defined.
            var bisect = d3.bisector(function(d) { return d[0]; });
            var data = interpolateData(nations, bisect, 1800);

            var container = d3.select('#wealth-and-health-of-nations');
            var chart = example.chart();
            chart.on('yearChange', function(year) {
                    enableInteraction();
                    displayYear(year);
                });

            container.transition()
                .duration(30000)
                .ease('linear')
                .tween('year', tweenYear)
                .each('end', enableInteraction);

            var render = fc.util.render(function() {
                    container.datum(data)
                        .call(chart)
                        .layoutSuspended(true);
                });

            // Tweens the entire chart by first tweening the year, and then the data.
            // For the interpolated data, the dots and label are redrawn.
            function tweenYear() {
                var year = d3.interpolateNumber(1800, 2009);
                return function(t) { displayYear(year(t)); };
            }

            function displayYear(year) {
                data = interpolateData(nations, bisect, year);
                data.year = Math.round(year);
                render();
            }

            var interactionEnabled = false;
            function enableInteraction() {
                if (!interactionEnabled) {
                    interactionEnabled = true;

                    // Cancel the current transition, if any.
                    container.transition()
                        .duration(0);
                }
            }
        });
    }());

}(d3, fc, {}));
<|endoftext|>


<commit_before>/**
 * @file describableedit.js
 * @brief Describable entity item edit view
 * @author Frédéric SCHERMA (INRA UMR1095)
 * @date 2016-12-15
 * @copyright Copyright (c) 2016 INRA/CIRAD
 * @license MIT (see LICENSE file)
 * @details
 */

let ItemView = require('../../main/views/itemview');

let View = ItemView.extend({
    tagName: 'div',
    template: require('../templates/describableedit.html'),
    templateContext: function () {
        let result = {};

        let i;
        for (i = 0; i < this.descriptorCollection.length; i++) {
            let model = this.descriptorCollection.models[i];
            result[model.get('name')] = model.attributes;
        }

        return {
            panels: this.descriptorMetaModelLayout.layout_content.panels,
            target: this.descriptorMetaModelLayout.target,
            descriptors_data: result
        }
    },

    ui: {
        "descriptor": "tr.descriptor",
        "cancel": "button.cancel",
        "apply": "button.apply",
        "showDescriptorHistory": "span.show-descriptor-history"
    },

    events: {
        "click @ui.cancel": "onCancel",
        "click @ui.apply": "onApply",
        "click @ui.showDescriptorHistory": "onShowDescriptorHistory"
    },

    initialize: function(options) {
        View.__super__.initialize.apply(this);

        this.descriptorMetaModelLayout = options.descriptorMetaModelLayout;
        this.descriptorCollection = options.descriptorCollection;

        // no need to follow changes during edition
        // this.listenTo(this.model, 'change:descriptors', this.render, this);
    },

    onRender: function() {
        let view = this;
        let model = this.model;
        let exists = !model.isNew();

        $.each(this.ui.descriptor, function(index) {
            let el = $(this);

            let pi = el.attr('panel-index');
            let i = el.attr('index');
            let descriptorModelType = view.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
            let descriptorType = view.descriptorCollection.findWhere({name: descriptorModelType.name});
            let format = descriptorType.get('format');

            let definesValues = false;
            let defaultValues = null;

            // default value or current descriptor value
            if (exists) {
                defaultValues = model.get('descriptors')[descriptorType.get('code')];
                definesValues = defaultValues != null && defaultValues != undefined;
            } else {
                // @todo default value from descriptor type
                switch (format.type) {
                    case "boolean":
                        defaultValues = false;
                        definesValues = true;
                        break;
                    default:
                        break;
                }
            }

            let widget = application.descriptor.widgets.newElement(format.type);
            if (widget) {
                widget.create(format, el.children('td.descriptor-value'), {
                    readOnly: false,
                    history: true,
                    descriptorTypeId: descriptorType.get('id')
                });

                widget.set(format, definesValues, defaultValues, {
                    descriptorTypeId: descriptorType.get('id'),
                    descriptorModelType: descriptorModelType
                });

                if (descriptorModelType.set_once && exists) {
                    widget.disable();
                }
            }

            // save the descriptor format type widget instance
            descriptorModelType.widget = widget;
        });
    },

    onDomRefresh: function() {
        let descriptors = {};

        // firstly make a list for each descriptor of which descriptors need them for a condition
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                let condition = descriptorModelType.condition;

                // if given set initials values for the widget
                if (this.model.isNew()) {
                    // @todo
                }

                if (condition && condition.defined) {
                    /* @todo optimize with model not dom */
                    let target = this.$el.find("tr.descriptor[descriptor-model-type=" + condition.target + "]");
                    let targetDescriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[target.attr('panel-index')].descriptors[target.attr('index')];

                    if (targetDescriptorModelType.widget && descriptorModelType.widget) {
                        if (targetDescriptorModelType.id in descriptors) {
                            descriptors[targetDescriptorModelType.id].listeners.push(descriptorModelType.widget);
                        } else {
                            descriptors[targetDescriptorModelType.id] = {
                                widget: targetDescriptorModelType.widget,
                                conditionType: condition.condition,
                                conditionValue: condition.values,
                                listeners: [descriptorModelType.widget]
                            };
                        }

                        // initial state of the condition
                        let display = targetDescriptorModelType.widget.checkCondition(condition.condition, condition.values);

                        if (!display) {
                            // hide at tr level
                            descriptorModelType.widget.parent.parent().hide(false);
                        }
                    }
                }
            }
        }

        // once lists are done attach them to their widgets and bind the change event
        for (let k in descriptors) {
            let descriptor = descriptors[k];
            descriptor.widget.bindConditionListener(descriptor.listeners, descriptor.conditionType, descriptor.conditionValue);
        }
    },

    onBeforeDetach: function() {
        // destroy any widgets
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                if (descriptorModelType.widget) {
                    descriptorModelType.widget.destroy();
                }
            }
        }
    },

    findDescriptorModelTypeForConditionTarget: function(target) {
        let pi = target.attr('panel-index');
        let i = target.attr('index');
        let targetDescriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];

        // find el from target
        let descriptorModelTypes = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors;
        for (let i = 0; i < descriptorModelTypes.length; ++i) {
            if (descriptorModelTypes[i].condition.target === targetDescriptorModelType.id) {
                let descriptorModelType = descriptorModelTypes[i];

                return {
                    targetDescriptorModelType: targetDescriptorModelType,
                    descriptorModelType: descriptorModelType,
                    el: this.$el.find("tr.descriptor[descriptor-model-type=" + descriptorModelType.id + "]")
                }
            }
        }

        return null;
    },

    prepareDescriptors: function () {
        let descriptors = {};

        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];

                let mandatory = descriptorModelType.mandatory;

                let currValue = this.model.get('descriptors')[descriptorModelType.name];
                let values = null;

                // display of the tr
                if (descriptorModelType.widget && descriptorModelType.widget.parent.parent().css('display') !== "none") {
                    values = descriptorModelType.widget.values();
                }

                if (mandatory && values === null) {
                    $.alert.error(_t("Field " + descriptorModelType.label + " is required"));
                    return null;
                }

                let write = true;
                if (descriptorModelType.set_once && currValue !== null) {
                    write = false;
                }

                if (descriptorModelType.widget && descriptorModelType.widget.compare(values, currValue)) {
                    write = false;
                }

                if (write) {
                    descriptors[descriptorModelType.name] = values;
                }
            }
        }

        return descriptors;
    },

    cancel: function() {
        // destroy any widgets
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                if (descriptorModelType.widget) {
                    descriptorModelType.widget.destroy();
                }
            }
        }
    },

    onCancel: function() {
        this.cancel();

        // non optimized default behavior reload url
        Backbone.history.loadUrl();
    },

    onApply: function() {
        // non optimized default behavior, load after save
        let model = this.model;

        let descriptors = this.prepareDescriptors();
        if (descriptors === null) {
            return;
        }

        this.model.save({descriptors: descriptors}, {wait: true, patch: !model.isNew()}).then(function () {
            Backbone.history.loadUrl();
        });
    },

    onShowDescriptorHistory: function(e) {
        let tr = $(e.target).closest("tr");
        let panelIndex = tr.attr("panel-index");
        let index = tr.attr("index");

        let dmt = this.descriptorMetaModelLayout.layout_content.panels[panelIndex].descriptors[index];
        if (dmt && dmt.widget) {
            let tokens = this.model.url().split('/');

            let appLabel = tokens[tokens.length-4];
            let modelName = tokens[tokens.length-3];
            let objectId = tokens[tokens.length-2];
            let valueName = '#' + dmt.name;

            let options = {};

            dmt.widget.showHistory(appLabel, modelName, objectId, valueName, dmt, options);
        }
    }
});

module.exports = View;
<commit_msg>FIX: widgets destruction<commit_after>/**
 * @file describableedit.js
 * @brief Describable entity item edit view
 * @author Frédéric SCHERMA (INRA UMR1095)
 * @date 2016-12-15
 * @copyright Copyright (c) 2016 INRA/CIRAD
 * @license MIT (see LICENSE file)
 * @details
 */

let ItemView = require('../../main/views/itemview');

let View = ItemView.extend({
    tagName: 'div',
    template: require('../templates/describableedit.html'),
    templateContext: function () {
        let result = {};

        let i;
        for (i = 0; i < this.descriptorCollection.length; i++) {
            let model = this.descriptorCollection.models[i];
            result[model.get('name')] = model.attributes;
        }

        return {
            panels: this.descriptorMetaModelLayout.layout_content.panels,
            target: this.descriptorMetaModelLayout.target,
            descriptors_data: result
        }
    },

    ui: {
        "descriptor": "tr.descriptor",
        "cancel": "button.cancel",
        "apply": "button.apply",
        "showDescriptorHistory": "span.show-descriptor-history"
    },

    events: {
        "click @ui.cancel": "onCancel",
        "click @ui.apply": "onApply",
        "click @ui.showDescriptorHistory": "onShowDescriptorHistory"
    },

    initialize: function(options) {
        View.__super__.initialize.apply(this);

        this.descriptorMetaModelLayout = options.descriptorMetaModelLayout;
        this.descriptorCollection = options.descriptorCollection;

        // no need to follow changes during edition
        // this.listenTo(this.model, 'change:descriptors', this.render, this);
    },

    onRender: function() {
        let view = this;
        let model = this.model;
        let exists = !model.isNew();

        $.each(this.ui.descriptor, function(index) {
            let el = $(this);

            let pi = el.attr('panel-index');
            let i = el.attr('index');
            let descriptorModelType = view.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
            let descriptorType = view.descriptorCollection.findWhere({name: descriptorModelType.name});
            let format = descriptorType.get('format');

            let definesValues = false;
            let defaultValues = null;

            // default value or current descriptor value
            if (exists) {
                defaultValues = model.get('descriptors')[descriptorType.get('code')];
                definesValues = defaultValues != null && defaultValues != undefined;
            } else {
                // @todo default value from descriptor type
                switch (format.type) {
                    case "boolean":
                        defaultValues = false;
                        definesValues = true;
                        break;
                    default:
                        break;
                }
            }

            let widget = application.descriptor.widgets.newElement(format.type);
            if (widget) {
                widget.create(format, el.children('td.descriptor-value'), {
                    readOnly: false,
                    history: true,
                    descriptorTypeId: descriptorType.get('id')
                });

                widget.set(format, definesValues, defaultValues, {
                    descriptorTypeId: descriptorType.get('id'),
                    descriptorModelType: descriptorModelType
                });

                if (descriptorModelType.set_once && exists) {
                    widget.disable();
                }
            }

            // save the descriptor format type widget instance
            descriptorModelType.widget = widget;
        });
    },

    onDomRefresh: function() {
        let descriptors = {};

        // firstly make a list for each descriptor of which descriptors need them for a condition
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                let condition = descriptorModelType.condition;

                // if given set initials values for the widget
                if (this.model.isNew()) {
                    // @todo
                }

                if (condition && condition.defined) {
                    /* @todo optimize with model not dom */
                    let target = this.$el.find("tr.descriptor[descriptor-model-type=" + condition.target + "]");
                    let targetDescriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[target.attr('panel-index')].descriptors[target.attr('index')];

                    if (targetDescriptorModelType.widget && descriptorModelType.widget) {
                        if (targetDescriptorModelType.id in descriptors) {
                            descriptors[targetDescriptorModelType.id].listeners.push(descriptorModelType.widget);
                        } else {
                            descriptors[targetDescriptorModelType.id] = {
                                widget: targetDescriptorModelType.widget,
                                conditionType: condition.condition,
                                conditionValue: condition.values,
                                listeners: [descriptorModelType.widget]
                            };
                        }

                        // initial state of the condition
                        let display = targetDescriptorModelType.widget.checkCondition(condition.condition, condition.values);

                        if (!display) {
                            // hide at tr level
                            descriptorModelType.widget.parent.parent().hide(false);
                        }
                    }
                }
            }
        }

        // once lists are done attach them to their widgets and bind the change event
        for (let k in descriptors) {
            let descriptor = descriptors[k];
            descriptor.widget.bindConditionListener(descriptor.listeners, descriptor.conditionType, descriptor.conditionValue);
        }
    },

    onBeforeDetach: function() {
        // destroy any widgets
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                if (descriptorModelType.widget) {
                    descriptorModelType.widget.destroy();
                    descriptorModelType.widget = null;
                }
            }
        }
    },

    findDescriptorModelTypeForConditionTarget: function(target) {
        let pi = target.attr('panel-index');
        let i = target.attr('index');
        let targetDescriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];

        // find el from target
        let descriptorModelTypes = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors;
        for (let i = 0; i < descriptorModelTypes.length; ++i) {
            if (descriptorModelTypes[i].condition.target === targetDescriptorModelType.id) {
                let descriptorModelType = descriptorModelTypes[i];

                return {
                    targetDescriptorModelType: targetDescriptorModelType,
                    descriptorModelType: descriptorModelType,
                    el: this.$el.find("tr.descriptor[descriptor-model-type=" + descriptorModelType.id + "]")
                }
            }
        }

        return null;
    },

    prepareDescriptors: function () {
        let descriptors = {};

        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];

                let mandatory = descriptorModelType.mandatory;

                let currValue = this.model.get('descriptors')[descriptorModelType.name];
                let values = null;

                // display of the tr
                if (descriptorModelType.widget && descriptorModelType.widget.parent.parent().css('display') !== "none") {
                    values = descriptorModelType.widget.values();
                }

                if (mandatory && values === null) {
                    $.alert.error(_t("Field " + descriptorModelType.label + " is required"));
                    return null;
                }

                let write = true;
                if (descriptorModelType.set_once && currValue !== null) {
                    write = false;
                }

                if (descriptorModelType.widget && descriptorModelType.widget.compare(values, currValue)) {
                    write = false;
                }

                if (write) {
                    descriptors[descriptorModelType.name] = values;
                }
            }
        }

        return descriptors;
    },

    cancel: function() {
        // destroy any widgets
        for (let pi = 0; pi < this.descriptorMetaModelLayout.layout_content.panels.length; ++pi) {
            for (let i = 0; i < this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors.length; ++i) {
                let descriptorModelType = this.descriptorMetaModelLayout.layout_content.panels[pi].descriptors[i];
                if (descriptorModelType.widget) {
                    descriptorModelType.widget.cancel();
                }
            }
        }
    },

    onCancel: function() {
        this.cancel();

        // non optimized default behavior reload url
        Backbone.history.loadUrl();
    },

    onApply: function() {
        // non optimized default behavior, load after save
        let model = this.model;

        let descriptors = this.prepareDescriptors();
        if (descriptors === null) {
            return;
        }

        this.model.save({descriptors: descriptors}, {wait: true, patch: !model.isNew()}).then(function () {
            Backbone.history.loadUrl();
        });
    },

    onShowDescriptorHistory: function(e) {
        let tr = $(e.target).closest("tr");
        let panelIndex = tr.attr("panel-index");
        let index = tr.attr("index");

        let dmt = this.descriptorMetaModelLayout.layout_content.panels[panelIndex].descriptors[index];
        if (dmt && dmt.widget) {
            let tokens = this.model.url().split('/');

            let appLabel = tokens[tokens.length-4];
            let modelName = tokens[tokens.length-3];
            let objectId = tokens[tokens.length-2];
            let valueName = '#' + dmt.name;

            let options = {};

            dmt.widget.showHistory(appLabel, modelName, objectId, valueName, dmt, options);
        }
    }
});

module.exports = View;
<|endoftext|>