<commit_before>// function completer.
//
// completer should be a class that take an cell instance
var IPython = (function (IPython) {
    // that will prevent us from misspelling
    "use strict";

    // easyier key mapping
    var key = IPython.utils.keycodes;

    // what is the common start of all completions
    function _existing_completion(item, completion_array){
        if(item.substr(0,1) == '.') {
            for( var c in completion_array ) {
                if(completion_array[c].substr(-item.length) == item)
                { return true; }
            }
            return false;
        }
        else {return completion_array.indexOf(item) != -1}
    }

    function shared_start(B) {
        if (B.length == 1) {
            return B[0];
        }
        var A = new Array();
        for (var i = 0; i < B.length; i++) {
            A.push(B[i].str);
        }
        if (A.length > 1) {
            var tem1, tem2, s;
            A = A.slice(0).sort();
            tem1 = A[0];
            s = tem1.length;
            tem2 = A.pop();
            while (s && tem2.indexOf(tem1) == -1) {
                tem1 = tem1.substring(0, --s);
            }
            if (tem1 == "" || tem2.indexOf(tem1) != 0) {
                return null;
            }
            return {
                str: tem1,
                type: "computed",
                from: B[0].from,
                to: B[0].to
            };
        }
        return null;
    }

    var Completer = function (cell) {
            this.editor = cell.code_mirror;
            var that = this;
            $([IPython.events]).on('status_busy.Kernel', function () {
                that.skip_kernel_completion = true;
            });
            $([IPython.events]).on('status_idle.Kernel', function () {
                that.skip_kernel_completion = false;
            });

        };

    Completer.prototype.startCompletion = function () {
        // call for a 'first' completion, that will set the editor and do some
        // special behaviour like autopicking if only one completion availlable
        //
        if (this.editor.somethingSelected()) return;
        this.done = false;
        // use to get focus back on opera
        this.carry_on_completion(true);
    };

    Completer.prototype.carry_on_completion = function (ff) {
        // Pass true as parameter if you want the commpleter to autopick when
        // only one completion. This function is automatically reinvoked at
        // each keystroke with ff = false
        var cur = this.editor.getCursor();
        var line = this.editor.getLine(cur.line);
        var pre_cursor = this.editor.getRange({
            line: cur.line,
            ch: cur.ch - 1
        }, cur);

        // we need to check that we are still on a word boundary
        // because while typing the completer is still reinvoking itself
        if (!/[0-9a-z._]/i.test(pre_cursor)) {
            this.close();
            return;
        }

        this.autopick = false;
        if (ff != 'undefined' && ff == true) {
            this.autopick = true;
        }

        // We want a single cursor position.
        if (this.editor.somethingSelected()) return;

        // one kernel completion came back, finish_completing will be called with the results
        // we fork here and directly call finish completing if kernel is busy
        if (this.skip_kernel_completion == true) {
            this.finish_completing({
                'matches': [],
                matched_text: ""
            })
        } else {
            var callbacks = {
                'complete_reply': $.proxy(this.finish_completing, this)
            };
            IPython.notebook.kernel.complete(line, cur.ch, callbacks);
        }
    };

    Completer.prototype.finish_completing = function (content) {
        // let's build a function that wrap all that stuff into what is needed
        // for the new completer:
        var matched_text = content.matched_text;
        var matches = content.matches;

        var cur = this.editor.getCursor();
        var results = CodeMirror.contextHint(this.editor);
        var filterd_results = Array();
        //remove results from context completion
        //that are already in kernel completion
        for(var elm in results) {
            if(_existing_completion(results[elm]['str'],matches) == false)
            { filterd_results.push(results[elm]); }
        }

        // append the introspection result, in order, at at the beginning of
        // the table and compute the replacement range from current cursor
        // positon and matched_text length.
        for (var i = matches.length - 1; i >= 0; --i) {
            filterd_results.unshift({
                str: matches[i],
                type: "introspection",
                from: {
                    line: cur.line,
                    ch: cur.ch - matched_text.length
                },
                to: {
                    line: cur.line,
                    ch: cur.ch
                }
            });
        }

        // one the 2 sources results have been merge, deal with it
        this.raw_result = filterd_results;

        // if empty result return
        if (!this.raw_result || !this.raw_result.length) return;

        // When there is only one completion, use it directly.
        if (this.autopick == true && this.raw_result.length == 1) {
            this.insert(this.raw_result[0]);
            return;
        }

        if (this.raw_result.length == 1) {
            // test if first and only completion totally matches
            // what is typed, in this case dismiss
            var str = this.raw_result[0].str;
            var pre_cursor = this.editor.getRange({
                line: cur.line,
                ch: cur.ch - str.length
            }, cur);
            if (pre_cursor == str) {
                this.close();
                return;
            }
        }

        this.complete = $('<div/>').addClass('completions');
        this.complete.attr('id', 'complete');

        this.sel = $('<select/>').attr('multiple', 'true').attr('size', Math.min(10, this.raw_result.length));
        var pos = this.editor.cursorCoords();

        // TODO: I propose to remove enough horizontal pixel
        // to align the text later
        this.complete.css('left', pos.x + 'px');
        this.complete.css('top', pos.yBot + 'px');
        this.complete.append(this.sel);

        $('body').append(this.complete);
        //build the container
        var that = this;
        this.sel.dblclick(function () {
            that.pick();
        });
        this.sel.blur(this.close);
        this.sel.keydown(function (event) {
            that.keydown(event);
        });

        this.build_gui_list(this.raw_result);

        this.sel.focus();
        // Opera sometimes ignores focusing a freshly created node
        if (window.opera) setTimeout(function () {
            if (!this.done) this.sel.focus();
        }, 100);
        return true;
    }

    Completer.prototype.insert = function (completion) {
        this.editor.replaceRange(completion.str, completion.from, completion.to);
    }

    Completer.prototype.build_gui_list = function (completions) {
        // Need to clear the all list
        for (var i = 0; i < completions.length; ++i) {
            var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
            this.sel.append(opt);
        }
        this.sel.children().first().attr('selected', 'true');
    }

    Completer.prototype.close = function () {
        if (this.done) return;
        this.done = true;
        $('.completions').remove();
    }

    Completer.prototype.pick = function () {
        this.insert(this.raw_result[this.sel[0].selectedIndex]);
        this.close();
        var that = this;
        setTimeout(function () {
            that.editor.focus();
        }, 50);
    }

    Completer.prototype.keydown = function (event) {
        var code = event.keyCode;
        var that = this;
        // Enter
        if (code == key.ENTER) {
            CodeMirror.e_stop(event);
            this.pick();
        }
        // Escape or backspace
        else if (code == key.ESC) {
            CodeMirror.e_stop(event);
            this.close();
            this.editor.focus();
        } else if (code == key.SPACE || code == key.BACKSPACE) {
            this.close();
            this.editor.focus();
        } else if (code == key.TAB) {
            //all the fastforwarding operation,
            //Check that shared start is not null which can append with prefixed completion
            // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
            // to erase py
            var sh = shared_start(this.raw_result);
            if (sh) {
                this.insert(sh);
            }
            this.close();
            CodeMirror.e_stop(event);
            this.editor.focus();
            //reinvoke self
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        } else if (code == key.UPARROW || code == key.DOWNARROW) {
            // need to do that to be able to move the arrow
            // when on the first or last line ofo a code cell
            event.stopPropagation();
        } else if (code != key.UPARROW && code != key.DOWNARROW) {
            this.close();
            this.editor.focus();
            //we give focus to the editor immediately and call sell in 50 ms
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        }
    }

    IPython.Completer = Completer;

    return IPython;
}(IPython));
<commit_msg>don't need to check for leading dot<commit_after>// function completer.
//
// completer should be a class that take an cell instance
var IPython = (function (IPython) {
    // that will prevent us from misspelling
    "use strict";

    // easyier key mapping
    var key = IPython.utils.keycodes;

    function _existing_completion(item, completion_array){
       for( var c in completion_array ) {
           if(completion_array[c].substr(-item.length) == item)
           { return true; }
       }
       return false;
    }

    // what is the common start of all completions
    function shared_start(B) {
        if (B.length == 1) {
            return B[0];
        }
        var A = new Array();
        for (var i = 0; i < B.length; i++) {
            A.push(B[i].str);
        }
        if (A.length > 1) {
            var tem1, tem2, s;
            A = A.slice(0).sort();
            tem1 = A[0];
            s = tem1.length;
            tem2 = A.pop();
            while (s && tem2.indexOf(tem1) == -1) {
                tem1 = tem1.substring(0, --s);
            }
            if (tem1 == "" || tem2.indexOf(tem1) != 0) {
                return null;
            }
            return {
                str: tem1,
                type: "computed",
                from: B[0].from,
                to: B[0].to
            };
        }
        return null;
    }

    var Completer = function (cell) {
            this.editor = cell.code_mirror;
            var that = this;
            $([IPython.events]).on('status_busy.Kernel', function () {
                that.skip_kernel_completion = true;
            });
            $([IPython.events]).on('status_idle.Kernel', function () {
                that.skip_kernel_completion = false;
            });

        };

    Completer.prototype.startCompletion = function () {
        // call for a 'first' completion, that will set the editor and do some
        // special behaviour like autopicking if only one completion availlable
        //
        if (this.editor.somethingSelected()) return;
        this.done = false;
        // use to get focus back on opera
        this.carry_on_completion(true);
    };

    Completer.prototype.carry_on_completion = function (ff) {
        // Pass true as parameter if you want the commpleter to autopick when
        // only one completion. This function is automatically reinvoked at
        // each keystroke with ff = false
        var cur = this.editor.getCursor();
        var line = this.editor.getLine(cur.line);
        var pre_cursor = this.editor.getRange({
            line: cur.line,
            ch: cur.ch - 1
        }, cur);

        // we need to check that we are still on a word boundary
        // because while typing the completer is still reinvoking itself
        if (!/[0-9a-z._]/i.test(pre_cursor)) {
            this.close();
            return;
        }

        this.autopick = false;
        if (ff != 'undefined' && ff == true) {
            this.autopick = true;
        }

        // We want a single cursor position.
        if (this.editor.somethingSelected()) return;

        // one kernel completion came back, finish_completing will be called with the results
        // we fork here and directly call finish completing if kernel is busy
        if (this.skip_kernel_completion == true) {
            this.finish_completing({
                'matches': [],
                matched_text: ""
            })
        } else {
            var callbacks = {
                'complete_reply': $.proxy(this.finish_completing, this)
            };
            IPython.notebook.kernel.complete(line, cur.ch, callbacks);
        }
    };

    Completer.prototype.finish_completing = function (content) {
        // let's build a function that wrap all that stuff into what is needed
        // for the new completer:
        var matched_text = content.matched_text;
        var matches = content.matches;

        var cur = this.editor.getCursor();
        var results = CodeMirror.contextHint(this.editor);
        var filterd_results = Array();
        //remove results from context completion
        //that are already in kernel completion
        for(var elm in results) {
            if(_existing_completion(results[elm]['str'], matches) == false)
            { filterd_results.push(results[elm]); }
        }

        // append the introspection result, in order, at at the beginning of
        // the table and compute the replacement range from current cursor
        // positon and matched_text length.
        for (var i = matches.length - 1; i >= 0; --i) {
            filterd_results.unshift({
                str: matches[i],
                type: "introspection",
                from: {
                    line: cur.line,
                    ch: cur.ch - matched_text.length
                },
                to: {
                    line: cur.line,
                    ch: cur.ch
                }
            });
        }

        // one the 2 sources results have been merge, deal with it
        this.raw_result = filterd_results;

        // if empty result return
        if (!this.raw_result || !this.raw_result.length) return;

        // When there is only one completion, use it directly.
        if (this.autopick == true && this.raw_result.length == 1) {
            this.insert(this.raw_result[0]);
            return;
        }

        if (this.raw_result.length == 1) {
            // test if first and only completion totally matches
            // what is typed, in this case dismiss
            var str = this.raw_result[0].str;
            var pre_cursor = this.editor.getRange({
                line: cur.line,
                ch: cur.ch - str.length
            }, cur);
            if (pre_cursor == str) {
                this.close();
                return;
            }
        }

        this.complete = $('<div/>').addClass('completions');
        this.complete.attr('id', 'complete');

        this.sel = $('<select/>').attr('multiple', 'true').attr('size', Math.min(10, this.raw_result.length));
        var pos = this.editor.cursorCoords();

        // TODO: I propose to remove enough horizontal pixel
        // to align the text later
        this.complete.css('left', pos.x + 'px');
        this.complete.css('top', pos.yBot + 'px');
        this.complete.append(this.sel);

        $('body').append(this.complete);
        //build the container
        var that = this;
        this.sel.dblclick(function () {
            that.pick();
        });
        this.sel.blur(this.close);
        this.sel.keydown(function (event) {
            that.keydown(event);
        });

        this.build_gui_list(this.raw_result);

        this.sel.focus();
        // Opera sometimes ignores focusing a freshly created node
        if (window.opera) setTimeout(function () {
            if (!this.done) this.sel.focus();
        }, 100);
        return true;
    }

    Completer.prototype.insert = function (completion) {
        this.editor.replaceRange(completion.str, completion.from, completion.to);
    }

    Completer.prototype.build_gui_list = function (completions) {
        // Need to clear the all list
        for (var i = 0; i < completions.length; ++i) {
            var opt = $('<option/>').text(completions[i].str).addClass(completions[i].type);
            this.sel.append(opt);
        }
        this.sel.children().first().attr('selected', 'true');
    }

    Completer.prototype.close = function () {
        if (this.done) return;
        this.done = true;
        $('.completions').remove();
    }

    Completer.prototype.pick = function () {
        this.insert(this.raw_result[this.sel[0].selectedIndex]);
        this.close();
        var that = this;
        setTimeout(function () {
            that.editor.focus();
        }, 50);
    }

    Completer.prototype.keydown = function (event) {
        var code = event.keyCode;
        var that = this;
        // Enter
        if (code == key.ENTER) {
            CodeMirror.e_stop(event);
            this.pick();
        }
        // Escape or backspace
        else if (code == key.ESC) {
            CodeMirror.e_stop(event);
            this.close();
            this.editor.focus();
        } else if (code == key.SPACE || code == key.BACKSPACE) {
            this.close();
            this.editor.focus();
        } else if (code == key.TAB) {
            //all the fastforwarding operation,
            //Check that shared start is not null which can append with prefixed completion
            // like %pylab , pylab have no shred start, and ff will result in py<tab><tab>
            // to erase py
            var sh = shared_start(this.raw_result);
            if (sh) {
                this.insert(sh);
            }
            this.close();
            CodeMirror.e_stop(event);
            this.editor.focus();
            //reinvoke self
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        } else if (code == key.UPARROW || code == key.DOWNARROW) {
            // need to do that to be able to move the arrow
            // when on the first or last line ofo a code cell
            event.stopPropagation();
        } else if (code != key.UPARROW && code != key.DOWNARROW) {
            this.close();
            this.editor.focus();
            //we give focus to the editor immediately and call sell in 50 ms
            setTimeout(function () {
                that.carry_on_completion();
            }, 50);
        }
    }

    IPython.Completer = Completer;

    return IPython;
}(IPython));
<|endoftext|>


<commit_before>const _ = require('lodash');
const Promise = require('bluebird');
const errors = require('@tryghost/errors');
const logging = require('@tryghost/logging');
const tpl = require('@tryghost/tpl');

const settingsCache = require('./settings-cache');
const config = require('./config');

const messages = {
    errorMessage: 'The \\{\\{{helperName}\\}\\} helper is not available.',
    errorContext: 'The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.',
    errorHelp: 'See {url}'
};

// flags in this list always return `true`, allows quick global enable prior to full flag removal
const GA_FEATURES = [
];

// NOTE: this allowlist is meant to be used to filter out any unexpected
//       input for the "labs" setting value
const BETA_FEATURES = [
    'activitypub',
    'multipleProducts'
];

const ALPHA_FEATURES = [
    'oauthLogin',
    'membersActivity',
    'cardSettingsPanel',
    'urlCache',
    'beforeAfterCard',
    'tweetGridCard',
    'membersActivityFeed',
    'improvedOnboarding'
];

module.exports.GA_KEYS = [...GA_FEATURES];
module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];

module.exports.getAll = () => {
    const labs = _.cloneDeep(settingsCache.get('labs')) || {};

    ALPHA_FEATURES.forEach((alphaKey) => {
        if (labs[alphaKey] && !(config.get('enableDeveloperExperiments') || process.env.NODE_ENV.startsWith('test'))) {
            delete labs[alphaKey];
        }
    });

    GA_FEATURES.forEach((gaKey) => {
        labs[gaKey] = true;
    });

    labs.members = settingsCache.get('members_signup_access') !== 'none';

    return labs;
};

/**
 * @param {string} flag
 * @returns {boolean}
 */
module.exports.isSet = function isSet(flag) {
    const labsConfig = module.exports.getAll();

    return !!(labsConfig && labsConfig[flag] && labsConfig[flag] === true);
};

/**
 *
 * @param {object} options
 * @param {string} options.flagKey the internal lookup key of the flag e.g. labs.isSet(matchHelper)
 * @param {string} options.flagName the user-facing name of the flag e.g. Match helper
 * @param {string} options.helperName Name of the helper to be enabled/disabled
 * @param {string} [options.errorMessage] Optional replacement error message
 * @param {string} [options.errorContext] Optional replacement context message
 * @param {string} [options.errorHelp] Optional replacement help message
 * @param {string} [options.helpUrl] Url to show in the help message
 * @param {string} [options.async] is the helper async?
 * @param {function} callback
 * @returns {Promise<Handlebars.SafeString>|Handlebars.SafeString}
 */
module.exports.enabledHelper = function enabledHelper(options, callback) {
    const errDetails = {};
    let errString;

    if (module.exports.isSet(options.flagKey) === true) {
        // helper is active, use the callback
        return callback();
    }

    // Else, the helper is not active and we need to handle this as an error
    errDetails.message = tpl(options.errorMessage || messages.errorMessage, {helperName: options.helperName});
    errDetails.context = tpl(options.errorContext || messages.errorContext, {
        helperName: options.helperName,
        flagName: options.flagName
    });
    errDetails.help = tpl(options.errorHelp || messages.errorHelp, {url: options.helpUrl});

    // eslint-disable-next-line no-restricted-syntax
    logging.error(new errors.DisabledFeatureError(errDetails));

    const {SafeString} = require('express-hbs');
    errString = new SafeString(`<script>console.error("${_.values(errDetails).join(' ')}");</script>`);

    if (options.async) {
        return Promise.resolve(errString);
    }

    return errString;
};

module.exports.enabledMiddleware = flag => (req, res, next) => {
    if (module.exports.isSet(flag) === true) {
        return next();
    } else {
        return next(new errors.NotFoundError());
    }
};
<commit_msg>Remove Editor card settings panel alpha flag (#14087)<commit_after>const _ = require('lodash');
const Promise = require('bluebird');
const errors = require('@tryghost/errors');
const logging = require('@tryghost/logging');
const tpl = require('@tryghost/tpl');

const settingsCache = require('./settings-cache');
const config = require('./config');

const messages = {
    errorMessage: 'The \\{\\{{helperName}\\}\\} helper is not available.',
    errorContext: 'The {flagName} flag must be enabled in labs if you wish to use the \\{\\{{helperName}\\}\\} helper.',
    errorHelp: 'See {url}'
};

// flags in this list always return `true`, allows quick global enable prior to full flag removal
const GA_FEATURES = [
];

// NOTE: this allowlist is meant to be used to filter out any unexpected
//       input for the "labs" setting value
const BETA_FEATURES = [
    'activitypub',
    'multipleProducts'
];

const ALPHA_FEATURES = [
    'oauthLogin',
    'membersActivity',
    'urlCache',
    'beforeAfterCard',
    'tweetGridCard',
    'membersActivityFeed',
    'improvedOnboarding'
];

module.exports.GA_KEYS = [...GA_FEATURES];
module.exports.WRITABLE_KEYS_ALLOWLIST = [...BETA_FEATURES, ...ALPHA_FEATURES];

module.exports.getAll = () => {
    const labs = _.cloneDeep(settingsCache.get('labs')) || {};

    ALPHA_FEATURES.forEach((alphaKey) => {
        if (labs[alphaKey] && !(config.get('enableDeveloperExperiments') || process.env.NODE_ENV.startsWith('test'))) {
            delete labs[alphaKey];
        }
    });

    GA_FEATURES.forEach((gaKey) => {
        labs[gaKey] = true;
    });

    labs.members = settingsCache.get('members_signup_access') !== 'none';

    return labs;
};

/**
 * @param {string} flag
 * @returns {boolean}
 */
module.exports.isSet = function isSet(flag) {
    const labsConfig = module.exports.getAll();

    return !!(labsConfig && labsConfig[flag] && labsConfig[flag] === true);
};

/**
 *
 * @param {object} options
 * @param {string} options.flagKey the internal lookup key of the flag e.g. labs.isSet(matchHelper)
 * @param {string} options.flagName the user-facing name of the flag e.g. Match helper
 * @param {string} options.helperName Name of the helper to be enabled/disabled
 * @param {string} [options.errorMessage] Optional replacement error message
 * @param {string} [options.errorContext] Optional replacement context message
 * @param {string} [options.errorHelp] Optional replacement help message
 * @param {string} [options.helpUrl] Url to show in the help message
 * @param {string} [options.async] is the helper async?
 * @param {function} callback
 * @returns {Promise<Handlebars.SafeString>|Handlebars.SafeString}
 */
module.exports.enabledHelper = function enabledHelper(options, callback) {
    const errDetails = {};
    let errString;

    if (module.exports.isSet(options.flagKey) === true) {
        // helper is active, use the callback
        return callback();
    }

    // Else, the helper is not active and we need to handle this as an error
    errDetails.message = tpl(options.errorMessage || messages.errorMessage, {helperName: options.helperName});
    errDetails.context = tpl(options.errorContext || messages.errorContext, {
        helperName: options.helperName,
        flagName: options.flagName
    });
    errDetails.help = tpl(options.errorHelp || messages.errorHelp, {url: options.helpUrl});

    // eslint-disable-next-line no-restricted-syntax
    logging.error(new errors.DisabledFeatureError(errDetails));

    const {SafeString} = require('express-hbs');
    errString = new SafeString(`<script>console.error("${_.values(errDetails).join(' ')}");</script>`);

    if (options.async) {
        return Promise.resolve(errString);
    }

    return errString;
};

module.exports.enabledMiddleware = flag => (req, res, next) => {
    if (module.exports.isSet(flag) === true) {
        return next();
    } else {
        return next(new errors.NotFoundError());
    }
};
<|endoftext|>


<commit_before>"use strict";
var assert = chai.assert;
var should = chai.should();
var expect = chai.expect;
sessionStorage.setItem('sfFlashMessage', JSON.stringify({message:'test', timeout: 1 }));
require("../../../src/index");

describe('AJAX', function () {
    var server,
        data = { "id": 12, "comment": "Hey there",'action':{'flash':{'message':'testFlashMessage'}}};
    beforeEach(function(){
        server = sinon.fakeServer.create();
        server.respondWith("POST","/test", [200, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test1", [500, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test2", [999, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test4", [200, {"Content-Type":"application/json"}, 'not json data']);
        server.respondWith("POST","/test5", [200,{"Content-Type":"fake"},'']);
    });

    it("#AJAX.send()", function (done) {
        sf.ajax.send({url:"/test", data: {}, onProgress: function(){}}).then(function(answer){expect(answer.id).to.equals(12); done()});
        server.respond();
    });
    it("#AJAX.send() error without url", function (done) {
        sf.ajax.send({}).then(function(){},function(error){expect(error).to.equals("You should provide url"); done();});
        server.respond();
    });
    it("#AJAX.send() answer with code 500 should call error callback ", function (done) {
        sf.ajax.send({url:"/test1", data: {}}).then(function(){},function(){done();});
        server.respond();
    });
    it("#AJAX.send() answer with code 999 should call error callback ", function (done) {
        sf.ajax.send({url:"/test2", data: {}}).then(function(){},function(){done();});
        server.respond();
    });
    it("#AJAX.send() should resolve width not json answer ", function (done) {
        sf.ajax.send({url:"/test4", data: {}}).then(function(){done()},function(error){done(error)});
        server.respond();
    });
    it("#AJAX.send() should resolve width empty answer ", function (done) {
        sf.ajax.send({url:"/test5", data: {}}).then(function(){done()},function(error){done(error)});
        server.respond();
    });
    it("#sessionStorage sfFlashMessage should has data", function () {
        expect(JSON.parse(sessionStorage.getItem('sfFlashMessage')).message).to.equals('testFlashMessage')
    });
    it("#flashMessage div should exist", function () {
        expect(document.getElementsByClassName('flash-wrapper')[0]).to.exist
    });
    afterEach(function(){
        server.restore();
    });

});
<commit_msg>AJAX.baseEvents tests<commit_after>"use strict";
var assert = chai.assert;
var should = chai.should();
var expect = chai.expect;
sessionStorage.setItem('sfFlashMessage', JSON.stringify({message:'test', timeout: 1 }));
require("../../../src/index");

describe('AJAX', function () {
    var server,
        data = { "id": 12, "comment": "Hey there",'action':{'flash':{'message':'testFlashMessage'}}};
    beforeEach(function(){
        server = sinon.fakeServer.create();
        server.respondWith("POST","/test", [200, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test1", [500, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test2", [999, {"Content-Type":"application/json"}, JSON.stringify(data)]);
        server.respondWith("POST","/test4", [200, {"Content-Type":"application/json"}, 'not json data']);
        server.respondWith("POST","/test5", [200,{"Content-Type":"fake"},'']);
    });

    it("#AJAX.send()", function (done) {
        sf.ajax.send({url:"/test", data: {}, onProgress: function(){}}).then(function(answer){expect(answer.id).to.equals(12); done()});
        server.respond();
    });
    it("#AJAX.send() error without url", function (done) {
        sf.ajax.send({}).then(function(){},function(error){expect(error).to.equals("You should provide url"); done();});
        server.respond();
    });
    it("#AJAX.send() answer with code 500 should call error callback ", function (done) {
        sf.ajax.send({url:"/test1", data: {}}).then(function(){},function(){done();});
        server.respond();
    });
    it("#AJAX.send() answer with code 999 should call error callback ", function (done) {
        sf.ajax.send({url:"/test2", data: {}}).then(function(){},function(){done();});
        server.respond();
    });
    it("#AJAX.send() should resolve width not json answer ", function (done) {
        sf.ajax.send({url:"/test4", data: {}}).then(function(){done()},function(error){done(error)});
        server.respond();
    });
    it("#AJAX.send() should resolve width empty answer ", function (done) {
        sf.ajax.send({url:"/test5", data: {}}).then(function(){done()},function(error){done(error)});
        server.respond();
    });

    it("#sessionStorage sfFlashMessage should has data", function () {
        expect(JSON.parse(sessionStorage.getItem('sfFlashMessage')).message).to.equals('testFlashMessage')
    });
    it("#flashMessage div should exist", function () {
        expect(document.getElementsByClassName('flash-wrapper')[0]).to.exist
    });
    afterEach(function(){
        server.restore();
    });

});

describe('AJAX base actions', function () {
    var AJAXserver,
        reload = {'action':'reload'},
        redirect = {'action':{'redirect': '/test'}},
        redirectName = {"action":{"name":"redirect","url":"/account"}};

    sinon.stub(sf.events._storage.reload, [0]);
    sinon.stub(sf.events._storage.redirect, [0]);
    AJAXserver = sinon.fakeServer.create();
    AJAXserver.respondWith("POST","/reload", [200, {"Content-Type":"application/json"}, JSON.stringify(reload)]);
    AJAXserver.respondWith("POST","/redirect", [200, {"Content-Type":"application/json"}, JSON.stringify(redirect)]);
    AJAXserver.respondWith("POST","/redirectName", [200, {"Content-Type":"application/json"}, JSON.stringify(redirectName)]);
    sf.ajax.send({url:"/reload", data: {}});
    AJAXserver.respond();
    sf.ajax.send({url:"/redirect", data: {}});
    AJAXserver.respond();
    sf.ajax.send({url:"/redirectName", data: {}});
    AJAXserver.respond();
    describe("reload", function() {
        it("#reload should be called after {'action':'reload'} answer", function () {
            expect(sf.events._storage.reload[0].calledOnce).to.be.true;
        });
        after(function(){
            sf.events._storage.reload[0].restore()
        })
    });
    describe("redirect", function() {
        it("#redirect should be called after {'action':{'redirect': '/test'}} &&  answer", function () {
           expect(sf.events._storage.redirect[0].calledTwice).to.be.true;
        });
        after(function(){
            sf.events._storage.redirect[0].restore()
        })
    });
    afterEach(function(){
        AJAXserver.restore();
    });

});
<|endoftext|>


<commit_before>const _ = require('underscore');

const Locale = require('locale/index');
const Polyglot = require('node-polyglot');

const ACTIVE_LOCALE = 'en';

const polyglot = new Polyglot({
  locale: ACTIVE_LOCALE,
  phrases: Locale[ACTIVE_LOCALE]
});

window._t = polyglot.t.bind(polyglot);

const LocalStorage = require('dashboard/helpers/local-storage');
const ConfigModel = require('dashboard/data/config-model');
const UserModel = require('dashboard/data/user-model');
const CreateDialog = require('dashboard/views/dashboard/dialogs/create-dialog/dialog-view');
const OrganizationModel = require('dashboard/data/organization-model');
const UserGroupsCollection = require('dashboard/data/user-groups-collection');
const UserNotificationView = require('dashboard/components/dashboard-header/notifications/user-notifications-view');
const UserNotificationModel = require('dashboard/components/user-notification/user-notification-model');
const DashboardBackgroundPollingModel = require('dashboard/data/background-polling/dashboard-background-polling-model');
const BackgroundPollingView = require('dashboard/views/dashboard/background-polling/background-polling-view');
const VisualizationsCollection = require('dashboard/data/visualizations-collection');
const MetricsTracker = require('dashboard/views/dashboard/metrics-tracker');
const MetricsTypes = require('builder/components/metrics/metrics-types');

const Router = require('dashboard/common/router-dashboard');
const MainView = require('dashboard/views/dashboard/main-view');

require('dashboard/data/backbone/sync-options');

window.StaticConfig = window.StaticConfig || {};
window.CartoConfig = window.CartoConfig || {};

const ForbiddenAction = require('builder/data/backbone/network-interceptors/interceptors/forbidden-403');
const NetworkResponseInterceptor = require('builder/data/backbone/network-interceptors/interceptor');
NetworkResponseInterceptor.addURLPattern('api/v');
NetworkResponseInterceptor.addErrorInterceptor(ForbiddenAction());
NetworkResponseInterceptor.start();

function getAssetsVersion (configModel) {
  const query = window.location.search.substring(1);
  const vars = query.split('&');

  for (let i = 0; i < vars.length; i++) {
    const pair = vars[i].split('=');

    if (pair[0] === 'v') {
      return pair[1];
    }
  }

  return window.StaticConfig.assetVersion || window.CartoConfig.data.user_frontend_version || configModel.get('assets_url').split('/assets/')[1];
}

function dataLoaded (data) {
  const userData = data.user_data;
  const default_fallback_basemap = data.default_fallback_basemap;
  const dashboard_notifications = data.dashboard_notifications;
  const organizationNotifications = data.organization_notifications;
  const isJustLoggedIn = data.isJustLoggedIn;
  const isFirstTimeViewingDashboard = data.isFirstTimeViewingDashboard;

  resetOrderWhenLikesIsApplied();

  const configModel = new ConfigModel(
    _.extend(
      data.config,
      {
        base_url: userData.base_url,
        url_prefix: userData.base_url,
        default_fallback_basemap
      }
    )
  );

  const currentUser = new UserModel(userData);

  if (userData.organization) {
    currentUser.setOrganization(new OrganizationModel(userData.organization, { configModel }));
  }

  if (userData.groups) {
    currentUser.setGroups(new UserGroupsCollection(userData.groups, { configModel }));
  }

  document.title = currentUser.get('username') + ' | CARTO';

  const router = new Router({
    dashboardUrl: currentUser.viewUrl().dashboard()
  });

  if (!configModel.get('cartodb_com_hosted')) {
    if (currentUser.get('actions').builder_enabled &&
      currentUser.get('show_builder_activated_message') &&
      _.isEmpty(dashboard_notifications)) {
      const userNotificationModel = new UserNotificationModel(dashboard_notifications, {
        key: 'dashboard',
        configModel
      });

      new UserNotificationView({ // eslint-disable-line
        notification: userNotificationModel,
        user: currentUser,
        configModel
      });
    }
  }

  // Why not have only one collection?
  const collection = new VisualizationsCollection(null, { configModel });

  const backgroundPollingModel = new DashboardBackgroundPollingModel({
    showGeocodingDatasetURLButton: true,
    geocodingsPolling: true,
    importsPolling: true
  }, {
    userModel: currentUser,
    configModel
  });

  const backgroundPollingView = new BackgroundPollingView({
    model: backgroundPollingModel,
    // Only create a visualization from an import if user is in maps section
    createVis: router.model.isMaps(),
    userModel: currentUser,
    configModel
  });

  const dashboard = new MainView({
    el: document.body,
    collection,
    userModel: currentUser,
    configModel,
    routerModel: router,
    backgroundPollingModel,
    backgroundPollingView,
    organizationNotifications,
    assetsVersion: getAssetsVersion()
  });
  window.dashboard = dashboard;

  router.enableAfterMainView();

  // Event tracking "Visited Dashboard"
  const InternalMetricsTracker = require('builder/components/metrics/metrics-tracker');
  InternalMetricsTracker.init({
    userId: currentUser.get('id'),
    configModel
  });

  InternalMetricsTracker.track(MetricsTypes.VISITED_PRIVATE_PAGE, {
    page: 'dashboard'
  });

  const metrics = new MetricsTracker();
  metrics.trackEvent('visited_dashboard', {
    email: userData.email
  });

  if (isJustLoggedIn) {
    // Event tracking "Logged in"
    metrics.trackEvent('logged_in', {
      email: userData.email
    });
  }

  if (isFirstTimeViewingDashboard) {
    // Event tracking "Visited Dashboard for the first time"
    metrics.trackEvent('visited_dashboard_first_time', {
      email: userData.email
    });
  }

  CreateDialog.setViewProperties({
    configModel,
    userModel: currentUser,
    pollingModel: backgroundPollingModel,
    pollingView: backgroundPollingView,
    routerModel: router
  });
}

if (window.CartoConfig.data) {
  dataLoaded(window.CartoConfig.data);
} else {
  const client = new CartoNode.AuthenticatedClient();

  client.getConfig(function (err, response, data) {
    if (err) {
      console.error(err);

      return err;
    } else {
      window.CartoConfig.data = data;
      dataLoaded(data);
    }
  });
}

function resetOrderWhenLikesIsApplied () {
  const localStorageInstance = new LocalStorage();
  const dashboardOrder = localStorageInstance.get('dashboard.order');

  if (dashboardOrder === 'likes') {
    localStorageInstance.set({'dashboard.order': 'mapviews'});
  }
}
<commit_msg>Set `updated_at` as default for Dashboard order<commit_after>const _ = require('underscore');

const Locale = require('locale/index');
const Polyglot = require('node-polyglot');

const ACTIVE_LOCALE = 'en';

const polyglot = new Polyglot({
  locale: ACTIVE_LOCALE,
  phrases: Locale[ACTIVE_LOCALE]
});

window._t = polyglot.t.bind(polyglot);

const LocalStorage = require('dashboard/helpers/local-storage');
const ConfigModel = require('dashboard/data/config-model');
const UserModel = require('dashboard/data/user-model');
const CreateDialog = require('dashboard/views/dashboard/dialogs/create-dialog/dialog-view');
const OrganizationModel = require('dashboard/data/organization-model');
const UserGroupsCollection = require('dashboard/data/user-groups-collection');
const UserNotificationView = require('dashboard/components/dashboard-header/notifications/user-notifications-view');
const UserNotificationModel = require('dashboard/components/user-notification/user-notification-model');
const DashboardBackgroundPollingModel = require('dashboard/data/background-polling/dashboard-background-polling-model');
const BackgroundPollingView = require('dashboard/views/dashboard/background-polling/background-polling-view');
const VisualizationsCollection = require('dashboard/data/visualizations-collection');
const MetricsTracker = require('dashboard/views/dashboard/metrics-tracker');
const MetricsTypes = require('builder/components/metrics/metrics-types');

const Router = require('dashboard/common/router-dashboard');
const MainView = require('dashboard/views/dashboard/main-view');

require('dashboard/data/backbone/sync-options');

window.StaticConfig = window.StaticConfig || {};
window.CartoConfig = window.CartoConfig || {};

const ForbiddenAction = require('builder/data/backbone/network-interceptors/interceptors/forbidden-403');
const NetworkResponseInterceptor = require('builder/data/backbone/network-interceptors/interceptor');
NetworkResponseInterceptor.addURLPattern('api/v');
NetworkResponseInterceptor.addErrorInterceptor(ForbiddenAction());
NetworkResponseInterceptor.start();

function getAssetsVersion (configModel) {
  const query = window.location.search.substring(1);
  const vars = query.split('&');

  for (let i = 0; i < vars.length; i++) {
    const pair = vars[i].split('=');

    if (pair[0] === 'v') {
      return pair[1];
    }
  }

  return window.StaticConfig.assetVersion || window.CartoConfig.data.user_frontend_version || configModel.get('assets_url').split('/assets/')[1];
}

function dataLoaded (data) {
  const userData = data.user_data;
  const default_fallback_basemap = data.default_fallback_basemap;
  const dashboard_notifications = data.dashboard_notifications;
  const organizationNotifications = data.organization_notifications;
  const isJustLoggedIn = data.isJustLoggedIn;
  const isFirstTimeViewingDashboard = data.isFirstTimeViewingDashboard;

  resetOrderWhenLikesIsApplied();

  const configModel = new ConfigModel(
    _.extend(
      data.config,
      {
        base_url: userData.base_url,
        url_prefix: userData.base_url,
        default_fallback_basemap
      }
    )
  );

  const currentUser = new UserModel(userData);

  if (userData.organization) {
    currentUser.setOrganization(new OrganizationModel(userData.organization, { configModel }));
  }

  if (userData.groups) {
    currentUser.setGroups(new UserGroupsCollection(userData.groups, { configModel }));
  }

  document.title = currentUser.get('username') + ' | CARTO';

  const router = new Router({
    dashboardUrl: currentUser.viewUrl().dashboard()
  });

  if (!configModel.get('cartodb_com_hosted')) {
    if (currentUser.get('actions').builder_enabled &&
      currentUser.get('show_builder_activated_message') &&
      _.isEmpty(dashboard_notifications)) {
      const userNotificationModel = new UserNotificationModel(dashboard_notifications, {
        key: 'dashboard',
        configModel
      });

      new UserNotificationView({ // eslint-disable-line
        notification: userNotificationModel,
        user: currentUser,
        configModel
      });
    }
  }

  // Why not have only one collection?
  const collection = new VisualizationsCollection(null, { configModel });

  const backgroundPollingModel = new DashboardBackgroundPollingModel({
    showGeocodingDatasetURLButton: true,
    geocodingsPolling: true,
    importsPolling: true
  }, {
    userModel: currentUser,
    configModel
  });

  const backgroundPollingView = new BackgroundPollingView({
    model: backgroundPollingModel,
    // Only create a visualization from an import if user is in maps section
    createVis: router.model.isMaps(),
    userModel: currentUser,
    configModel
  });

  const dashboard = new MainView({
    el: document.body,
    collection,
    userModel: currentUser,
    configModel,
    routerModel: router,
    backgroundPollingModel,
    backgroundPollingView,
    organizationNotifications,
    assetsVersion: getAssetsVersion()
  });
  window.dashboard = dashboard;

  router.enableAfterMainView();

  // Event tracking "Visited Dashboard"
  const InternalMetricsTracker = require('builder/components/metrics/metrics-tracker');
  InternalMetricsTracker.init({
    userId: currentUser.get('id'),
    configModel
  });

  InternalMetricsTracker.track(MetricsTypes.VISITED_PRIVATE_PAGE, {
    page: 'dashboard'
  });

  const metrics = new MetricsTracker();
  metrics.trackEvent('visited_dashboard', {
    email: userData.email
  });

  if (isJustLoggedIn) {
    // Event tracking "Logged in"
    metrics.trackEvent('logged_in', {
      email: userData.email
    });
  }

  if (isFirstTimeViewingDashboard) {
    // Event tracking "Visited Dashboard for the first time"
    metrics.trackEvent('visited_dashboard_first_time', {
      email: userData.email
    });
  }

  CreateDialog.setViewProperties({
    configModel,
    userModel: currentUser,
    pollingModel: backgroundPollingModel,
    pollingView: backgroundPollingView,
    routerModel: router
  });
}

if (window.CartoConfig.data) {
  dataLoaded(window.CartoConfig.data);
} else {
  const client = new CartoNode.AuthenticatedClient();

  client.getConfig(function (err, response, data) {
    if (err) {
      console.error(err);

      return err;
    } else {
      window.CartoConfig.data = data;
      dataLoaded(data);
    }
  });
}

function resetOrderWhenLikesIsApplied () {
  const localStorageInstance = new LocalStorage();
  const dashboardOrder = localStorageInstance.get('dashboard.order');

  if (dashboardOrder === 'likes') {
    localStorageInstance.set({'dashboard.order': 'updated_at'});
  }
}
<|endoftext|>


<commit_before>"use strict";

var sj = new function(endpoint) {

	this.setEndpoint = function(url) { endpoint = url };
	this.setRequestWait = function(callback) { onRequestWait = callback };
	this.setRequestComplete = function(callback) { setRequestComplete = callback };
	this.setClickCallback = function(callback) { onClickCallback = callback };
	this.setErrorCallback = function(callback) { onErrorCallback = callback };
	this.setActionCallback = function(callback) { onActionCallback = callback };
	this.setBeforeRequestCallback = function(callback) { onBeforeRequestCallback = callback };

	// Render function
	function renderSingle(el, k, v) {
		if (!loaded || !el) return;
		var list = k == "_" ? [el] : el.querySelectorAll("[name="+k+"]");
		if (!list.length) return console.error("Element not found", el, k, v);

		if (typeof v == "string" || typeof v == "number") v = { _: v };
		else if (typeof v == "boolean") v = { hidden: !v };

		for (var i in v) {
			for (j = 0; j < list.length; j++) {
				var l = list[j];
				if (i == "_") {
					if (l.tagName == "INPUT" && (l.type == "radio" || l.type == "checkbox")) {
						l.checked = (l.value == v[i]);
					} else if (l.tagName == "INPUT") {
						l.value = v[i];
					} else if (l.hasAttribute('data-html')) {
						l.innerHTML = v[i];
					} else {
						l.innerHTML = "";
						l.appendChild(document.createTextNode(v[i]));
					}
				} else if (i == "style") {
					for (var j in v[i]) l.style[j] = v[i][j];
				} else if (v[i] === false) {
					l.removeAttribute(i);
				} else if (v[i] === true) {
					l.setAttribute(i, "");
				} else {
					l.setAttribute((i == "className" ? "class" : i), v[i]);
				}
			}
		}
	}

	// Utilities
	this.hasClass = function(el, c) {
		return (el.className.search(/\bc\b/) > -1);
	}

	this.removeClass = function(el, c) {
		var re = new RegExp("\\b\s*"+c+"\\b");
		el.className = el.className.replace(re, '').replace(/^\s+|\s$/g, '');
	}

	this.addClass = function(el, c) {
		if (!sj.hasClass(el, c)) el.className += " "+c;
	}

	this.toggleClass = function(el, c) {
		if (sj.hasClass(el, c)) {
			sj.addClass(el, c);
		} else {
			sj.removeClass(el, c);
		}
	}

	var componentClass = function(id, obj) {

		this.oncreate = function() {}
		this.onshow = function() {}
		this.onpostshow = function() {}
		this.onprehide = function() {}
		this.onhide = function() {}
		this.onclick = function(ev, target) {}
		this.onaction = function(ev, target) {
			onActionCallback(ev, this.el, target);
		}
		this.onsubmit = function(ev, target) {
			onSubmitCallback(ev, this.el, target);
		}
		this.onrequest = function() {}
		this.onresponse = function() {}
		this.onerror = function(json) {
			if (onSubmitCallback) onErrorCallback(json);
			else console.error(json);
		}

		this.hide = function() {
			this.onprehide();
			this.el.hidden = true;
			this.onhide();
		}
		this.show = function() {
			this.onshow();
			this.el.hidden = false;
			this.onpostshow();
		}
		this.visible = function(b) {
			if (b) this.show(); else this.hide();
		}

		this.hasClass = function(c) { return hasClass(this.el, c) }
		this.removeClass = function(c) { return removeClass(this.el, c) }
		this.addClass = function(c) { return addClass(this.el, c) }
		this.toggleClass = function(c) { return toggleClass(this.el, c) }

		this.action = function(q) {
			sj.request(q);
		},

		this.render = function(p, sub) {
			var el;
			if (!p || typeof p != "object") return;
			if (typeof sub == "string") {
				el = this.el.querySelector("[name="+sub+"]");
			} else if (typeof sub == "object") {
				var t = sub;
				while (t != document.body && t != this.el) t = t.parentNode;
				el = (t == this.el) ? t : null;
			}
			if (!el) el = this.el;
			for (var i in p) {
				var l = el.querySelector("[name="+i+"]");
				if (p[i] instanceof Array) {
					while (!l.lastElementChild.hidden) l.removeChild(l.lastElementChild);
					for (var j = 0; j < p[i].length; j++) {
						var clone = l.firstElementChild.cloneNode(true);
						clone.hidden = false;
						for (var k in p[i][j]) {
							renderSingle(clone, k, p[i][j][k]);
						}
						l.appendChild(clone);
					}
				} else {
					renderSingle(el, i, p[i]);
				}
			}
			if (this.onrender) this.onrender();
		}

		this.el = document.getElementById(id);
		if (!this.el) throw "Component `"+id+"` not found";
		this.el.setAttribute("data-component", "");
		this.loop = this.el;

		for (var i in obj) {
			if (i == "loop") obj[i] = document.getElementById(obj[i]);
			this[i] = obj[i];
		}

	}

	this.component = function(id, obj) {
		components[id] = (obj ? obj : {});
		if (loaded) {
			components[id] = new componentClass(id, obj);
			components[id].oncreate();
		}
	}

	function containerClass(id, start, obj) {
		this.current = start;
		this.onChange = function(current) {}

		this.set = function(id) {
			this.current = id;
			for (var i = 0; i < this.el.children.length; i++) {
				var el = this.el.children[i];
				if (el.hasAttribute("data-component")) {
					sj.$(el.id).visible(el.id == id);
				}
			}
			this.onChange(this.current);
		}

		this.get = function() {
			return this.current;
		}

		this.el = document.getElementById(id);
		if (!this.el) throw "Container `"+id+"` not found";
		this.el.setAttribute("data-container", "");

		for (var i = 0; i < this.el.children.length; i++) {
			var el = this.el.children[i];
			sj.$(el.id).visible(el.id == start);
		}

		if (obj) for (var i in obj) {
			this[i] = obj[i];
		}
	}

	this.container = function(id, start, obj) {
		containers[id] = [start, obj];
		if (loaded) {
			containers[id] = new containerClass(id, start, obj);
		}
	}

	this.$ = function(id) {
		if (components[id]) {
			return components[id];
		} else if (containers[id]) {
			return containers[id];
		} else {
			console.error("`"+id+"` not found");
		}
	}

	function parseHash(hash) {
		var q = {};

		var n = hash.search(/#[!\?]/);
		if (n > -1) hash = hash.substring(n+2);

		var p = hash.split(/[&=]/);
		for (var i = 0; i < p.length; i+=2) q[p[i]] = (p[i+1] ? p[i+1] : null);
		return q;
	}

	function onaction(ev) {
		var el = ev.target;
		if (!el) return;
		while (!el.hasAttribute("data-component") && el != document.body) el = el.parentNode;
		if (el == document.body) return;
		var id = el.id;
		var target = ev.target;

		while (target.tagName != "A" && target.tagName != "FORM" && target != el) target = target.parentNode;
		if (target.tagName == "A") {
			if (target.hash.search(/^#\?/) > -1) {
				components[id].onrequest(ev, target);
				sj.request(target.hash);
				ev.preventDefault();
			} else if (target.hash.search(/^#!/) > -1) {
				components[id].onaction(target.hash.substring(2), target);
				ev.preventDefault();
			}
		} else if (target.tagName == "FORM" && ev.type == 'submit') {
			if (target.action.search(/#\?/) > -1) {
				components[id].onrequest(ev, target);
				sj.request(target.action, target);
				ev.preventDefault();
			} else if (target.action.search(/#!/) > -1) {
				var p = target.action.search(/#!/);
				components[id].onaction(parseHash(target.action), target);
				ev.preventDefault();
			}
		} else {
			components[id].onclick(ev, target);
		}
	}

	this.request = function(query, form) {
		if (!endpoint) throw "No endpoint defined!";
		var xhr = new XMLHttpRequest();
		xhr.addEventListener("load", function(ev) {
			var json = JSON.parse(ev.target.responseText);
			if (json.error) {
				components[json.component].onerror(json);
			} else if (json.component && components[json.component]) {
				components[json.component].onresponse(json);
			} else {
				throw "Component `"+json.component+"` not found";
			}
			if (onRequestComplete) onRequestComplete();
			sj.removeClass(document.body, "wait");
		}, false);
		xhr.addEventListener("error", function(ev) {
			console.error(ev.message, ev);
		}, false);

		if (typeof query == "string") query = parseHash(query);
		var q = [];
		for (var i in query) q.push(i+"="+encodeURIComponent(query[i]));
		if (onBeforeRequestCallback) onBeforeRequestCallback(q);
		var url = endpoint+"?"+q.join("&");

		if (form) {
			if (!(form instanceof FormData)) form = new FormData(form);
			xhr.open('POST', url);
			xhr.send(form);
		} else {
			xhr.open('GET', url);
			xhr.send();
		}

		if (onRequestWait) onRequestWait();
	}

	var loaded = false;
	var endpoint = null;
	var components = {};
	var containers = {};

	var onBeforeRequestCallback = null;
	var onRequestWait = null;
	var onRequestComplete = null;

	var onActionCallback = null;
	var onClickCallback = null;
	var onSubmitCallback = null;
	var onErrorCallback = null;

	document.addEventListener('DOMContentLoaded', function() {
		document.body.addEventListener("click", onaction, false);
		document.body.addEventListener("submit", onaction, false);

		loaded = true;
		for (var i in components) {
			sj.component(i, components[i]);
		}
		for (var i in containers) {
			sj.container(i, containers[i][0], containers[i][1]);
		}

		var event = document.createEvent('Event');
		event.initEvent('SjStarted', true, true);
		document.dispatchEvent(event);
	}, false);
};
<commit_msg>checkbox fix<commit_after>"use strict";

var sj = new function(endpoint) {

	this.setEndpoint = function(url) { endpoint = url };
	this.setRequestWait = function(callback) { onRequestWait = callback };
	this.setRequestComplete = function(callback) { setRequestComplete = callback };
	this.setClickCallback = function(callback) { onClickCallback = callback };
	this.setErrorCallback = function(callback) { onErrorCallback = callback };
	this.setActionCallback = function(callback) { onActionCallback = callback };
	this.setBeforeRequestCallback = function(callback) { onBeforeRequestCallback = callback };

	// Render function
	function renderSingle(el, k, v) {
		if (!loaded || !el) return;
		var list = k == "_" ? [el] : el.querySelectorAll("[name="+k+"]");
		if (!list.length) return console.error("Element not found", el, k, v);
		if (typeof v == "string" || typeof v == "number") v = { _: v };
		else if (typeof v == "boolean") v = { hidden: !v };

		for (var i in v) {
			for (j = 0; j < list.length; j++) {
				var l = list[j];
				if (i == "_") {
					if (l.tagName == "INPUT" && l.type == "radio") {
						l.checked = (l.value == v[i]);
					} else if (l.tagName == "INPUT") {
						l.value = v[i];
					} else if (l.hasAttribute('data-html')) {
						l.innerHTML = v[i];
					} else {
						l.innerHTML = "";
						l.appendChild(document.createTextNode(v[i]));
					}
				} else if (i == "style") {
					for (var j in v[i]) l.style[j] = v[i][j];
				} else if (v[i] === false) {
					l.removeAttribute(i);
				} else if (v[i] === true) {
					l.setAttribute(i, "");
				} else {
					l.setAttribute((i == "className" ? "class" : i), v[i]);
				}
			}
		}
	}

	// Utilities
	this.hasClass = function(el, c) {
		return (el.className.search(/\bc\b/) > -1);
	}

	this.removeClass = function(el, c) {
		var re = new RegExp("\\b\s*"+c+"\\b");
		el.className = el.className.replace(re, '').replace(/^\s+|\s$/g, '');
	}

	this.addClass = function(el, c) {
		if (!sj.hasClass(el, c)) el.className += " "+c;
	}

	this.toggleClass = function(el, c) {
		if (sj.hasClass(el, c)) {
			sj.addClass(el, c);
		} else {
			sj.removeClass(el, c);
		}
	}

	var componentClass = function(id, obj) {

		this.oncreate = function() {}
		this.onshow = function() {}
		this.onpostshow = function() {}
		this.onprehide = function() {}
		this.onhide = function() {}
		this.onclick = function(ev, target) {}
		this.onaction = function(ev, target) {
			onActionCallback(ev, this.el, target);
		}
		this.onsubmit = function(ev, target) {
			onSubmitCallback(ev, this.el, target);
		}
		this.onrequest = function() {}
		this.onresponse = function() {}
		this.onerror = function(json) {
			if (onSubmitCallback) onErrorCallback(json);
			else console.error(json);
		}

		this.hide = function() {
			this.onprehide();
			this.el.hidden = true;
			this.onhide();
		}
		this.show = function() {
			this.onshow();
			this.el.hidden = false;
			this.onpostshow();
		}
		this.visible = function(b) {
			if (b) this.show(); else this.hide();
		}

		this.hasClass = function(c) { return hasClass(this.el, c) }
		this.removeClass = function(c) { return removeClass(this.el, c) }
		this.addClass = function(c) { return addClass(this.el, c) }
		this.toggleClass = function(c) { return toggleClass(this.el, c) }

		this.action = function(q) {
			sj.request(q);
		},

		this.render = function(p, sub) {
			var el;
			if (!p || typeof p != "object") return;
			if (typeof sub == "string") {
				el = this.el.querySelector("[name="+sub+"]");
			} else if (typeof sub == "object") {
				var t = sub;
				while (t != document.body && t != this.el) t = t.parentNode;
				el = (t == this.el) ? t : null;
			}
			if (!el) el = this.el;
			for (var i in p) {
				var l = el.querySelector("[name="+i+"]");
				if (p[i] instanceof Array) {
					while (!l.lastElementChild.hidden) l.removeChild(l.lastElementChild);
					for (var j = 0; j < p[i].length; j++) {
						var clone = l.firstElementChild.cloneNode(true);
						clone.hidden = false;
						for (var k in p[i][j]) {
							renderSingle(clone, k, p[i][j][k]);
						}
						l.appendChild(clone);
					}
				} else {
					renderSingle(el, i, p[i]);
				}
			}
			if (this.onrender) this.onrender();
		}

		this.el = document.getElementById(id);
		if (!this.el) throw "Component `"+id+"` not found";
		this.el.setAttribute("data-component", "");
		this.loop = this.el;

		for (var i in obj) {
			if (i == "loop") obj[i] = document.getElementById(obj[i]);
			this[i] = obj[i];
		}

	}

	this.component = function(id, obj) {
		components[id] = (obj ? obj : {});
		if (loaded) {
			components[id] = new componentClass(id, obj);
			components[id].oncreate();
		}
	}

	function containerClass(id, start, obj) {
		this.current = start;
		this.onChange = function(current) {}

		this.set = function(id) {
			this.current = id;
			for (var i = 0; i < this.el.children.length; i++) {
				var el = this.el.children[i];
				if (el.hasAttribute("data-component")) {
					sj.$(el.id).visible(el.id == id);
				}
			}
			this.onChange(this.current);
		}

		this.get = function() {
			return this.current;
		}

		this.el = document.getElementById(id);
		if (!this.el) throw "Container `"+id+"` not found";
		this.el.setAttribute("data-container", "");

		for (var i = 0; i < this.el.children.length; i++) {
			var el = this.el.children[i];
			sj.$(el.id).visible(el.id == start);
		}

		if (obj) for (var i in obj) {
			this[i] = obj[i];
		}
	}

	this.container = function(id, start, obj) {
		containers[id] = [start, obj];
		if (loaded) {
			containers[id] = new containerClass(id, start, obj);
		}
	}

	this.$ = function(id) {
		if (components[id]) {
			return components[id];
		} else if (containers[id]) {
			return containers[id];
		} else {
			console.error("`"+id+"` not found");
		}
	}

	function parseHash(hash) {
		var q = {};

		var n = hash.search(/#[!\?]/);
		if (n > -1) hash = hash.substring(n+2);

		var p = hash.split(/[&=]/);
		for (var i = 0; i < p.length; i+=2) q[p[i]] = (p[i+1] ? p[i+1] : null);
		return q;
	}

	function onaction(ev) {
		var el = ev.target;
		if (!el) return;
		while (!el.hasAttribute("data-component") && el != document.body) el = el.parentNode;
		if (el == document.body) return;
		var id = el.id;
		var target = ev.target;

		while (target.tagName != "A" && target.tagName != "FORM" && target != el) target = target.parentNode;
		if (target.tagName == "A") {
			if (target.hash.search(/^#\?/) > -1) {
				components[id].onrequest(ev, target);
				sj.request(target.hash);
				ev.preventDefault();
			} else if (target.hash.search(/^#!/) > -1) {
				components[id].onaction(target.hash.substring(2), target);
				ev.preventDefault();
			}
		} else if (target.tagName == "FORM" && ev.type == 'submit') {
			if (target.action.search(/#\?/) > -1) {
				components[id].onrequest(ev, target);
				sj.request(target.action, target);
				ev.preventDefault();
			} else if (target.action.search(/#!/) > -1) {
				var p = target.action.search(/#!/);
				components[id].onaction(parseHash(target.action), target);
				ev.preventDefault();
			}
		} else {
			components[id].onclick(ev, target);
		}
	}

	this.request = function(query, form) {
		if (!endpoint) throw "No endpoint defined!";
		var xhr = new XMLHttpRequest();
		xhr.addEventListener("load", function(ev) {
			var json = JSON.parse(ev.target.responseText);
			if (json.error) {
				components[json.component].onerror(json);
			} else if (json.component && components[json.component]) {
				components[json.component].onresponse(json);
			} else {
				throw "Component `"+json.component+"` not found";
			}
			if (onRequestComplete) onRequestComplete();
			sj.removeClass(document.body, "wait");
		}, false);
		xhr.addEventListener("error", function(ev) {
			console.error(ev.message, ev);
		}, false);

		if (typeof query == "string") query = parseHash(query);
		var q = [];
		for (var i in query) q.push(i+"="+encodeURIComponent(query[i]));
		if (onBeforeRequestCallback) onBeforeRequestCallback(q);
		var url = endpoint+"?"+q.join("&");

		if (form) {
			if (!(form instanceof FormData)) form = new FormData(form);
			xhr.open('POST', url);
			xhr.send(form);
		} else {
			xhr.open('GET', url);
			xhr.send();
		}

		if (onRequestWait) onRequestWait();
	}

	var loaded = false;
	var endpoint = null;
	var components = {};
	var containers = {};

	var onBeforeRequestCallback = null;
	var onRequestWait = null;
	var onRequestComplete = null;

	var onActionCallback = null;
	var onClickCallback = null;
	var onSubmitCallback = null;
	var onErrorCallback = null;

	document.addEventListener('DOMContentLoaded', function() {
		document.body.addEventListener("click", onaction, false);
		document.body.addEventListener("submit", onaction, false);

		loaded = true;
		for (var i in components) {
			sj.component(i, components[i]);
		}
		for (var i in containers) {
			sj.container(i, containers[i][0], containers[i][1]);
		}

		var event = document.createEvent('Event');
		event.initEvent('SjStarted', true, true);
		document.dispatchEvent(event);
	}, false);
};
<|endoftext|>


<commit_before>/* global QUnit*/

sap.ui.define([
	'sap/ui/dt/Util'
],
function(
	Util
) {
	'use strict';

	QUnit.module('wrapError()', function () {
		QUnit.test("string as parameter", function (assert) {
			var sError = "I am an error as string";
			assert.strictEqual(Util.wrapError(sError).message, sError, "error as string is correctly wrapped");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var oError = new Error("I am an error inside an object");
			assert.strictEqual(Util.wrapError(oError), oError, "same error object is returned");
		});
		QUnit.test("any object as parameter", function (assert) {
			var oError = Util.wrapError({});
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
		QUnit.test("no parameter is specified", function (assert) {
			var oError = Util.wrapError();
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
		QUnit.test("null as parameter", function (assert) {
			var oError = Util.wrapError(null);
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
	});

	QUnit.module('isForeignError()', function () {
		QUnit.test("foreign error as parameter", function (assert) {
			var oError = new Error("This error happened somewhere else");
			assert.equal(Util.isForeignError(oError), true, "error from somewhere else is recognized as foreign");
		});
		QUnit.test("foreign error as parameter", function (assert) {
			var oError = new Error("This error happened in DT");
			oError.name = "sap.ui.dt.FileName#MethodName";
			assert.equal(Util.isForeignError(oError), false, "error from sap.ui.dt is recognized as not foreign");
		});
		QUnit.test("wrong parameter specified", function (assert) {
			assert.throws(
				function () {
					Util.isForeignError();
				},
				"error throws if no parameter is specified"
			);
			assert.throws(
				function () {
					Util.isForeignError('error');
				},
				"error throws if a string specified"
			);
			assert.throws(
				function () {
					Util.isForeignError({
						name: ''
					});
				},
				"error throws if a plain object specified"
			);
			assert.throws(
				function () {
					Util.isForeignError(function () {});
				},
				"error throws if a function object specified"
			);
			assert.throws(
				function () {
					Util.isForeignError(null);
				},
				"error throws if a null object specified"
			);
		});
	});

	QUnit.module('createError()', function () {
		QUnit.test("both parameters are specified", function (assert) {
			var oError = Util.createError("FileName#MethodName", "custom text message");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') !== -1, "Name of the error contains location information");
			assert.equal(oError.message, "custom text message", "message of the error equals to specified message");
		});
		QUnit.test("message is omitted", function (assert) {
			var oError = Util.createError("FileName#MethodName");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') !== -1, "Name of the error contains location information");
			assert.equal(oError.message, undefined, "message is empty");
		});
		QUnit.test("location is omitted", function (assert) {
			var oError = Util.createError(null, "custom text message");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') === -1, "Name of the error doesn't contain location information");
			assert.equal(oError.message, "custom text message", "message of the error equals to specified message");
		});
		QUnit.test("called without parameters", function (assert) {
			var oError = Util.createError();
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.equal(oError.message, undefined, "message is empty");
		});
	});

	QUnit.module('errorToString()', function () {
		QUnit.test("string as parameter", function (assert) {
			var sError = Util.errorToString('error message');
			assert.strictEqual(sError, 'error message', "message is correct");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var sError = Util.errorToString(new Error('error message'));
			assert.ok(sError.indexOf('error message') !== -1, "error message is in output");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var oError = new Error('error message');
			var sStack = oError.stack;
			oError.stack = oError.toString() + "\t\n \t\n" + oError.stack + '  \n';
			assert.ok(oError.toString() + sStack, "duplicate text and whitespaces are trimmed");
		});
		QUnit.test("called with wrong parameter", function (assert) {
			assert.throws(
				function () {
					Util.errorToString();
				},
				"throws when no parameter is specified"
			);
			assert.throws(
				function () {
					Util.errorToString(null);
				},
				"throws when null is specified as parameter"
			);
			assert.throws(
				function () {
					Util.errorToString({});
				},
				"throws when plain object is specified as parameter"
			);
		});
	});

	QUnit.module('propagateError()', function () {
		QUnit.test("when Error is foreign", function (assert) {
			var oError = new Error('original error message');
			var oErrorPropagated = Util.propagateError(oError, 'FileName#MethodName', 'custom error message');

			assert.ok(oErrorPropagated instanceof Error, "Error object is returned");
			assert.strictEqual(oErrorPropagated, oError, "same Error object is returned");
			assert.ok(oErrorPropagated.message.indexOf('original error message') !== -1, "message contains original message");
			assert.ok(oErrorPropagated.message.indexOf('custom error message') !== -1, "message contains custom message");
		});
		QUnit.test("when Error is not foreign", function (assert) {
			var oError = Util.createError('FileName#MethodName', 'original error message');
			var oErrorPropagated = Util.propagateError(oError, 'FileName#MethodName2', 'custom error message');

			assert.ok(oErrorPropagated instanceof Error, "Error object is returned");
			assert.strictEqual(oErrorPropagated, oError, "same Error object is returned");
			assert.ok(oErrorPropagated.message.indexOf('original error message') !== -1, "message contains original message");
			assert.ok(oErrorPropagated.message.indexOf('custom error message') === -1, "message contains custom message");
		});
	});

	QUnit.module('printf()', function () {
		QUnit.test("basic functionality", function(assert){
			assert.equal(
				Util.printf("Arg1: {0}, Arg2: {1}", "Val1", "Val2"),
				"Arg1: Val1, Arg2: Val2",
				"The string has been correctly assembled"
			);
		});
	});

	QUnit.module('curry()', function () {
		QUnit.test("curry()", function(assert){
			var fnOriginalFunction = function(sPar1, sPar2){
				return sPar1 + sPar2;
			};
			var fnCurriedFunction = Util.curry(fnOriginalFunction);

			assert.ok(jQuery.isFunction(fnCurriedFunction), "function has been returned");
			assert.equal(fnCurriedFunction("Curried")("Test"), "CurriedTest", "the function has been properly curried");
		});
	});

	QUnit.module('objectValues()', function () {
		QUnit.test("objectValues()", function(assert){
			var sValue1 = "test1";
			var sValue2 = "test2";
			var mObject = {
				value1: sValue1,
				value2: sValue2
			};

			assert.deepEqual(Util.objectValues(mObject), [sValue1, sValue2], "the correct object values were returned");
		});
	});

	QUnit.module('intersection()', function () {
		QUnit.test("basic functionality", function(assert){
			var aArray1 = [0, 1, 2, 3];
			var aArray2 = [1, 3, 5, 6];

			assert.deepEqual(Util.intersection(aArray1, aArray2), [1, 3], "only those, which are presented in both arrays, are returned");
		});
	});

	QUnit.module('isInteger()', function () {
		QUnit.test("basic functionality", function(assert){
			assert.ok(Util.isInteger(0), "zero is an integer");
			assert.ok(Util.isInteger(1.0), "real number pretended as integer is an integer");
			assert.notOk(Util.isInteger("0"), "string of zero is not an integer");
			assert.notOk(Util.isInteger(0.1), "real number is not an integer");
			assert.notOk(Util.isInteger(true), "boolean is not an integer");
			assert.notOk(Util.isInteger(), "undefined is not an integer");
			assert.notOk(Util.isInteger(null), "null is not an integer");
			assert.notOk(Util.isInteger({}), "object is not an integer");
		});
	});

	QUnit.module('castArray()', function () {
		QUnit.test("castArray()", function(assert){
			var sValue = "test1";
			var nValue = 7;
			var aValue = ["xyz", 1, {text: "test"}];
			var mObject = {
				value1: sValue,
				value2: nValue
			};

			assert.deepEqual(Util.castArray(sValue), [sValue], "the correct string in an array is returned");
			assert.deepEqual(Util.castArray(aValue), ["xyz", 1, {text: "test"}], "the correct array is returned");
			assert.deepEqual(Util.castArray(mObject), [mObject], "the correct object in an array is returned");
			assert.deepEqual(Util.castArray(), [], "the correct empty array is returned");
		});
	});

	QUnit.module('wrapIntoPromise()', function () {
		QUnit.test("basic functionality", function (assert) {
			assert.ok(jQuery.isFunction(Util.wrapIntoPromise(function () {})));
			assert.ok(Util.wrapIntoPromise(function () {})() instanceof Promise);
			assert.ok(Util.wrapIntoPromise(function () {})() instanceof Promise);
		});
		QUnit.test("promise resolve with certain value", function (assert) {
			return Util.wrapIntoPromise(function () {
				return 'value';
			})().then(function (vValue) {
				assert.strictEqual(vValue, 'value');
			});
		});
		QUnit.test("nested promises resolve with certain value", function (assert) {
			return Util.wrapIntoPromise(function () {
				return Promise.resolve('value');
			})().then(function (vValue) {
				assert.strictEqual(vValue, 'value');
			});
		});
		QUnit.test("non-function is passed", function (assert) {
			assert.throws(function () {
				Util.wrapIntoPromise({});
			});
		});
	});

	QUnit.module('pick()', function () {
		QUnit.test("basic functionality", function (assert) {
			assert.deepEqual(Util.pick({ a: 1, b: 2, c: undefined }, 'a'), { a: 1 });
			assert.deepEqual(Util.pick({ a: 1, b: 2, c: undefined }, ['a', 'c']), { a: 1, c: undefined });
			assert.deepEqual(Util.pick({ null: 1, b: 2 }, null), {null: 1}); // eslint-disable-line quote-props
			assert.deepEqual(Util.pick({ undefined: 1, b: 2 }), {});
			assert.deepEqual(Util.pick({ undefined: 1, b: 2 }, undefined), {undefined: 1});
		});
		QUnit.test("when wrong parameters specified", function (assert) {
			assert.deepEqual(Util.pick(), {});
			assert.deepEqual(Util.pick(1), {});
			assert.deepEqual(Util.pick(1, 'a'), {});
			assert.deepEqual(Util.pick({ 1: 1, b: 2 }, 1), { 1: 1});
			assert.deepEqual(Util.pick({ a: 1, b: 2 }), {});
		});
	});

	QUnit.module('debounce()', function() {
		QUnit.test("when a function is called with a timeout several times", function(assert) {
			var done = assert.async();
			var iFunctionCalled = 0;
			var iDebounceCalled = 0;
			var fnDebounce = Util.debounce(function() {
				iDebounceCalled++;
				assert.equal(iFunctionCalled, 5, "the function was called 10 times");
				assert.equal(iDebounceCalled, 1, "the debounce callback was only called once");
				done();
			}, 15);

			function callDebounceDelayed() {
				setTimeout(function() {
					iFunctionCalled++;
					fnDebounce();
					if (iFunctionCalled < 5) {
						callDebounceDelayed();
					}
				}, 10);
			}
			callDebounceDelayed();
		});
	});

	QUnit.done(function() {
		jQuery("#qunit-fixture").hide();
	});
});<commit_msg>[INTERNAL] sap.ui.dt: replace timeout with sinon fakeTimers in test<commit_after>/* global QUnit*/

sap.ui.define([
	"sap/ui/thirdparty/jquery",
	"sap/ui/thirdparty/sinon-4",
	"sap/ui/dt/Util"
],
function(
	jQuery,
	sinon,
	Util
) {
	'use strict';

	QUnit.module('wrapError()', function () {
		QUnit.test("string as parameter", function (assert) {
			var sError = "I am an error as string";
			assert.strictEqual(Util.wrapError(sError).message, sError, "error as string is correctly wrapped");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var oError = new Error("I am an error inside an object");
			assert.strictEqual(Util.wrapError(oError), oError, "same error object is returned");
		});
		QUnit.test("any object as parameter", function (assert) {
			var oError = Util.wrapError({});
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
		QUnit.test("no parameter is specified", function (assert) {
			var oError = Util.wrapError();
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
		QUnit.test("null as parameter", function (assert) {
			var oError = Util.wrapError(null);
			assert.ok(oError instanceof Error, "error object returned");
			assert.strictEqual(oError.message, '', "message is empty");
		});
	});

	QUnit.module('isForeignError()', function () {
		QUnit.test("foreign error as parameter", function (assert) {
			var oError = new Error("This error happened somewhere else");
			assert.equal(Util.isForeignError(oError), true, "error from somewhere else is recognized as foreign");
		});
		QUnit.test("foreign error as parameter", function (assert) {
			var oError = new Error("This error happened in DT");
			oError.name = "sap.ui.dt.FileName#MethodName";
			assert.equal(Util.isForeignError(oError), false, "error from sap.ui.dt is recognized as not foreign");
		});
		QUnit.test("wrong parameter specified", function (assert) {
			assert.throws(
				function () {
					Util.isForeignError();
				},
				"error throws if no parameter is specified"
			);
			assert.throws(
				function () {
					Util.isForeignError('error');
				},
				"error throws if a string specified"
			);
			assert.throws(
				function () {
					Util.isForeignError({
						name: ''
					});
				},
				"error throws if a plain object specified"
			);
			assert.throws(
				function () {
					Util.isForeignError(function () {});
				},
				"error throws if a function object specified"
			);
			assert.throws(
				function () {
					Util.isForeignError(null);
				},
				"error throws if a null object specified"
			);
		});
	});

	QUnit.module('createError()', function () {
		QUnit.test("both parameters are specified", function (assert) {
			var oError = Util.createError("FileName#MethodName", "custom text message");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') !== -1, "Name of the error contains location information");
			assert.equal(oError.message, "custom text message", "message of the error equals to specified message");
		});
		QUnit.test("message is omitted", function (assert) {
			var oError = Util.createError("FileName#MethodName");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') !== -1, "Name of the error contains location information");
			assert.equal(oError.message, undefined, "message is empty");
		});
		QUnit.test("location is omitted", function (assert) {
			var oError = Util.createError(null, "custom text message");
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.ok(oError.name.indexOf('FileName#MethodName') === -1, "Name of the error doesn't contain location information");
			assert.equal(oError.message, "custom text message", "message of the error equals to specified message");
		});
		QUnit.test("called without parameters", function (assert) {
			var oError = Util.createError();
			assert.ok(oError instanceof Error, "Error instance returned from the factory");
			assert.ok(oError.name.indexOf('sap.ui.dt') !== -1, "Name of the error contains library information");
			assert.equal(oError.message, undefined, "message is empty");
		});
	});

	QUnit.module('errorToString()', function () {
		QUnit.test("string as parameter", function (assert) {
			var sError = Util.errorToString('error message');
			assert.strictEqual(sError, 'error message', "message is correct");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var sError = Util.errorToString(new Error('error message'));
			assert.ok(sError.indexOf('error message') !== -1, "error message is in output");
		});
		QUnit.test("Error object as parameter", function (assert) {
			var oError = new Error('error message');
			var sStack = oError.stack;
			oError.stack = oError.toString() + "\t\n \t\n" + oError.stack + '  \n';
			assert.ok(oError.toString() + sStack, "duplicate text and whitespaces are trimmed");
		});
		QUnit.test("called with wrong parameter", function (assert) {
			assert.throws(
				function () {
					Util.errorToString();
				},
				"throws when no parameter is specified"
			);
			assert.throws(
				function () {
					Util.errorToString(null);
				},
				"throws when null is specified as parameter"
			);
			assert.throws(
				function () {
					Util.errorToString({});
				},
				"throws when plain object is specified as parameter"
			);
		});
	});

	QUnit.module('propagateError()', function () {
		QUnit.test("when Error is foreign", function (assert) {
			var oError = new Error('original error message');
			var oErrorPropagated = Util.propagateError(oError, 'FileName#MethodName', 'custom error message');

			assert.ok(oErrorPropagated instanceof Error, "Error object is returned");
			assert.strictEqual(oErrorPropagated, oError, "same Error object is returned");
			assert.ok(oErrorPropagated.message.indexOf('original error message') !== -1, "message contains original message");
			assert.ok(oErrorPropagated.message.indexOf('custom error message') !== -1, "message contains custom message");
		});
		QUnit.test("when Error is not foreign", function (assert) {
			var oError = Util.createError('FileName#MethodName', 'original error message');
			var oErrorPropagated = Util.propagateError(oError, 'FileName#MethodName2', 'custom error message');

			assert.ok(oErrorPropagated instanceof Error, "Error object is returned");
			assert.strictEqual(oErrorPropagated, oError, "same Error object is returned");
			assert.ok(oErrorPropagated.message.indexOf('original error message') !== -1, "message contains original message");
			assert.ok(oErrorPropagated.message.indexOf('custom error message') === -1, "message contains custom message");
		});
	});

	QUnit.module('printf()', function () {
		QUnit.test("basic functionality", function(assert){
			assert.equal(
				Util.printf("Arg1: {0}, Arg2: {1}", "Val1", "Val2"),
				"Arg1: Val1, Arg2: Val2",
				"The string has been correctly assembled"
			);
		});
	});

	QUnit.module('curry()', function () {
		QUnit.test("curry()", function(assert){
			var fnOriginalFunction = function(sPar1, sPar2){
				return sPar1 + sPar2;
			};
			var fnCurriedFunction = Util.curry(fnOriginalFunction);

			assert.ok(jQuery.isFunction(fnCurriedFunction), "function has been returned");
			assert.equal(fnCurriedFunction("Curried")("Test"), "CurriedTest", "the function has been properly curried");
		});
	});

	QUnit.module('objectValues()', function () {
		QUnit.test("objectValues()", function(assert){
			var sValue1 = "test1";
			var sValue2 = "test2";
			var mObject = {
				value1: sValue1,
				value2: sValue2
			};

			assert.deepEqual(Util.objectValues(mObject), [sValue1, sValue2], "the correct object values were returned");
		});
	});

	QUnit.module('intersection()', function () {
		QUnit.test("basic functionality", function(assert){
			var aArray1 = [0, 1, 2, 3];
			var aArray2 = [1, 3, 5, 6];

			assert.deepEqual(Util.intersection(aArray1, aArray2), [1, 3], "only those, which are presented in both arrays, are returned");
		});
	});

	QUnit.module('isInteger()', function () {
		QUnit.test("basic functionality", function(assert){
			assert.ok(Util.isInteger(0), "zero is an integer");
			assert.ok(Util.isInteger(1.0), "real number pretended as integer is an integer");
			assert.notOk(Util.isInteger("0"), "string of zero is not an integer");
			assert.notOk(Util.isInteger(0.1), "real number is not an integer");
			assert.notOk(Util.isInteger(true), "boolean is not an integer");
			assert.notOk(Util.isInteger(), "undefined is not an integer");
			assert.notOk(Util.isInteger(null), "null is not an integer");
			assert.notOk(Util.isInteger({}), "object is not an integer");
		});
	});

	QUnit.module('castArray()', function () {
		QUnit.test("castArray()", function(assert){
			var sValue = "test1";
			var nValue = 7;
			var aValue = ["xyz", 1, {text: "test"}];
			var mObject = {
				value1: sValue,
				value2: nValue
			};

			assert.deepEqual(Util.castArray(sValue), [sValue], "the correct string in an array is returned");
			assert.deepEqual(Util.castArray(aValue), ["xyz", 1, {text: "test"}], "the correct array is returned");
			assert.deepEqual(Util.castArray(mObject), [mObject], "the correct object in an array is returned");
			assert.deepEqual(Util.castArray(), [], "the correct empty array is returned");
		});
	});

	QUnit.module('wrapIntoPromise()', function () {
		QUnit.test("basic functionality", function (assert) {
			assert.ok(jQuery.isFunction(Util.wrapIntoPromise(function () {})));
			assert.ok(Util.wrapIntoPromise(function () {})() instanceof Promise);
			assert.ok(Util.wrapIntoPromise(function () {})() instanceof Promise);
		});
		QUnit.test("promise resolve with certain value", function (assert) {
			return Util.wrapIntoPromise(function () {
				return 'value';
			})().then(function (vValue) {
				assert.strictEqual(vValue, 'value');
			});
		});
		QUnit.test("nested promises resolve with certain value", function (assert) {
			return Util.wrapIntoPromise(function () {
				return Promise.resolve('value');
			})().then(function (vValue) {
				assert.strictEqual(vValue, 'value');
			});
		});
		QUnit.test("non-function is passed", function (assert) {
			assert.throws(function () {
				Util.wrapIntoPromise({});
			});
		});
	});

	QUnit.module('pick()', function () {
		QUnit.test("basic functionality", function (assert) {
			assert.deepEqual(Util.pick({ a: 1, b: 2, c: undefined }, 'a'), { a: 1 });
			assert.deepEqual(Util.pick({ a: 1, b: 2, c: undefined }, ['a', 'c']), { a: 1, c: undefined });
			assert.deepEqual(Util.pick({ null: 1, b: 2 }, null), {null: 1}); // eslint-disable-line quote-props
			assert.deepEqual(Util.pick({ undefined: 1, b: 2 }), {});
			assert.deepEqual(Util.pick({ undefined: 1, b: 2 }, undefined), {undefined: 1});
		});
		QUnit.test("when wrong parameters specified", function (assert) {
			assert.deepEqual(Util.pick(), {});
			assert.deepEqual(Util.pick(1), {});
			assert.deepEqual(Util.pick(1, 'a'), {});
			assert.deepEqual(Util.pick({ 1: 1, b: 2 }, 1), { 1: 1});
			assert.deepEqual(Util.pick({ a: 1, b: 2 }), {});
		});
	});

	QUnit.module('debounce()', function() {
		QUnit.test("when a function is called with a timeout several times", function(assert) {
			var clock = sinon.useFakeTimers();
			var done = assert.async();
			var iFunctionCalled = 0;
			var iDebounceCalled = 0;
			var fnDebounce = Util.debounce(function() {
				iDebounceCalled++;
				assert.equal(iFunctionCalled, 5, "the function was called 5 times");
				assert.equal(iDebounceCalled, 1, "the debounce callback was only called once");
				clock.restore();
				done();
			}, 20);

			for (var i = 0; i < 5; i++) {
				iFunctionCalled++;
				fnDebounce();
				clock.tick(11);
			}
			clock.tick(10);
		});
	});

	QUnit.done(function() {
		jQuery("#qunit-fixture").hide();
	});
});<|endoftext|>