<commit_before>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack Button application that adds a bot to one or many slack teams.

# RUN THE APP:
  Create a Slack app. Make sure to configure the bot user!
    -> https://api.slack.com/applications/new
    -> Add the Redirect URI: http://localhost:3000/oauth
  Run your bot from the command line:
    clientId=<my client id> clientSecret=<my client secret> port=3000 node slackbutton_bot.js
# USE THE APP
  Add the app to your Slack by visiting the login page:
    -> http://localhost:3000/login
  After you've added the app, try talking to your bot!
# EXTEND THE APP:
  Botkit has many features for building cool and useful bots!
  Read all about it here:
    -> http://howdy.ai/botkit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/* Uses the slack button feature to offer a real time bot to multiple teams */
var Botkit = require('../lib/Botkit.js');

if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
  console.log('Error: Specify clientId clientSecret and port in environment');
  process.exit(1);
}

var controller = Botkit.slackbot({
  // interactive_replies: true, // tells botkit to send button clicks into conversations
  json_file_store: './db_slackbutton_bot/',
}).configureSlackApp(
  {
    clientId: process.env.clientId,
    clientSecret: process.env.clientSecret,
    scopes: ['bot'],
  }
);

controller.setupWebserver(process.env.port,function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);

  controller.createOauthEndpoints(controller.webserver,function(err,req,res) {
    if (err) {
      res.status(500).send('ERROR: ' + err);
    } else {
      res.send('Success!');
    }
  });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};
function trackBot(bot) {
  _bots[bot.config.token] = bot;
}

controller.on('interactive_message_callback', function(bot, message) {

    var ids = message.callback_id.split(/\-/);
    var user_id = ids[0];
    var item_id = ids[1];

    controller.storage.users.get(user_id, function(err, user) {

        if (!user) {
            user = {
                id: user_id,
                list: []
            }
        }

        for (var x = 0; x < user.list.length; x++) {
            if (user.list[x].id == item_id) {
                if (message.actions[0].value=='flag') {
                    user.list[x].flagged = !user.list[x].flagged;
                }
                if (message.actions[0].value=='delete') {
                    user.list.splice(x,1);
                }
            }
        }

        var reply = {
            text: 'Here is <@' + user_id + '>s list:',
            attachments: [],
        }

        for (var x = 0; x < user.list.length; x++) {
            reply.attachments.push({
                title: user.list[x].text + (user.list[x].flagged? ' *FLAGGED*' : ''),
                callback_id: user_id + '-' + user.list[x].id,
                attachment_type: 'default',
                actions: [
                    {
                        "name":"flag",
                        "text": ":waving_black_flag: Flag",
                        "value": "flag",
                        "type": "button",
                    },
                    {
                       "text": "Delete",
                        "name": "delete",
                        "value": "delete",
                        "style": "danger",
                        "type": "button",
                        "confirm": {
                          "title": "Are you sure?",
                          "text": "This will do something!",
                          "ok_text": "Yes",
                          "dismiss_text": "No"
                        }
                    }
                ]
            })
        }

        bot.replyInteractive(message, reply);
        controller.storage.users.save(user);

    });

});

controller.on('create_bot',function(bot,config) {

  if (_bots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM(function(err) {

      if (!err) {
        trackBot(bot);
      }

      bot.startPrivateConversation({user: config.createdBy},function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          convo.say('I am a bot that has just joined your team');
          convo.say('You must now /invite me to a channel so that I can be of use!');
        }
      });

    });
  }

});

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
  console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
  console.log('** The RTM api just closed');
  // you may want to attempt to re-open
});

controller.hears(['add (.*)'],'direct_mention,direct_message',function(bot,message) {

    controller.storage.users.get(message.user, function(err, user) {

        if (!user) {
            user = {
                id: message.user,
                list: []
            }
        }

        user.list.push({
            id: message.ts,
            text: message.match[1],
        });

        bot.reply(message,'Added to list. Say `list` to view or manage list.');

        controller.storage.users.save(user);

    });
});

controller.hears(['list','tasks'],'direct_mention,direct_message',function(bot,message) {

    controller.storage.users.get(message.user, function(err, user) {

        if (!user) {
            user = {
                id: message.user,
                list: []
            }
        }

        if (!user.list || !user.list.length) {
            user.list = [
                {
                    'id': 1,
                    'text': 'Test Item 1'
                },
                {
                    'id': 2,
                    'text': 'Test Item 2'
                },
                {
                    'id': 3,
                    'text': 'Test Item 3'
                }
            ]
        }

        var reply = {
            text: 'Here is your list. Say `add <item>` to add items.',
            attachments: [],
        }

        for (var x = 0; x < user.list.length; x++) {
            reply.attachments.push({
                title: user.list[x].text + (user.list[x].flagged? ' *FLAGGED*' : ''),
                callback_id: message.user + '-' + user.list[x].id,
                attachment_type: 'default',
                actions: [
                    {
                        "name":"flag",
                        "text": ":waving_black_flag: Flag",
                        "value": "flag",
                        "type": "button",
                    },
                    {
                       "text": "Delete",
                        "name": "delete",
                        "value": "delete",
                        "style": "danger",
                        "type": "button",
                        "confirm": {
                          "title": "Are you sure?",
                          "text": "This will do something!",
                          "ok_text": "Yes",
                          "dismiss_text": "No"
                        }
                    }
                ]
            })
        }

        bot.reply(message, reply);

        controller.storage.users.save(user);

    });

});

controller.hears('interactive', 'direct_message', function(bot, message) {

    bot.reply(message, {
        attachments:[
            {
                title: 'Do you want to interact with my buttons?',
                callback_id: '123',
                attachment_type: 'default',
                actions: [
                    {
                        "name":"yes",
                        "text": "Yes",
                        "value": "yes",
                        "type": "button",
                    },
                    {
                        "name":"no",
                        "text": "No",
                        "value": "no",
                        "type": "button",
                    }
                ]
            }
        ]
    });
});

controller.hears('^stop','direct_message',function(bot,message) {
  bot.reply(message,'Goodbye');
  bot.rtm.close();
});

controller.on(['direct_message','mention','direct_mention'],function(bot,message) {
  bot.api.reactions.add({
    timestamp: message.ts,
    channel: message.channel,
    name: 'robot_face',
  },function(err) {
    if (err) { console.log(err) }
    bot.reply(message,'I heard you loud and clear boss.');
  });
});

controller.storage.teams.all(function(err,teams) {

  if (err) {
    throw new Error(err);
  }

  // connect all teams with bots up to slack!
  for (var t  in teams) {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM(function(err, bot) {
        if (err) {
          console.log('Error connecting bot to Slack:',err);
        } else {
          trackBot(bot);
        }
      });
    }
  }

});
<commit_msg>Adjust the example command-line call to use the new filename.<commit_after>/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           ______     ______     ______   __  __     __     ______
          /\  == \   /\  __ \   /\__  _\ /\ \/ /    /\ \   /\__  _\
          \ \  __<   \ \ \/\ \  \/_/\ \/ \ \  _"-.  \ \ \  \/_/\ \/
           \ \_____\  \ \_____\    \ \_\  \ \_\ \_\  \ \_\    \ \_\
            \/_____/   \/_____/     \/_/   \/_/\/_/   \/_/     \/_/

This is a sample Slack Button application that adds a bot to one or many slack teams.

# RUN THE APP:
  Create a Slack app. Make sure to configure the bot user!
    -> https://api.slack.com/applications/new
    -> Add the Redirect URI: http://localhost:3000/oauth
  Run your bot from the command line:
    clientId=<my client id> clientSecret=<my client secret> port=3000 node slackbutton_bot_interactivemsg.js
# USE THE APP
  Add the app to your Slack by visiting the login page:
    -> http://localhost:3000/login
  After you've added the app, try talking to your bot!
# EXTEND THE APP:
  Botkit has many features for building cool and useful bots!
  Read all about it here:
    -> http://howdy.ai/botkit
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

/* Uses the slack button feature to offer a real time bot to multiple teams */
var Botkit = require('../lib/Botkit.js');

if (!process.env.clientId || !process.env.clientSecret || !process.env.port) {
  console.log('Error: Specify clientId clientSecret and port in environment');
  process.exit(1);
}

var controller = Botkit.slackbot({
  // interactive_replies: true, // tells botkit to send button clicks into conversations
  json_file_store: './db_slackbutton_bot/',
}).configureSlackApp(
  {
    clientId: process.env.clientId,
    clientSecret: process.env.clientSecret,
    scopes: ['bot'],
  }
);

controller.setupWebserver(process.env.port,function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);

  controller.createOauthEndpoints(controller.webserver,function(err,req,res) {
    if (err) {
      res.status(500).send('ERROR: ' + err);
    } else {
      res.send('Success!');
    }
  });
});

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};
function trackBot(bot) {
  _bots[bot.config.token] = bot;
}

controller.on('interactive_message_callback', function(bot, message) {

    var ids = message.callback_id.split(/\-/);
    var user_id = ids[0];
    var item_id = ids[1];

    controller.storage.users.get(user_id, function(err, user) {

        if (!user) {
            user = {
                id: user_id,
                list: []
            }
        }

        for (var x = 0; x < user.list.length; x++) {
            if (user.list[x].id == item_id) {
                if (message.actions[0].value=='flag') {
                    user.list[x].flagged = !user.list[x].flagged;
                }
                if (message.actions[0].value=='delete') {
                    user.list.splice(x,1);
                }
            }
        }

        var reply = {
            text: 'Here is <@' + user_id + '>s list:',
            attachments: [],
        }

        for (var x = 0; x < user.list.length; x++) {
            reply.attachments.push({
                title: user.list[x].text + (user.list[x].flagged? ' *FLAGGED*' : ''),
                callback_id: user_id + '-' + user.list[x].id,
                attachment_type: 'default',
                actions: [
                    {
                        "name":"flag",
                        "text": ":waving_black_flag: Flag",
                        "value": "flag",
                        "type": "button",
                    },
                    {
                       "text": "Delete",
                        "name": "delete",
                        "value": "delete",
                        "style": "danger",
                        "type": "button",
                        "confirm": {
                          "title": "Are you sure?",
                          "text": "This will do something!",
                          "ok_text": "Yes",
                          "dismiss_text": "No"
                        }
                    }
                ]
            })
        }

        bot.replyInteractive(message, reply);
        controller.storage.users.save(user);

    });

});

controller.on('create_bot',function(bot,config) {

  if (_bots[bot.config.token]) {
    // already online! do nothing.
  } else {
    bot.startRTM(function(err) {

      if (!err) {
        trackBot(bot);
      }

      bot.startPrivateConversation({user: config.createdBy},function(err,convo) {
        if (err) {
          console.log(err);
        } else {
          convo.say('I am a bot that has just joined your team');
          convo.say('You must now /invite me to a channel so that I can be of use!');
        }
      });

    });
  }

});

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
  console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
  console.log('** The RTM api just closed');
  // you may want to attempt to re-open
});

controller.hears(['add (.*)'],'direct_mention,direct_message',function(bot,message) {

    controller.storage.users.get(message.user, function(err, user) {

        if (!user) {
            user = {
                id: message.user,
                list: []
            }
        }

        user.list.push({
            id: message.ts,
            text: message.match[1],
        });

        bot.reply(message,'Added to list. Say `list` to view or manage list.');

        controller.storage.users.save(user);

    });
});

controller.hears(['list','tasks'],'direct_mention,direct_message',function(bot,message) {

    controller.storage.users.get(message.user, function(err, user) {

        if (!user) {
            user = {
                id: message.user,
                list: []
            }
        }

        if (!user.list || !user.list.length) {
            user.list = [
                {
                    'id': 1,
                    'text': 'Test Item 1'
                },
                {
                    'id': 2,
                    'text': 'Test Item 2'
                },
                {
                    'id': 3,
                    'text': 'Test Item 3'
                }
            ]
        }

        var reply = {
            text: 'Here is your list. Say `add <item>` to add items.',
            attachments: [],
        }

        for (var x = 0; x < user.list.length; x++) {
            reply.attachments.push({
                title: user.list[x].text + (user.list[x].flagged? ' *FLAGGED*' : ''),
                callback_id: message.user + '-' + user.list[x].id,
                attachment_type: 'default',
                actions: [
                    {
                        "name":"flag",
                        "text": ":waving_black_flag: Flag",
                        "value": "flag",
                        "type": "button",
                    },
                    {
                       "text": "Delete",
                        "name": "delete",
                        "value": "delete",
                        "style": "danger",
                        "type": "button",
                        "confirm": {
                          "title": "Are you sure?",
                          "text": "This will do something!",
                          "ok_text": "Yes",
                          "dismiss_text": "No"
                        }
                    }
                ]
            })
        }

        bot.reply(message, reply);

        controller.storage.users.save(user);

    });

});

controller.hears('interactive', 'direct_message', function(bot, message) {

    bot.reply(message, {
        attachments:[
            {
                title: 'Do you want to interact with my buttons?',
                callback_id: '123',
                attachment_type: 'default',
                actions: [
                    {
                        "name":"yes",
                        "text": "Yes",
                        "value": "yes",
                        "type": "button",
                    },
                    {
                        "name":"no",
                        "text": "No",
                        "value": "no",
                        "type": "button",
                    }
                ]
            }
        ]
    });
});

controller.hears('^stop','direct_message',function(bot,message) {
  bot.reply(message,'Goodbye');
  bot.rtm.close();
});

controller.on(['direct_message','mention','direct_mention'],function(bot,message) {
  bot.api.reactions.add({
    timestamp: message.ts,
    channel: message.channel,
    name: 'robot_face',
  },function(err) {
    if (err) { console.log(err) }
    bot.reply(message,'I heard you loud and clear boss.');
  });
});

controller.storage.teams.all(function(err,teams) {

  if (err) {
    throw new Error(err);
  }

  // connect all teams with bots up to slack!
  for (var t  in teams) {
    if (teams[t].bot) {
      controller.spawn(teams[t]).startRTM(function(err, bot) {
        if (err) {
          console.log('Error connecting bot to Slack:',err);
        } else {
          trackBot(bot);
        }
      });
    }
  }

});
<|endoftext|>


<commit_before>const path = require('path');
const assert = require('yeoman-assert');
const helpers = require('yeoman-test');
const ClientGenerator = require('../../generators/client');
const constants = require('../../generators/generator-constants');

const CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR;

const mockBlueprintSubGen = class extends ClientGenerator {
    constructor(args, opts) {
        super(args, { fromBlueprint: true, ...opts }); // fromBlueprint variable is important

        const jhContext = (this.jhipsterContext = this.options.jhipsterContext);

        if (!jhContext) {
            this.error('This is a JHipster blueprint and should be used only like jhipster --blueprint myblueprint');
        }

        this.configOptions = jhContext.configOptions || {};
        // This sets up options for this sub generator and is being reused from JHipster
        jhContext.setupEntityOptions(this, jhContext, this);
    }

    get initializing() {
        return super._initializing();
    }

    get prompting() {
        return super._prompting();
    }

    get configuring() {
        return super._configuring();
    }

    get default() {
        return super._default();
    }

    get writing() {
        const phaseFromJHipster = super._writing();
        const customPhaseSteps = {
            addAppCssStep() {
                // please change this to public API when it will be available see https://github.com/jhipster/generator-jhipster/issues/9234
                this.addAppSCSSStyle('@import without-comment');
                this.addAppSCSSStyle('@import with-comment', 'my comment');
            },
            addEntityToMenuStep() {
                this.addEntityToMenu('routerName', false, 'react', false);
            },
            addEntityToModuleStep() {
                this.addEntityToModule(
                    'entityInstance',
                    'entityClass',
                    'entityName',
                    'entityFolderName',
                    'entityFileName',
                    'entityUrl',
                    'react',
                    'microServiceNam'
                );
            }
        };
        return { ...phaseFromJHipster, ...customPhaseSteps };
    }
};

describe('needle API React: JHipster client generator with blueprint', () => {
    before(done => {
        helpers
            .run(path.join(__dirname, '../../generators/client'))
            .withOptions({
                'from-cli': true,
                build: 'maven',
                auth: 'jwt',
                db: 'mysql',
                skipInstall: true,
                blueprint: 'myblueprint',
                skipChecks: true
            })
            .withGenerators([[mockBlueprintSubGen, 'jhipster-myblueprint:client']])
            .withPrompts({
                baseName: 'jhipster',
                clientFramework: 'react',
                enableTranslation: true,
                nativeLanguage: 'en',
                languages: ['en', 'fr']
            })
            .on('end', done);
    });

    it('Assert entity is added to menu', () => {
        assert.fileContent(
            `${CLIENT_MAIN_SRC_DIR}app/shared/layout/menus/entities.tsx`,
            '<MenuItem icon="asterisk" to="/entity/routerName">\n      Router Name\n    </MenuItem>'
        );
    });

    it('Assert entity is added to module', () => {
        const indexModulePath = `${CLIENT_MAIN_SRC_DIR}app/entities/index.tsx`;
        const indexReducerPath = `${CLIENT_MAIN_SRC_DIR}app/shared/reducers/index.ts`;

        assert.fileContent(indexModulePath, "import entityName from './entityFolderName';");
        assert.fileContent(indexModulePath, '<ErrorBoundaryRoute path={`${match.url}/entityFileName`} component={entityName} />'); // eslint-disable-line

        assert.fileContent(
            indexReducerPath,
            '// prettier-ignore\n' +
                'import entityInstance, {\n' +
                '  entityNameState\n' +
                "} from 'app/entities/entityFolderName/entityFileName.reducer';"
        );
        assert.fileContent(indexReducerPath, 'readonly entityInstance: entityNameState;');
        assert.fileContent(indexReducerPath, 'entityInstance,');
    });

    it('Assert app.scss is updated', () => {
        assert.fileContent(`${CLIENT_MAIN_SRC_DIR}app/app.scss`, '@import without-comment');
        assert.fileContent(`${CLIENT_MAIN_SRC_DIR}app/app.scss`, '@import with-comment');
        assert.fileContent(
            `${CLIENT_MAIN_SRC_DIR}app/app.scss`,
            '* ==========================================================================\n' +
                'my comment\n' +
                '========================================================================== */\n'
        );
    });
});
<commit_msg>fix npm tests<commit_after>const path = require('path');
const assert = require('yeoman-assert');
const helpers = require('yeoman-test');
const ClientGenerator = require('../../generators/client');
const constants = require('../../generators/generator-constants');

const CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR;

const mockBlueprintSubGen = class extends ClientGenerator {
    constructor(args, opts) {
        super(args, { fromBlueprint: true, ...opts }); // fromBlueprint variable is important

        const jhContext = (this.jhipsterContext = this.options.jhipsterContext);

        if (!jhContext) {
            this.error('This is a JHipster blueprint and should be used only like jhipster --blueprint myblueprint');
        }

        this.configOptions = jhContext.configOptions || {};
        // This sets up options for this sub generator and is being reused from JHipster
        jhContext.setupEntityOptions(this, jhContext, this);
    }

    get initializing() {
        return super._initializing();
    }

    get prompting() {
        return super._prompting();
    }

    get configuring() {
        return super._configuring();
    }

    get default() {
        return super._default();
    }

    get writing() {
        const phaseFromJHipster = super._writing();
        const customPhaseSteps = {
            addAppCssStep() {
                // please change this to public API when it will be available see https://github.com/jhipster/generator-jhipster/issues/9234
                this.addAppSCSSStyle('@import without-comment');
                this.addAppSCSSStyle('@import with-comment', 'my comment');
            },
            addEntityToMenuStep() {
                this.addEntityToMenu('routerName', false, 'react', false);
            },
            addEntityToModuleStep() {
                this.addEntityToModule(
                    'entityInstance',
                    'entityClass',
                    'entityName',
                    'entityFolderName',
                    'entityFileName',
                    'entityUrl',
                    'react',
                    'microServiceNam'
                );
            }
        };
        return { ...phaseFromJHipster, ...customPhaseSteps };
    }
};

describe('needle API React: JHipster client generator with blueprint', () => {
    before(done => {
        helpers
            .run(path.join(__dirname, '../../generators/client'))
            .withOptions({
                'from-cli': true,
                build: 'maven',
                auth: 'jwt',
                db: 'mysql',
                skipInstall: true,
                blueprint: 'myblueprint',
                skipChecks: true
            })
            .withGenerators([[mockBlueprintSubGen, 'jhipster-myblueprint:client']])
            .withPrompts({
                baseName: 'jhipster',
                clientFramework: 'react',
                enableTranslation: true,
                nativeLanguage: 'en',
                languages: ['en', 'fr']
            })
            .on('end', done);
    });

    it('Assert entity is added to menu', () => {
        assert.fileContent(
            `${CLIENT_MAIN_SRC_DIR}app/shared/layout/menus/entities.tsx`,
            '<MenuItem icon="asterisk" to="/routerName">\n      Router Name\n    </MenuItem>'
        );
    });

    it('Assert entity is added to module', () => {
        const indexModulePath = `${CLIENT_MAIN_SRC_DIR}app/entities/index.tsx`;
        const indexReducerPath = `${CLIENT_MAIN_SRC_DIR}app/shared/reducers/index.ts`;

        assert.fileContent(indexModulePath, "import entityName from './entityFolderName';");
        assert.fileContent(indexModulePath, '<ErrorBoundaryRoute path={`${match.url}entityFileName`} component={entityName} />'); // eslint-disable-line

        assert.fileContent(
            indexReducerPath,
            '// prettier-ignore\n' +
                'import entityInstance, {\n' +
                '  entityNameState\n' +
                "} from 'app/entities/entityFolderName/entityFileName.reducer';"
        );
        assert.fileContent(indexReducerPath, 'readonly entityInstance: entityNameState;');
        assert.fileContent(indexReducerPath, 'entityInstance,');
    });

    it('Assert app.scss is updated', () => {
        assert.fileContent(`${CLIENT_MAIN_SRC_DIR}app/app.scss`, '@import without-comment');
        assert.fileContent(`${CLIENT_MAIN_SRC_DIR}app/app.scss`, '@import with-comment');
        assert.fileContent(
            `${CLIENT_MAIN_SRC_DIR}app/app.scss`,
            '* ==========================================================================\n' +
                'my comment\n' +
                '========================================================================== */\n'
        );
    });
});
<|endoftext|>


<commit_before>var assert = require("assert");

var vumigo = require("../lib");
var test_utils = vumigo.test_utils;
var App = vumigo.App;
var State = vumigo.states.State;
var StateError = vumigo.states.StateError;

describe("AppStates", function () {
    var im;
    var app;
    var states;

    beforeEach(function() {
        return test_utils.make_im().then(function(new_im) {
            im = new_im;
            app = im.app;
            states = app.states;
        });
    });

    describe(".setup", function() {
        beforeEach(function() {
            var p = test_utils.make_im({setup: false});
            return p.then(function(new_im) {
                im = new_im;
                app = im.app;
                states = app.states;
            });
        });

        it("should emit a 'setup' event", function() {
            var p = app.once.resolved('setup');
            states.setup().thenResolve(p);
        });
    });

    describe(".add", function() {
        describe("if a state is given", function() {
            it("should add a creator for the state", function() {
                var state = new State('spam');
                states.add(state);
                assert.strictEqual(states.creators.spam(), state);
            });
        });

        describe("if a state creator is given", function() {
            it("should add the creator", function() {
                function f() {}
                states.add('spam', f);
                assert.strictEqual(states.creators.spam, f);
            });
        });
    });

    describe(".add.creator", function() {
        it("should add the creator", function() {
            function f() {}
            states.add.creator('spam', f);
            assert.strictEqual(states.creators.spam, f);
        });

        describe("if the creator already exists", function() {
            it("should throw an error", function() {
                function f() {}
                function g() {}

                states.add.creator('spam', f);
                assert.throws(function() {
                    states.add.creator('spam', g);
                }, StateError);
            });
        });
    });

    describe(".add.state", function() {
        it("should add a 'functor' state creator with the given state",
        function() {
            var state = new State('spam');
            states.add(state);
            assert.strictEqual(states.creators.spam(), state);
        });
    });

    describe(".create", function() {
        it("should invoke the creator associated with the request state",
        function() {
            var state = new State('spam');
            states.add(state);
            return states.create('spam').then(function(new_state) {
                assert.strictEqual(new_state, state);
            });
        });

        describe("if the state does not exist", function() {
            var start_state;

            beforeEach(function() {
                start_state = new State('start');
                states.add(start_state);
            });

            it("should create the start state", function() {
                return states.create('spam').then(function(new_state) {
                    assert.strictEqual(new_state, start_state);
                });
            });

            it("should log a message", function() {
                var msg = [
                    "Unknown state 'spam'.",
                    "Switching to start state 'start'."].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('spam').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            describe("if the start state does not exist", function() {
                beforeEach(function() {
                    states.remove('start');
                });

                it("should log a message", function() {
                    var msg = [
                        "Unknown start state 'start'.",
                        "Switching to error state."].join(' ');

                    assert(!im.api.in_logs(msg));
                    return states.create('spam').then(function(new_state) {
                        assert(im.api.in_logs(msg));
                    });
                });

                it("should create the error state", function() {
                    return states.create('spam').then(function(new_state) {
                        assert.equal(
                            new_state.name,
                            '__error__');

                        assert.equal(
                            new_state.end_text,
                            'An error occurred. Please try again later.');
                    });
                });
            });
        });

        describe("if the created state is not a State instance", function() {
            beforeEach(function() {
                states.add('bad', function() {
                    return 7;
                });
            });

            it("should log an error", function() {
                var msg = [
                    "Creator for state 'bad' should create a state,",
                    "but instead created something of type 'number'"
                ].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('bad').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            it("should create the error state", function() {
                return states.create('bad').then(function(new_state) {
                    assert.equal(
                        new_state.name,
                        '__error__');

                    assert.equal(
                        new_state.end_text,
                        'An error occurred. Please try again later.');
                });
            });
        });

        describe("if the state has the wrong name", function() {
            beforeEach(function() {
                states.add('bad', function() {
                    return new State('badder');
                });
            });

            it("should log an error", function() {
                var msg = [
                    "Creator for state 'bad' created a state with",
                    "a different name: 'badder'"
                ].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('bad').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            it("should create the error state", function() {
                return states.create('bad').then(function(new_state) {
                    assert.equal(
                        new_state.name,
                        '__error__');

                    assert.equal(
                        new_state.end_text,
                        'An error occurred. Please try again later.');
                });
            });
        });
    });
});

describe("App", function () {
    var im;
    var app;

    beforeEach(function() {
        return test_utils.make_im().then(function(new_im) {
            im = new_im;
            app = im.app;
        });
    });

    describe(".setup", function() {
        beforeEach(function() {
            var p = test_utils.make_im({setup: false});
            return p.then(function(new_im) {
                im = new_im;
                app = im.app;
            });
        });

        it("should setup its states set", function() {
            var p = app.states.once.resolved('setup');
            app.setup(im).thenResolve(p);
        });

        it("should emit a 'setup' event", function() {
            var p = app.once.resolved('setup');
            app.setup(im).thenResolve(p);
        });
    });
});
<commit_msg>Add missing `return`s in tests<commit_after>var assert = require("assert");

var vumigo = require("../lib");
var test_utils = vumigo.test_utils;
var App = vumigo.App;
var State = vumigo.states.State;
var StateError = vumigo.states.StateError;

describe("AppStates", function () {
    var im;
    var app;
    var states;

    beforeEach(function() {
        return test_utils.make_im().then(function(new_im) {
            im = new_im;
            app = im.app;
            states = app.states;
        });
    });

    describe(".setup", function() {
        beforeEach(function() {
            var p = test_utils.make_im({setup: false});
            return p.then(function(new_im) {
                im = new_im;
                app = im.app;
                states = app.states;
            });
        });

        it("should emit a 'setup' event", function() {
            var p = app.once.resolved('setup');
            states.setup().thenResolve(p);
        });
    });

    describe(".add", function() {
        describe("if a state is given", function() {
            it("should add a creator for the state", function() {
                var state = new State('spam');
                states.add(state);
                assert.strictEqual(states.creators.spam(), state);
            });
        });

        describe("if a state creator is given", function() {
            it("should add the creator", function() {
                function f() {}
                states.add('spam', f);
                assert.strictEqual(states.creators.spam, f);
            });
        });
    });

    describe(".add.creator", function() {
        it("should add the creator", function() {
            function f() {}
            states.add.creator('spam', f);
            assert.strictEqual(states.creators.spam, f);
        });

        describe("if the creator already exists", function() {
            it("should throw an error", function() {
                function f() {}
                function g() {}

                states.add.creator('spam', f);
                assert.throws(function() {
                    states.add.creator('spam', g);
                }, StateError);
            });
        });
    });

    describe(".add.state", function() {
        it("should add a 'functor' state creator with the given state",
        function() {
            var state = new State('spam');
            states.add(state);
            assert.strictEqual(states.creators.spam(), state);
        });
    });

    describe(".create", function() {
        it("should invoke the creator associated with the request state",
        function() {
            var state = new State('spam');
            states.add(state);
            return states.create('spam').then(function(new_state) {
                assert.strictEqual(new_state, state);
            });
        });

        describe("if the state does not exist", function() {
            var start_state;

            beforeEach(function() {
                start_state = new State('start');
                states.add(start_state);
            });

            it("should create the start state", function() {
                return states.create('spam').then(function(new_state) {
                    assert.strictEqual(new_state, start_state);
                });
            });

            it("should log a message", function() {
                var msg = [
                    "Unknown state 'spam'.",
                    "Switching to start state 'start'."].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('spam').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            describe("if the start state does not exist", function() {
                beforeEach(function() {
                    states.remove('start');
                });

                it("should log a message", function() {
                    var msg = [
                        "Unknown start state 'start'.",
                        "Switching to error state."].join(' ');

                    assert(!im.api.in_logs(msg));
                    return states.create('spam').then(function(new_state) {
                        assert(im.api.in_logs(msg));
                    });
                });

                it("should create the error state", function() {
                    return states.create('spam').then(function(new_state) {
                        assert.equal(
                            new_state.name,
                            '__error__');

                        assert.equal(
                            new_state.end_text,
                            'An error occurred. Please try again later.');
                    });
                });
            });
        });

        describe("if the created state is not a State instance", function() {
            beforeEach(function() {
                states.add('bad', function() {
                    return 7;
                });
            });

            it("should log an error", function() {
                var msg = [
                    "Creator for state 'bad' should create a state,",
                    "but instead created something of type 'number'"
                ].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('bad').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            it("should create the error state", function() {
                return states.create('bad').then(function(new_state) {
                    assert.equal(
                        new_state.name,
                        '__error__');

                    assert.equal(
                        new_state.end_text,
                        'An error occurred. Please try again later.');
                });
            });
        });

        describe("if the state has the wrong name", function() {
            beforeEach(function() {
                states.add('bad', function() {
                    return new State('badder');
                });
            });

            it("should log an error", function() {
                var msg = [
                    "Creator for state 'bad' created a state with",
                    "a different name: 'badder'"
                ].join(' ');

                assert(!im.api.in_logs(msg));
                return states.create('bad').then(function(new_state) {
                    assert(im.api.in_logs(msg));
                });
            });

            it("should create the error state", function() {
                return states.create('bad').then(function(new_state) {
                    assert.equal(
                        new_state.name,
                        '__error__');

                    assert.equal(
                        new_state.end_text,
                        'An error occurred. Please try again later.');
                });
            });
        });
    });
});

describe("App", function () {
    var im;
    var app;

    beforeEach(function() {
        return test_utils.make_im().then(function(new_im) {
            im = new_im;
            app = im.app;
        });
    });

    describe(".setup", function() {
        beforeEach(function() {
            var p = test_utils.make_im({setup: false});
            return p.then(function(new_im) {
                im = new_im;
                app = im.app;
            });
        });

        it("should setup its states set", function() {
            var p = app.states.once.resolved('setup');
            return app.setup(im).thenResolve(p);
        });

        it("should emit a 'setup' event", function() {
            var p = app.once.resolved('setup');
            return app.setup(im).thenResolve(p);
        });
    });
});
<|endoftext|>


<commit_before>/*
    ***** BEGIN LICENSE BLOCK *****

    Copyright © 2009 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org

    This file is part of Zotero.

    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.

    ***** END LICENSE BLOCK *****
*/

Components.utils.import("resource://gre/modules/Services.jsm");

/**
 * This object contains the various functions for the interface
 */
const ZoteroStandalone = new function() {
	/**
	 * Run when standalone window first opens
	 */
	this.onLoad = function() {
		if(!Zotero || !Zotero.initialized) {
			ZoteroPane.displayStartupError();
			window.close();
			return;
		}
		ZoteroPane.init();
		ZoteroPane.makeVisible();

		// Run check for corrupt installation, where the wrong Gecko runtime is being used
		if(Zotero.isMac && Zotero.isStandalone) {
			 var greDir = Components.classes["@mozilla.org/file/directory_service;1"]
				.getService(Components.interfaces.nsIProperties)
				.get("GreD", Components.interfaces.nsIFile);
			if(greDir.isSymlink() || greDir.leafName !== "Current") {
				var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
										.getService(Components.interfaces.nsIPromptService);
				ps.alert(null, "", Zotero.getString('standalone.corruptInstallation'));
			}
		}

		// Don't ask before handing http and https URIs
		var eps = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
				.getService(Components.interfaces.nsIExternalProtocolService);
		var hs = Components.classes["@mozilla.org/uriloader/handler-service;1"]
				.getService(Components.interfaces.nsIHandlerService);
		for each(var scheme in ["http", "https"]) {
			var handlerInfo = eps.getProtocolHandlerInfo(scheme);
			handlerInfo.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
			handlerInfo.alwaysAskBeforeHandling = false;
			hs.store(handlerInfo);
		}

		// Add add-on listeners (not yet hooked up)
		Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
	}

	/**
	 * Builds new item menu
	 */
	this.buildNewItemMenu = function() {
		var addMenu = document.getElementById('menu_NewItemPopup');

		// Remove all nodes so we can regenerate
		while(addMenu.hasChildNodes()) addMenu.removeChild(addMenu.firstChild);

		var typeSets = [Zotero.ItemTypes.getPrimaryTypes(), Zotero.ItemTypes.getSecondaryTypes()];
		for(var j=0; j<typeSets.length; j++) {
			var t = typeSets[j];

			// Sort by localized name
			var itemTypes = [];
			for (var i=0; i<t.length; i++) {
				itemTypes.push({
					id: t[i].id,
					name: t[i].name,
					localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
				});
			}
			var collation = Zotero.getLocaleCollation();
			itemTypes.sort(function(a, b) {
				return collation.compareString(1, a.localized, b.localized);
			});

			for (var i = 0; i<itemTypes.length; i++) {
				var menuitem = document.createElement("menuitem");
				menuitem.setAttribute("label", itemTypes[i].localized);
				menuitem.setAttribute("tooltiptext", "");
				let type = itemTypes[i].id;
				menuitem.addEventListener("command", function() { ZoteroPane_Local.newItem(type); }, false);
				menuitem.className = "zotero-tb-add";
				addMenu.appendChild(menuitem);
			}

			// add separator between sets
			if(j !== typeSets.length-1) {
				addMenu.appendChild(document.createElement("menuseparator"));
			}
		}
	}

	/**
	 * Opens a URL in the basic viewer
	 */
	this.openInViewer = function(uri) {
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
			.getService(Components.interfaces.nsIWindowMediator);
		var win = wm.getMostRecentWindow("zotero:basicViewer");
		if(win) {
			win.loadURI(uri);
		} else {
			window.openDialog("chrome://zotero/content/standalone/basicViewer.xul",
				"basicViewer", "chrome,resizable,centerscreen,menubar,scrollbars", uri);
		}
	}

	/**
	 * Handles help menu requests
	 */
	this.openHelp = function(type) {
		if(type === "troubleshooting") {
			ZoteroPane.loadURI("http://www.zotero.org/support/getting_help");
		} else if(type === "feedback") {
			ZoteroPane.loadURI("http://forums.zotero.org/categories/");
		} else {
			ZoteroPane.loadURI("http://www.zotero.org/support/");
		}
	}

	/**
	 * Checks for updates
	 */
	this.checkForUpdates = function() {
		window.open('chrome://mozapps/content/update/updates.xul', 'updateChecker', 'chrome,centerscreen');
	}

	/**
	 * Called before standalone window is closed
	 */
	this.onUnload = function() {
		ZoteroPane.destroy();
	}
}

/** Taken from browser.js **/
function toJavaScriptConsole() {
	toOpenWindowByType("global:console", "chrome://global/content/console.xul");
}

function toOpenWindowByType(inType, uri, features)
{
	var topWindow = Services.wm.getMostRecentWindow(inType);

	if (topWindow) {
		topWindow.focus();
	} else if(features) {
		window.open(uri, "_blank", features);
	} else {
		window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
	}
}

const gXPInstallObserver = {
	observe: function (aSubject, aTopic, aData) {
		var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
		var win = installInfo.originatingWindow;
		switch (aTopic) {
			case "addon-install-disabled":
			case "addon-install-blocked":
			case "addon-install-failed":
				var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
					.getService(Components.interfaces.nsIPromptService);
				promptService.alert(win, Zotero.getString("standalone.addonInstallationFailed.title"),
					Zotero.getString("standalone.addonInstallationFailed.body", installInfo.installs[0].name));
				break;
			/*case "addon-install-started":
			case "addon-install-complete":*/
		}
	}
};

window.addEventListener("load", function(e) { ZoteroStandalone.onLoad(e); }, false);
window.addEventListener("unload", function(e) { ZoteroStandalone.onUnload(e); }, false);<commit_msg>Remove corruption check, since it's no longer necessary and likely to be wrong<commit_after>/*
    ***** BEGIN LICENSE BLOCK *****

    Copyright © 2009 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org

    This file is part of Zotero.

    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.

    ***** END LICENSE BLOCK *****
*/

Components.utils.import("resource://gre/modules/Services.jsm");

/**
 * This object contains the various functions for the interface
 */
const ZoteroStandalone = new function() {
	/**
	 * Run when standalone window first opens
	 */
	this.onLoad = function() {
		if(!Zotero || !Zotero.initialized) {
			ZoteroPane.displayStartupError();
			window.close();
			return;
		}
		ZoteroPane.init();
		ZoteroPane.makeVisible();

		// Don't ask before handing http and https URIs
		var eps = Components.classes['@mozilla.org/uriloader/external-protocol-service;1']
				.getService(Components.interfaces.nsIExternalProtocolService);
		var hs = Components.classes["@mozilla.org/uriloader/handler-service;1"]
				.getService(Components.interfaces.nsIHandlerService);
		for each(var scheme in ["http", "https"]) {
			var handlerInfo = eps.getProtocolHandlerInfo(scheme);
			handlerInfo.preferredAction = Components.interfaces.nsIHandlerInfo.useSystemDefault;
			handlerInfo.alwaysAskBeforeHandling = false;
			hs.store(handlerInfo);
		}

		// Add add-on listeners (not yet hooked up)
		Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
		Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
	}

	/**
	 * Builds new item menu
	 */
	this.buildNewItemMenu = function() {
		var addMenu = document.getElementById('menu_NewItemPopup');

		// Remove all nodes so we can regenerate
		while(addMenu.hasChildNodes()) addMenu.removeChild(addMenu.firstChild);

		var typeSets = [Zotero.ItemTypes.getPrimaryTypes(), Zotero.ItemTypes.getSecondaryTypes()];
		for(var j=0; j<typeSets.length; j++) {
			var t = typeSets[j];

			// Sort by localized name
			var itemTypes = [];
			for (var i=0; i<t.length; i++) {
				itemTypes.push({
					id: t[i].id,
					name: t[i].name,
					localized: Zotero.ItemTypes.getLocalizedString(t[i].id)
				});
			}
			var collation = Zotero.getLocaleCollation();
			itemTypes.sort(function(a, b) {
				return collation.compareString(1, a.localized, b.localized);
			});

			for (var i = 0; i<itemTypes.length; i++) {
				var menuitem = document.createElement("menuitem");
				menuitem.setAttribute("label", itemTypes[i].localized);
				menuitem.setAttribute("tooltiptext", "");
				let type = itemTypes[i].id;
				menuitem.addEventListener("command", function() { ZoteroPane_Local.newItem(type); }, false);
				menuitem.className = "zotero-tb-add";
				addMenu.appendChild(menuitem);
			}

			// add separator between sets
			if(j !== typeSets.length-1) {
				addMenu.appendChild(document.createElement("menuseparator"));
			}
		}
	}

	/**
	 * Opens a URL in the basic viewer
	 */
	this.openInViewer = function(uri) {
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
			.getService(Components.interfaces.nsIWindowMediator);
		var win = wm.getMostRecentWindow("zotero:basicViewer");
		if(win) {
			win.loadURI(uri);
		} else {
			window.openDialog("chrome://zotero/content/standalone/basicViewer.xul",
				"basicViewer", "chrome,resizable,centerscreen,menubar,scrollbars", uri);
		}
	}

	/**
	 * Handles help menu requests
	 */
	this.openHelp = function(type) {
		if(type === "troubleshooting") {
			ZoteroPane.loadURI("http://www.zotero.org/support/getting_help");
		} else if(type === "feedback") {
			ZoteroPane.loadURI("http://forums.zotero.org/categories/");
		} else {
			ZoteroPane.loadURI("http://www.zotero.org/support/");
		}
	}

	/**
	 * Checks for updates
	 */
	this.checkForUpdates = function() {
		window.open('chrome://mozapps/content/update/updates.xul', 'updateChecker', 'chrome,centerscreen');
	}

	/**
	 * Called before standalone window is closed
	 */
	this.onUnload = function() {
		ZoteroPane.destroy();
	}
}

/** Taken from browser.js **/
function toJavaScriptConsole() {
	toOpenWindowByType("global:console", "chrome://global/content/console.xul");
}

function toOpenWindowByType(inType, uri, features)
{
	var topWindow = Services.wm.getMostRecentWindow(inType);

	if (topWindow) {
		topWindow.focus();
	} else if(features) {
		window.open(uri, "_blank", features);
	} else {
		window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
	}
}

const gXPInstallObserver = {
	observe: function (aSubject, aTopic, aData) {
		var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
		var win = installInfo.originatingWindow;
		switch (aTopic) {
			case "addon-install-disabled":
			case "addon-install-blocked":
			case "addon-install-failed":
				var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
					.getService(Components.interfaces.nsIPromptService);
				promptService.alert(win, Zotero.getString("standalone.addonInstallationFailed.title"),
					Zotero.getString("standalone.addonInstallationFailed.body", installInfo.installs[0].name));
				break;
			/*case "addon-install-started":
			case "addon-install-complete":*/
		}
	}
};

window.addEventListener("load", function(e) { ZoteroStandalone.onLoad(e); }, false);
window.addEventListener("unload", function(e) { ZoteroStandalone.onUnload(e); }, false);<|endoftext|>


<commit_before>
function analyzeObject(element) {
  if (element === null) {
    console.error("Null object to analyze");
    return {
      type: "notflash"
    };
  }
  var param = element.firstChild;
  if (param === null) {
    console.error("No child to analyze in", element.localName);
    return {
      type: "notflash"
    };
  }
  var width, height;
  width = element.getAttribute("width");
  height = element.getAttribute("height");

  while (param) {
    if (param.localName == 'embed') {

      var embed_type = param.getAttribute("type")
      if (embed_type != "application/x-shockwave-flash") {
        console.log("Not flash, but", embed_type);
        return {
          type: "notflash"
        };
      }

      width = param.hasAttribute("width") ? param.getAttribute("width") : width;
      height = param.hasAttribute("height") ? param.getAttribute("height") : height;
//      allowfullscreen = param.getAttribute("allowfullscreen");

      if (param.hasAttribute('src')) {
        var url = param.getAttribute('src');

        console.log("matching url", url);

        // YOUTUBE
        var matches = url.match('^https?:\/\/(?:www\.)?youtube\.(?:googleapis\.)?com/v/([A-Za-z0-9_\-]{11})');
        if (matches) {
          return {
            type: "youtube",
            src: url,
            width: width,
            height: height,
            videoid: matches[1],
            processor: replaceYT
          };
        }

        // VIMEO
        matches = url.match('^https?:\/\/(www\.)?vimeo.com/moogaloop\.swf\?.*clip_id=([0-9]*)');
        if (matches) {
          return {
            type: "vimeo",
            src: url,
            width: width,
            height: height,
            videoid: matches[2],
            processor: replaceVimeo
          };
        }
      }
    }
    param = param.nextSibling;
  }
}

// replace the vimeo embed by its iframe
function replaceVimeo(container, a) {
  if (a.type != "vimeo") {
    console.error("Wrong type, got", a.type);
    return;
  }

  if (a.videoid === undefined) {
    console.error("Couldn't match VIMEO video ID in URL", a.src);
    return ;
  }

  var replacement = document.createElement("iframe");
  replacement.setAttribute('src', 'https://player.vimeo.com/video/' + a.videoid);
  replacement.setAttribute('width', a.width);
  replacement.setAttribute('height', a.height);
  replacement.setAttribute('frameborder', '0');
//  replacement.setAttribute('webkitallowfullscreen'); // not needed
//  replacement.setAttribute('mozallowfullscreen'); // deprecated
  replacement.setAttribute('allowfullscreen', '');

  replaceObjectTag(container, replacement);
}

// replace the youtube embed by its iframe
function replaceYT(container, a) {
  if (a.type != "youtube") {
    console.error("Wrong type, got", a.type);
    return;
  }

  if (a.videoid === undefined) {
    console.error("Couldn't match YT video ID in URL", a.src);
    return ;
  }

  var replacement = document.createElement("iframe");
  replacement.setAttribute('class', 'youtube-player');
  replacement.setAttribute('type', 'text/html');
  replacement.setAttribute('width', a.width);
  replacement.setAttribute('height', a.height);
  replacement.setAttribute('src', 'https://www.youtube.com/embed/' + a.videoid);
  replacement.setAttribute('allowfullscreen', '');
  replacement.setAttribute('frameborder', '0');

  replaceObjectTag(container, replacement);
}

function replaceObjectTag(obj, replacement) {
  obj.parentNode.insertBefore(replacement, obj);
  obj.parentNode.removeChild(obj);
}

var elements = document.getElementsByTagName("object")

var embeds = [];
for (var i = 0; i < elements.length; i++) {
  embeds.push(elements.item(i));
}

for (var i in embeds) {
  try {
    console.log('processing', i);
    var analyzed = analyzeObject(embeds[i]);

    if (typeof analyzed.processor === 'function') {
      analyzed.processor(embeds[i], analyzed);
    } else {
      console.log("Found unprocessable object", embeds[i].localName, "of type", analyzed.type);
    }
  }
  catch (e) {
    console.error("Exception:", e.message, e.stack);
  }
}

<commit_msg>Make the video replacer more robust.<commit_after>//
// This is injected in the content.
// Author: Hubert Figuiere
//

function analyzeObject(element) {
  var param = element ? element.firstChild : null;
  if (param === null) {
    console.error("No child or element to analyze in",
                  element ? element.localName : '(null)');
    return {
      type: "notflash"
    };
  }
  var width, height;
  width = element.getAttribute("width");
  height = element.getAttribute("height");

  while (param) {
    if (param.localName == 'embed') {

      var embed_type = param.getAttribute("type")
      if (embed_type != "application/x-shockwave-flash") {
        console.log("Not flash, but", embed_type);
        return {
          type: "notflash"
        };
      }

      width = param.hasAttribute("width") ? param.getAttribute("width") : width;
      height = param.hasAttribute("height") ? param.getAttribute("height") : height;
//      allowfullscreen = param.getAttribute("allowfullscreen");

      if (param.hasAttribute('src')) {
        var url = param.getAttribute('src');

        console.log("matching url", url);

        // YOUTUBE
        var matches = url.match('^https?:\/\/(?:www\.)?youtube\.(?:googleapis\.)?com/v/([A-Za-z0-9_\-]{11})');
        if (matches) {
          return {
            type: "youtube",
            src: url,
            width: width,
            height: height,
            videoid: matches[1],
            processor: replaceYT
          };
        }

        // VIMEO
        matches = url.match('^https?:\/\/(www\.)?vimeo.com/moogaloop\.swf\?.*clip_id=([0-9]*)');
        if (matches) {
          return {
            type: "vimeo",
            src: url,
            width: width,
            height: height,
            videoid: matches[2],
            processor: replaceVimeo
          };
        }

      }
    }
    param = param.nextSibling;
  }
}

// replace the vimeo embed by its iframe
function replaceVimeo(container, a) {
  if (a.type != "vimeo") {
    console.error("Wrong type, got", a.type);
    return;
  }

  if (a.videoid === undefined) {
    console.error("Couldn't match VIMEO video ID in URL", a.src);
    return ;
  }

  var replacement = document.createElement("iframe");
  replacement.setAttribute('src', 'https://player.vimeo.com/video/' + a.videoid);
  replacement.setAttribute('width', a.width);
  replacement.setAttribute('height', a.height);
  replacement.setAttribute('frameborder', '0');
//  replacement.setAttribute('webkitallowfullscreen'); // not needed
//  replacement.setAttribute('mozallowfullscreen'); // deprecated
  replacement.setAttribute('allowfullscreen', '');

  replaceObjectTag(container, replacement);
}

// replace the youtube embed by its iframe
function replaceYT(container, a) {
  if (a.type != "youtube") {
    console.error("Wrong type, got", a.type);
    return;
  }

  if (a.videoid === undefined) {
    console.error("Couldn't match YT video ID in URL", a.src);
    return ;
  }

  var replacement = document.createElement("iframe");
  replacement.setAttribute('class', 'youtube-player');
  replacement.setAttribute('type', 'text/html');
  replacement.setAttribute('width', a.width);
  replacement.setAttribute('height', a.height);
  replacement.setAttribute('src', 'https://www.youtube.com/embed/' + a.videoid);
  replacement.setAttribute('allowfullscreen', '');
  replacement.setAttribute('frameborder', '0');

  replaceObjectTag(container, replacement);
}

function replaceObjectTag(obj, replacement) {
  obj.parentNode.insertBefore(replacement, obj);
  obj.parentNode.removeChild(obj);
}

var elements = document.getElementsByTagName("object")

var embeds = [];
for (var i = 0; i < elements.length; i++) {
  embeds.push(elements.item(i));
}

for (var i in embeds) {
  try {
    console.log('processing', i);
    var analyzed = analyzeObject(embeds[i]);

    if (typeof analyzed.processor === 'function') {
      analyzed.processor(embeds[i], analyzed);
    } else {
      console.log("Found unprocessable object", embeds[i].localName, "of type", analyzed.type);
    }
  }
  catch (e) {
    console.error("Exception:", e.message, e.stack);
  }
}

<|endoftext|>


<commit_before>import Vue from 'vue'

function checkPrefixedProp (prop) {
  var el = document.createElement('div')
  var upper = prop.charAt(0).toUpperCase() + prop.slice(1)
  if (!(prop in el.style)) {
    var prefixes = ['Webkit', 'Moz', 'ms']
    var i = prefixes.length
    while (i--) {
      if ((prefixes[i] + upper) in el.style) {
        prop = prefixes[i] + upper
      }
    }
  }
  return prop
}

describe('Directive v-bind:style', () => {
  let vm

  beforeEach(() => {
    vm = new Vue({
      template: '

',
      data () {
        return {
          styles: {},
          fontSize: 16
        }
      }
    }).$mount()
  })

  it('string', done => {
    vm.styles = 'color:red;'
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
    }).then(done)
  })

  it('falsy number', done => {
    vm.styles = { opacity: 0 }
    waitForUpdate(() => {
      expect(vm.$el.style.opacity).toBe('0')
    }).then(done)
  })

  it('plain object', done => {
    vm.styles = { color: 'red' }
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
    }).then(done)
  })

  it('camelCase', done => {
    vm.styles = { marginRight: '10px' }
    waitForUpdate(() => {
      expect(vm.$el.style.marginRight).toBe('10px')
    }).then(done)
  })

  it('remove if falsy value', done => {
    vm.$el.style.color = 'red'
    waitForUpdate(() => {
      vm.styles = { color: null }
    }).then(() => {
      expect(vm.$el.style.color).toBe('')
    }).then(done)
  })

  it('ignore unsupported property', done => {
    vm.styles = { foo: 'bar' }
    waitForUpdate(() => {
      expect(vm.$el.style.foo).not.toBe('bar')
    }).then(done)
  })

  it('auto prefix', done => {
    const prop = checkPrefixedProp('transform')
    const val = 'scale(0.5)'
    vm.styles = { transform: val }
    waitForUpdate(() => {
      expect(vm.$el.style[prop]).toBe(val)
    }).then(done)
  })

  it('object with multiple entries', done => {
    vm.$el.style.color = 'red'
    vm.styles = {
      marginLeft: '10px',
      marginRight: '15px'
    }
    waitForUpdate(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('red')
      expect(vm.$el.style.getPropertyValue('margin-left')).toBe('10px')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBe('15px')
      vm.styles = {
        color: 'blue',
        padding: null
      }
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('blue')
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
      // handle falsy value
      vm.styles = null
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
    }).then(done)
  })

  it('array of objects', done => {
    vm.$el.style.padding = '10px'
    vm.styles = [{ color: 'red' }, { marginRight: '20px' }]

    waitForUpdate(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('red')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBe('20px')
      expect(vm.$el.style.getPropertyValue('padding')).toBe('10px')
      vm.styles = [{ color: 'blue' }, { padding: null }]
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('blue')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
    }).then(done)
  })

  it('updates objects deeply', done => {
    vm.styles = { display: 'none' }
    waitForUpdate(() => {
      expect(vm.$el.style.display).toBe('none')
      vm.styles.display = 'block'
    }).then(() => {
      expect(vm.$el.style.display).toBe('block')
    }).then(done)
  })

  it('background size with only one value', done => {
    vm.styles = { backgroundSize: '100%' }
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toMatch(/background-size:100%(auto)?;/)
    }).then(done)
  })

  it('should work with interpolation', done => {
    vm.styles = { fontSize: `${vm.fontSize}px` }
    waitForUpdate(() => {
      expect(vm.$el.style.fontSize).toBe('16px')
    }).then(done)
  })

  const supportCssVariable = () => {
    const el = document.createElement('div')
    el.style.setProperty('--color', 'red')
    return el.style.getPropertyValue('--color') === 'red'
  }

  if (supportCssVariable()) {
    it('CSS variables', done => {
      vm.styles = { '--color': 'red' }
      waitForUpdate(() => {
        expect(vm.$el.style.getPropertyValue('--color')).toBe('red')
      }).then(done)
    })
  }

  it('should merge static style with binding style', () => {
    const vm = new Vue({
      template: '

',
      data: {
        test: { color: 'red', fontSize: '12px' }
      }
    }).$mount()
    const style = vm.$el.style
    expect(style.getPropertyValue('background-image')).toMatch('https://vuejs.org/images/logo.png')
    expect(style.getPropertyValue('color')).toBe('red')
    expect(style.getPropertyValue('font-size')).toBe('12px')
  })

  it('should merge between parent and child', done => {
    const vm = new Vue({
      template: '<child style="text-align: left;margin-right:20px" :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '

',
          data: () => ({ marginLeft: '16px' })
        }
      }
    }).$mount()
    const style = vm.$el.style
    const child = vm.$children[0]
    const css = style.cssText.replace(/\s/g, '')
    expect(css).toContain('margin-right:20px;')
    expect(css).toContain('margin-left:16px;')
    expect(css).toContain('text-align:left;')
    expect(css).toContain('color:red;')
    expect(css).toContain('font-size:12px;')
    expect(style.color).toBe('red')
    expect(style.marginRight).toBe('20px')
    vm.test.color = 'blue'
    waitForUpdate(() => {
      expect(style.color).toBe('blue')
      child.marginLeft = '30px'
    }).then(() => {
      expect(style.marginLeft).toBe('30px')
      child.fontSize = '30px'
    }).then(() => {
      expect(style.fontSize).toBe('12px')
    }).then(done)
  })

  it('should not pass to child root element', () => {
    const vm = new Vue({
      template: '<child :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '
<nested ref="nested" style="color: blue;text-align:left"></nested>
',
          components: {
            nested: {
              template: '

'
            }
          }
        }
      }
    }).$mount()
    const style = vm.$el.style
    expect(style.color).toBe('red')
    expect(style.textAlign).toBe('')
    expect(style.fontSize).toBe('12px')
    expect(vm.$children[0].$refs.nested.$el.style.color).toBe('blue')
  })

  it('should merge between nested components', (done) => {
    const vm = new Vue({
      template: '<child :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '<nested style="color: blue;text-align:left"></nested>',
          components: {
            nested: {
              template: '

',
              data: () => ({ nestedStyle: { marginLeft: '30px' }})
            }
          }
        }
      }
    }).$mount()
    const style = vm.$el.style
    const child = vm.$children[0].$children[0]
    expect(style.color).toBe('red')
    expect(style.marginLeft).toBe('30px')
    expect(style.textAlign).toBe('left')
    expect(style.fontSize).toBe('12px')
    vm.test.color = 'yellow'
    waitForUpdate(() => {
      child.nestedStyle.marginLeft = '60px'
    }).then(() => {
      expect(style.marginLeft).toBe('60px')
      child.nestedStyle = {
        fontSize: '14px',
        marginLeft: '40px'
      }
    }).then(() => {
      expect(style.fontSize).toBe('12px')
      expect(style.marginLeft).toBe('40px')
    }).then(done)
  })

  it('should not merge for different adjacent elements', (done) => {
    const vm = new Vue({
      template:
        '
' +
          '

' +
          '

' +
          '

' +
        '
',
      data: {
        bool: false,
        style: {
          fontSize: '12px'
        }
      }
    }).$mount()
    const style = vm.$el.children[0].style
    expect(style.fontSize).toBe('12px')
    expect(style.color).toBe('blue')
    waitForUpdate(() => {
      vm.bool = true
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginTop).toBe('12px')
    }).then(done)
  })

  it('should not merge for v-if, v-else-if and v-else elements', (done) => {
    const vm = new Vue({
      template:
        '
' +
          '

' +
          '

' +
          '

' +
          '

' +
        '
',
      data: {
        foo: true,
        bar: false,
        style: {
          fontSize: '12px'
        }
      }
    }).$mount()
    const style = vm.$el.children[0].style
    expect(style.fontSize).toBe('12px')
    expect(style.color).toBe('blue')
    waitForUpdate(() => {
      vm.foo = false
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginBottom).toBe('24px')
      vm.bar = true
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginBottom).toBe('')
      expect(style.marginTop).toBe('12px')
    }).then(done)
  })
})
<commit_msg>test for static style !important support<commit_after>import Vue from 'vue'

function checkPrefixedProp (prop) {
  var el = document.createElement('div')
  var upper = prop.charAt(0).toUpperCase() + prop.slice(1)
  if (!(prop in el.style)) {
    var prefixes = ['Webkit', 'Moz', 'ms']
    var i = prefixes.length
    while (i--) {
      if ((prefixes[i] + upper) in el.style) {
        prop = prefixes[i] + upper
      }
    }
  }
  return prop
}

describe('Directive v-bind:style', () => {
  let vm

  beforeEach(() => {
    vm = new Vue({
      template: '

',
      data () {
        return {
          styles: {},
          fontSize: 16
        }
      }
    }).$mount()
  })

  it('string', done => {
    vm.styles = 'color:red;'
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
    }).then(done)
  })

  it('falsy number', done => {
    vm.styles = { opacity: 0 }
    waitForUpdate(() => {
      expect(vm.$el.style.opacity).toBe('0')
    }).then(done)
  })

  it('plain object', done => {
    vm.styles = { color: 'red' }
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toBe('color:red;')
    }).then(done)
  })

  it('camelCase', done => {
    vm.styles = { marginRight: '10px' }
    waitForUpdate(() => {
      expect(vm.$el.style.marginRight).toBe('10px')
    }).then(done)
  })

  it('remove if falsy value', done => {
    vm.$el.style.color = 'red'
    waitForUpdate(() => {
      vm.styles = { color: null }
    }).then(() => {
      expect(vm.$el.style.color).toBe('')
    }).then(done)
  })

  it('ignore unsupported property', done => {
    vm.styles = { foo: 'bar' }
    waitForUpdate(() => {
      expect(vm.$el.style.foo).not.toBe('bar')
    }).then(done)
  })

  it('auto prefix', done => {
    const prop = checkPrefixedProp('transform')
    const val = 'scale(0.5)'
    vm.styles = { transform: val }
    waitForUpdate(() => {
      expect(vm.$el.style[prop]).toBe(val)
    }).then(done)
  })

  it('!important', () => {
    vm.styles = { display: 'block !important' }
    waitForUpdate(() => {
      expect(vm.$el.style.getPropertyPriority('display')).toBe('important')
    })
  })

  it('object with multiple entries', done => {
    vm.$el.style.color = 'red'
    vm.styles = {
      marginLeft: '10px',
      marginRight: '15px'
    }
    waitForUpdate(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('red')
      expect(vm.$el.style.getPropertyValue('margin-left')).toBe('10px')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBe('15px')
      vm.styles = {
        color: 'blue',
        padding: null
      }
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('blue')
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
      // handle falsy value
      vm.styles = null
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-left')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
    }).then(done)
  })

  it('array of objects', done => {
    vm.$el.style.padding = '10px'
    vm.styles = [{ color: 'red' }, { marginRight: '20px' }]

    waitForUpdate(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('red')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBe('20px')
      expect(vm.$el.style.getPropertyValue('padding')).toBe('10px')
      vm.styles = [{ color: 'blue' }, { padding: null }]
    }).then(() => {
      expect(vm.$el.style.getPropertyValue('color')).toBe('blue')
      expect(vm.$el.style.getPropertyValue('margin-right')).toBeFalsy()
      expect(vm.$el.style.getPropertyValue('padding')).toBeFalsy()
    }).then(done)
  })

  it('updates objects deeply', done => {
    vm.styles = { display: 'none' }
    waitForUpdate(() => {
      expect(vm.$el.style.display).toBe('none')
      vm.styles.display = 'block'
    }).then(() => {
      expect(vm.$el.style.display).toBe('block')
    }).then(done)
  })

  it('background size with only one value', done => {
    vm.styles = { backgroundSize: '100%' }
    waitForUpdate(() => {
      expect(vm.$el.style.cssText.replace(/\s/g, '')).toMatch(/background-size:100%(auto)?;/)
    }).then(done)
  })

  it('should work with interpolation', done => {
    vm.styles = { fontSize: `${vm.fontSize}px` }
    waitForUpdate(() => {
      expect(vm.$el.style.fontSize).toBe('16px')
    }).then(done)
  })

  const supportCssVariable = () => {
    const el = document.createElement('div')
    el.style.setProperty('--color', 'red')
    return el.style.getPropertyValue('--color') === 'red'
  }

  if (supportCssVariable()) {
    it('CSS variables', done => {
      vm.styles = { '--color': 'red' }
      waitForUpdate(() => {
        expect(vm.$el.style.getPropertyValue('--color')).toBe('red')
      }).then(done)
    })
  }

  it('should merge static style with binding style', () => {
    const vm = new Vue({
      template: '

',
      data: {
        test: { color: 'red', fontSize: '12px' }
      }
    }).$mount()
    const style = vm.$el.style
    expect(style.getPropertyValue('background-image')).toMatch('https://vuejs.org/images/logo.png')
    expect(style.getPropertyValue('color')).toBe('red')
    expect(style.getPropertyValue('font-size')).toBe('12px')
  })

  it('should merge between parent and child', done => {
    const vm = new Vue({
      template: '<child style="text-align: left;margin-right:20px" :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '

',
          data: () => ({ marginLeft: '16px' })
        }
      }
    }).$mount()
    const style = vm.$el.style
    const child = vm.$children[0]
    const css = style.cssText.replace(/\s/g, '')
    expect(css).toContain('margin-right:20px;')
    expect(css).toContain('margin-left:16px;')
    expect(css).toContain('text-align:left;')
    expect(css).toContain('color:red;')
    expect(css).toContain('font-size:12px;')
    expect(style.color).toBe('red')
    expect(style.marginRight).toBe('20px')
    vm.test.color = 'blue'
    waitForUpdate(() => {
      expect(style.color).toBe('blue')
      child.marginLeft = '30px'
    }).then(() => {
      expect(style.marginLeft).toBe('30px')
      child.fontSize = '30px'
    }).then(() => {
      expect(style.fontSize).toBe('12px')
    }).then(done)
  })

  it('should not pass to child root element', () => {
    const vm = new Vue({
      template: '<child :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '
<nested ref="nested" style="color: blue;text-align:left"></nested>
',
          components: {
            nested: {
              template: '

'
            }
          }
        }
      }
    }).$mount()
    const style = vm.$el.style
    expect(style.color).toBe('red')
    expect(style.textAlign).toBe('')
    expect(style.fontSize).toBe('12px')
    expect(vm.$children[0].$refs.nested.$el.style.color).toBe('blue')
  })

  it('should merge between nested components', (done) => {
    const vm = new Vue({
      template: '<child :style="test"></child>',
      data: {
        test: { color: 'red', fontSize: '12px' }
      },
      components: {
        child: {
          template: '<nested style="color: blue;text-align:left"></nested>',
          components: {
            nested: {
              template: '

',
              data: () => ({ nestedStyle: { marginLeft: '30px' }})
            }
          }
        }
      }
    }).$mount()
    const style = vm.$el.style
    const child = vm.$children[0].$children[0]
    expect(style.color).toBe('red')
    expect(style.marginLeft).toBe('30px')
    expect(style.textAlign).toBe('left')
    expect(style.fontSize).toBe('12px')
    vm.test.color = 'yellow'
    waitForUpdate(() => {
      child.nestedStyle.marginLeft = '60px'
    }).then(() => {
      expect(style.marginLeft).toBe('60px')
      child.nestedStyle = {
        fontSize: '14px',
        marginLeft: '40px'
      }
    }).then(() => {
      expect(style.fontSize).toBe('12px')
      expect(style.marginLeft).toBe('40px')
    }).then(done)
  })

  it('should not merge for different adjacent elements', (done) => {
    const vm = new Vue({
      template:
        '
' +
          '

' +
          '

' +
          '

' +
        '
',
      data: {
        bool: false,
        style: {
          fontSize: '12px'
        }
      }
    }).$mount()
    const style = vm.$el.children[0].style
    expect(style.fontSize).toBe('12px')
    expect(style.color).toBe('blue')
    waitForUpdate(() => {
      vm.bool = true
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginTop).toBe('12px')
    }).then(done)
  })

  it('should not merge for v-if, v-else-if and v-else elements', (done) => {
    const vm = new Vue({
      template:
        '
' +
          '

' +
          '

' +
          '

' +
          '

' +
        '
',
      data: {
        foo: true,
        bar: false,
        style: {
          fontSize: '12px'
        }
      }
    }).$mount()
    const style = vm.$el.children[0].style
    expect(style.fontSize).toBe('12px')
    expect(style.color).toBe('blue')
    waitForUpdate(() => {
      vm.foo = false
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginBottom).toBe('24px')
      vm.bar = true
    }).then(() => {
      expect(style.color).toBe('')
      expect(style.fontSize).toBe('')
      expect(style.marginBottom).toBe('')
      expect(style.marginTop).toBe('12px')
    }).then(done)
  })
})
<|endoftext|>


<commit_before>import createAdaptiveStore from './storage/adaptive'
import { AUTHENTICATE, FETCH } from './actionTypes'
import {
  authenticateFailed,
  authenticateSucceeded,
  invalidateSession,
  restore,
  restoreFailed
} from './actions'

export default (
  {
    authenticator,
    authenticators,
    authorize,
    storage = createAdaptiveStore()
  } = {}
) => {
  if (authenticator == null && authenticators == null) {
    throw new Error(
      'No authenticator was given. Be sure to configure an authenticator ' +
        'by using the `authenticator` option for a single authenticator or ' +
        'using the `authenticators` option to allow multiple authenticators'
    )
  }

  if (!Array.isArray(authenticators) && authenticator == null) {
    throw new Error(
      'Expected `authenticators` to be an array. If you only need a single ' +
        'authenticator, consider using the `authenticator` option.'
    )
  }

  if (Array.isArray(authenticator) && authenticators == null) {
    throw new Error(
      'Expected `authenticator` to be an object. If you need multiple ' +
        'authenticators, consider using the `authenticators` option.'
    )
  }

  const findAuthenticator = authenticator
    ? () => authenticator
    : name => authenticators.find(authenticator => authenticator.name === name)

  return ({ dispatch, getState }) => {
    const { authenticated = {} } = storage.restore()
    const { authenticator: authenticatorName, ...data } = authenticated
    const authenticator = findAuthenticator(authenticatorName)

    if (authenticator) {
      authenticator
        .restore(data)
        .then(
          () => dispatch(restore(authenticated)),
          () => dispatch(restoreFailed())
        )
    }

    return next => action => {
      switch (action.type) {
        case AUTHENTICATE: {
          const authenticator = findAuthenticator(action.meta.authenticator)

          if (!authenticator) {
            throw new Error(
              `No authenticator with name \`${action.meta.authenticator}\` ` +
                'was found. Be sure you have defined it in the authenticators ' +
                'config.'
            )
          }

          return authenticator
            .authenticate(action.payload)
            .then(
              data => dispatch(authenticateSucceeded(authenticator.name, data)),
              () => dispatch(authenticateFailed())
            )
        }
        case FETCH: {
          const { session } = getState()
          const { url, options = {} } = action.payload
          const { headers = {} } = options

          if (authorize) {
            authorize(session.data, (name, value) => {
              headers[name] = value
            })
          }

          return fetch(url, { ...options, headers }).then(response => {
            if (response.status === 401 && session.isAuthenticated) {
              dispatch(invalidateSession())
            }

            return response
          })
        }
        default: {
          const { session: prevSession } = getState()
          next(action)
          const { session } = getState()

          if (session.data !== prevSession.data) {
            const { authenticator, data } = session

            storage.persist({ authenticated: { ...data, authenticator } })
          }
        }
      }
    }
  }
}
<commit_msg>Pull out validation to separate functions<commit_after>import createAdaptiveStore from './storage/adaptive'
import isPlainObject from 'lodash.isplainobject'
import { AUTHENTICATE, FETCH } from './actionTypes'
import {
  authenticateFailed,
  authenticateSucceeded,
  invalidateSession,
  restore,
  restoreFailed
} from './actions'

const validateAuthenticatorsPresence = ({ authenticator, authenticators }) => {
  if (authenticator == null && authenticators == null) {
    throw new Error(
      'No authenticator was given. Be sure to configure an authenticator ' +
        'by using the `authenticator` option for a single authenticator or ' +
        'using the `authenticators` option to allow multiple authenticators'
    )
  }
}

const validateAuthenticatorsIsArray = authenticators => {
  if (!Array.isArray(authenticators)) {
    throw new Error(
      'Expected `authenticators` to be an array. If you only need a single ' +
        'authenticator, consider using the `authenticator` option.'
    )
  }
}

const validateAuthenticatorIsObject = authenticator => {
  if (!isPlainObject(authenticator)) {
    throw new Error(
      'Expected `authenticator` to be an object. If you need multiple ' +
        'authenticators, consider using the `authenticators` option.'
    )
  }
}

const validateConfig = config => {
  const { authenticator, authenticators } = config

  validateAuthenticatorsPresence(config)
  authenticator == null && validateAuthenticatorsIsArray(authenticators)
  authenticators == null && validateAuthenticatorIsObject(authenticator)
}

export default (config = {}) => {
  validateConfig(config)

  const {
    authenticator,
    authenticators,
    authorize,
    storage = createAdaptiveStore()
  } = config

  const findAuthenticator = authenticator
    ? () => authenticator
    : name => authenticators.find(authenticator => authenticator.name === name)

  return ({ dispatch, getState }) => {
    const { authenticated = {} } = storage.restore()
    const { authenticator: authenticatorName, ...data } = authenticated
    const authenticator = findAuthenticator(authenticatorName)

    if (authenticator) {
      authenticator
        .restore(data)
        .then(
          () => dispatch(restore(authenticated)),
          () => dispatch(restoreFailed())
        )
    }

    return next => action => {
      switch (action.type) {
        case AUTHENTICATE: {
          const authenticator = findAuthenticator(action.meta.authenticator)

          if (!authenticator) {
            throw new Error(
              `No authenticator with name \`${action.meta.authenticator}\` ` +
                'was found. Be sure you have defined it in the authenticators ' +
                'config.'
            )
          }

          return authenticator
            .authenticate(action.payload)
            .then(
              data => dispatch(authenticateSucceeded(authenticator.name, data)),
              () => dispatch(authenticateFailed())
            )
        }
        case FETCH: {
          const { session } = getState()
          const { url, options = {} } = action.payload
          const { headers = {} } = options

          if (authorize) {
            authorize(session.data, (name, value) => {
              headers[name] = value
            })
          }

          return fetch(url, { ...options, headers }).then(response => {
            if (response.status === 401 && session.isAuthenticated) {
              dispatch(invalidateSession())
            }

            return response
          })
        }
        default: {
          const { session: prevSession } = getState()
          next(action)
          const { session } = getState()

          if (session.data !== prevSession.data) {
            const { authenticator, data } = session

            storage.persist({ authenticated: { ...data, authenticator } })
          }
        }
      }
    }
  }
}
<|endoftext|>