<commit_before>/* jshint unused:true, node:true */
"use strict";

var debug     = require('debug')('irc-gitter-adapter');
var irc       = require('./protocol');
var Gitter    = require('node-gitter');
var manifest  = require('../package.json');
var VERSION   = manifest.version;
var DATE      = Date();

var log = console.log;

function Adapter(client) {
  this.client = client;

  // Keep track of the active rooms
  this.rooms = {};

  this.hookEvents();
}

// Map IRC commands to adapter functions
Adapter.prototype.hookEvents = function() {
  var commands = {
    PASS:     this.setup,
    PRIVMSG:  this.sendMessage,
    QUIT:     this.quit,
    NICK:     this.nick,
    USER:     this.register,
    JOIN:     this.joinRooms,
    PART:     this.leaveRooms,
    WHO:      this.listUsers,
    LIST:     this.listRooms,
    MOTD:     this.messageOfTheDay
  };

  Object.keys(commands).map(function(cmd) {
    this.client.on(cmd, commands[cmd].bind(this));
  }.bind(this));
};

Adapter.prototype.listenForOneToOnes = function() {
  var c = this.client;

  var handleNotification = function(msg) {
    if (msg.notification !== 'user_notification') return;

    this.gitterClient.rooms.find(msg.troupeId)
    .then(function(room) {
      if (!room.oneToOne) return;
      var nick = room.user.username;
      var mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      msg.text.split('\n').forEach(function(line) {
        c.send(mask, 'PRIVMSG', c.nick, ':' + line);
      });
      if (msg.chatId) this.user.markAsRead(room.id, [msg.chatId]);
      c.trackEvent('1-to-1-from-faye');
    }.bind(this))
    .catch(function(err) {
      log('Error handling user_notification', this.user.username, err);
      log(err.stack);
    });
  };

  this.oneToOneSub = this.gitterClient.faye.client.subscribe('/api/v1/user/' + this.user.id, handleNotification.bind(this));
};

Adapter.prototype.setup = function(token) {
  var c = this.client;

  // TODO move this to a config file
  var opts = {};
  if (process.env.DEV) opts = {client: {host: "localhost", port: 5000, prefix: true}, faye: {host: 'http://localhost:5000/faye'}};

  this.gitterClient = new Gitter(token, opts);
  this.gitterClient.currentUser()
  .then(function(user) {
    debug('Logged in as ' + user.username);
    this.user = user;
    this.client.authenticate(user);
    this.listenForOneToOnes();
  }.bind(this))
  .catch(function(err) {
    log('Authentication failed for token ', token);
    var mask = ':gitter!gitter@irc.gitter.im';
    c.send(mask, 'PRIVMSG', 'gitter', ': Authentication failed. Get a valid token from https://irc.gitter.im');
    this.quit();
  }.bind(this));
};

Adapter.prototype.subscribeToRoom = function(room) {
  var c = this.client;
  var uri = (room.uri || room.name).toLowerCase();

  debug('Subscribing to room ' + uri);

  if (this.rooms[uri]) return;

  this.rooms[uri] = room;
  room.subscribe();

  room.on('chatMessages', function(evt) {
    if (['create','update'].indexOf(evt.operation) === -1) return;

    var message = evt.model;
    var nick = message.fromUser.username;
    if (nick === c.nick) return; // User's own message

    var mask = ':' + nick + '!' + nick + '@irc.gitter.im';

    var text;
    message.text.split('\n').forEach(function(line) {
      text = evt.operation === 'update' ? '[edit] ' + line : line;
      c.send(mask, 'PRIVMSG', '#' + room.uri, ':' + text);
    });

    this.user.markAsRead(room.id, [message.id]);
    c.trackEvent('message-from-faye', room.id, message.id);
  }.bind(this));

  room.on('events', function(evt) {
    if (evt.operation !== 'create') return;
    var message = evt.model;
    var mask = ':gitter!gitter@irc.gitter.im';

    c.send(mask, 'PRIVMSG', '#' + room.uri, ':' + message.text);
    c.trackEvent('event-from-faye');
  }.bind(this));

  room.on('users', function(evt) {
    var nick, mask;
    if (evt.operation === 'create') {
      nick = evt.model.username;
      mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      c.send(mask, 'JOIN', '#' + room.uri);
    }
    if (evt.operation === 'remove') {
      nick = evt.model.username;
      mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      c.send(mask, 'PART', '#' + room.uri);
    }
  });

};

Adapter.prototype.sendMessage = function(target, message) {
  var c = this.client;
  var uri = target.replace('#','');
  var isStatus = /^\u0001ACTION/.test(message);

  // This are /me IRC messages.
  if (isStatus) {
    message = message
    .replace(/^\u0001ACTION /, '@' + c.nick + ' ')
    .replace(/\u0001$/, '');
  }

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    return isStatus ? room.sendStatus(message) : room.send(message);
  })
  .catch(function(err){
    log('Error sending message to ', uri, err);
    log(err.stack);
  });
};

Adapter.prototype.nick = function(nick) {
  var c = this.client;
  c.send(c.mask(), 'NICK', ':' + c.nick);
};

Adapter.prototype.register = function(username, hostname, servername, realname) {
  var c = this.client;
  c.send(c.host, irc.reply.welcome, c.nick, 'Gitter', c.mask());
  c.send(c.host, irc.reply.yourHost, c.nick, 'Version:', VERSION);
  c.send(c.host, irc.reply.created, c.nick, 'Created on', DATE);
  c.send(c.host, irc.reply.myInfo, c.hostname, VERSION, 'wo', 'ntr');
  this.messageOfTheDay();
};

Adapter.prototype.joinRooms = function(channels, key) {
  channels.split(',').map(this.joinRoom.bind(this));
};

Adapter.prototype.joinRoom = function(channel) {
  var c     = this.client;
  var uri   = channel.replace('#','');

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    if (room.oneToOne) throw('Room is one-to-one');
    return [room, room.users()];
  })
  .spread(function(room, users) {
    var _channel = '#' + room.uri;
    var channelType = '*'; // TODO set right type -> * private, @ secret, = public

    var usernames = users.map(function(u) { return u.username; });
    usernames.push('gitter'); // Fake gitter user for Events

    c.send(c.mask(), 'JOIN', _channel);
    c.send(c.host, irc.reply.topic,     c.nick, _channel, ':' + room.topic);
    c.send(c.host, irc.reply.nameReply, c.nick, channelType, _channel, ':' + usernames.join(' '));
    c.send(c.host, irc.reply.endNames,  c.nick, _channel, ': /NAMES');

    return room;
  })
  .then(this.subscribeToRoom.bind(this))
  .catch(function(err) {
    c.send(c.host, irc.errors.noSuchChannel, ': Invalid channel or insufficient permissions');
    log('Error joining room ', uri, err);
    log(err.stack);
  });
};

Adapter.prototype.leaveRooms = function(channels, key) {
  channels.split(',').map(this.leaveRoom.bind(this));
};

Adapter.prototype.leaveRoom = function(channel) {
  var uri = channel.replace('#','').toLowerCase();
  this.client.send(this.client.mask(), 'PART', channel);
  this._unsubscribe(uri);
};

Adapter.prototype.listUsers = function(channel, key) {
  var c = this.client;

  if (channel === undefined) {
    c.send(c.mask(), 'WHO', ':');
    return;
  }
  if (channel.charAt(0) !== '#') {
    c.send(c.mask(), 'WHO', ':' + channel);
    return;
  }
  var uri = channel.replace('#','');

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    return room.users();
  })
  .then(function(users) {
    users.forEach(function(user) {
      c.send(c.host, irc.reply.who, c.nick, channel, user.username, c.hostname, c.hostname, user.username, 'H', ':0', user.displayName);
    });
    c.send(c.host, irc.reply.who, c.nick, channel, 'gitter', c.host, c.hostname, 'gitter', 'H', ':0', 'Gitter Bot');
    c.send(c.host, irc.reply.endWho, c.nick, channel, ':End of /WHO list.');
  })
  .catch(function(err) {
    log('Error listing users for ', uri, err);
    log(err.stack);
    c.send(c.host, irc.errors.noSuchChannel, c.nick, ':No such channel');
  });
};

Adapter.prototype.listRooms = function() {
  var c = this.client;

  this.gitterClient.currentUser()
  .then(function(user) {
    return user.rooms();
  })
  .then(function(rooms) {
    c.send(c.host, irc.reply.listStart, c.nick, 'Channel', ':Users Name');

    rooms.forEach(function(room) {
      if (room.oneToOne) return; // Do not show 1-to-1 chats
      c.send(c.host, irc.reply.list, c.nick, '#' + room.uri, room.userCount, ': ' + room.topic);
    });

    c.send(c.host, irc.reply.listEnd, c.nick, ':End of /LIST');
  });
};

Adapter.prototype.messageOfTheDay = function() {
  var c = this.client;

  c.send(c.host, irc.reply.motdStart, c.nick, ':- Message of the Day -');
  c.send(c.host, irc.reply.motd,      c.nick, 'Welcome To Gitter IRC!');
  c.send(c.host, irc.reply.motd,      c.nick, 'Info at https://irc.gitter.im');
  c.send(c.host, irc.reply.motd,      c.nick, 'Code at https://github.com/gitterHQ/irc-bridge');
  c.send(c.host, irc.reply.motdEnd,   c.nick, ':End of /MOTD command.');
};

Adapter.prototype.quit = function() {
  this.client.disconnect();
};

Adapter.prototype._unsubscribe = function(uri) {
  if (!this.rooms[uri]) return;

  this.rooms[uri].unsubscribe();
  this.rooms[uri].removeAllListeners();
  delete this.rooms[uri];
};

Adapter.prototype._teardown = function() {
  if (this.oneToOneSub) this.oneToOneSub.cancel();
  Object.keys(this.rooms).map(this._unsubscribe.bind(this));
  if (this.gitterClient) this.gitterClient.faye.client.disconnect();
};

module.exports = Adapter;
<commit_msg>Fix replies<commit_after>/* jshint unused:true, node:true */
"use strict";

var debug     = require('debug')('irc-gitter-adapter');
var irc       = require('./protocol');
var Gitter    = require('node-gitter');
var manifest  = require('../package.json');
var VERSION   = manifest.version;
var DATE      = Date();

var log = console.log;

function Adapter(client) {
  this.client = client;

  // Keep track of the active rooms
  this.rooms = {};

  this.hookEvents();
}

// Map IRC commands to adapter functions
Adapter.prototype.hookEvents = function() {
  var commands = {
    PASS:     this.setup,
    PRIVMSG:  this.sendMessage,
    QUIT:     this.quit,
    NICK:     this.nick,
    USER:     this.register,
    JOIN:     this.joinRooms,
    PART:     this.leaveRooms,
    WHO:      this.listUsers,
    LIST:     this.listRooms,
    MOTD:     this.messageOfTheDay
  };

  Object.keys(commands).map(function(cmd) {
    this.client.on(cmd, commands[cmd].bind(this));
  }.bind(this));
};

Adapter.prototype.listenForOneToOnes = function() {
  var c = this.client;

  var handleNotification = function(msg) {
    if (msg.notification !== 'user_notification') return;

    this.gitterClient.rooms.find(msg.troupeId)
    .then(function(room) {
      if (!room.oneToOne) return;
      var nick = room.user.username;
      var mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      msg.text.split('\n').forEach(function(line) {
        c.send(mask, 'PRIVMSG', c.nick, ':' + line);
      });
      if (msg.chatId) this.user.markAsRead(room.id, [msg.chatId]);
      c.trackEvent('1-to-1-from-faye');
    }.bind(this))
    .catch(function(err) {
      log('Error handling user_notification', this.user.username, err);
      log(err.stack);
    });
  };

  this.oneToOneSub = this.gitterClient.faye.client.subscribe('/api/v1/user/' + this.user.id, handleNotification.bind(this));
};

Adapter.prototype.setup = function(token) {
  var c = this.client;

  // TODO move this to a config file
  var opts = {};
  if (process.env.DEV) opts = {client: {host: "localhost", port: 5000, prefix: true}, faye: {host: 'http://localhost:5000/faye'}};

  this.gitterClient = new Gitter(token, opts);
  this.gitterClient.currentUser()
  .then(function(user) {
    debug('Logged in as ' + user.username);
    this.user = user;
    this.client.authenticate(user);
    this.listenForOneToOnes();
  }.bind(this))
  .catch(function(err) {
    log('Authentication failed for token ', token);
    var mask = ':gitter!gitter@irc.gitter.im';
    c.send(mask, 'PRIVMSG', 'gitter', ': Authentication failed. Get a valid token from https://irc.gitter.im');
    this.quit();
  }.bind(this));
};

Adapter.prototype.subscribeToRoom = function(room) {
  var c = this.client;
  var uri = (room.uri || room.name).toLowerCase();

  debug('Subscribing to room ' + uri);

  if (this.rooms[uri]) return;

  this.rooms[uri] = room;
  room.subscribe();

  room.on('chatMessages', function(evt) {
    if (['create','update'].indexOf(evt.operation) === -1) return;

    var message = evt.model;
    var nick = message.fromUser.username;
    if (nick === c.nick) return; // User's own message

    var mask = ':' + nick + '!' + nick + '@irc.gitter.im';

    var text;
    message.text.split('\n').forEach(function(line) {
      text = evt.operation === 'update' ? '[edit] ' + line : line;
      c.send(mask, 'PRIVMSG', '#' + room.uri, ':' + text);
    });

    this.user.markAsRead(room.id, [message.id]);
    c.trackEvent('message-from-faye', room.id, message.id);
  }.bind(this));

  room.on('events', function(evt) {
    if (evt.operation !== 'create') return;
    var message = evt.model;
    var mask = ':gitter!gitter@irc.gitter.im';

    c.send(mask, 'PRIVMSG', '#' + room.uri, ':' + message.text);
    c.trackEvent('event-from-faye');
  }.bind(this));

  room.on('users', function(evt) {
    var nick, mask;
    if (evt.operation === 'create') {
      nick = evt.model.username;
      mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      c.send(mask, 'JOIN', '#' + room.uri);
    }
    if (evt.operation === 'remove') {
      nick = evt.model.username;
      mask = ':' + nick + '!' + nick + '@irc.gitter.im';
      c.send(mask, 'PART', '#' + room.uri);
    }
  });

};

Adapter.prototype.sendMessage = function(target, message) {
  var c = this.client;
  var uri = target.replace('#','');
  var isStatus = /^\u0001ACTION/.test(message);

  // This are /me IRC messages.
  if (isStatus) {
    message = message
    .replace(/^\u0001ACTION /, '@' + c.nick + ' ')
    .replace(/\u0001$/, '');
  }

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    return isStatus ? room.sendStatus(message) : room.send(message);
  })
  .catch(function(err){
    log('Error sending message to ', uri, err);
    log(err.stack);
  });
};

Adapter.prototype.nick = function(nick) {
  var c = this.client;
  c.send(c.mask(), 'NICK', ':' + c.nick);
};

Adapter.prototype.register = function(username, hostname, servername, realname) {
  var c = this.client;
  c.send(c.host, irc.reply.welcome, c.nick, 'Gitter', c.mask());
  c.send(c.host, irc.reply.yourHost, c.nick, 'Version:', VERSION);
  c.send(c.host, irc.reply.created, c.nick, 'Created on', DATE);
  c.send(c.host, irc.reply.myInfo, c.nick, c.hostname, VERSION, 'wo', 'ntr');
  this.messageOfTheDay();
};

Adapter.prototype.joinRooms = function(channels, key) {
  channels.split(',').map(this.joinRoom.bind(this));
};

Adapter.prototype.joinRoom = function(channel) {
  var c     = this.client;
  var uri   = channel.replace('#','');

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    if (room.oneToOne) throw('Room is one-to-one');
    return [room, room.users()];
  })
  .spread(function(room, users) {
    var _channel = '#' + room.uri;
    var channelType = '*'; // TODO set right type -> * private, @ secret, = public

    var usernames = users.map(function(u) { return u.username; });
    usernames.push('gitter'); // Fake gitter user for Events

    c.send(c.mask(), 'JOIN', _channel);
    c.send(c.host, irc.reply.topic,     c.nick, _channel, ':' + room.topic);
    c.send(c.host, irc.reply.nameReply, c.nick, channelType, _channel, ':' + usernames.join(' '));
    c.send(c.host, irc.reply.endNames,  c.nick, _channel, ': /NAMES');

    return room;
  })
  .then(this.subscribeToRoom.bind(this))
  .catch(function(err) {
    c.send(c.host, irc.errors.noSuchChannel, c.nick, ':Invalid channel or insufficient permissions');
    log('Error joining room ', uri, err);
    log(err.stack);
  });
};

Adapter.prototype.leaveRooms = function(channels, key) {
  channels.split(',').map(this.leaveRoom.bind(this));
};

Adapter.prototype.leaveRoom = function(channel) {
  var uri = channel.replace('#','').toLowerCase();
  this.client.send(this.client.mask(), 'PART', channel);
  this._unsubscribe(uri);
};

Adapter.prototype.listUsers = function(channel, key) {
  var c = this.client;

  if (channel === undefined) {
    c.send(c.mask(), 'WHO', ':');
    return;
  }
  if (channel.charAt(0) !== '#') {
    c.send(c.mask(), 'WHO', ':' + channel);
    return;
  }
  var uri = channel.replace('#','');

  this.gitterClient.rooms.join(uri)
  .then(function(room) {
    return room.users();
  })
  .then(function(users) {
    users.forEach(function(user) {
      c.send(c.host, irc.reply.who, c.nick, channel, user.username, c.hostname, c.hostname, user.username, 'H', ':0', user.displayName);
    });
    c.send(c.host, irc.reply.who, c.nick, channel, 'gitter', c.host, c.hostname, 'gitter', 'H', ':0', 'Gitter Bot');
    c.send(c.host, irc.reply.endWho, c.nick, channel, ':End of /WHO list.');
  })
  .catch(function(err) {
    log('Error listing users for ', uri, err);
    log(err.stack);
    c.send(c.host, irc.errors.noSuchChannel, c.nick, ':No such channel');
  });
};

Adapter.prototype.listRooms = function() {
  var c = this.client;

  this.gitterClient.currentUser()
  .then(function(user) {
    return user.rooms();
  })
  .then(function(rooms) {
    c.send(c.host, irc.reply.listStart, c.nick, 'Channel', ':Users Name');

    rooms.forEach(function(room) {
      if (room.oneToOne) return; // Do not show 1-to-1 chats
      c.send(c.host, irc.reply.list, c.nick, '#' + room.uri, room.userCount, ': ' + room.topic);
    });

    c.send(c.host, irc.reply.listEnd, c.nick, ':End of /LIST');
  });
};

Adapter.prototype.messageOfTheDay = function() {
  var c = this.client;

  c.send(c.host, irc.reply.motdStart, c.nick, ':- Message of the Day -');
  c.send(c.host, irc.reply.motd,      c.nick, 'Welcome To Gitter IRC!');
  c.send(c.host, irc.reply.motd,      c.nick, 'Info at https://irc.gitter.im');
  c.send(c.host, irc.reply.motd,      c.nick, 'Code at https://github.com/gitterHQ/irc-bridge');
  c.send(c.host, irc.reply.motdEnd,   c.nick, ':End of /MOTD command.');
};

Adapter.prototype.quit = function() {
  this.client.disconnect();
};

Adapter.prototype._unsubscribe = function(uri) {
  if (!this.rooms[uri]) return;

  this.rooms[uri].unsubscribe();
  this.rooms[uri].removeAllListeners();
  delete this.rooms[uri];
};

Adapter.prototype._teardown = function() {
  if (this.oneToOneSub) this.oneToOneSub.cancel();
  Object.keys(this.rooms).map(this._unsubscribe.bind(this));
  if (this.gitterClient) this.gitterClient.faye.client.disconnect();
};

module.exports = Adapter;
<|endoftext|>


<commit_before>JARS.internal('ModuleState', function moduleStateSetup(InternalsManager) {
    'use strict';

    var ModuleLogger = InternalsManager.get('ModuleLogger'),

        // Module states
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_WAITING = 1,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADING = 2,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADED = 3,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_REGISTERED = 4,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADED_MANUAL = 5,

        // Bundle states
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_WAITING = 0,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_LOADING = 1,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_REQUESTED = 2,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_LOADED = 3,
        WAITING_STATES = [MODULE_WAITING, MODULE_BUNDLE_WAITING],
        LOADING_STATES = [MODULE_LOADING, MODULE_BUNDLE_LOADING],
        LOADED_STATES = [MODULE_LOADED, MODULE_BUNDLE_LOADED],
        LOADED_MANUAL_STATES = [MODULE_LOADED_MANUAL],
        REGISTERED_STATES = [MODULE_REGISTERED],
        REQUESTED_STATES = [null, MODULE_BUNDLE_REQUESTED],
        MODULE = 'module',
        BUNDLE = 'bundle',
        LOADING = 'loading',
        LOADED = 'loaded',
        MANUAL = ' manual',
        STARTED_LOADING = 'started ' + LOADING + ' ',
        FINISHED_LOADING = 'finished ' + LOADING + ' ',
        ATTEMPTED_TO = 'attempted to ',
        ATTEMPTED_TO_LOAD = ATTEMPTED_TO + 'load ',
        BUT_ALREADY = ' but is already ',
        ABORTED_LOADING = 'aborted ' + LOADING + ' ',
        PROBLEMS_WITH = ' because of problems with ',
        // Error when module or bundle is aborted
        MSG_MODULE_ABORTED = addAbortionMessage('given path "${path}" after ${sec} second(s) - file may not exist'),
        MSG_MODULE_DEPENDENCY_ABORTED = addAbortionMessage('dependency "${dep}"'),
        MSG_BUNDLE_ABORTED = addAbortionMessage('parent "${dep}"', true),
        MSG_BUNDLE_SUBMODULE_ABORTED = addAbortionMessage('submodule "${dep}"', true),
        // Show module or bundle is requested
        MSG_MODULE_REQUESTED = addRequestMessage(),
        MSG_BUNDLE_REQUESTED = addRequestMessage(true),
        // Show loading progress for module or bundle
        MSG_MODULE_LOADED = addLoadingMessage(),
        MSG_MODULE_LOADING = addLoadingMessage(true),
        MSG_BUNDLE_LOADED = addLoadingMessage(false, true),
        MSG_BUNDLE_LOADING = addLoadingMessage(true, true),
        // Info when loading is already in progress or done
        MSG_MODULE_ALREADY_LOADED = addLoadAttemptMessage(LOADED),
        MSG_MODULE_ALREADY_LOADED_MANUAL = addLoadAttemptMessage(LOADED + MANUAL),
        MSG_MODULE_ALREADY_LOADING = addLoadAttemptMessage(LOADING),
        MSG_BUNDLE_ALREADY_LOADED = addLoadAttemptMessage(LOADED, true),
        MSG_BUNDLE_ALREADY_LOADING = addLoadAttemptMessage(LOADING, true),
        // Warning when a module is registered twice
        MSG_MODULE_ALREADY_REGISTERED = ModuleLogger.addWarning(ATTEMPTED_TO + 'register ' + MODULE + BUT_ALREADY + ' registered'),
        // Show special cases for module
        MSG_MODULE_LOADED_MANUAL = ModuleLogger.addDebug(MODULE + ' was ' + LOADED, MANUAL),
        MSG_MODULE_REGISTERING = ModuleLogger.addDebug(MODULE + ' registering...');

    /**
    * @access public
    *
    * @constructor ModuleState
    *
    * @memberof JARS
    * @inner
    *
    * @param {JARS~Module} module
    */
    function ModuleState(module) {
        var moduleState = this;

        moduleState._module = module;

        moduleState.setWaiting();
        moduleState.setWaiting(true);
    }

    ModuleState.prototype = {
        /**
         * @access public
         *
         * @alias JARS~ModuleState
         *
         * @memberof JARS~ModuleState#
         */
         constructor: ModuleState,
        /**
         * @access private
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Number[]} states
         * @param {Boolean} compareBundleState
         */
        _compareState: function(states, compareBundleState) {
            return this[getStateProp(compareBundleState)] === states[compareBundleState ? 1 : 0];
        },
        /**
         * @access private
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Number[]} states
         * @param {Boolean} setBundleState
         */
        _set: function(states, setBundleState) {
            this[getStateProp(setBundleState)] = states[setBundleState ? 1 : 0];
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} isBundleState
         */
        isWaiting: function(isBundleState) {
            return this._compareState(WAITING_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isBundleRequested: function() {
            return this._compareState(REQUESTED_STATES, true);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} isBundleState
         */
        isLoading: function(isBundleState) {
            return this._compareState(LOADING_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isRegistered: function() {
            return this._compareState(REGISTERED_STATES) || this.isLoadedManual() || this.isLoaded();
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isLoadedManual: function() {
            return this._compareState(LOADED_MANUAL_STATES);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        isLoaded: function(isBundleState) {
            return this._compareState(LOADED_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setWaiting: function(setBundleState) {
            this._set(WAITING_STATES, setBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setLoading: function(setBundleState) {
            var moduleState = this,
                module = moduleState._module;

            moduleState._set(LOADING_STATES, setBundleState);
            module.logger.log(setBundleState ? MSG_BUNDLE_LOADING : MSG_MODULE_LOADING, {
                path: module.getFullPath()
            });
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        setRegistered: function() {
            this._set(REGISTERED_STATES);
            this._module.logger.log(MSG_MODULE_REGISTERING);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        setLoadedManual: function() {
            this._set(LOADED_MANUAL_STATES);
            this._module.logger.log(MSG_MODULE_LOADED_MANUAL);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setLoaded: function(setBundleState) {
            var moduleState = this,
                module = moduleState._module;

            moduleState._set(LOADED_STATES, setBundleState);
            module.logger.log(setBundleState ? MSG_BUNDLE_LOADED : MSG_MODULE_LOADED);
        },

        trySetRequested: function(setBundleState) {
            var moduleState = this,
                logger = moduleState._module.logger,
                isWaiting = moduleState.isWaiting(setBundleState);

            logger.log(setBundleState ? MSG_BUNDLE_REQUESTED : MSG_MODULE_REQUESTED);

            if(!isWaiting) {
                logger.log(getRequestStateMessage(moduleState, setBundleState));
            }
            else {
                setBundleState ? moduleState._set(REQUESTED_STATES, setBundleState) : moduleState.setLoading();
            }

            return isWaiting;
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @return {Boolean}
         */
        trySetRegistered: function() {
            var moduleState = this,
                registered = false;

            if (!moduleState.isRegistered()) {
                if (moduleState.isLoading()) {
                    moduleState.setRegistered();
                }
                else {
                    moduleState.setLoadedManual();
                }

                registered = true;
            }
            else {
                moduleState._module.logger.log(MSG_MODULE_ALREADY_REGISTERED);
            }

            return registered;
        },

        trySetAborted: function(setBundleState, abortionInfo) {
            var moduleState = this,
                module = moduleState._module,
                aborted = false,
                abortionMessage;

            if (moduleState.isLoading(setBundleState)) {
                moduleState.setWaiting(setBundleState);

                if (setBundleState || !module.findRecover()) {
                    aborted = true;
                    abortionMessage = setBundleState ? MSG_BUNDLE_SUBMODULE_ABORTED : MSG_MODULE_ABORTED;
                }
            }
            else if (moduleState.isRegistered()) {
                aborted = true;
                abortionMessage = setBundleState ? MSG_BUNDLE_ABORTED : MSG_MODULE_DEPENDENCY_ABORTED;
            }

            aborted && module.logger.log(abortionMessage, abortionInfo);

            return aborted;
        }
    };

    /**
     * @access private
     *
     * @memberof JARS~ModuleState
     * @inner
     *
     * @param {Boolean} isBundleState
     *
     * @return {String}
     */
    function getStateProp(isBundleState) {
        return isBundleState ? '_bundleState' : '_state';
    }

    function getRequestStateMessage(moduleState, setBundleState) {
        var requestStateMsg;

        if(moduleState.isLoaded(setBundleState)) {
            requestStateMsg = setBundleState ? MSG_BUNDLE_ALREADY_LOADED : MSG_MODULE_ALREADY_LOADED;
        }
        else if (moduleState.isLoadedManual()) {
            requestStateMsg = MSG_MODULE_ALREADY_LOADED_MANUAL;
        }
        else {
            requestStateMsg = setBundleState ? MSG_BUNDLE_ALREADY_LOADING : MSG_MODULE_ALREADY_LOADING;
        }

        return requestStateMsg;
    }

    function getModuleOrBundleString(forBundle) {
        return forBundle ? BUNDLE : MODULE;
    }

    function addRequestMessage(forBundle) {
        return ModuleLogger.addDebug(getModuleOrBundleString(forBundle) + ' requested', forBundle);
    }

    function addLoadingMessage(start, forBundle) {
        return ModuleLogger.addInfo((start ? STARTED_LOADING : FINISHED_LOADING) + getModuleOrBundleString(forBundle), forBundle);
    }

    function addLoadAttemptMessage(loadingProgress, forBundle) {
        return ModuleLogger.addInfo(ATTEMPTED_TO_LOAD + getModuleOrBundleString(forBundle) + BUT_ALREADY + loadingProgress, forBundle);
    }

    function addAbortionMessage(abortionReason, forBundle) {
        return ModuleLogger.addError(ABORTED_LOADING + getModuleOrBundleString(forBundle) + PROBLEMS_WITH + abortionReason, forBundle);
    }

    return ModuleState;
});
<commit_msg>Fix in adding loaded manual message<commit_after>JARS.internal('ModuleState', function moduleStateSetup(InternalsManager) {
    'use strict';

    var ModuleLogger = InternalsManager.get('ModuleLogger'),

        // Module states
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_WAITING = 1,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADING = 2,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADED = 3,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_REGISTERED = 4,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_LOADED_MANUAL = 5,

        // Bundle states
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_WAITING = 0,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_LOADING = 1,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_REQUESTED = 2,
        /**
         * @access private
         *
         * @constant {Number}
         * @default
         *
         * @memberof JARS~ModuleState
         * @inner
         */
        MODULE_BUNDLE_LOADED = 3,
        WAITING_STATES = [MODULE_WAITING, MODULE_BUNDLE_WAITING],
        LOADING_STATES = [MODULE_LOADING, MODULE_BUNDLE_LOADING],
        LOADED_STATES = [MODULE_LOADED, MODULE_BUNDLE_LOADED],
        LOADED_MANUAL_STATES = [MODULE_LOADED_MANUAL],
        REGISTERED_STATES = [MODULE_REGISTERED],
        REQUESTED_STATES = [null, MODULE_BUNDLE_REQUESTED],
        MODULE = 'module',
        BUNDLE = 'bundle',
        LOADING = 'loading',
        LOADED = 'loaded',
        LOADED_MANUAL = LOADED + ' manual',
        STARTED_LOADING = 'started ' + LOADING + ' ',
        FINISHED_LOADING = 'finished ' + LOADING + ' ',
        ATTEMPTED_TO = 'attempted to ',
        ATTEMPTED_TO_LOAD = ATTEMPTED_TO + 'load ',
        BUT_ALREADY = ' but is already ',
        ABORTED_LOADING = 'aborted ' + LOADING + ' ',
        PROBLEMS_WITH = ' because of problems with ',
        // Error when module or bundle is aborted
        MSG_MODULE_ABORTED = addAbortionMessage('given path "${path}" after ${sec} second(s) - file may not exist'),
        MSG_MODULE_DEPENDENCY_ABORTED = addAbortionMessage('dependency "${dep}"'),
        MSG_BUNDLE_ABORTED = addAbortionMessage('parent "${dep}"', true),
        MSG_BUNDLE_SUBMODULE_ABORTED = addAbortionMessage('submodule "${dep}"', true),
        // Show module or bundle is requested
        MSG_MODULE_REQUESTED = addRequestMessage(),
        MSG_BUNDLE_REQUESTED = addRequestMessage(true),
        // Show loading progress for module or bundle
        MSG_MODULE_LOADED = addLoadingMessage(),
        MSG_MODULE_LOADING = addLoadingMessage(true),
        MSG_BUNDLE_LOADED = addLoadingMessage(false, true),
        MSG_BUNDLE_LOADING = addLoadingMessage(true, true),
        // Info when loading is already in progress or done
        MSG_MODULE_ALREADY_LOADED = addLoadAttemptMessage(LOADED),
        MSG_MODULE_ALREADY_LOADED_MANUAL = addLoadAttemptMessage(LOADED_MANUAL),
        MSG_MODULE_ALREADY_LOADING = addLoadAttemptMessage(LOADING),
        MSG_BUNDLE_ALREADY_LOADED = addLoadAttemptMessage(LOADED, true),
        MSG_BUNDLE_ALREADY_LOADING = addLoadAttemptMessage(LOADING, true),
        // Warning when a module is registered twice
        MSG_MODULE_ALREADY_REGISTERED = ModuleLogger.addWarning(ATTEMPTED_TO + 'register ' + MODULE + BUT_ALREADY + ' registered'),
        // Show special cases for module
        MSG_MODULE_LOADED_MANUAL = ModuleLogger.addDebug(MODULE + ' was ' + LOADED_MANUAL),
        MSG_MODULE_REGISTERING = ModuleLogger.addDebug(MODULE + ' registering...');

    /**
    * @access public
    *
    * @constructor ModuleState
    *
    * @memberof JARS
    * @inner
    *
    * @param {JARS~Module} module
    */
    function ModuleState(module) {
        var moduleState = this;

        moduleState._module = module;

        moduleState.setWaiting();
        moduleState.setWaiting(true);
    }

    ModuleState.prototype = {
        /**
         * @access public
         *
         * @alias JARS~ModuleState
         *
         * @memberof JARS~ModuleState#
         */
         constructor: ModuleState,
        /**
         * @access private
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Number[]} states
         * @param {Boolean} compareBundleState
         */
        _compareState: function(states, compareBundleState) {
            return this[getStateProp(compareBundleState)] === states[compareBundleState ? 1 : 0];
        },
        /**
         * @access private
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Number[]} states
         * @param {Boolean} setBundleState
         */
        _set: function(states, setBundleState) {
            this[getStateProp(setBundleState)] = states[setBundleState ? 1 : 0];
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} isBundleState
         */
        isWaiting: function(isBundleState) {
            return this._compareState(WAITING_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isBundleRequested: function() {
            return this._compareState(REQUESTED_STATES, true);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} isBundleState
         */
        isLoading: function(isBundleState) {
            return this._compareState(LOADING_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isRegistered: function() {
            return this._compareState(REGISTERED_STATES) || this.isLoadedManual() || this.isLoaded();
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        isLoadedManual: function() {
            return this._compareState(LOADED_MANUAL_STATES);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        isLoaded: function(isBundleState) {
            return this._compareState(LOADED_STATES, isBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setWaiting: function(setBundleState) {
            this._set(WAITING_STATES, setBundleState);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setLoading: function(setBundleState) {
            var moduleState = this,
                module = moduleState._module;

            moduleState._set(LOADING_STATES, setBundleState);
            module.logger.log(setBundleState ? MSG_BUNDLE_LOADING : MSG_MODULE_LOADING, {
                path: module.getFullPath()
            });
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        setRegistered: function() {
            this._set(REGISTERED_STATES);
            this._module.logger.log(MSG_MODULE_REGISTERING);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         */
        setLoadedManual: function() {
            this._set(LOADED_MANUAL_STATES);
            this._module.logger.log(MSG_MODULE_LOADED_MANUAL);
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @param {Boolean} setBundleState
         */
        setLoaded: function(setBundleState) {
            var moduleState = this,
                module = moduleState._module;

            moduleState._set(LOADED_STATES, setBundleState);
            module.logger.log(setBundleState ? MSG_BUNDLE_LOADED : MSG_MODULE_LOADED);
        },

        trySetRequested: function(setBundleState) {
            var moduleState = this,
                logger = moduleState._module.logger,
                isWaiting = moduleState.isWaiting(setBundleState);

            logger.log(setBundleState ? MSG_BUNDLE_REQUESTED : MSG_MODULE_REQUESTED);

            if(!isWaiting) {
                logger.log(getRequestStateMessage(moduleState, setBundleState));
            }
            else {
                setBundleState ? moduleState._set(REQUESTED_STATES, setBundleState) : moduleState.setLoading();
            }

            return isWaiting;
        },
        /**
         * @access public
         *
         * @memberof JARS~ModuleState#
         *
         * @return {Boolean}
         */
        trySetRegistered: function() {
            var moduleState = this,
                registered = false;

            if (!moduleState.isRegistered()) {
                if (moduleState.isLoading()) {
                    moduleState.setRegistered();
                }
                else {
                    moduleState.setLoadedManual();
                }

                registered = true;
            }
            else {
                moduleState._module.logger.log(MSG_MODULE_ALREADY_REGISTERED);
            }

            return registered;
        },

        trySetAborted: function(setBundleState, abortionInfo) {
            var moduleState = this,
                module = moduleState._module,
                aborted = false,
                abortionMessage;

            if (moduleState.isLoading(setBundleState)) {
                moduleState.setWaiting(setBundleState);

                if (setBundleState || !module.findRecover()) {
                    aborted = true;
                    abortionMessage = setBundleState ? MSG_BUNDLE_SUBMODULE_ABORTED : MSG_MODULE_ABORTED;
                }
            }
            else if (moduleState.isRegistered()) {
                aborted = true;
                abortionMessage = setBundleState ? MSG_BUNDLE_ABORTED : MSG_MODULE_DEPENDENCY_ABORTED;
            }

            aborted && module.logger.log(abortionMessage, abortionInfo);

            return aborted;
        }
    };

    /**
     * @access private
     *
     * @memberof JARS~ModuleState
     * @inner
     *
     * @param {Boolean} isBundleState
     *
     * @return {String}
     */
    function getStateProp(isBundleState) {
        return isBundleState ? '_bundleState' : '_state';
    }

    function getRequestStateMessage(moduleState, setBundleState) {
        var requestStateMsg;

        if(moduleState.isLoaded(setBundleState)) {
            requestStateMsg = setBundleState ? MSG_BUNDLE_ALREADY_LOADED : MSG_MODULE_ALREADY_LOADED;
        }
        else if (moduleState.isLoadedManual()) {
            requestStateMsg = MSG_MODULE_ALREADY_LOADED_MANUAL;
        }
        else {
            requestStateMsg = setBundleState ? MSG_BUNDLE_ALREADY_LOADING : MSG_MODULE_ALREADY_LOADING;
        }

        return requestStateMsg;
    }

    function getModuleOrBundleString(forBundle) {
        return forBundle ? BUNDLE : MODULE;
    }

    function addRequestMessage(forBundle) {
        return ModuleLogger.addDebug(getModuleOrBundleString(forBundle) + ' requested', forBundle);
    }

    function addLoadingMessage(start, forBundle) {
        return ModuleLogger.addInfo((start ? STARTED_LOADING : FINISHED_LOADING) + getModuleOrBundleString(forBundle), forBundle);
    }

    function addLoadAttemptMessage(loadingProgress, forBundle) {
        return ModuleLogger.addInfo(ATTEMPTED_TO_LOAD + getModuleOrBundleString(forBundle) + BUT_ALREADY + loadingProgress, forBundle);
    }

    function addAbortionMessage(abortionReason, forBundle) {
        return ModuleLogger.addError(ABORTED_LOADING + getModuleOrBundleString(forBundle) + PROBLEMS_WITH + abortionReason, forBundle);
    }

    return ModuleState;
});
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React from 'react';

/**
 * The intro component
 */
const Intro = ( page ) => {

	const theme = page._pages[ page._ID ].theme ? page._pages[ page._ID ].theme : 'dark';

	return (
		<div className={`au-body au-grid intro intro--${ theme }`}>

							{ page.category && { page.category } }

{ page.title ? page.title : page._pages[ page._ID ].pagetitle }

							{ page.subtitle &&
{ page.subtitle }
 }

{ page._body }

	);
}

Intro.propTypes = {
	/**
	 * category: Content strategy
	 */
	category: PropTypes.string,

	/**
	 * title: Page title  # if not given, it takes the title from the current page
	 */
	title: PropTypes.string,

	/**
	 * subtitle: Welcome to our intro
	 */
	subtitle: PropTypes.string,

	/**
	 * _body: (text)(4)
	 */
	_body: PropTypes.node.isRequired,
};

Intro.defaultProps = {};

export default Intro;
<commit_msg>add the Section 'What/Why..' into the Intro_Category.js module<commit_after>import PropTypes from 'prop-types';
import Slugify from 'slugify';
import React from 'react';

/**
 * The intro component
 */
const Intro = ( page ) => {

	const theme = page._pages[ page._ID ].theme ? page._pages[ page._ID ].theme : 'dark';

	return (
		<div className={`au-body au-grid intro intro--${ theme }`}>

						{ page.section }

							{ page.category && { page.category } }

{ page.title ? page.title : page._pages[ page._ID ].pagetitle }

							{ page.subtitle &&
{ page.subtitle }
 }

{ page._body }

	);
}

Intro.propTypes = {
	/**
	 * category: Content strategy
	 */
	category: PropTypes.string,

	/**
	 * title: Page title  # if not given, it takes the title from the current page
	 */
	title: PropTypes.string,

	/**
	 * subtitle: Welcome to our intro
	 */
	subtitle: PropTypes.string,

	/**
	 * _body: (text)(4)
	 */
	_body: PropTypes.node.isRequired,
};

Intro.defaultProps = {};

export default Intro;
<|endoftext|>


<commit_before>/*
  Author:bachvtuan@gmail.com
  https://github.com/bachvtuan/html5-sortable-angularjs
  A directive that support sortable list via html5 for angularjs
  Read the readme.md and take a look at example code before using.
*/

var sortable_app = angular.module('html5.sortable', []);
sortable_app.directive('htmlSortable', function($parse,$timeout, $log, $window) {

  return {
    restrict: 'A',
    require: '?ngModel',
    scope: {
      htmlSortable: '=',
      ngModel : '=',
      ngExtraSortable:'='
    },

    //scope: true,   // optionally create a child scope
    link: function(scope, element, attrs,ngModel) {
      //var model = $parse(attrs.htmlSortable);
      /*attrs.html5Sortable*/

      var sortable = {};
      sortable.is_handle = false;
      sortable.in_use = false;

      sortable.handleDragStart = function(e) {

         $window['drag_source'] = null;
         $window['drag_source_extra'] = null;

        if ( sortable.options &&  !sortable.is_handle && sortable.options.handle ){
          e.preventDefault();
          return;
        }

        sortable.is_handle  = false;
        e.dataTransfer.effectAllowed = 'move';
        //Fixed on firefox
        e.dataTransfer.setData('text/plain', 'anything');

         $window['drag_source'] = this;
         $window['drag_source_extra'] = element.extra_data;

        // this/e.target is the source node.
        this.classList.add('moving');
      };

      sortable.handleDragOver = function(e) {
        if (e.preventDefault) {
          e.preventDefault(); // Allows us to drop.
        }

        e.dataTransfer.dropEffect = 'move';

        return false;
      };

      sortable.handleDragEnter = function(e) {
        this.classList.add('over');
      };

      sortable.handleDragLeave = function(e) {
        this.classList.remove('over');
      };

      sortable.handleDrop = function(e) {
        // this/e.target is current target element.
        if (e.stopPropagation) {
          // stops the browser from redirecting.
          e.stopPropagation();
        }
        e.preventDefault();
        this.classList.remove('over');

        // Don't do anything if we're dropping on the same column we're dragging.
        if ( $window['drag_source'] != this) {

          if ( $window['drag_source'] == null){
            $log.info("Invalid sortable");
            return;
          }

          var source_model = $window['drag_source'].model;
          var drop_index = this.index;

          if (ngModel.$modelValue.indexOf(source_model) != -1){

            var drag_index =  $window['drag_source'].index;
            var temp = angular.copy(ngModel.$modelValue[drag_index]);

            sortable.unbind();

            ngModel.$modelValue.splice(drag_index,1);
            ngModel.$modelValue.splice(drop_index,0, temp);

          }
          else if ( sortable.options.allow_cross ){
            ngModel.$modelValue.splice(drop_index,0, source_model);
          }
          else{
            $log.info("disabled cross dropping");
            return;
          }

          //return;
          scope.$apply();

          if ( sortable.options &&  angular.isDefined(sortable.options.stop) ){
            $log.info('Make callback');
            sortable.options.stop(ngModel.$modelValue,drop_index,
              element.extra_data,$window['drag_source_extra']);
          }
        }

        return false;
      };

      sortable.handleDragEnd = function(e) {
        // this/e.target is the source node.
        [].forEach.call(sortable.cols_, function (col) {
          col.classList.remove('over');
          col.classList.remove('moving');
        });

      };

      //Unbind all events are registed before
      sortable.unbind = function(){

        $log.info('Unbind sortable');
        [].forEach.call(sortable.cols_, function (col) {
          col.removeAttribute('draggable');
          col.removeEventListener('dragstart', sortable.handleDragStart, false);
          col.removeEventListener('dragenter', sortable.handleDragEnter, false);
          col.removeEventListener('dragover', sortable.handleDragOver, false);
          col.removeEventListener('dragleave', sortable.handleDragLeave, false);
          col.removeEventListener('drop', sortable.handleDrop, false);
          col.removeEventListener('dragend', sortable.handleDragEnd, false);
        });
        sortable.in_use = false;
      }

      sortable.activehandle = function(){
        sortable.is_handle = true;
      }

      sortable.update = function(){
        $log.info("Update sortable");
         $window['drag_source'] = null;
        var index = 0;
        this.cols_ =  element[0].children;

        [].forEach.call(this.cols_, function (col) {
          if ( sortable.options &&  sortable.options.handle){
            var handle = col.querySelectorAll(sortable.options.handle)[0];
            handle.addEventListener('mousedown', sortable.activehandle, false);
          }

          col.index = index;
          col.model = ngModel.$modelValue[index];

          index++;

          col.setAttribute('draggable', 'true');  // Enable columns to be draggable.
          col.addEventListener('dragstart', sortable.handleDragStart, false);
          col.addEventListener('dragenter', sortable.handleDragEnter, false);
          col.addEventListener('dragover', sortable.handleDragOver, false);
          col.addEventListener('dragleave', sortable.handleDragLeave, false);
          col.addEventListener('drop', sortable.handleDrop, false);
          col.addEventListener('dragend', sortable.handleDragEnd, false);
        });

        sortable.in_use = true;
      }

      if (ngModel) {
        ngModel.$render = function() {
          $timeout(function(){
            //Init flag indicate the first load sortable is done or not
            sortable.first_load = false;

            scope.$watch('ngExtraSortable',function(value){
              element.extra_data = value;
              //sortable.extra_data = value;
            })

            scope.$watch('htmlSortable', function(value) {

              $log.info("The fist time load html5-sortable");
              sortable.options = angular.copy(value) ;

              if (value == "destroy" ){
                $log.info("destroy");
                if (sortable.in_use){
                  sortable.unbind();
                  sortable.in_use = false;
                }
                return;
              }

              if ( !angular.isDefined(sortable.options)){
                sortable.options = {};
              }

              if ( !angular.isDefined(sortable.options.allow_cross)){
                sortable.options.allow_cross = false
              }

              if ( angular.isDefined(sortable.options.construct) ){
                sortable.options.construct(ngModel.$modelValue);
              }
              element[0].classList.add('html5-sortable');
              sortable.update();
              $timeout(function(){
                sortable.first_load = true;
              })
            }, true);

            //Watch ngModel and narrate it
            scope.$watch('ngModel', function(value) {
              if ( !sortable.first_load || sortable.options == 'destroy' ){
                //Ignore on first load
                return;
              }
              $log.info("Model changed");

              $timeout(function(){
                sortable.update();
              });

            },true);

          });
        };
      }
      else{
        $log.info('Missing ng-model in template');
      }
    }
  };
});
<commit_msg>update source from gh-page branch<commit_after>/*
  Author:bachvtuan@gmail.com
  https://github.com/bachvtuan/html5-sortable-angularjs
  A directive that support sortable list via html5 for angularjs
  Read the readme.md and take a look at example code before using.
*/

var sortable_app = angular.module('html5.sortable', []);
sortable_app.directive('htmlSortable', function($parse,$timeout, $log, $window) {

  return {
    restrict: 'A',
    require: '?ngModel',
    scope: {
      htmlSortable: '=',
      ngModel : '=',
      ngExtraSortable:'='
    },

    //scope: true,   // optionally create a child scope
    link: function(scope, element, attrs,ngModel) {
      //var model = $parse(attrs.htmlSortable);
      /*attrs.html5Sortable*/

      var sortable = {};
      sortable.is_handle = false;
      sortable.in_use = false;

      sortable.handleDragStart = function(e) {

         $window['drag_source'] = null;
         $window['drag_source_extra'] = null;

        if ( sortable.options &&  !sortable.is_handle && sortable.options.handle ){
          e.preventDefault();
          return;
        }

        sortable.is_handle  = false;
        e.dataTransfer.effectAllowed = 'move';
        //Fixed on firefox
        e.dataTransfer.setData('text/plain', 'anything');

         $window['drag_source'] = this;
         $window['drag_source_extra'] = element.extra_data;

        // this/e.target is the source node.
        this.classList.add('moving');
      };

      sortable.handleDragOver = function(e) {
        if (e.preventDefault) {
          e.preventDefault(); // Allows us to drop.
        }

        e.dataTransfer.dropEffect = 'move';

        if ( !this.classList.contains('over') ){
          this.classList.add('over');
        }

        //return false;
      };

      sortable.handleDragEnter = function(e) {
        if ( !this.classList.contains('over') ){
          this.classList.add('over');
        }
      };

      sortable.handleDragLeave = function(e) {
        this.classList.remove('over');
      };

      sortable.handleDrop = function(e) {
        // this/e.target is current target element.
        if (e.stopPropagation) {
          // stops the browser from redirecting.
          e.stopPropagation();
        }
        e.preventDefault();
        this.classList.remove('over');

        // Don't do anything if we're dropping on the same column we're dragging.
        if ( $window['drag_source'] != this) {

          if ( $window['drag_source'] == null){
            $log.info("Invalid sortable");
            return;
          }

          var source_model = $window['drag_source'].model;
          var drop_index = this.index;

          if (ngModel.$modelValue.indexOf(source_model) != -1){

            var drag_index =  $window['drag_source'].index;
            var temp = angular.copy(ngModel.$modelValue[drag_index]);

            sortable.unbind();

            ngModel.$modelValue.splice(drag_index,1);
            ngModel.$modelValue.splice(drop_index,0, temp);

          }
          else if ( sortable.options.allow_cross ){
            ngModel.$modelValue.splice(drop_index,0, source_model);
          }
          else{
            $log.info("disabled cross dropping");
            return;
          }

          //return;
          scope.$apply();

          if ( sortable.options &&  angular.isDefined(sortable.options.stop) ){
            $log.info('Make callback');
            sortable.options.stop(ngModel.$modelValue,drop_index,
              element.extra_data,$window['drag_source_extra']);
          }
        }

        return false;
      };

      sortable.handleDragEnd = function(e) {
        // this/e.target is the source node.
        [].forEach.call(sortable.cols_, function (col) {
          col.classList.remove('over');
          col.classList.remove('moving');
        });

      };

      //Unbind all events are registed before
      sortable.unbind = function(){

        $log.info('Unbind sortable');
        [].forEach.call(sortable.cols_, function (col) {
          col.removeAttribute('draggable');
          col.removeEventListener('dragstart', sortable.handleDragStart, false);
          col.removeEventListener('dragenter', sortable.handleDragEnter, false);
          col.removeEventListener('dragover', sortable.handleDragOver, false);
          col.removeEventListener('dragleave', sortable.handleDragLeave, false);
          col.removeEventListener('drop', sortable.handleDrop, false);
          col.removeEventListener('dragend', sortable.handleDragEnd, false);
        });
        sortable.in_use = false;
      }

      sortable.activehandle = function(){
        sortable.is_handle = true;
      }

      sortable.update = function(){
        $log.info("Update sortable");
         $window['drag_source'] = null;
        var index = 0;
        this.cols_ =  element[0].children;

        [].forEach.call(this.cols_, function (col) {
          if ( sortable.options &&  sortable.options.handle){
            var handle = col.querySelectorAll(sortable.options.handle)[0];
            handle.addEventListener('mousedown', sortable.activehandle, false);
          }

          col.index = index;
          col.model = ngModel.$modelValue[index];

          index++;

          col.setAttribute('draggable', 'true');  // Enable columns to be draggable.
          col.addEventListener('dragstart', sortable.handleDragStart, false);
          col.addEventListener('dragenter', sortable.handleDragEnter, false);
          col.addEventListener('dragover', sortable.handleDragOver, false);
          col.addEventListener('dragleave', sortable.handleDragLeave, false);
          col.addEventListener('drop', sortable.handleDrop, false);
          col.addEventListener('dragend', sortable.handleDragEnd, false);
        });

        sortable.in_use = true;
      }

      if (ngModel) {
        ngModel.$render = function() {
          $timeout(function(){
            //Init flag indicate the first load sortable is done or not
            sortable.first_load = false;

            scope.$watch('ngExtraSortable',function(value){
              element.extra_data = value;
              //sortable.extra_data = value;
            })

            scope.$watch('htmlSortable', function(value) {

              $log.info("The fist time load html5-sortable");
              sortable.options = angular.copy(value) ;

              if (value == "destroy" ){
                $log.info("destroy");
                if (sortable.in_use){
                  sortable.unbind();
                  sortable.in_use = false;
                }
                return;
              }

              if ( !angular.isDefined(sortable.options)){
                sortable.options = {};
              }

              if ( !angular.isDefined(sortable.options.allow_cross)){
                sortable.options.allow_cross = false
              }

              if ( angular.isDefined(sortable.options.construct) ){
                sortable.options.construct(ngModel.$modelValue);
              }
              element[0].classList.add('html5-sortable');
              sortable.update();
              $timeout(function(){
                sortable.first_load = true;
              })
            }, true);

            //Watch ngModel and narrate it
            scope.$watch('ngModel', function(value) {
              if ( !sortable.first_load || sortable.options == 'destroy' ){
                //Ignore on first load
                return;
              }
              $log.info("Model changed");

              $timeout(function(){
                sortable.update();
              });

            },true);

          });
        };
      }
      else{
        $log.info('Missing ng-model in template');
      }
    }
  };
});
<|endoftext|>


<commit_before>// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import path from 'path';
import {app, BrowserWindow, nativeImage, systemPreferences, ipcMain} from 'electron';
import log from 'electron-log';

import {MAXIMIZE_CHANGE, HISTORY, GET_LOADING_SCREEN_DATA, REACT_APP_INITIALIZED, LOADING_SCREEN_ANIMATION_FINISHED, FOCUS_THREE_DOT_MENU} from 'common/communication';
import urlUtils from 'common/utils/url';

import {getAdjustedWindowBoundaries} from '../utils';

import {ViewManager} from '../views/viewManager';
import {CriticalErrorHandler} from '../CriticalErrorHandler';

import {createSettingsWindow} from './settingsWindow';
import createMainWindow from './mainWindow';

// singleton module to manage application's windows

const status = {
    mainWindow: null,
    settingsWindow: null,
    config: null,
    viewManager: null,
};
const assetsDir = path.resolve(app.getAppPath(), 'assets');

ipcMain.on(HISTORY, handleHistory);
ipcMain.handle(GET_LOADING_SCREEN_DATA, handleLoadingScreenDataRequest);
ipcMain.on(REACT_APP_INITIALIZED, handleReactAppInitialized);
ipcMain.on(LOADING_SCREEN_ANIMATION_FINISHED, handleLoadingScreenAnimationFinished);

export function setConfig(data) {
    if (data) {
        status.config = data;
    }
    if (status.viewManager) {
        status.viewManager.reloadConfiguration(status.config.teams);
    }
}

export function showSettingsWindow() {
    if (status.settingsWindow) {
        status.settingsWindow.show();
    } else {
        if (!status.mainWindow) {
            showMainWindow();
        }
        const withDevTools = process.env.MM_DEBUG_SETTINGS || false;

        status.settingsWindow = createSettingsWindow(status.mainWindow, status.config, withDevTools);
        status.settingsWindow.on('closed', () => {
            status.settingsWindow = null;
            focusBrowserView();
        });
    }
}

export function showMainWindow(deeplinkingURL) {
    if (status.mainWindow) {
        if (status.mainWindow.isVisible()) {
            status.mainWindow.focus();
        } else {
            status.mainWindow.show();
        }
    } else {
        status.mainWindow = createMainWindow(status.config, {
            linuxAppIcon: path.join(assetsDir, 'linux', 'app_icon.png'),
        });

        if (!status.mainWindow) {
            log.error('unable to create main window');
            app.quit();
        }

        // window handlers
        status.mainWindow.on('closed', () => {
            log.warn('main window closed');
            status.mainWindow = null;
        });
        status.mainWindow.on('unresponsive', () => {
            const criticalErrorHandler = new CriticalErrorHandler();
            criticalErrorHandler.setMainWindow(status.mainWindow);
            criticalErrorHandler.windowUnresponsiveHandler();
        });
        status.mainWindow.on('crashed', handleMainWindowWebContentsCrashed);
        status.mainWindow.on('maximize', handleMaximizeMainWindow);
        status.mainWindow.on('unmaximize', handleUnmaximizeMainWindow);
        status.mainWindow.on('resize', handleResizeMainWindow);
        status.mainWindow.on('focus', focusBrowserView);
        status.mainWindow.on('enter-full-screen', () => sendToRenderer('enter-full-screen'));
        status.mainWindow.on('leave-full-screen', () => sendToRenderer('leave-full-screen'));

        if (process.env.MM_DEBUG_SETTINGS) {
            status.mainWindow.webContents.openDevTools({mode: 'detach'});
        }

        if (status.viewManager) {
            status.viewManager.updateMainWindow(status.mainWindow);
        }
    }
    initializeViewManager();

    if (deeplinkingURL) {
        status.viewManager.handleDeepLink(deeplinkingURL);
    }
}

export function getMainWindow(ensureCreated) {
    if (ensureCreated && status.mainWindow === null) {
        showMainWindow();
    }
    return status.mainWindow;
}

export function on(event, listener) {
    return status.mainWindow.on(event, listener);
}

function handleMainWindowWebContentsCrashed() {
    throw new Error('webContents \'crashed\' event has been emitted');
}

function handleMaximizeMainWindow() {
    sendToRenderer(MAXIMIZE_CHANGE, true);
}

function handleUnmaximizeMainWindow() {
    sendToRenderer(MAXIMIZE_CHANGE, false);
}

function handleResizeMainWindow() {
    const currentView = status.viewManager.getCurrentView();
    let bounds;

    // Workaround for linux maximizing/minimizing, which doesn't work properly because of these bugs:
    // https://github.com/electron/electron/issues/28699
    // https://github.com/electron/electron/issues/28106
    if (process.platform === 'linux') {
        const size = status.mainWindow.getSize();
        bounds = {width: size[0], height: size[1]};
    } else {
        bounds = status.mainWindow.getContentBounds();
    }

    if (currentView) {
        currentView.setBounds(getAdjustedWindowBoundaries(bounds.width, bounds.height, !urlUtils.isTeamUrl(currentView.server.url, currentView.view.webContents.getURL())));
    }
    status.viewManager.setLoadingScreenBounds();
}

export function sendToRenderer(channel, ...args) {
    if (!status.mainWindow) {
        showMainWindow();
    }
    status.mainWindow.webContents.send(channel, ...args);
    if (status.settingsWindow && status.settingsWindow.isVisible()) {
        status.settingsWindow.webContents.send(channel, ...args);
    }
}

export function sendToAll(channel, ...args) {
    sendToRenderer(channel, ...args);
    if (status.settingsWindow) {
        status.settingsWindow.webContents.send(channel, ...args);
    }

    // TODO: should we include popups?
}

export function sendToMattermostViews(channel, ...args) {
    if (status.viewManager) {
        status.viewManager.sendToAllViews(channel, ...args);
    }
}

export function restoreMain() {
    log.info('restoreMain');
    if (!status.mainWindow) {
        showMainWindow();
    }
    if (!status.mainWindow.isVisible() || status.mainWindow.isMinimized()) {
        if (status.mainWindow.isMinimized()) {
            status.mainWindow.restore();
        } else {
            status.mainWindow.show();
        }
        if (status.settingsWindow) {
            status.settingsWindow.focus();
        } else {
            status.mainWindow.focus();
        }
        if (process.platform === 'darwin') {
            app.dock.show();
        }
    } else if (status.settingsWindow) {
        status.settingsWindow.focus();
    } else {
        status.mainWindow.focus();
    }
}

export function flashFrame(flash) {
    if (process.platform === 'linux' || process.platform === 'win32') {
        status.mainWindow.flashFrame(flash);
        if (status.settingsWindow) {
            // main might be hidden behind the settings
            status.settingsWindow.flashFrame(flash);
        }
    }
    if (process.platform === 'darwin' && status.config.notifications.bounceIcon) {
        app.dock.bounce(status.config.notifications.bounceIconType);
    }
}

function drawBadge(text, small) {
    const scale = 2; // should rely display dpi
    const size = (small ? 20 : 16) * scale;
    const canvas = document.createElement('canvas');
    canvas.setAttribute('width', size);
    canvas.setAttribute('height', size);
    const ctx = canvas.getContext('2d');

    // circle
    ctx.fillStyle = '#FF1744'; // Material Red A400
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
    ctx.fill();

    // text
    ctx.fillStyle = '#ffffff';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.font = (11 * scale) + 'px sans-serif';
    ctx.fillText(text, size / 2, size / 2, size);

    return canvas.toDataURL();
}

function createDataURL(text, small) {
    const win = status.mainWindow;
    if (!win) {
        return null;
    }

    // since we don't have a document/canvas object in the main process, we use the webcontents from the window to draw.
    const safeSmall = Boolean(small);
    const code = `
    window.drawBadge = ${drawBadge};
    window.drawBadge('${text || ''}', ${safeSmall});
  `;
    return win.webContents.executeJavaScript(code);
}

export async function setOverlayIcon(badgeText, description, small) {
    if (process.platform === 'win32') {
        let overlay = null;
        if (status.mainWindow && badgeText) {
            try {
                const dataUrl = await createDataURL(badgeText, small);
                overlay = nativeImage.createFromDataURL(dataUrl);
            } catch (err) {
                log.error(`Couldn't generate a badge: ${err}`);
            }
        }
        status.mainWindow.setOverlayIcon(overlay, description);
    }
}

export function isMainWindow(window) {
    return status.mainWindow && status.mainWindow === window;
}

export function handleDoubleClick(e, windowType) {
    let action = 'Maximize';
    if (process.platform === 'darwin') {
        action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
    }
    const win = (windowType === 'settings') ? status.settingsWindow : status.mainWindow;
    if (!win) {
        return;
    }
    switch (action) {
    case 'Minimize':
        if (win.isMinimized()) {
            win.restore();
        } else {
            win.minimize();
        }
        break;
    case 'Maximize':
    default:
        if (win.isMaximized()) {
            win.unmaximize();
        } else {
            win.maximize();
        }
        break;
    }
}

function initializeViewManager() {
    if (!status.viewManager) {
        status.viewManager = new ViewManager(status.config, status.mainWindow);
        status.viewManager.load();
        status.viewManager.showInitial();
    }
}

export function switchServer(serverName) {
    showMainWindow();
    status.viewManager.showByName(serverName);
}

export function focusBrowserView() {
    if (status.viewManager) {
        status.viewManager.focus();
    } else {
        log.error('Trying to call focus when the viewmanager has not yet been initialized');
    }
}

export function openBrowserViewDevTools() {
    if (status.viewManager) {
        status.viewManager.openViewDevTools();
    }
}

export function focusThreeDotMenu() {
    if (status.mainWindow) {
        status.mainWindow.webContents.focus();
        status.mainWindow.webContents.send(FOCUS_THREE_DOT_MENU);
    }
}

function handleLoadingScreenDataRequest() {
    return {
        darkMode: status.config.darkMode,
    };
}

function handleReactAppInitialized(_, server) {
    if (status.viewManager) {
        status.viewManager.setServerInitialized(server);
    }
}

function handleLoadingScreenAnimationFinished() {
    if (status.viewManager) {
        status.viewManager.hideLoadingScreen();
    }
}

export function updateLoadingScreenDarkMode(darkMode) {
    if (status.viewManager) {
        status.viewManager.updateLoadingScreenDarkMode(darkMode);
    }
}

export function getServerNameByWebContentsId(webContentsId) {
    if (status.viewManager) {
        return status.viewManager.findByWebContent(webContentsId);
    }
    return null;
}

export function close() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused.id === status.mainWindow.id) {
    // TODO: figure out logic for closing
        focused.close();
    } else {
        focused.close();
    }
}
export function maximize() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.maximize();
    }
}
export function minimize() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.minimize();
    }
}
export function restore() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.restore();
    }
}

export function reload() {
    const currentView = status.viewManager.getCurrentView();
    if (currentView) {
        status.viewManager.showLoadingScreen();
        currentView.reload();
    }
}

export function sendToFind() {
    const currentView = status.viewManager.getCurrentView();
    if (currentView) {
        currentView.view.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: ['CmdOrCtrl', 'Shift']});
    }
}

export function handleHistory(event, offset) {
    if (status.viewManager) {
        const activeView = status.viewManager.getCurrentView();
        if (activeView && activeView.view.webContents.canGoToOffset(offset)) {
            try {
                activeView.view.webContents.goToOffset(offset);
            } catch (error) {
                log.error(error);
                activeView.load(activeView.server.url);
            }
        }
    }
}
<commit_msg>[MM-35781] Another workaround for Linux resizing (#1608)<commit_after>// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.

import path from 'path';
import {app, BrowserWindow, nativeImage, systemPreferences, ipcMain} from 'electron';
import log from 'electron-log';

import {MAXIMIZE_CHANGE, HISTORY, GET_LOADING_SCREEN_DATA, REACT_APP_INITIALIZED, LOADING_SCREEN_ANIMATION_FINISHED, FOCUS_THREE_DOT_MENU} from 'common/communication';
import urlUtils from 'common/utils/url';

import {getAdjustedWindowBoundaries} from '../utils';

import {ViewManager} from '../views/viewManager';
import {CriticalErrorHandler} from '../CriticalErrorHandler';

import {createSettingsWindow} from './settingsWindow';
import createMainWindow from './mainWindow';

// singleton module to manage application's windows

const status = {
    mainWindow: null,
    settingsWindow: null,
    config: null,
    viewManager: null,
};
const assetsDir = path.resolve(app.getAppPath(), 'assets');

ipcMain.on(HISTORY, handleHistory);
ipcMain.handle(GET_LOADING_SCREEN_DATA, handleLoadingScreenDataRequest);
ipcMain.on(REACT_APP_INITIALIZED, handleReactAppInitialized);
ipcMain.on(LOADING_SCREEN_ANIMATION_FINISHED, handleLoadingScreenAnimationFinished);

export function setConfig(data) {
    if (data) {
        status.config = data;
    }
    if (status.viewManager) {
        status.viewManager.reloadConfiguration(status.config.teams);
    }
}

export function showSettingsWindow() {
    if (status.settingsWindow) {
        status.settingsWindow.show();
    } else {
        if (!status.mainWindow) {
            showMainWindow();
        }
        const withDevTools = process.env.MM_DEBUG_SETTINGS || false;

        status.settingsWindow = createSettingsWindow(status.mainWindow, status.config, withDevTools);
        status.settingsWindow.on('closed', () => {
            status.settingsWindow = null;
            focusBrowserView();
        });
    }
}

export function showMainWindow(deeplinkingURL) {
    if (status.mainWindow) {
        if (status.mainWindow.isVisible()) {
            status.mainWindow.focus();
        } else {
            status.mainWindow.show();
        }
    } else {
        status.mainWindow = createMainWindow(status.config, {
            linuxAppIcon: path.join(assetsDir, 'linux', 'app_icon.png'),
        });

        if (!status.mainWindow) {
            log.error('unable to create main window');
            app.quit();
        }

        // window handlers
        status.mainWindow.on('closed', () => {
            log.warn('main window closed');
            status.mainWindow = null;
        });
        status.mainWindow.on('unresponsive', () => {
            const criticalErrorHandler = new CriticalErrorHandler();
            criticalErrorHandler.setMainWindow(status.mainWindow);
            criticalErrorHandler.windowUnresponsiveHandler();
        });
        status.mainWindow.on('crashed', handleMainWindowWebContentsCrashed);
        status.mainWindow.on('maximize', handleMaximizeMainWindow);
        status.mainWindow.on('unmaximize', handleUnmaximizeMainWindow);
        status.mainWindow.on('resize', handleResizeMainWindow);
        status.mainWindow.on('focus', focusBrowserView);
        status.mainWindow.on('enter-full-screen', () => sendToRenderer('enter-full-screen'));
        status.mainWindow.on('leave-full-screen', () => sendToRenderer('leave-full-screen'));

        if (process.env.MM_DEBUG_SETTINGS) {
            status.mainWindow.webContents.openDevTools({mode: 'detach'});
        }

        if (status.viewManager) {
            status.viewManager.updateMainWindow(status.mainWindow);
        }
    }
    initializeViewManager();

    if (deeplinkingURL) {
        status.viewManager.handleDeepLink(deeplinkingURL);
    }
}

export function getMainWindow(ensureCreated) {
    if (ensureCreated && status.mainWindow === null) {
        showMainWindow();
    }
    return status.mainWindow;
}

export function on(event, listener) {
    return status.mainWindow.on(event, listener);
}

function handleMainWindowWebContentsCrashed() {
    throw new Error('webContents \'crashed\' event has been emitted');
}

function handleMaximizeMainWindow() {
    sendToRenderer(MAXIMIZE_CHANGE, true);
}

function handleUnmaximizeMainWindow() {
    sendToRenderer(MAXIMIZE_CHANGE, false);
}

function handleResizeMainWindow() {
    const currentView = status.viewManager.getCurrentView();
    let bounds;

    // Workaround for linux maximizing/minimizing, which doesn't work properly because of these bugs:
    // https://github.com/electron/electron/issues/28699
    // https://github.com/electron/electron/issues/28106
    if (process.platform === 'linux') {
        const size = status.mainWindow.getSize();
        bounds = {width: size[0], height: size[1]};
    } else {
        bounds = status.mainWindow.getContentBounds();
    }

    const setBoundsFunction = () => {
        if (currentView) {
            currentView.setBounds(getAdjustedWindowBoundaries(bounds.width, bounds.height, !urlUtils.isTeamUrl(currentView.server.url, currentView.view.webContents.getURL())));
        }
    };

    // Another workaround since the window doesn't update properly under Linux for some reason
    // See above comment
    if (process.platform === 'linux') {
        setTimeout(setBoundsFunction, 10);
    } else {
        setBoundsFunction();
    }
    status.viewManager.setLoadingScreenBounds();
}

export function sendToRenderer(channel, ...args) {
    if (!status.mainWindow) {
        showMainWindow();
    }
    status.mainWindow.webContents.send(channel, ...args);
    if (status.settingsWindow && status.settingsWindow.isVisible()) {
        status.settingsWindow.webContents.send(channel, ...args);
    }
}

export function sendToAll(channel, ...args) {
    sendToRenderer(channel, ...args);
    if (status.settingsWindow) {
        status.settingsWindow.webContents.send(channel, ...args);
    }

    // TODO: should we include popups?
}

export function sendToMattermostViews(channel, ...args) {
    if (status.viewManager) {
        status.viewManager.sendToAllViews(channel, ...args);
    }
}

export function restoreMain() {
    log.info('restoreMain');
    if (!status.mainWindow) {
        showMainWindow();
    }
    if (!status.mainWindow.isVisible() || status.mainWindow.isMinimized()) {
        if (status.mainWindow.isMinimized()) {
            status.mainWindow.restore();
        } else {
            status.mainWindow.show();
        }
        if (status.settingsWindow) {
            status.settingsWindow.focus();
        } else {
            status.mainWindow.focus();
        }
        if (process.platform === 'darwin') {
            app.dock.show();
        }
    } else if (status.settingsWindow) {
        status.settingsWindow.focus();
    } else {
        status.mainWindow.focus();
    }
}

export function flashFrame(flash) {
    if (process.platform === 'linux' || process.platform === 'win32') {
        status.mainWindow.flashFrame(flash);
        if (status.settingsWindow) {
            // main might be hidden behind the settings
            status.settingsWindow.flashFrame(flash);
        }
    }
    if (process.platform === 'darwin' && status.config.notifications.bounceIcon) {
        app.dock.bounce(status.config.notifications.bounceIconType);
    }
}

function drawBadge(text, small) {
    const scale = 2; // should rely display dpi
    const size = (small ? 20 : 16) * scale;
    const canvas = document.createElement('canvas');
    canvas.setAttribute('width', size);
    canvas.setAttribute('height', size);
    const ctx = canvas.getContext('2d');

    // circle
    ctx.fillStyle = '#FF1744'; // Material Red A400
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
    ctx.fill();

    // text
    ctx.fillStyle = '#ffffff';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.font = (11 * scale) + 'px sans-serif';
    ctx.fillText(text, size / 2, size / 2, size);

    return canvas.toDataURL();
}

function createDataURL(text, small) {
    const win = status.mainWindow;
    if (!win) {
        return null;
    }

    // since we don't have a document/canvas object in the main process, we use the webcontents from the window to draw.
    const safeSmall = Boolean(small);
    const code = `
    window.drawBadge = ${drawBadge};
    window.drawBadge('${text || ''}', ${safeSmall});
  `;
    return win.webContents.executeJavaScript(code);
}

export async function setOverlayIcon(badgeText, description, small) {
    if (process.platform === 'win32') {
        let overlay = null;
        if (status.mainWindow && badgeText) {
            try {
                const dataUrl = await createDataURL(badgeText, small);
                overlay = nativeImage.createFromDataURL(dataUrl);
            } catch (err) {
                log.error(`Couldn't generate a badge: ${err}`);
            }
        }
        status.mainWindow.setOverlayIcon(overlay, description);
    }
}

export function isMainWindow(window) {
    return status.mainWindow && status.mainWindow === window;
}

export function handleDoubleClick(e, windowType) {
    let action = 'Maximize';
    if (process.platform === 'darwin') {
        action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string');
    }
    const win = (windowType === 'settings') ? status.settingsWindow : status.mainWindow;
    if (!win) {
        return;
    }
    switch (action) {
    case 'Minimize':
        if (win.isMinimized()) {
            win.restore();
        } else {
            win.minimize();
        }
        break;
    case 'Maximize':
    default:
        if (win.isMaximized()) {
            win.unmaximize();
        } else {
            win.maximize();
        }
        break;
    }
}

function initializeViewManager() {
    if (!status.viewManager) {
        status.viewManager = new ViewManager(status.config, status.mainWindow);
        status.viewManager.load();
        status.viewManager.showInitial();
    }
}

export function switchServer(serverName) {
    showMainWindow();
    status.viewManager.showByName(serverName);
}

export function focusBrowserView() {
    if (status.viewManager) {
        status.viewManager.focus();
    } else {
        log.error('Trying to call focus when the viewmanager has not yet been initialized');
    }
}

export function openBrowserViewDevTools() {
    if (status.viewManager) {
        status.viewManager.openViewDevTools();
    }
}

export function focusThreeDotMenu() {
    if (status.mainWindow) {
        status.mainWindow.webContents.focus();
        status.mainWindow.webContents.send(FOCUS_THREE_DOT_MENU);
    }
}

function handleLoadingScreenDataRequest() {
    return {
        darkMode: status.config.darkMode,
    };
}

function handleReactAppInitialized(_, server) {
    if (status.viewManager) {
        status.viewManager.setServerInitialized(server);
    }
}

function handleLoadingScreenAnimationFinished() {
    if (status.viewManager) {
        status.viewManager.hideLoadingScreen();
    }
}

export function updateLoadingScreenDarkMode(darkMode) {
    if (status.viewManager) {
        status.viewManager.updateLoadingScreenDarkMode(darkMode);
    }
}

export function getServerNameByWebContentsId(webContentsId) {
    if (status.viewManager) {
        return status.viewManager.findByWebContent(webContentsId);
    }
    return null;
}

export function close() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused.id === status.mainWindow.id) {
    // TODO: figure out logic for closing
        focused.close();
    } else {
        focused.close();
    }
}
export function maximize() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.maximize();
    }
}
export function minimize() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.minimize();
    }
}
export function restore() {
    const focused = BrowserWindow.getFocusedWindow();
    if (focused) {
        focused.restore();
    }
}

export function reload() {
    const currentView = status.viewManager.getCurrentView();
    if (currentView) {
        status.viewManager.showLoadingScreen();
        currentView.reload();
    }
}

export function sendToFind() {
    const currentView = status.viewManager.getCurrentView();
    if (currentView) {
        currentView.view.webContents.sendInputEvent({type: 'keyDown', keyCode: 'F', modifiers: ['CmdOrCtrl', 'Shift']});
    }
}

export function handleHistory(event, offset) {
    if (status.viewManager) {
        const activeView = status.viewManager.getCurrentView();
        if (activeView && activeView.view.webContents.canGoToOffset(offset)) {
            try {
                activeView.view.webContents.goToOffset(offset);
            } catch (error) {
                log.error(error);
                activeView.load(activeView.server.url);
            }
        }
    }
}
<|endoftext|>


<commit_before>;(function ($, window, document, undefined) {

  var _pluginName = 'hoverDirection',
      _defaults = {
        cssPrefix : 'hover'
      },
      _settings,
      _stylePrefixRegex;

  // -- Private ----------------------------------------------------
  function _convertFaceToDir (face) {
    switch (face) {
      case 0:
        return 'top';
      case 1:
        return 'right';
      case 2:
        return 'bottom';
      case 3:
        return 'left';
      default :
        break;
    }
  }

  function _constructStyleClass (event) {
    var $this       = $(this),
        h           = $this.height(),
        w           = $this.width(),
        x           = (event.pageX - $this.offset().left - (w/2)) * ( w > h ? (h/w) : 1 ),
        y           = (event.pageY - $this.offset().top  - (h/2)) * ( h > w ? (w/h) : 1 ),
        eventDir    = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 ) + 3 )  % 4,
        prefixClass = _settings.cssPrefix,
        typeClass   = (event.type === 'mouseleave') ? 'leave' : 'enter',
        dirClass    = _convertFaceToDir(eventDir),
        prefixRegex = _stylePrefixRegex;

    return prefixClass + '-' + typeClass + '-' + dirClass;
  }

  function _addClass (event) {
    var className = _constructStyleClass.apply(this, [event]);
    $(this).addClass(className);
  }

  function _removeClass() {
    $(this).removeClass(function (index, styleClass) {
      return (styleClass.match(_stylePrefixRegex) || []).join(' ');
    });
  }

  function _handleMouse (event) {
    _removeClass.apply(this);
    _addClass.apply(this, [event]);
  }

  // -- Public ----------------------------------------------------
  var methods = {
    init : function (options) {
      _settings = $.extend(_defaults, options);
      _stylePrefixRegex = new RegExp('\\' + _settings.cssPrefix + '\\S+','g');

      // Main plugin code
      return this.each(function () {
        $(this).on('mouseenter mouseleave', _handleMouse);
      });

    },

    removeClass : function () {
      _removeClass.apply(this);
    },

    destroy : function () {
      _removeClass.apply(this);
      return this.each(function () {
        $(this).off('mouseenter mouseleave', _handleMouse);
      });
    }
  };

  $.fn[_pluginName] = function (method) {
    if (methods[method]) { // Call Method
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method) { // Initialize
      return methods.init.apply(this);
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.' + _pluginName);
    }
  };
} (jQuery, window, document));<commit_msg>Remvoed unused var<commit_after>;(function ($, window, document, undefined) {

  var _pluginName = 'hoverDirection',
      _defaults = {
        cssPrefix : 'hover'
      },
      _settings,
      _stylePrefixRegex;

  // -- Private ----------------------------------------------------
  function _convertFaceToDir (face) {
    switch (face) {
      case 0:
        return 'top';
      case 1:
        return 'right';
      case 2:
        return 'bottom';
      case 3:
        return 'left';
      default :
        break;
    }
  }

  function _constructStyleClass (event) {
    var $this       = $(this),
        h           = $this.height(),
        w           = $this.width(),
        x           = (event.pageX - $this.offset().left - (w/2)) * ( w > h ? (h/w) : 1 ),
        y           = (event.pageY - $this.offset().top  - (h/2)) * ( h > w ? (w/h) : 1 ),
        eventDir    = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 ) + 3 )  % 4,
        prefixClass = _settings.cssPrefix,
        typeClass   = (event.type === 'mouseleave') ? 'leave' : 'enter',
        dirClass    = _convertFaceToDir(eventDir);

    return prefixClass + '-' + typeClass + '-' + dirClass;
  }

  function _addClass (event) {
    var className = _constructStyleClass.apply(this, [event]);
    $(this).addClass(className);
  }

  function _removeClass() {
    $(this).removeClass(function (index, styleClass) {
      return (styleClass.match(_stylePrefixRegex) || []).join(' ');
    });
  }

  function _handleMouse (event) {
    _removeClass.apply(this);
    _addClass.apply(this, [event]);
  }

  // -- Public ----------------------------------------------------
  var methods = {
    init : function (options) {
      _settings = $.extend(_defaults, options);
      _stylePrefixRegex = new RegExp('\\' + _settings.cssPrefix + '\\S+','g');

      // Main plugin code
      return this.each(function () {
        $(this).on('mouseenter mouseleave', _handleMouse);
      });

    },

    removeClass : function () {
      _removeClass.apply(this);
    },

    destroy : function () {
      _removeClass.apply(this);
      return this.each(function () {
        $(this).off('mouseenter mouseleave', _handleMouse);
      });
    }
  };

  $.fn[_pluginName] = function (method) {
    if (methods[method]) { // Call Method
      return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
    } else if (typeof method === 'object' || !method) { // Initialize
      return methods.init.apply(this);
    } else {
      $.error('Method ' + method + ' does not exist on jQuery.' + _pluginName);
    }
  };
} (jQuery, window, document));<|endoftext|>