<commit_before>/*
 * Class: FlightDeck
 * Initializes all needed functionality
 */

var FlightDeck = new Class({
    Implements: [Options, Events],
    options: {
        menu_el: 'UI_Editor_Menu',
        try_in_browser_class: 'XPI_test',
        xpi_hashtag: '',        // hashtag for the XPI creation
        max_request_number: 50, // how many times should system try to download XPI
        request_interval: 5000  // try to download XPI every 5 sec
        //user: ''
    },
    initialize: function() {
        this.tests = {}; // placeholder for testing spinners
        this.uri = new URI();
        if (this.uri.getData('redirect','fragment')) {
            window.location.href = this.uri.getData('redirect', 'fragment');
        }
        this.warning = this.error = this.message = {
            'alert': function(title, message) {
                alert(title+"\n"+message);
            }
        };
        this.editors = [];
        this.parseTooltips();
        this.createActionSections();
        this.parseTestButtons.bind(this).delay(10);
        this.addEvent('xpi_downloaded', this.whenXpiDownloaded);
        this.addEvent('xpi_installed', this.whenXpiInstalled);
        this.addEvent('xpi_uninstalled', this.whenXpiUninstalled);
        //if (!this.isAddonInstalled()) $log('FD: No Addon Builder Helper')
    },

    parseTooltips: function() {
        this.tips = new Tips({
            fixed: false,
            className: 'UI_tooltip',
            offset: {
                x: 0,
                y: 16
            }
        });

        $$('div.UI_tooltip_source').each(function(tipSource){
            tipSource.hide();
            var target = $(tipSource.get('data-tooltip-for'));
            target.set('title', '');
            target.store('tip:title', tipSource.get('data-tooltip-title'));
            target.store('tip:text', tipSource.get('html'));
            this.tips.attach(target);
        }, this);
    },

    setURIRedirect: function(url) {
        // change the URL add #/path/to/saved/revision
        if (this.uri.get('directory') != url) {
            this.uri.setData({'redirect': url}, false, 'fragment');
            this.uri.go();
        }
    },

    whenXpiInstalled: function(name) {
        this.parseTestButtons();
        this.message.alert('Add-ons Builder', 'Add-on installed');
        $log('FD: INFO: Add-on installed');
    },

    whenXpiDownloaded: function(hashtag) {
        // remove SDK from disk
        if (this.tests[hashtag].rm_xpi_url) {
            new Request.JSON({
                url: this.tests[hashtag].rm_xpi_url,
                onSuccess: function() {
                    this.fireEvent('sdk_removed');
                }.bind(this)
            }).send();
            this.rm_xpi_url = undefined;
        }
    },

    whenXpiUninstalled: function() {
        this.parseTestButtons();
        this.message.alert('Add-ons Builder', 'Add-on uninstalled');
    },

    /*
    Method: whenAddonInstalled
    create listener for a callback function
     */
    whenAddonInstalled: function(callback) {
        var removeListener = function() {
            document.body.removeEventListener('addonbuilderhelperstart', callback, false);
        }
        document.body.addEventListener('addonbuilderhelperstart', callback, false);
        (function() {
            $log('FD: Warning: not listening to addonbuilderhelperstart, is Helper installed?');
            removeListener();
        }).delay(100000);
    },

    parseTestButtons: function() {
        var installed = (this.isAddonInstalled()) ? this.isXpiInstalled() : false;
        if (installed) {
            $$('.{try_in_browser_class} a'.substitute(this.options)).each(function(test_button){
                if (installed && installed.installedID == test_button.get('rel')) {
                    test_button.getParent('li').addClass('pressed');
                } else {
                    test_button.getParent('li').removeClass('pressed');
                }
            }, this);
        }
    },

    /*
     * Method: downloadXPI it's running in Request's scope
     */
    downloadXPI: function(response) {
        $log('FD: DEBUG: XPI delayed ... try to load every ' + fd.options.request_interval/1000 + ' seconds' );
        var hashtag = this.options.data.hashtag;
        var filename = this.options.data.filename;
        fd.tests[hashtag].download_request_number = 0;
        fd.tryDownloadXPI.delay(1000, fd, [hashtag, filename]);
        fd.tests[hashtag].download_ID = fd.tryDownloadXPI.periodical(
                fd.options.request_interval, fd, [hashtag, filename]);
    },

    /*
     * Method: tryDownloadXPI
     *
     * Try to download XPI
     * if finished - stop periodical, stop spinner
     */
    tryDownloadXPI: function(hashtag, filename) {
        var test_request = this.tests[hashtag];
        if (!test_request.download_xpi_request || (
                    test_request.download_xpi_request &&
                    !test_request.download_xpi_request.isRunning())) {
            test_request.download_request_number++;
            var url = '/xpi/check_download/'+hashtag+'/';
            $log('FD: DEBUG: checking if ' + url + ' is prepared');
            test_request.download_xpi_request = new Request.JSON({
                url: url,
                onSuccess: function(response) {
                    if (response.ready || test_request.download_request_number > 50) {
                        clearInterval(test_request.download_ID);
                        test_request.spinner.destroy();
                    }
                    if (response.ready) {
                        var url = '/xpi/download/'+hashtag+'/'+filename+'/';
                        $log('FD: downloading ' + filename + '.xpi from ' + url );
                        window.open(url, 'dl');
                    }
                },
                addOnFailure: function() {
                    clearInterval(test_request.download_ID);
                    test_request.spinner.destroy();
                }
            }).send();
        }
    },

    /*
     * Method: testXPI it's running in Request's scope
     */
    testXPI: function(response) {
        $log('FD: DEBUG: XPI delayed ... try to load every ' + fd.options.request_interval/1000 + ' seconds' );
        var hashtag = this.options.data.hashtag;
        fd.tests[hashtag].request_number = 0;
        fd.tests[hashtag].rm_xpi_url = response.rm_xpi_url;
        fd.tryInstallXPI.delay(1000, fd, hashtag);
        fd.tests[hashtag].install_ID = fd.tryInstallXPI.periodical(
                fd.options.request_interval, fd, hashtag);
    },

    isXpiInstalled: function() {
        return window.mozFlightDeck.send({cmd:'isInstalled'});
    },

    /*
     * Method: tryInstallXPI
     *
     * Try to download XPI
     * if successful - stop periodical
     */
    tryInstallXPI: function(hashtag) {
        if (this.alertIfNoAddOn()) {
            var test_request = this.tests[hashtag];
            if (!test_request.install_xpi_request || (
                        test_request.install_xpi_request &&
                        !test_request.install_xpi_request.isRunning())) {
                test_request.request_number++;
                url = '/xpi/test/'+hashtag+'/';
                $log('FD: installing from ' + url);
                test_request.install_xpi_request = new Request({
                    url: url,
                    headers: {'Content-Type': 'text/plain; charset=x-user-defined'},
                    onSuccess: function(responseText) {
                        if (responseText || test_request.request_number > 50) {
                            clearInterval(test_request.install_ID);
                            test_request.spinner.destroy();
                        }
                        if (responseText) {
                            this.fireEvent('xpi_downloaded', hashtag);
                            var result = window.mozFlightDeck.send({cmd: "install", contents: responseText});
                            if (result && result.success) {
                                this.fireEvent('xpi_installed', '');
                            } else {
                                if (result) $log(result);
                                this.warning.alert(
                                    'Add-ons Builder',
                                    'Wrong response from Add-ons Helper. Please let us know'
                                );
                            }
                        }
                    }.bind(this)
                }).send(this);
            } else {
                $log('FD: DEBUG request is running');
            }
        }
    },
    /*
     * Method: uninstallXPI
     *
     * Remove Add-on from Browser
     */
    uninstallXPI: function(jid) {
        $log('FD: uninstalling ' + jid);
        var result = window.mozFlightDeck.send({cmd:'uninstall'});
        if (result.success) this.fireEvent('xpi_uninstalled');
    },
    /*
     * Method: enableMenuButtons
     */
    enableMenuButtons: function() {
        $$('.' + this.options.menu_el + ' li').each(function(menuItem){
            if (menuItem.hasClass('disabled')){
                menuItem.removeClass('disabled');
            }
        });
    },

    isAddonInstalled: function() {
        return (window.mozFlightDeck) ? true : false;
    },

    /*
     * Method: alertIfNoAddOn
     */
    alertIfNoAddOn: function(callback, text, title) {
        if (this.isAddonInstalled()) return true;
        text = [text,
                "To test this add-on, please install the Add-ons Builder Helper add-on".substitute(settings)].pick();
        title = [title, "Install Add-ons Builder Helper"].pick();
        fd.warning.alert(title, text);
        return false;
    },
    /*
     * Method: createActionSections
     */
    createActionSections: function(){
        $$('.UI_Editor_Menu_Separator').each(function(separator){
            separator.getPrevious('li').addClass('UI_Section_Close');
            separator.getNext('li').addClass('UI_Section_Open');
        });

        var UI_Editor_Menu_Button = $$('.UI_Editor_Menu_Button');

        if (UI_Editor_Menu_Button.length === 1){
            UI_Editor_Menu_Button[0].addClass('UI_Single');
        }
    }
});

/*
 * Add delay do Spinner
 */

Spinner = Class.refactor(Spinner, {
    options: { delay: 400 },
});

/*
 * Default onFailure in all Requests
 */

Request = Class.refactor(Request, {
    options: {
        onFailure: function(xhr) {
            if (this.options.addOnFailure) {
              this.options.addOnFailure();
            }
            fd.error.alert(
                'Error {status}'.substitute(xhr),
                '{statusText}
{responseText}'.substitute(xhr)
                );
        }
    },
    initialize: function(options) {
      this.previous(options);
      // It happened to be unnecessary
      //if (csrfmiddlewaretoken && (this.options.method == 'post' || this.options.method == 'POST')) {
      //  this.options.data['csrfmiddlewaretoken'] = csrfmiddlewaretoken;
      //}

    },
  // overloading processScripts to *not* execute JS responses
    processScripts: function(text){
        if (this.options.evalResponse) return Browser.exec(text);
        return text.stripScripts(this.options.evalScripts);
    },
});

/*
 * Inspired by
 * http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

    isHidden: function(){
        var w = this.offsetWidth, h = this.offsetHeight,
        force = (this.tagName.toLowerCase() === 'tr');
        return (w===0 && h===0 && !force)
            ? true
            : (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
    },
    isVisible: function(){
        return !this.isHidden();
    },
    getSiblings: function(match,nocache) {
        return this.getParent().getChildren(match,nocache).erase(this);
    }

});

/*
    Add $name mutator - specifies the type of the created Class
    Usage:
        var C = new Class({$name = 'sometype', inititate: function() {}});
        var c = new C();
        alert(typeOf(c)); // 'sometype'
 */
Class.Mutators.$name = function(name){ this.implement('$family', {name: name}); };

/*
 * Add validation for alphanum + "-_."
 */
Form.Validator.addAllThese([
    ['validate-alphanum_plus', {
        errorMsg:     'Please use only letters (a-z),
'+
                    'numbers (0-9) or \"_.-\" only in this field.
'+
                    'No spaces or other characters are allowed.',
        test: function(element){
            return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9._\-]+$/).test(element.get('value'));
        }
    }],
    ['validate-alphanum_plus_space', {
        errorMsg:     'Please use only letters (a-z),
'+
                    'numbers (0-9) spaces or \"_().-\" only in this field.
'+
                    'No other characters are allowed.',
        test: function(element){
            return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9\ _\(\).\-]+$/).test(element.get('value'));
        }
    }]
]);

(function(){
    var html_symbols = ['&','"','<','>','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''],
    html_names = ['&','"','<','>','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿'],
    js_symbols = ['\\(','\\)','\\{','\\}'],
    html_number = ['\(','\)','\{','\}'];

    String.implement({
        escapeHTML: function() {
            text = this;
            html_symbols.each(function(symbol, i){
                text = text.replace(new RegExp(symbol, 'g'), html_names[i]);
            });
            return text;
        },
        escapeJS: function() {
            text = this;
            js_symbols.each(function(symbol, i){
                text = text.replace(new RegExp(symbol, 'g'), html_number[i]);
            });
            return text;
        },
        escapeAll: function() {
            return this.escapeHTML()//.escapeJS();
        }
    });
})();

// Add volatile events to Element, Window and Events
// from http://jsfiddle.net/ZVbWP/
Events.implement({
    addVolatileEvent: function(type, fn, counter, internal){
        if(!counter) {
            counter = 1;
        }
        var volatileFn = function(){
            fn.apply(this, arguments);
            counter -= 1;
            if(counter < 1) {
                this.removeEvent(type, volatileFn);
            }
        }
        this.addEvent(type, volatileFn, internal);
    }
});

/*
    Listen to an event fired when Extension is installed
    This wasn't working
window.addEvent('load', function() {
    if (window.mozFlightDeck) {
        window.mozFlightDeck.whenMessaged(function(data) {
            // This gets called when one of our extensions has been installed
            // successfully, or failed somehow.
            fd.message.alert('Add-ons Builder', 'Add-on {msg}'.substitute(data));
            // log to console result of isInstalled command
            $log('sending isInstalled to window.mozFlightDeck');
            $log(fd.isXpiInstalled());
        });
    }
});
 */
<commit_msg>no more Error 0<commit_after>/*
 * Class: FlightDeck
 * Initializes all needed functionality
 */

var FlightDeck = new Class({
    Implements: [Options, Events],
    options: {
        menu_el: 'UI_Editor_Menu',
        try_in_browser_class: 'XPI_test',
        xpi_hashtag: '',        // hashtag for the XPI creation
        max_request_number: 50, // how many times should system try to download XPI
        request_interval: 5000  // try to download XPI every 5 sec
        //user: ''
    },
    initialize: function() {
        this.tests = {}; // placeholder for testing spinners
        this.uri = new URI();
        if (this.uri.getData('redirect','fragment')) {
            window.location.href = this.uri.getData('redirect', 'fragment');
        }
        this.warning = this.error = this.message = {
            'alert': function(title, message) {
                alert(title+"\n"+message);
            }
        };
        this.editors = [];
        this.parseTooltips();
        this.createActionSections();
        this.parseTestButtons.bind(this).delay(10);
        this.addEvent('xpi_downloaded', this.whenXpiDownloaded);
        this.addEvent('xpi_installed', this.whenXpiInstalled);
        this.addEvent('xpi_uninstalled', this.whenXpiUninstalled);
        //if (!this.isAddonInstalled()) $log('FD: No Addon Builder Helper')
    },

    parseTooltips: function() {
        this.tips = new Tips({
            fixed: false,
            className: 'UI_tooltip',
            offset: {
                x: 0,
                y: 16
            }
        });

        $$('div.UI_tooltip_source').each(function(tipSource){
            tipSource.hide();
            var target = $(tipSource.get('data-tooltip-for'));
            target.set('title', '');
            target.store('tip:title', tipSource.get('data-tooltip-title'));
            target.store('tip:text', tipSource.get('html'));
            this.tips.attach(target);
        }, this);
    },

    setURIRedirect: function(url) {
        // change the URL add #/path/to/saved/revision
        if (this.uri.get('directory') != url) {
            this.uri.setData({'redirect': url}, false, 'fragment');
            this.uri.go();
        }
    },

    whenXpiInstalled: function(name) {
        this.parseTestButtons();
        this.message.alert('Add-ons Builder', 'Add-on installed');
        $log('FD: INFO: Add-on installed');
    },

    whenXpiDownloaded: function(hashtag) {
        // remove SDK from disk
        if (this.tests[hashtag].rm_xpi_url) {
            new Request.JSON({
                url: this.tests[hashtag].rm_xpi_url,
                onSuccess: function() {
                    this.fireEvent('sdk_removed');
                }.bind(this)
            }).send();
            this.rm_xpi_url = undefined;
        }
    },

    whenXpiUninstalled: function() {
        this.parseTestButtons();
        this.message.alert('Add-ons Builder', 'Add-on uninstalled');
    },

    /*
    Method: whenAddonInstalled
    create listener for a callback function
     */
    whenAddonInstalled: function(callback) {
        var removeListener = function() {
            document.body.removeEventListener('addonbuilderhelperstart', callback, false);
        }
        document.body.addEventListener('addonbuilderhelperstart', callback, false);
        (function() {
            $log('FD: Warning: not listening to addonbuilderhelperstart, is Helper installed?');
            removeListener();
        }).delay(100000);
    },

    parseTestButtons: function() {
        var installed = (this.isAddonInstalled()) ? this.isXpiInstalled() : false;
        if (installed) {
            $$('.{try_in_browser_class} a'.substitute(this.options)).each(function(test_button){
                if (installed && installed.installedID == test_button.get('rel')) {
                    test_button.getParent('li').addClass('pressed');
                } else {
                    test_button.getParent('li').removeClass('pressed');
                }
            }, this);
        }
    },

    /*
     * Method: downloadXPI it's running in Request's scope
     */
    downloadXPI: function(response) {
        $log('FD: DEBUG: XPI delayed ... try to load every ' + fd.options.request_interval/1000 + ' seconds' );
        var hashtag = this.options.data.hashtag;
        var filename = this.options.data.filename;
        fd.tests[hashtag].download_request_number = 0;
        fd.tryDownloadXPI.delay(1000, fd, [hashtag, filename]);
        fd.tests[hashtag].download_ID = fd.tryDownloadXPI.periodical(
                fd.options.request_interval, fd, [hashtag, filename]);
    },

    /*
     * Method: tryDownloadXPI
     *
     * Try to download XPI
     * if finished - stop periodical, stop spinner
     */
    tryDownloadXPI: function(hashtag, filename) {
        var test_request = this.tests[hashtag];
        if (!test_request.download_xpi_request || (
                    test_request.download_xpi_request &&
                    !test_request.download_xpi_request.isRunning())) {
            test_request.download_request_number++;
            var url = '/xpi/check_download/'+hashtag+'/';
            $log('FD: DEBUG: checking if ' + url + ' is prepared');
            test_request.download_xpi_request = new Request.JSON({
                url: url,
                onSuccess: function(response) {
                    if (response.ready || test_request.download_request_number > 50) {
                        clearInterval(test_request.download_ID);
                        test_request.spinner.destroy();
                    }
                    if (response.ready) {
                        var url = '/xpi/download/'+hashtag+'/'+filename+'/';
                        $log('FD: downloading ' + filename + '.xpi from ' + url );
                        window.open(url, 'dl');
                    }
                },
                addOnFailure: function() {
                    clearInterval(test_request.download_ID);
                    test_request.spinner.destroy();
                }
            }).send();
        }
    },

    /*
     * Method: testXPI it's running in Request's scope
     */
    testXPI: function(response) {
        $log('FD: DEBUG: XPI delayed ... try to load every ' + fd.options.request_interval/1000 + ' seconds' );
        var hashtag = this.options.data.hashtag;
        fd.tests[hashtag].request_number = 0;
        fd.tests[hashtag].rm_xpi_url = response.rm_xpi_url;
        fd.tryInstallXPI.delay(1000, fd, hashtag);
        fd.tests[hashtag].install_ID = fd.tryInstallXPI.periodical(
                fd.options.request_interval, fd, hashtag);
    },

    isXpiInstalled: function() {
        return window.mozFlightDeck.send({cmd:'isInstalled'});
    },

    /*
     * Method: tryInstallXPI
     *
     * Try to download XPI
     * if successful - stop periodical
     */
    tryInstallXPI: function(hashtag) {
        if (this.alertIfNoAddOn()) {
            var test_request = this.tests[hashtag];
            if (!test_request.install_xpi_request || (
                        test_request.install_xpi_request &&
                        !test_request.install_xpi_request.isRunning())) {
                test_request.request_number++;
                url = '/xpi/test/'+hashtag+'/';
                $log('FD: installing from ' + url);
                test_request.install_xpi_request = new Request({
                    url: url,
                    headers: {'Content-Type': 'text/plain; charset=x-user-defined'},
                    onSuccess: function(responseText) {
                        if (responseText || test_request.request_number > 50) {
                            clearInterval(test_request.install_ID);
                            test_request.spinner.destroy();
                        }
                        if (responseText) {
                            this.fireEvent('xpi_downloaded', hashtag);
                            var result = window.mozFlightDeck.send({cmd: "install", contents: responseText});
                            if (result && result.success) {
                                this.fireEvent('xpi_installed', '');
                            } else {
                                if (result) $log(result);
                                this.warning.alert(
                                    'Add-ons Builder',
                                    'Wrong response from Add-ons Helper. Please let us know'
                                );
                            }
                        }
                    }.bind(this)
                }).send(this);
            } else {
                $log('FD: DEBUG request is running');
            }
        }
    },
    /*
     * Method: uninstallXPI
     *
     * Remove Add-on from Browser
     */
    uninstallXPI: function(jid) {
        $log('FD: uninstalling ' + jid);
        var result = window.mozFlightDeck.send({cmd:'uninstall'});
        if (result.success) this.fireEvent('xpi_uninstalled');
    },
    /*
     * Method: enableMenuButtons
     */
    enableMenuButtons: function() {
        $$('.' + this.options.menu_el + ' li').each(function(menuItem){
            if (menuItem.hasClass('disabled')){
                menuItem.removeClass('disabled');
            }
        });
    },

    isAddonInstalled: function() {
        return (window.mozFlightDeck) ? true : false;
    },

    /*
     * Method: alertIfNoAddOn
     */
    alertIfNoAddOn: function(callback, text, title) {
        if (this.isAddonInstalled()) return true;
        text = [text,
                "To test this add-on, please install the Add-ons Builder Helper add-on".substitute(settings)].pick();
        title = [title, "Install Add-ons Builder Helper"].pick();
        fd.warning.alert(title, text);
        return false;
    },
    /*
     * Method: createActionSections
     */
    createActionSections: function(){
        $$('.UI_Editor_Menu_Separator').each(function(separator){
            separator.getPrevious('li').addClass('UI_Section_Close');
            separator.getNext('li').addClass('UI_Section_Open');
        });

        var UI_Editor_Menu_Button = $$('.UI_Editor_Menu_Button');

        if (UI_Editor_Menu_Button.length === 1){
            UI_Editor_Menu_Button[0].addClass('UI_Single');
        }
    }
});

/*
 * Add delay do Spinner
 */

Spinner = Class.refactor(Spinner, {
    options: { delay: 400 },
});

/*
 * Default onFailure in all Requests
 */

Request = Class.refactor(Request, {
    options: {
        onFailure: function(xhr) {
            if (this.options.addOnFailure) {
              this.options.addOnFailure();
            }
			if (xhr.status != 0 && xhr.responseText) {
				fd.error.alert(
					'Error {status}'.substitute(xhr),
					'{statusText}
{responseText}'.substitute(xhr)
					);
			}
        }
    },
    initialize: function(options) {
      this.previous(options);
      // It happened to be unnecessary
      //if (csrfmiddlewaretoken && (this.options.method == 'post' || this.options.method == 'POST')) {
      //  this.options.data['csrfmiddlewaretoken'] = csrfmiddlewaretoken;
      //}

    },
  // overloading processScripts to *not* execute JS responses
    processScripts: function(text){
        if (this.options.evalResponse) return Browser.exec(text);
        return text.stripScripts(this.options.evalScripts);
    },
});

/*
 * Inspired by
 * http://github.com/jeresig/sizzle/commit/7631f9c3f85e5fa72ac51532399cb593c2cdc71f
 * and this http://github.com/jeresig/sizzle/commit/5716360040a440041da19823964f96d025ca734b
 * and then http://dev.jquery.com/ticket/4512
 */

Element.implement({

    isHidden: function(){
        var w = this.offsetWidth, h = this.offsetHeight,
        force = (this.tagName.toLowerCase() === 'tr');
        return (w===0 && h===0 && !force)
            ? true
            : (w!==0 && h!==0 && !force) ? false : this.getStyle('display') === 'none';
    },
    isVisible: function(){
        return !this.isHidden();
    },
    getSiblings: function(match,nocache) {
        return this.getParent().getChildren(match,nocache).erase(this);
    }

});

/*
    Add $name mutator - specifies the type of the created Class
    Usage:
        var C = new Class({$name = 'sometype', inititate: function() {}});
        var c = new C();
        alert(typeOf(c)); // 'sometype'
 */
Class.Mutators.$name = function(name){ this.implement('$family', {name: name}); };

/*
 * Add validation for alphanum + "-_."
 */
Form.Validator.addAllThese([
    ['validate-alphanum_plus', {
        errorMsg:     'Please use only letters (a-z),
'+
                    'numbers (0-9) or \"_.-\" only in this field.
'+
                    'No spaces or other characters are allowed.',
        test: function(element){
            return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9._\-]+$/).test(element.get('value'));
        }
    }],
    ['validate-alphanum_plus_space', {
        errorMsg:     'Please use only letters (a-z),
'+
                    'numbers (0-9) spaces or \"_().-\" only in this field.
'+
                    'No other characters are allowed.',
        test: function(element){
            return Form.Validator.getValidator('IsEmpty').test(element) ||  (/^[a-zA-Z0-9\ _\(\).\-]+$/).test(element.get('value'));
        }
    }]
]);

(function(){
    var html_symbols = ['&','"','<','>','','','','','','','','','','','','','','','','','','','','','','','','','','','','','',''],
    html_names = ['&','"','<','>','¡','¢','£','¤','¥','¦','§','¨','©','ª','«','¬','®','¯','°','±','²','³','´','µ','¶','·','¸','¹','º','»','¼','½','¾','¿'],
    js_symbols = ['\\(','\\)','\\{','\\}'],
    html_number = ['\(','\)','\{','\}'];

    String.implement({
        escapeHTML: function() {
            text = this;
            html_symbols.each(function(symbol, i){
                text = text.replace(new RegExp(symbol, 'g'), html_names[i]);
            });
            return text;
        },
        escapeJS: function() {
            text = this;
            js_symbols.each(function(symbol, i){
                text = text.replace(new RegExp(symbol, 'g'), html_number[i]);
            });
            return text;
        },
        escapeAll: function() {
            return this.escapeHTML()//.escapeJS();
        }
    });
})();

// Add volatile events to Element, Window and Events
// from http://jsfiddle.net/ZVbWP/
Events.implement({
    addVolatileEvent: function(type, fn, counter, internal){
        if(!counter) {
            counter = 1;
        }
        var volatileFn = function(){
            fn.apply(this, arguments);
            counter -= 1;
            if(counter < 1) {
                this.removeEvent(type, volatileFn);
            }
        }
        this.addEvent(type, volatileFn, internal);
    }
});

/*
    Listen to an event fired when Extension is installed
    This wasn't working
window.addEvent('load', function() {
    if (window.mozFlightDeck) {
        window.mozFlightDeck.whenMessaged(function(data) {
            // This gets called when one of our extensions has been installed
            // successfully, or failed somehow.
            fd.message.alert('Add-ons Builder', 'Add-on {msg}'.substitute(data));
            // log to console result of isInstalled command
            $log('sending isInstalled to window.mozFlightDeck');
            $log(fd.isXpiInstalled());
        });
    }
});
 */
<|endoftext|>


<commit_before>;(function() {
'use strict'

i18n.init({
  lng: 'en-US',
  customLoad: function(lng, ns, options, loadComplete) {
    loadComplete(null, {
      key: 'key translated',
      key2: 'key2 translated',
      opt_key: 'opt: __opt__',
      nested: {
        child: 'child translated'
      },
      // Character codes are harder to test, because depending on the platform
      // they may not be escaped when queried via element.html()
      //html: "Symbols & “Quotes”"
      html: "Symbols &"
    })
  }
})

describe('isActive', function () {
  var $compile
  var $parentScope
  var elm

  beforeEach(angular.mock.module('i18ng'))

  beforeEach(inject(function(_$compile_, $rootScope) {
    $compile = _$compile_
    $parentScope = $rootScope.$new()
  }))

  afterEach(inject(function(_$compile_, $rootScope) {
    elm = undefined
    $parentScope = undefined
  }))

  function compileDirective(tpl) {
    if (!tpl) {
      tpl = ""
    }
    elm = $compile(tpl)($parentScope)
    $parentScope.$digest()
  }

  it('should initialize', function() {
    compileDirective()
  })

  it('should translate direct strings', function() {
    compileDirective()
    expect(elm.text()).toBe('key translated')
  })

  it('should translate direct strings w/ options', function() {
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate direct strings MISSING options', function() {
    compileDirective("")
    expect(elm.text()).toBe('opt: __opt__')
  })

  it('should translate scoped values', function() {
    $parentScope.scopeKey = 'key'
    compileDirective("")
    expect(elm.text()).toBe('key translated')
  })

  it('should translate scoped value w/ options', function() {
    $parentScope.scopeKey = 'opt_key'
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate scoped value w/ scoped options', function() {
    $parentScope.scopeKey = 'opt_key'
    $parentScope.opts = { opt: 1 }
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate and set as html (NO ESCAPING)', function() {
    $parentScope.scopeKey = 'html'
    compileDirective("")
    expect(elm.html()).toBe("Symbols &")
  })

  describe('#attributes', function () {
    it('should translate and set an attribute', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('key translated')
    })

    it('should translate and set an attribute w/opts', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: 1')
    })

    it('should translate element and an attribute', function() {
      compileDirective("")
      expect(elm.text()).toBe('key translated')
      expect(elm.attr('title')).toBe('key2 translated')
    })

    it('should translate and set two attributes', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('key translated')
      expect(elm.attr('other')).toBe('key2 translated')
    })

    it('should translate and set two attributes w/ opts', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: title')
      expect(elm.attr('other')).toBe('opt: other')
    })

    it('should apply opts to the correct attribute', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: title')
      expect(elm.attr('other')).toBe('opt: __opt__')
    })
  })
})

})();
<commit_msg>Add comment about "i18ng" (only attrib with "g")<commit_after>;(function() {
'use strict'

i18n.init({
  lng: 'en-US',
  customLoad: function(lng, ns, options, loadComplete) {
    loadComplete(null, {
      key: 'key translated',
      key2: 'key2 translated',
      opt_key: 'opt: __opt__',
      nested: {
        child: 'child translated'
      },
      // Character codes are harder to test, because depending on the platform
      // they may not be escaped when queried via element.html()
      //html: "Symbols & “Quotes”"
      html: "Symbols &"
    })
  }
})

describe('isActive', function () {
  var $compile
  var $parentScope
  var elm

  beforeEach(angular.mock.module('i18ng'))

  beforeEach(inject(function(_$compile_, $rootScope) {
    $compile = _$compile_
    $parentScope = $rootScope.$new()
  }))

  afterEach(inject(function(_$compile_, $rootScope) {
    elm = undefined
    $parentScope = undefined
  }))

  function compileDirective(tpl) {
    if (!tpl) {
      tpl = ""
    }
    elm = $compile(tpl)($parentScope)
    $parentScope.$digest()
  }

  it('should initialize', function() {
    compileDirective()
  })

  it('should translate direct strings', function() {
    compileDirective()
    expect(elm.text()).toBe('key translated')
  })

  it('should translate direct strings w/ options', function() {
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate direct strings MISSING options', function() {
    compileDirective("")
    expect(elm.text()).toBe('opt: __opt__')
  })

  it('should translate scoped values', function() {
    $parentScope.scopeKey = 'key'
    compileDirective("")
    expect(elm.text()).toBe('key translated')
  })

  it('should translate scoped value w/ options', function() {
    $parentScope.scopeKey = 'opt_key'
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate scoped value w/ scoped options', function() {
    $parentScope.scopeKey = 'opt_key'
    $parentScope.opts = { opt: 1 }
    compileDirective("")
    expect(elm.text()).toBe('opt: 1')
  })

  it('should translate and set as html (NO ESCAPING)', function() {
    $parentScope.scopeKey = 'html'
    // Note this uses "i18ng" - only attrib with "g"
    compileDirective("")
    expect(elm.html()).toBe("Symbols &")
  })

  describe('#attributes', function () {
    it('should translate and set an attribute', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('key translated')
    })

    it('should translate and set an attribute w/opts', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: 1')
    })

    it('should translate element and an attribute', function() {
      compileDirective("")
      expect(elm.text()).toBe('key translated')
      expect(elm.attr('title')).toBe('key2 translated')
    })

    it('should translate and set two attributes', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('key translated')
      expect(elm.attr('other')).toBe('key2 translated')
    })

    it('should translate and set two attributes w/ opts', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: title')
      expect(elm.attr('other')).toBe('opt: other')
    })

    it('should apply opts to the correct attribute', function() {
      compileDirective("")
      expect(elm.attr('title')).toBe('opt: title')
      expect(elm.attr('other')).toBe('opt: __opt__')
    })
  })
})

})();
<|endoftext|>


<commit_before>define(['xaml!sprd/view/svg/SpecialFlexConfigurationRenderer', "sprd/entity/Size"], function(SpecialFlexConfigurationRenderer, Size) {

    return SpecialFlexConfigurationRenderer.inherit("sprd.view.svg.BendingTextConfigurationRendererClass", {

        defaults: {
            tagName: "g",
            componentClass: "bendingText-configuration",
            productViewer: null,
            configurationViewer: null,
            configuration: null,

            textPath: null,
            path: null,

            oldSize: null
        },

        ctor: function() {
            this.callBase();
            this.bind("dom:add", this.recalculateSize, this);

            this.bind("configuration", "recalculateSize", this.recalculateSize, this);
            this.bind("configuration", "change:font", this.loadFont, this);
            this.bind("configuration", "change:_size", this.balanceConfiguration, this);
            this.bind("productViewer.product", ["configurations", "reset"], function() {
                var configuration = this.$.configuration;
                if (configuration) {
                    configuration.mainConfigurationRenderer = null;
                }
            }, this);

            var timer = null;
            this.bind("configuration", "change:angle", function() {
                var path = this.$.path;

                if (timer) {
                    clearTimeout(timer);
                }

                if (path) {
                    path.set("selected", true);

                    timer = setTimeout(function() {
                        path.set("selected", false);
                    }, 1000);
                }
            }, this);
        },

        _initializationComplete: function() {
            this.callBase();
            this.loadFont();
            this.recalculateSize();
        },

        render: function() {
            if (this.$stage && this.$stage.$el && this.$stage.$el.parentNode) {
                this.loadFont();
            }

            return this.callBase();
        },

        loadFont: function() {
            var svgRoot = this.getSvgRoot(),
                font = this.get("configuration.font"),
                extension = this.$stage.$browser.isIOS ? "svg#font" : "woff",
                self = this;

            if (!font || !svgRoot) {
                return;
            }

            svgRoot.fontManager.loadExternalFont(font.getUniqueFontName(), this.$.imageService.fontUrl(font, extension), function() {
                self.recalculateSize();
            });
        },

        recalculateSize: function() {
            var textPath = this.$.textPath;
            var path = this.$.path;
            if (textPath && textPath.$el && path && path.$el) {

                var configuration = this.$.configuration;
                if (configuration && configuration.mainConfigurationRenderer && configuration.mainConfigurationRenderer != this) {
                    return;
                }

                configuration.mainConfigurationRenderer = this;

                var textPathRect = textPath.$parent.$el.getBBox();
                var pathRect = path.$el.getBBox();

                configuration.set({
                    textPathOffsetX: 0,
                    textPathOffsetY: 0
                });

                var textBBox = this.$.text.$el.getBBox();

                configuration.set({
                    _size: new Size({
                        width: textPathRect.width,
                        height: textPathRect.height
                    }),
                    textPathOffsetX: (textPathRect.width - pathRect.width) * 0.5,
                    textPathOffsetY: -textBBox.y
                });

                configuration.trigger("sizeChanged");
            }
        },

        balanceConfiguration: function() {
            var configuration = this.$.configruation;
            if (configuration) {
                var oldSize = this.$.oldSize,
                    newSize = configuration.$._size;

                if (oldSize) {
                    var offset = configuration.$.offset;
                    configuration.$.offset.set({
                        x: offset.$.x - (newSize.$.width - oldSize.$.width)
                    })
                }

                this.set("oldSize", newSize);
            }
        },

        getPrintColor: function() {
            var configuration = this.$.configuration,
                printColors = configuration.$.printColors,
                printColor = null;

            if (printColors && printColors.size() && !this.isSpecialFlex()) {
                printColor = printColors.at(0).toHexString();
            }

            return printColor;
        }.on(["configuration.printColors", "reset"]).onChange("configuration.printType")

    });
});<commit_msg>fixed positioning of bending text configuration after angle, scale changes<commit_after>define(['xaml!sprd/view/svg/SpecialFlexConfigurationRenderer', "sprd/entity/Size"], function(SpecialFlexConfigurationRenderer, Size) {

    return SpecialFlexConfigurationRenderer.inherit("sprd.view.svg.BendingTextConfigurationRendererClass", {

        defaults: {
            tagName: "g",
            componentClass: "bendingText-configuration",
            productViewer: null,
            configurationViewer: null,
            configuration: null,

            textPath: null,
            path: null,

            oldSize: null
        },

        ctor: function() {
            this.callBase();
            this.bind("dom:add", this.recalculateSize, this);

            this.bind("configuration", "recalculateSize", this.recalculateSize, this);
            this.bind("configuration", "change:font", this.loadFont, this);
            this.bind("configuration", "change:_size", this.balanceConfiguration, this);
            this.bind("productViewer.product", ["configurations", "reset"], function() {
                var configuration = this.$.configuration;
                if (configuration) {
                    configuration.mainConfigurationRenderer = null;
                }
            }, this);

            var timer = null;
            this.bind("configuration", "change:angle", function() {
                var path = this.$.path;

                if (timer) {
                    clearTimeout(timer);
                }

                if (path) {
                    path.set("selected", true);

                    timer = setTimeout(function() {
                        path.set("selected", false);
                    }, 1000);
                }
            }, this);
        },

        _initializationComplete: function() {
            this.callBase();
            this.loadFont();
            this.recalculateSize();
        },

        render: function() {
            if (this.$stage && this.$stage.$el && this.$stage.$el.parentNode) {
                this.loadFont();
            }

            return this.callBase();
        },

        loadFont: function() {
            var svgRoot = this.getSvgRoot(),
                font = this.get("configuration.font"),
                extension = this.$stage.$browser.isIOS ? "svg#font" : "woff",
                self = this;

            if (!font || !svgRoot) {
                return;
            }

            svgRoot.fontManager.loadExternalFont(font.getUniqueFontName(), this.$.imageService.fontUrl(font, extension), function() {
                self.recalculateSize();
            });
        },

        recalculateSize: function() {
            var textPath = this.$.textPath;
            var path = this.$.path;
            if (textPath && textPath.$el && path && path.$el) {

                var configuration = this.$.configuration;
                if (configuration && configuration.mainConfigurationRenderer && configuration.mainConfigurationRenderer != this) {
                    return;
                }

                configuration.mainConfigurationRenderer = this;

                var textPathRect = textPath.$parent.$el.getBBox();
                var pathRect = path.$el.getBBox();

                configuration.set({
                    textPathOffsetX: 0,
                    textPathOffsetY: 0
                });

                var textBBox = this.$.text.$el.getBBox();

                configuration.set({
                    _size: new Size({
                        width: textPathRect.width,
                        height: textPathRect.height
                    }),
                    textPathOffsetX: (textPathRect.width - pathRect.width) * 0.5,
                    textPathOffsetY: -textBBox.y
                });

                configuration.trigger("sizeChanged");
            }
        },

        balanceConfiguration: function() {
            var configuration = this.$.configuration;
            if (configuration) {
                var oldSize = this.$.oldSize,
                    newSize = configuration.$._size,
                    scale = configuration.$.scale.x;

                if (oldSize) {
                    var offset = configuration.$.offset;
                    configuration.$.offset.set({
                        x: offset.$.x - ((newSize.$.width - oldSize.$.width) / 2 ) * (scale / this.$.normalScale)
                    });
                } else {
                    this.set("normalScale", configuration.$.scale.x)
                }

                this.set("oldSize", newSize);
            }
        },

        getPrintColor: function() {
            var configuration = this.$.configuration,
                printColors = configuration.$.printColors,
                printColor = null;

            if (printColors && printColors.size() && !this.isSpecialFlex()) {
                printColor = printColors.at(0).toHexString();
            }

            return printColor;
        }.on(["configuration.printColors", "reset"]).onChange("configuration.printType")

    });
});<|endoftext|>


<commit_before>var	models = require('../models'),
	api = new require('../api')(),
	GeoFeatureCollection = models.GeoFeatureCollection,
	config = require('../config'),
	assert = require('assert'),
	mongoose = require('mongoose');

describe('KML', function() {
	var featureCollection, GeoFeature;

	before(function() {
		mongoose.connect(config.DB_URI);
	});

	it('should import a KML file and extract custom properties', function(done) {

		api.import.import({
			path: 'test/data/scenic_vista.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne({'properties.TrailHeadName': 'Mount Everest'}, function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'Point');
				assert.strictEqual(feature.properties.name, "Difficult trail");
				assert.strictEqual(feature.properties.TrailLength, 347.45);
				assert.strictEqual(feature.properties.ElevationGain, 10000);
				done();
			});
		});

	});

	it('should import a KML file and save a LineString', function(done) {

		api.import.import({
			path: 'test/data/linestring.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne(function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'LineString');
				assert.equal(feature.geometry.coordinates.length, 11);
				done();
			});
		});

	});

	it('should import a KML file and save a Polygon', function(done) {

		api.import.import({
			path: 'test/data/polygon.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne(function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'Polygon');
				console.log('--------');
				console.log(feature.geometry.coordinates.length);
				assert.equal(feature.geometry.coordinates[0].length, 6);
				assert.equal(feature.geometry.coordinates[1].length, 6);
				done();
			});
		});

	});

	after(function() {
		mongoose.disconnect();
	});

});
<commit_msg>Removed log message<commit_after>var	models = require('../models'),
	api = new require('../api')(),
	GeoFeatureCollection = models.GeoFeatureCollection,
	config = require('../config'),
	assert = require('assert'),
	mongoose = require('mongoose');

describe('KML', function() {
	var featureCollection, GeoFeature;

	before(function() {
		mongoose.connect(config.DB_URI);
	});

	it('should import a KML file and extract custom properties', function(done) {

		api.import.import({
			path: 'test/data/scenic_vista.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne({'properties.TrailHeadName': 'Mount Everest'}, function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'Point');
				assert.strictEqual(feature.properties.name, "Difficult trail");
				assert.strictEqual(feature.properties.TrailLength, 347.45);
				assert.strictEqual(feature.properties.ElevationGain, 10000);
				done();
			});
		});

	});

	it('should import a KML file and save a LineString', function(done) {

		api.import.import({
			path: 'test/data/linestring.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne(function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'LineString');
				assert.equal(feature.geometry.coordinates.length, 11);
				done();
			});
		});

	});

	it('should import a KML file and save a Polygon', function(done) {

		api.import.import({
			path: 'test/data/polygon.kml',
		}, null, null, function(err, collection) {
			if (err) throw err;
			collection.getFeatureModel().findOne(function(err, feature) {
				if (err) throw err;
				assert(feature);
				assert.equal(feature.geometry.type, 'Polygon');
				assert.equal(feature.geometry.coordinates[0].length, 6);
				assert.equal(feature.geometry.coordinates[1].length, 6);
				done();
			});
		});

	});

	after(function() {
		mongoose.disconnect();
	});

});
<|endoftext|>


<commit_before>var Ghost = require('../lib/ghost.js');
var path = require('path');

describe('Home', function () {
  var ghost;
  before(function () {
    ghost = new Ghost("Chrome", "http://shu.taobao.com/", {host: "10.13.191.77"});
  });

  after(function (done) {
    ghost.end(done);
  });

  it('Title should be 淘宝指数 - 淘宝消费者数据研究平台', function (done) {
    ghost.getTitle(function (title) {
      title.should.be.equal("淘宝指数 - 淘宝消费者数据研究平台");
      done();
    });
  });

  it('start page should not visible', function (done) {
    ghost.isVisible("#startpage", function (isVisible) {
      isVisible.should.be.equal(false);
    });
    var tmp = path.join(__dirname, "../screenshot/screenshot_" + new Date().getTime() + ".png");
    ghost.saveScreenshot(tmp, function () {
      done();
    });
  });

  it('start button should not be visible', function (done) {
    ghost.isVisible(".btn_start_index", function (isVisible) {
      isVisible.should.be.equal(true);
    })
    .click("img.btn_start_index", function () {})
    .pause(2000, function (){})
    .isVisible("#startpage", function (isVisible) {
      isVisible.should.be.equal(true);
    })
    .isVisible(".btn_start_index", function (isVisible) {
      isVisible.should.be.equal(false);
      done();
    });
  });
});
<commit_msg>Update<commit_after>var Ghost = require('../lib/ghost.js');
var path = require('path');

describe('Home', function () {
  var ghost;
  before(function () {
    ghost = new Ghost("chrome", "http://shu.taobao.com/", {host: "10.13.191.77"});
  });

  after(function (done) {
    ghost.end(done);
  });

  it('Title should be 淘宝指数 - 淘宝消费者数据研究平台', function (done) {
    ghost.getTitle(function (title) {
      title.should.be.equal("淘宝指数 - 淘宝消费者数据研究平台");
      done();
    });
  });

  it('start page should not visible', function (done) {
    ghost.isVisible("#startpage", function (isVisible) {
      isVisible.should.be.equal(false);
    });
    var tmp = path.join(__dirname, "../screenshot/screenshot_" + new Date().getTime() + ".png");
    ghost.saveScreenshot(tmp, function () {
      done();
    });
  });

  it('start button should not be visible', function (done) {
    ghost.isVisible(".btn_start_index", function (isVisible) {
      isVisible.should.be.equal(true);
    })
    .click("img.btn_start_index", function () {})
    .pause(2000, function (){})
    .isVisible("#startpage", function (isVisible) {
      isVisible.should.be.equal(true);
    })
    .isVisible(".btn_start_index", function (isVisible) {
      isVisible.should.be.equal(false);
      done();
    });
  });
});
<|endoftext|>


<commit_before>/* global describe,it */

// setup jsdom
const { JSDOM } = require('jsdom')

const jsdom = new JSDOM('<!doctype html>')
const { window } = jsdom

function copyProps(src, target) {
    const props = Object.getOwnPropertyNames(src)
        .filter(prop => typeof target[prop] === 'undefined')
        .map(prop => Object.getOwnPropertyDescriptor(src, prop))
    Object.defineProperties(target, props)
}

global.window = window
global.document = window.document
global.navigator = { userAgent: 'node.js' }
copyProps(window, global)

// setup tests (note: using import is troublesome, code gets executed in the wrong order)
const React = require('react')
global.React = React

const { mount, shallow } = require('enzyme')
const sinon = require('sinon')
const { expect } = require('chai')

const { Tabbordion, TabPanel } = require('../dist/module')

describe('Tabbordion', function() {
    it('should render className and have ARIA role of tablist', function() {
        const wrapper = shallow(
            React.createElement(Tabbordion, { className: 'test test--test' })
        )

        expect(wrapper.hasClass('test')).to.be.true
        expect(wrapper.hasClass('test--test')).to.be.true
        expect(wrapper.prop('role')).to.equal('tablist')
    })

    it('should render using given component', function() {
        const wrapper = shallow(
            React.createElement(Tabbordion, { component: 'span' })
        )

        expect(wrapper.matchesElement(React.createElement('span'))).to.be.true
    })

    it('should provide bem and tabbordion context', function() {
        const wrapper = shallow(React.createElement(Tabbordion))
        const context = wrapper.instance().getChildContext()

        expect(context.bem).to.be.ok
        expect(context.tabbordion).to.be.ok
        expect(context.tabbordionPanel).to.not.be.ok

        expect(typeof context.bem.getState).to.equal('function')
        expect(typeof context.bem.subscribe).to.equal('function')
        expect(typeof context.bem.unsubscribe).to.equal('function')
        expect(typeof context.tabbordion.getState).to.equal('function')
        expect(typeof context.tabbordion.subscribe).to.equal('function')
        expect(typeof context.tabbordion.unsubscribe).to.equal('function')
    })
})

function getBemState() {
    return {
        bemModifiers: {
            animated: 'animated',
            between: 'between',
            checked: 'checked',
            content: 'content',
            disabled: 'disabled',
            enabled: 'enabled',
            first: 'first',
            last: 'last',
            noContent: 'no-content',
            unchecked: 'unchecked',
        },
        bemSeparator: '--',
        blockElements: {
            content: 'content',
            label: 'label',
            panel: 'panel',
        },
    }
}

function getSinglePanelTabbordionState() {
    return {
        animateContent: false,
        checkedPanels: [0],
        disabledPanels: [],
        firstVisiblePanel: 0,
        lastVisiblePanel: 0,
        panelName: 'test',
        panelType: 'checkbox',
        tabbordionId: 'one'
    }
}

function getFourPanelTabbordionState() {
    return {
        animateContent: false,
        checkedPanels: [1, 2],
        disabledPanels: [3],
        firstVisiblePanel: 0,
        lastVisiblePanel: 3,
        panelName: 'test',
        panelType: 'checkbox',
        tabbordionId: 'four'
    }
}

const singlePanelContext = {
    bem: {
        getState: getBemState,
    },
    tabbordion: {
        getState: getSinglePanelTabbordionState,
    },
}

const fourPanelContext = {
    bem: {
        getState: getBemState,
    },
    tabbordion: {
        getState: getFourPanelTabbordionState,
    },
}

describe('TabPanel', function() {
    it('should provide tabbordionPanel context', function() {
        const options = { context: singlePanelContext }

        const wrapper = shallow(React.createElement(TabPanel), options)
        const context = wrapper.instance().getChildContext()

        expect(context.bem).to.not.be.ok
        expect(context.tabbordion).to.not.be.ok
        expect(context.tabbordionPanel).to.be.ok

        expect(typeof context.tabbordionPanel.getState).to.equal('function')
        expect(typeof context.tabbordionPanel.onClickLabel).to.equal('function')
        expect(typeof context.tabbordionPanel.subscribe).to.equal('function')
        expect(typeof context.tabbordionPanel.unsubscribe).to.equal('function')
    })

    it('should trigger context subscribes when mounted', function() {
        const subscribeBem = sinon.spy()
        const subscribeTabbordion = sinon.spy()
        const unsubscribeBem = sinon.spy()
        const unsubscribeTabbordion = sinon.spy()

        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: subscribeBem,
                    unsubscribe: unsubscribeBem,
                },
                tabbordion: {
                    getState: getSinglePanelTabbordionState,
                    subscribe: subscribeTabbordion,
                    unsubscribe: unsubscribeTabbordion,
                }
            }
        }

        const wrapper = mount(React.createElement(TabPanel), options)

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordion.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.false
        expect(unsubscribeTabbordion.calledOnce).to.be.false

        wrapper.unmount()

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordion.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.true
        expect(unsubscribeTabbordion.calledOnce).to.be.true
    })

    it('should generate BEM classes and other rendered props from given context', function() {
        const options = { context: singlePanelContext }

        const wrapper = shallow(React.createElement(TabPanel, { index: 0 }), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel')).to.be.true
        expect(wrapper.hasClass('panel--checked')).to.be.true
        expect(wrapper.hasClass('panel--enabled')).to.be.true
        expect(wrapper.hasClass('panel--first')).to.be.true
        expect(wrapper.hasClass('panel--last')).to.be.true
        expect(wrapper.hasClass('panel--no-content')).to.be.true

        expect(wrapper.prop('aria-expanded')).to.be.null
        expect(wrapper.prop('aria-selected')).to.equal('true')
        expect(wrapper.prop('role')).to.equal('tab')
        expect(wrapper.prop('style').display).to.be.null

        expect(wrapperInput.prop('aria-controls')).to.equal('one-0-content')
        expect(wrapperInput.prop('checked')).to.be.true
        expect(wrapperInput.prop('data-state')).to.equal('tabbordion')
        expect(wrapperInput.prop('disabled')).to.equal(false)
        expect(wrapperInput.prop('id')).to.equal('one-0')
        expect(wrapperInput.prop('name')).to.equal('test')
        expect(wrapperInput.prop('type')).to.equal('checkbox')
        expect(wrapperInput.prop('value')).to.equal('0')
    })

    it('should place other children always after the input box', function() {
        const options = { context: singlePanelContext }

        const div = React.createElement('div')
        const wrapper = shallow(React.createElement(TabPanel, { index: 0 }, div), options)

        expect(wrapper.childAt(1).equals(div)).be.true
    })

    it('should handle first/between/last BEM classes', function() {
        const options = { context: fourPanelContext }

        const panel0 = shallow(React.createElement(TabPanel, { index: 0 }), options)
        const panel1 = shallow(React.createElement(TabPanel, { index: 1 }), options)
        const panel2 = shallow(React.createElement(TabPanel, { index: 2 }), options)
        const panel3 = shallow(React.createElement(TabPanel, { index: 3 }), options)

        expect(panel0.hasClass('panel--first')).to.be.true
        expect(panel0.hasClass('panel--between')).to.be.false
        expect(panel0.hasClass('panel--last')).to.be.false

        expect(panel1.hasClass('panel--first')).to.be.false
        expect(panel1.hasClass('panel--between')).to.be.true
        expect(panel1.hasClass('panel--last')).to.be.false

        expect(panel2.hasClass('panel--first')).to.be.false
        expect(panel2.hasClass('panel--between')).to.be.true
        expect(panel2.hasClass('panel--last')).to.be.false

        expect(panel3.hasClass('panel--first')).to.be.false
        expect(panel3.hasClass('panel--between')).to.be.false
        expect(panel3.hasClass('panel--last')).to.be.true
    })

    it('should reflect disabled state', function() {
        const options = { context: fourPanelContext }

        const wrapper = shallow(React.createElement(TabPanel, { index: 3 }), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel--disabled')).to.be.true
        expect(wrapperInput.prop('disabled')).to.be.true
    })

    it('should allow props to override context', function() {
        const options = { context: singlePanelContext }
        const props = {
            checked: false,
            disabled: true,
            id: 'myself',
            index: 0,
            name: 'different',
            type: 'radio',
            value: 'best',
            visible: false,
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel--unchecked')).to.be.true
        expect(wrapper.hasClass('panel--disabled')).to.be.true
        expect(wrapper.prop('style').display).to.equal('none')

        expect(wrapperInput.prop('checked')).to.be.false
        expect(wrapperInput.prop('disabled')).to.equal(true)
        expect(wrapperInput.prop('id')).to.equal('myself')
        expect(wrapperInput.prop('name')).to.equal('different')
        expect(wrapperInput.prop('type')).to.equal('radio')
        expect(wrapperInput.prop('value')).to.equal('best')
    })

    it('should allow usage of custom BEM modifiers via props', function() {
        const options = { context: singlePanelContext }
        const props = {
            index: 0,
            modifiers: ['i-am-custom', 'i-am-too']
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)

        expect(wrapper.hasClass('panel--i-am-custom')).to.be.true
        expect(wrapper.hasClass('panel--i-am-too')).to.be.true
    })

    // this was a v0 feature that has been deprecated by modifiers prop
    it('should NOT extend custom BEM modifiers via className', function() {
        const options = { context: singlePanelContext }
        const props = {
            className: '--i-am-custom blergh --i-am-too',
            index: 0,
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)

        expect(wrapper.hasClass('panel--i-am-too')).to.be.false
        expect(wrapper.hasClass('--i-am-too')).to.be.true
        expect(wrapper.hasClass('panel--i-am-custom')).to.be.false
        expect(wrapper.hasClass('--i-am-custom')).to.be.true
        expect(wrapper.hasClass('blergh')).to.be.true
    })
})
<commit_msg>Add tests for TabLabel and TabContent<commit_after>/* global describe,it */

// setup jsdom
const { JSDOM } = require('jsdom')

const jsdom = new JSDOM('<!doctype html>')
const { window } = jsdom

function copyProps(src, target) {
    const props = Object.getOwnPropertyNames(src)
        .filter(prop => typeof target[prop] === 'undefined')
        .map(prop => Object.getOwnPropertyDescriptor(src, prop))
    Object.defineProperties(target, props)
}

global.window = window
global.document = window.document
global.navigator = { userAgent: 'node.js' }
copyProps(window, global)

// setup tests (note: using import is troublesome, code gets executed in the wrong order)
const React = require('react')
global.React = React

const { mount, shallow } = require('enzyme')
const sinon = require('sinon')
const { expect } = require('chai')

const { Tabbordion, TabPanel, TabLabel, TabContent } = require('../dist/module')

function NOOP(){}

describe('Tabbordion', function() {
    it('should render className and have ARIA role of tablist', function() {
        const wrapper = shallow(
            React.createElement(Tabbordion, { className: 'test test--test' })
        )

        expect(wrapper.hasClass('test')).to.be.true
        expect(wrapper.hasClass('test--test')).to.be.true
        expect(wrapper.prop('role')).to.equal('tablist')
    })

    it('should render using given component', function() {
        const wrapper = shallow(
            React.createElement(Tabbordion, { component: 'span' })
        )

        expect(wrapper.matchesElement(React.createElement('span'))).to.be.true
    })

    it('should provide bem and tabbordion context', function() {
        const wrapper = shallow(React.createElement(Tabbordion))
        const context = wrapper.instance().getChildContext()

        expect(context.bem).to.be.ok
        expect(context.tabbordion).to.be.ok
        expect(context.tabbordionPanel).to.not.be.ok

        expect(typeof context.bem.getState).to.equal('function')
        expect(typeof context.bem.subscribe).to.equal('function')
        expect(typeof context.bem.unsubscribe).to.equal('function')
        expect(typeof context.tabbordion.getState).to.equal('function')
        expect(typeof context.tabbordion.subscribe).to.equal('function')
        expect(typeof context.tabbordion.unsubscribe).to.equal('function')
    })
})

function getBemState() {
    return {
        bemModifiers: {
            animated: 'animated',
            between: 'between',
            checked: 'checked',
            content: 'content',
            disabled: 'disabled',
            enabled: 'enabled',
            first: 'first',
            last: 'last',
            noContent: 'no-content',
            unchecked: 'unchecked',
        },
        bemSeparator: '--',
        blockElements: {
            animator: 'animator',
            content: 'content',
            label: 'label',
            panel: 'panel',
        },
    }
}

function getSinglePanelTabbordionState() {
    return {
        animateContent: false,
        checkedPanels: [0],
        disabledPanels: [],
        firstVisiblePanel: 0,
        lastVisiblePanel: 0,
        panelName: 'test',
        panelType: 'checkbox',
        tabbordionId: 'one'
    }
}

function getFourPanelTabbordionState() {
    return {
        animateContent: false,
        checkedPanels: [1, 2],
        disabledPanels: [3],
        firstVisiblePanel: 0,
        lastVisiblePanel: 3,
        panelName: 'test',
        panelType: 'checkbox',
        tabbordionId: 'four'
    }
}

const singlePanelContext = {
    bem: {
        getState: getBemState,
    },
    tabbordion: {
        getState: getSinglePanelTabbordionState,
    },
}

const fourPanelContext = {
    bem: {
        getState: getBemState,
    },
    tabbordion: {
        getState: getFourPanelTabbordionState,
    },
}

describe('TabPanel', function() {
    it('should provide tabbordionPanel context', function() {
        const options = { context: singlePanelContext }

        const wrapper = shallow(React.createElement(TabPanel), options)
        const context = wrapper.instance().getChildContext()

        expect(context.bem).to.not.be.ok
        expect(context.tabbordion).to.not.be.ok
        expect(context.tabbordionPanel).to.be.ok

        expect(typeof context.tabbordionPanel.getState).to.equal('function')
        expect(typeof context.tabbordionPanel.onClickLabel).to.equal('function')
        expect(typeof context.tabbordionPanel.subscribe).to.equal('function')
        expect(typeof context.tabbordionPanel.unsubscribe).to.equal('function')
    })

    it('should trigger context subscribes when mounted', function() {
        const subscribeBem = sinon.spy()
        const subscribeTabbordion = sinon.spy()
        const unsubscribeBem = sinon.spy()
        const unsubscribeTabbordion = sinon.spy()

        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: subscribeBem,
                    unsubscribe: unsubscribeBem,
                },
                tabbordion: {
                    getState: getSinglePanelTabbordionState,
                    subscribe: subscribeTabbordion,
                    unsubscribe: unsubscribeTabbordion,
                }
            }
        }

        const wrapper = mount(React.createElement(TabPanel), options)

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordion.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.false
        expect(unsubscribeTabbordion.calledOnce).to.be.false

        wrapper.unmount()

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordion.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.true
        expect(unsubscribeTabbordion.calledOnce).to.be.true
    })

    it('should generate BEM classes and other rendered props from given context', function() {
        const options = { context: singlePanelContext }

        const wrapper = shallow(React.createElement(TabPanel, { index: 0 }), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel')).to.be.true
        expect(wrapper.hasClass('panel--checked')).to.be.true
        expect(wrapper.hasClass('panel--enabled')).to.be.true
        expect(wrapper.hasClass('panel--first')).to.be.true
        expect(wrapper.hasClass('panel--last')).to.be.true
        expect(wrapper.hasClass('panel--no-content')).to.be.true

        expect(wrapper.prop('aria-expanded')).to.be.null
        expect(wrapper.prop('aria-selected')).to.equal('true')
        expect(wrapper.prop('role')).to.equal('tab')
        expect(wrapper.prop('style').display).to.be.null

        expect(wrapperInput.prop('aria-controls')).to.equal('one-0-content')
        expect(wrapperInput.prop('checked')).to.be.true
        expect(wrapperInput.prop('data-state')).to.equal('tabbordion')
        expect(wrapperInput.prop('disabled')).to.equal(false)
        expect(wrapperInput.prop('id')).to.equal('one-0')
        expect(wrapperInput.prop('name')).to.equal('test')
        expect(wrapperInput.prop('type')).to.equal('checkbox')
        expect(wrapperInput.prop('value')).to.equal('0')
    })

    it('should place other children always after the input box', function() {
        const options = { context: singlePanelContext }

        const div = React.createElement('div')
        const wrapper = shallow(React.createElement(TabPanel, { index: 0 }, div), options)

        expect(wrapper.childAt(1).equals(div)).be.true
    })

    it('should handle first/between/last BEM classes', function() {
        const options = { context: fourPanelContext }

        const panel0 = shallow(React.createElement(TabPanel, { index: 0 }), options)
        const panel1 = shallow(React.createElement(TabPanel, { index: 1 }), options)
        const panel2 = shallow(React.createElement(TabPanel, { index: 2 }), options)
        const panel3 = shallow(React.createElement(TabPanel, { index: 3 }), options)

        expect(panel0.hasClass('panel--first')).to.be.true
        expect(panel0.hasClass('panel--between')).to.be.false
        expect(panel0.hasClass('panel--last')).to.be.false

        expect(panel1.hasClass('panel--first')).to.be.false
        expect(panel1.hasClass('panel--between')).to.be.true
        expect(panel1.hasClass('panel--last')).to.be.false

        expect(panel2.hasClass('panel--first')).to.be.false
        expect(panel2.hasClass('panel--between')).to.be.true
        expect(panel2.hasClass('panel--last')).to.be.false

        expect(panel3.hasClass('panel--first')).to.be.false
        expect(panel3.hasClass('panel--between')).to.be.false
        expect(panel3.hasClass('panel--last')).to.be.true
    })

    it('should reflect disabled state', function() {
        const options = { context: fourPanelContext }

        const wrapper = shallow(React.createElement(TabPanel, { index: 3 }), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel--disabled')).to.be.true
        expect(wrapperInput.prop('disabled')).to.be.true
    })

    it('should allow props to override context', function() {
        const options = { context: singlePanelContext }
        const props = {
            checked: false,
            disabled: true,
            id: 'myself',
            index: 0,
            name: 'different',
            type: 'radio',
            value: 'best',
            visible: false,
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)
        const wrapperInput = wrapper.childAt(0)

        expect(wrapper.hasClass('panel--unchecked')).to.be.true
        expect(wrapper.hasClass('panel--disabled')).to.be.true
        expect(wrapper.prop('style').display).to.equal('none')

        expect(wrapperInput.prop('checked')).to.be.false
        expect(wrapperInput.prop('disabled')).to.equal(true)
        expect(wrapperInput.prop('id')).to.equal('myself')
        expect(wrapperInput.prop('name')).to.equal('different')
        expect(wrapperInput.prop('type')).to.equal('radio')
        expect(wrapperInput.prop('value')).to.equal('best')
    })

    it('should allow usage of custom BEM modifiers via props', function() {
        const options = { context: singlePanelContext }
        const props = {
            index: 0,
            modifiers: ['i-am-custom', 'i-am-too']
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)

        expect(wrapper.hasClass('panel--i-am-custom')).to.be.true
        expect(wrapper.hasClass('panel--i-am-too')).to.be.true
    })

    // this was a v0 feature that has been deprecated by modifiers prop
    it('should NOT extend custom BEM modifiers via className', function() {
        const options = { context: singlePanelContext }
        const props = {
            className: '--i-am-custom blergh --i-am-too',
            index: 0,
        }

        const wrapper = shallow(React.createElement(TabPanel, props), options)

        expect(wrapper.hasClass('panel--i-am-too')).to.be.false
        expect(wrapper.hasClass('--i-am-too')).to.be.true
        expect(wrapper.hasClass('panel--i-am-custom')).to.be.false
        expect(wrapper.hasClass('--i-am-custom')).to.be.true
        expect(wrapper.hasClass('blergh')).to.be.true
    })
})

function makeGetTabbordionPanelState(animateContent = false) {
    return function() {
        return {
            animateContent,
            checked: true,
            contentId: 'contentId',
            disabled: false,
            inputId: 'inputId',
            index: 0,
            modifiers: ['modifier'],
            visible: true,
        }
    }
}

describe('TabLabel', function() {
    it('should trigger context subscribes when mounted', function() {
        const subscribeBem = sinon.spy()
        const subscribeTabbordionPanel = sinon.spy()
        const unsubscribeBem = sinon.spy()
        const unsubscribeTabbordionPanel = sinon.spy()

        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: subscribeBem,
                    unsubscribe: unsubscribeBem,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState(),
                    subscribe: subscribeTabbordionPanel,
                    unsubscribe: unsubscribeTabbordionPanel,
                }
            }
        }

        const wrapper = mount(React.createElement(TabLabel), options)

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordionPanel.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.false
        expect(unsubscribeTabbordionPanel.calledOnce).to.be.false

        wrapper.unmount()

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordionPanel.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.true
        expect(unsubscribeTabbordionPanel.calledOnce).to.be.true
    })

    it('should trigger onClickLabel when clicked on', function() {
        const onClickLabel = sinon.spy()

        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState(),
                    onClickLabel,
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                }
            }
        }

        const wrapper = mount(React.createElement(
            TabLabel,
            null,
            React.createElement('span', null),
            React.createElement('span', { onClick: function(event) { event.preventDefault() } })
        ), options)

        wrapper.simulate('click')
        expect(onClickLabel.calledOnce).to.be.true

        wrapper.childAt(0).simulate('click')
        expect(onClickLabel.calledTwice).to.be.true

        wrapper.childAt(1).simulate('click')
        expect(onClickLabel.calledTwice).to.be.true
    })

    it('should generate BEM classes and other rendered props from given context', function() {
        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState(),
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                }
            }
        }

        const wrapper = shallow(React.createElement(TabLabel), options)

        expect(wrapper.hasClass('label')).to.be.true
        expect(wrapper.hasClass('label--modifier')).to.be.true
        expect(wrapper.prop('htmlFor')).to.equal('inputId')
    })
})

describe('TabContent', function() {
    it('should trigger context subscribes when mounted', function() {
        const subscribeBem = sinon.spy()
        const subscribeTabbordionPanel = sinon.spy()
        const unsubscribeBem = sinon.spy()
        const unsubscribeTabbordionPanel = sinon.spy()

        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: subscribeBem,
                    unsubscribe: unsubscribeBem,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState(),
                    subscribe: subscribeTabbordionPanel,
                    unsubscribe: unsubscribeTabbordionPanel,
                }
            }
        }

        const wrapper = mount(React.createElement(TabContent), options)

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordionPanel.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.false
        expect(unsubscribeTabbordionPanel.calledOnce).to.be.false

        wrapper.unmount()

        expect(subscribeBem.calledOnce).to.be.true
        expect(subscribeTabbordionPanel.calledOnce).to.be.true
        expect(unsubscribeBem.calledOnce).to.be.true
        expect(unsubscribeTabbordionPanel.calledOnce).to.be.true
    })

    it('should generate BEM classes and other rendered props from given context without animateContent', function() {
        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState(),
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                }
            }
        }

        const wrapper = shallow(React.createElement(TabContent), options)

        expect(wrapper.hasClass('content')).to.be.true
        expect(wrapper.hasClass('content--modifier')).to.be.true
        expect(wrapper.prop('id')).to.equal('contentId')
        expect(wrapper.prop('aria-labelledby')).to.equal('inputId')
        expect(wrapper.prop('role')).to.equal('tabpanel')
    })

    it('should generate BEM classes and other rendered props from given context WITH animateContent', function() {
        const options = {
            context: {
                bem: {
                    getState: getBemState,
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                },
                tabbordionPanel: {
                    getState: makeGetTabbordionPanelState('height'),
                    subscribe: NOOP,
                    unsubscribe: NOOP,
                }
            }
        }

        const wrapper = shallow(React.createElement(TabContent), options)
        const wrapperChild = wrapper.childAt(0)

        expect(wrapper.hasClass('animator')).to.be.true
        expect(wrapper.hasClass('animator--modifier')).to.be.true
        expect(wrapper.prop('id')).to.equal('contentId')
        expect(wrapper.prop('aria-labelledby')).to.equal('inputId')
        expect(wrapper.prop('role')).to.equal('tabpanel')
        expect(wrapper.prop('style').height).to.equal('auto')
        expect(wrapper.prop('style').overflow).to.equal('hidden')

        expect(wrapperChild.hasClass('content')).to.be.true
        expect(wrapperChild.hasClass('content--modifier')).to.be.true
    })
})
<|endoftext|>


<commit_before>module("jquery/model/associations",{
	setup: function() {

		$.Model("MyTest.Person");
		$.Model("MyTest.Loan");
		$.Model("MyTest.Issue");

		$.Model.extend("MyTest.Customer",
		{
			attributes : {
				person : "MyTest.Person.model",
				loans : "MyTest.Loan.models",
				issues : "MyTest.Issue.models"
			}
		},
		{});
	}
})

test("associations work", function(){
	var c = new MyTest.Customer({
		id: 5,
		person : {
			id: 1,
			name: "Justin"
		},
		issues : [],
		loans : [
			{
				amount : 1000,
				id: 2
			},
			{
				amount : 19999,
				id: 3
			}
		]
	})
	equals(c.person.name, "Justin", "association present");
	equals(c.person.Class, MyTest.Person, "belongs to association typed");

	equals(c.issues.length, 0);

	equals(c.loans.length, 2);

	equals(c.loans[0].Class, MyTest.Loan);
})<commit_msg>Added test for serializing a Model.List during save<commit_after>module("jquery/model/associations",{
	setup: function() {

		$.Model("MyTest.Person");
		$.Model("MyTest.Loan");
		$.Model("MyTest.Issue");

		$.Model.extend("MyTest.Customer",
		{
			attributes : {
				person : "MyTest.Person.model",
				loans : "MyTest.Loan.models",
				issues : "MyTest.Issue.models"
			},

			update : function(id, attrs, success, error){
				return $.ajax({
					url : "/people/"+id,
					data : attrs,
					type : 'post',
					dataType : "json",
					fixture: function(){
						return [{loansAttr: attrs.loans}]
					},
					success : success
				})
			}
		},
		{});
	}
})

test("associations work", function(){
	var c = new MyTest.Customer({
		id: 5,
		person : {
			id: 1,
			name: "Justin"
		},
		issues : [],
		loans : [
			{
				amount : 1000,
				id: 2
			},
			{
				amount : 19999,
				id: 3
			}
		]
	})
	equals(c.person.name, "Justin", "association present");
	equals(c.person.Class, MyTest.Person, "belongs to association typed");

	equals(c.issues.length, 0);

	equals(c.loans.length, 2);

	equals(c.loans[0].Class, MyTest.Loan);
})

test("Model.List association serialize on save", function(){
	var c = new MyTest.Customer({
		id: 5,
		person : {
			id: 1,
			name: "thecountofzero"
		},
		issues : [],
		loans : [
			{
				amount : 1000,
				id: 2
			},
			{
				amount : 19999,
				id: 3
			}
		]
	}),
	cSave = c.save();

	stop();
	cSave.then(function(customer){
		start()
		ok(customer.loansAttr._namespace === undefined, "_namespace does not exist");
		ok(customer.loansAttr._data === undefined, "_data does not exist");
		ok(customer.loansAttr._use_call === undefined, "_use_call does not exist");
		ok(customer.loansAttr._changed === undefined, "_changed does not exist");

	});

})<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');

const { cleanup, dir, file, filepath } = require('../lib');

// private

const exists = (tempX, check = fs.F_OK) => fs.accessSync(tempX.filepath, check);
const read = (tempFile) => fs.readFileSync(tempFile.filepath, { encoding: 'utf8' });

// tests

describe('temporarily', () => {

  describe('cleanup', () => {

    it('should cleanup dirs', () => {
      const tempDir1 = dir();
      const tempDir2 = dir();
      cleanup();
      expect(() => exists(tempDir1)).toThrow();
      expect(() => exists(tempDir2)).toThrow();
    });

    it('should not cleanup dirs it didnt create', () => {
      const customDirPath = path.join(os.tmpdir(), 'nested');
      fs.mkdirSync(customDirPath);
      dir({ dir: customDirPath });
      cleanup();
      exists({ filepath: customDirPath });
      fs.rmdirSync(customDirPath);
    });

    it('should cleanup files', () => {
      const tempFile1 = file();
      const tempFile2 = file();
      cleanup();
      expect(() => exists(tempFile1)).toThrow();
      expect(() => exists(tempFile2)).toThrow();
    });

  });

  describe('dir', () => {

    it('should create dir', () => {
      const tempDir = dir();
      exists(tempDir);
    });

    it('should create dir recursively', () => {
      const tempDir = dir({ name: '1/2/3' });
      exists(tempDir);
    });

    it('should create dir with mode', () => {
      const tempDir = dir({ mode: 0o666 });
      expect(() => exists(tempDir, fs.X_OK)).toThrow();
    });

    it('should create children', () => {
      const tempDir = dir({ name: 'tempo' }, [
        dir([
          file({ name: 'nestedFile' }),
        ]),
        file({ data: 'Hello World!' }),
      ]);
      expect(tempDir.filepath).toMatch(/\/tempo$/);
      expect(tempDir.children).toHaveLength(2);
      expect(tempDir.children[0].children).toHaveLength(1);
      expect(tempDir.children[0].children[0]).toMatchObject({
        filepath: expect.stringMatching(/\/nestedFile$/),
      });
      expect(read(tempDir.children[1])).toBe('Hello World!');
    });

  });

  describe('file', () => {

    it('should create file', () => {
      const tempFile = file();
      exists(tempFile);
    });

    it('should create file in nested dir', () => {
      const tempFile = file({ dir: '4/5/6' });
      exists(tempFile);
    });

    it('should create file with data', () => {
      const tempFile = file({ data: 'Hello World!' });
      expect(read(tempFile)).toBe('Hello World!');
    });

  });

  describe('filepath', () => {

    it('should return filepath in OS tempdir with default name', () => {
      expect(filepath()).toMatch(
        new RegExp(`${os.tmpdir()}${path.sep}temporarily-\\w+\\d+`, 'i')
      );
    });

    it('should return filepath in specified dir with default name', () => {
      expect(filepath({ dir: os.homedir() })).toMatch(
        new RegExp(`${os.homedir()}${path.sep}temporarily-\\w+\\d+`, 'i')
      );
    });

    it('should return filepath with custom name', () => {
      expect(filepath({ name: 'abc-{wdwd}' })).toMatch(
        /\/abc-\w\d\w\d$/
      );
    });

    it('should return filepath with custom extention', () => {
      expect(filepath({ ext: 'json' })).toMatch(
        /\.json$/
      );
    });

    it('should throw on illegal template character', () => {
      expect(() => filepath({ name: 'test-{abc}' })).toThrow();
    });

  });

});
<commit_msg>Fix local test to clean up<commit_after>'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');

const { cleanup, dir, file, filepath } = require('../lib');

// private

const exists = (tempX, check = fs.F_OK) => fs.accessSync(tempX.filepath, check);
const read = (tempFile) => fs.readFileSync(tempFile.filepath, { encoding: 'utf8' });

// tests

describe('temporarily', () => {

  describe('cleanup', () => {

    it('should cleanup dirs', () => {
      const tempDir1 = dir();
      const tempDir2 = dir();
      cleanup();
      expect(() => exists(tempDir1)).toThrow();
      expect(() => exists(tempDir2)).toThrow();
    });

    it('should not cleanup dirs it didnt create', () => {
      const customDirPath = path.join(os.tmpdir(), 'nested');
      fs.mkdirSync(customDirPath);
      dir({ dir: customDirPath });
      cleanup();
      exists({ filepath: customDirPath });
      fs.rmdirSync(customDirPath);
    });

    it('should cleanup files', () => {
      const tempFile1 = file();
      const tempFile2 = file();
      cleanup();
      expect(() => exists(tempFile1)).toThrow();
      expect(() => exists(tempFile2)).toThrow();
    });

  });

  describe('dir', () => {

    it('should create dir', () => {
      const tempDir = dir();
      exists(tempDir);
    });

    it('should create dir recursively', () => {
      const tempDir = dir({ name: '1/2/3' });
      exists(tempDir);
    });

    it('should create dir with mode', () => {
      const tempDir = dir({ mode: 0o666 });
      expect(() => exists(tempDir, fs.X_OK)).toThrow();
    });

    it('should create children', () => {
      const tempDir = dir({ name: 'tempo' }, [
        dir([
          file({ name: 'nestedFile' }),
        ]),
        file({ data: 'Hello World!' }),
      ]);
      expect(tempDir.filepath).toMatch(/\/tempo$/);
      expect(tempDir.children).toHaveLength(2);
      expect(tempDir.children[0].children).toHaveLength(1);
      expect(tempDir.children[0].children[0]).toMatchObject({
        filepath: expect.stringMatching(/\/nestedFile$/),
      });
      expect(read(tempDir.children[1])).toBe('Hello World!');
    });

  });

  describe('file', () => {

    it('should create file', () => {
      const tempFile = file();
      exists(tempFile);
    });

    it('should create file in nested dir', () => {
      const tempFile = file({ dir: '4/5/6' });
      exists(tempFile);
      cleanup();
    });

    it('should create file with data', () => {
      const tempFile = file({ data: 'Hello World!' });
      expect(read(tempFile)).toBe('Hello World!');
    });

  });

  describe('filepath', () => {

    it('should return filepath in OS tempdir with default name', () => {
      expect(filepath()).toMatch(
        new RegExp(`${os.tmpdir()}${path.sep}temporarily-\\w+\\d+`, 'i')
      );
    });

    it('should return filepath in specified dir with default name', () => {
      expect(filepath({ dir: os.homedir() })).toMatch(
        new RegExp(`${os.homedir()}${path.sep}temporarily-\\w+\\d+`, 'i')
      );
    });

    it('should return filepath with custom name', () => {
      expect(filepath({ name: 'abc-{wdwd}' })).toMatch(
        /\/abc-\w\d\w\d$/
      );
    });

    it('should return filepath with custom extention', () => {
      expect(filepath({ ext: 'json' })).toMatch(
        /\.json$/
      );
    });

    it('should throw on illegal template character', () => {
      expect(() => filepath({ name: 'test-{abc}' })).toThrow();
    });

  });

});
<|endoftext|>


<commit_before>(function ($) {
	var options = {
		sectionWrapperSelector: ".section-wrapper",
		sectionClass: "section",
		animationSpeed: 300,
		headerHash: "header"
	}
	$.smartscroll = function(overrides) {
		$.extend( options, overrides );

		// Mouse Scroll

		var scrolling = false;
		var slideCount = $('.' + options.sectionClass).length;

		var getCurrentSlideIndex = function (floor) {
			var slidePosition = -($('.' + options.sectionClass)[0].getBoundingClientRect().top / $(window).height());
			return floor ? Math.floor(slidePosition) : Math.round(slidePosition);
		}

		var scrollTo = function (slideIndex) {
			scrolling = true;
			var scrollTopVal = $(options.sectionWrapperSelector + ':first').position().top;
			var scrollSpeed = 0;
			if(slideIndex >= 0) {
				scrollTopVal += ( (slideIndex) * $(window).height());
				scrollSpeed = options.animationSpeed;
			} else {
				scrollTopVal -= 53;
			}
			$('body').animate({
		        scrollTop: scrollTopVal
		    }, scrollSpeed, function() {
		    	scrolling = false;
		    });
			return false;
		}

		var scrollUp = function () {
			scrollTo(getCurrentSlideIndex() - 1);
		};
		var scrollDown = function () {
			scrollTo(getCurrentSlideIndex() + 1);
		};
		$(window).bind('mousewheel DOMMouseScroll', function(e){
			if(!scrolling
				&& window.document.body.scrollTop >= $(options.sectionWrapperSelector + ':first').position().top
				) {
				e.preventDefault()
				e.stopPropagation();
				if(e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
		            scrollUp();
		        }
		        else {
		        	scrollDown();
		        }
			}
	    });
		$('.' + options.sectionClass).css({
			"overflow": "hidden",
			"height": $(window).height()
		});

		$(window).bind('resize', function(e){
			$('.' + options.sectionClass).css({
				"height": $(window).height()
			});
	    });

		// Hash

		var currentHash = window.location.hash;

		$(window).bind('scroll', function(e){
			var newHash = $('.' + options.sectionClass + ':nth-of-type(' + (getCurrentSlideIndex(true) + 1) + ')').data('hash');
			if(! (window.location.hash === newHash)) {
				if(typeof newHash === 'undefined') {
					window.location.hash = options.headerHash;
				} else {
					window.location.hash = newHash;
				}
			}
	    });
	}
}(jQuery));<commit_msg>Separated features and allow them to be individually turned on and off<commit_after>(function ($) {
	var options = {
		autoHash: true,
		sectionScroll: true,
		sectionWrapperSelector: ".section-wrapper",
		sectionClass: "section",
		animationSpeed: 300,
		headerHash: "header"
	}
	$.smartscroll = function(overrides) {
		$.extend( options, overrides );

		// Section styles
		$(window).bind('resize', function(e){
			$('.' + options.sectionClass).css({
				"height": $(window).height()
			});
	    });

		$('.' + options.sectionClass).css({
			"overflow": "hidden",
			"height": $(window).height()
		});

		var getCurrentSlideIndex = function (floor) {
			var slidePosition = -($('.' + options.sectionClass)[0].getBoundingClientRect().top / $(window).height());
			return floor ? Math.floor(slidePosition) : Math.round(slidePosition);
		};

		// Mouse Scroll
		if(options.sectionScroll) {
			var scrolling = false;
			var slideCount = $('.' + options.sectionClass).length;

			var scrollTo = function (slideIndex) {
				scrolling = true;
				var scrollTopVal = $(options.sectionWrapperSelector + ':first').position().top;
				var scrollSpeed = 0;
				if(slideIndex >= 0) {
					scrollTopVal += ( (slideIndex) * $(window).height());
					scrollSpeed = options.animationSpeed;
				} else {
					scrollTopVal -= 53;
				}
				$('body').animate({
			        scrollTop: scrollTopVal
			    }, scrollSpeed, function() {
			    	scrolling = false;
			    });
				return false;
			};
			var scrollUp = function () {
				scrollTo(getCurrentSlideIndex() - 1);
			};
			var scrollDown = function () {
				scrollTo(getCurrentSlideIndex() + 1);
			};
			$(window).bind('mousewheel DOMMouseScroll', function(e){
				if(!scrolling
					&& window.document.body.scrollTop >= $(options.sectionWrapperSelector + ':first').position().top
					) {
					e.preventDefault()
					e.stopPropagation();
					if(e.originalEvent.wheelDelta > 0 || e.originalEvent.detail < 0) {
			            scrollUp();
			        }
			        else {
			        	scrollDown();
			        }
				}
		    });
		}

		// Hash

		var currentHash = window.location.hash;
		if(options.autoHash) {
			$(window).bind('scroll', function(e){
				var newHash = $('.' + options.sectionClass + ':nth-of-type(' + (getCurrentSlideIndex(true) + 1) + ')').data('hash');
				if(! (window.location.hash === newHash)) {
					if(typeof newHash === 'undefined') {
						window.location.hash = options.headerHash;
					} else {
						window.location.hash = newHash;
					}
				}
		    });
		}
	}
}(jQuery));<|endoftext|>