<commit_before>var m = require('mithril');
var partial = require('chessground').util.partial;
var classSet = require('chessground').util.classSet;
var nodeFullName = require('../util').nodeFullName;
var memberView = require('./studyMembers').view;
var chapterView = require('./studyChapters').view;
var chapterFormView = require('./chapterForm').view;
var commentFormView = require('./commentForm').view;

function form(ctrl) {
  return m('div.lichess_overboard.study_overboard', {
    config: function(el, isUpdate) {
      if (!isUpdate) lichess.loadCss('/assets/stylesheets/material.form.css');
    }
  }, [
    m('a.close.icon[data-icon=L]', {
      onclick: function() {
        ctrl.vm.editing = null;
      }
    }),
    m('h2', 'Edit study'),
    m('form.material.form', {
      onsubmit: function(e) {
        ctrl.update({
          name: e.target.querySelector('#study-name').value,
          visibility: e.target.querySelector('#study-visibility').value
        });
        e.stopPropagation();
        return false;
      }
    }, [
      m('div.game.form-group', [
        m('input#study-name', {
          value: ctrl.data.name
        }),
        m('label.control-label[for=study-name]', 'Name'),
        m('i.bar')
      ]),
      m('div.game.form-group', [
        m('select#study-visibility', [
          ['public', 'Public'],
          ['private', 'Invite only']
        ].map(function(o) {
          return m('option', {
            value: o[0],
            selected: ctrl.data.visibility === o[0]
          }, o[1]);
        })),
        m('label.control-label[for=study-visibility]', 'Visibility'),
        m('i.bar')
      ]),
      m('div.button-container',
        m('button.submit.button.text[type=submit][data-icon=E]', 'Update study')
      )
    ])
  ]);
}

function contextAction(icon, text, handler) {
  return m('a.action', {
    'data-icon': icon,
    onclick: handler
  }, text);
}

function currentComments(ctrl, includingMine) {
  var path = ctrl.vm.path;
  var node = ctrl.vm.node;
  var chapter = ctrl.study.currentChapter();
  var comments = node.comments || [];
  if (!comments.length) return;
  return m('div.study_comments', comments.map(function(comment) {
    var isMine = ctrl.userId === comment.by;
    if (!includingMine && isMine) return;
    var canDelete = isMine || ctrl.study.members.isOwner();
    return m('div.comment', [
      canDelete ? m('a.edit[data-icon=q][title=Delete]', {
        onclick: function() {
          if (confirm('Delete ' + comment.by + '\'s comment?'))
            ctrl.study.commentForm.delete(chapter.id, path, comment.by);
        }
      }) : null,
      isMine ? m('a.edit[data-icon=m][title=Edit]', {
        onclick: function() {
          ctrl.study.commentForm.open(chapter.id, path, node);
        }
      }) : null,
      m('span.user_link.ulpt', {
        'data-href': '/@/' + comment.by
      }, comment.by),
      ' about ',
      m('span.node', nodeFullName(node)),
      ': ',
      m('span.text', comment.text.replace(/\r\n/g, '
'))
    ]);
  }));
}

module.exports = {

  main: function(ctrl) {

    var activeTab = ctrl.vm.tab();

    var makeTab = function(key, name) {
      return m('a', {
        class: key + (activeTab === key ? ' active' : ''),
        onclick: partial(ctrl.vm.tab, key),
      }, name);
    };

    var tabs = m('div.study_tabs', [
      makeTab('members', 'Members'),
      makeTab('chapters', 'Chapters'),
      ctrl.members.isOwner() ? m('a.more', {
        onclick: function() {
          ctrl.vm.editing = !ctrl.vm.editing;
        }
      }, m('i', {
        'data-icon': '['
      })) : null
    ]);

    var panel;
    if (activeTab === 'members') panel = memberView(ctrl);
    else if (activeTab === 'chapters') panel = chapterView.main(ctrl);

    return [
      tabs,
      panel
    ];
  },

  contextMenu: function(ctrl, path, node) {
    return [
      contextAction('c', 'Comment this move', function() {
        ctrl.commentForm.open(ctrl.currentChapter().id, path, node);
      })
    ];
  },

  overboard: function(ctrl) {
    if (ctrl.chapters.form.vm.open) return chapterFormView(ctrl.chapters.form);
    if (ctrl.vm.editing) return form(ctrl);
  },

  underboard: function(ctrl) {
    var path = ctrl.vm.path;
    var node = ctrl.vm.node;
    var commentForm = commentFormView(ctrl.study.commentForm);
    return [
      currentComments(ctrl, !commentForm),
      commentForm,
      m('div.study_buttons', [
        m('span.sync.hint--top', {
          'data-hint': ctrl.study.vm.behind !== false ? 'Synchronize with other players' : 'Disconnect to play local moves'
        }, m('a.button', (function() {
          var attrs = {
            onclick: ctrl.study.toggleSync
          };
          if (ctrl.study.vm.behind > 0) {
            attrs['data-count'] = ctrl.study.vm.behind;
            attrs.class = 'data-count';
          }
          return attrs;
        })(), m('i', {
          'data-icon': ctrl.study.vm.behind !== false ? 'G' : 'Z'
        }))),
        m('a.button.hint--top', {
          'data-hint': 'Download as PGN',
          href: '/study/' + ctrl.study.data.id + '.pgn'
        }, m('i[data-icon=x]')),
        m('a.button.hint--top', {
          'data-hint': 'Comment this position',
          onclick: function() {
            ctrl.study.commentForm.toggle(ctrl.study.currentChapter().id, path, node)
          }
        }, m('i[data-icon=c]'))
      ])
    ];
  }
};
<commit_msg>multiline study comments<commit_after>var m = require('mithril');
var partial = require('chessground').util.partial;
var classSet = require('chessground').util.classSet;
var nodeFullName = require('../util').nodeFullName;
var memberView = require('./studyMembers').view;
var chapterView = require('./studyChapters').view;
var chapterFormView = require('./chapterForm').view;
var commentFormView = require('./commentForm').view;

function form(ctrl) {
  return m('div.lichess_overboard.study_overboard', {
    config: function(el, isUpdate) {
      if (!isUpdate) lichess.loadCss('/assets/stylesheets/material.form.css');
    }
  }, [
    m('a.close.icon[data-icon=L]', {
      onclick: function() {
        ctrl.vm.editing = null;
      }
    }),
    m('h2', 'Edit study'),
    m('form.material.form', {
      onsubmit: function(e) {
        ctrl.update({
          name: e.target.querySelector('#study-name').value,
          visibility: e.target.querySelector('#study-visibility').value
        });
        e.stopPropagation();
        return false;
      }
    }, [
      m('div.game.form-group', [
        m('input#study-name', {
          value: ctrl.data.name
        }),
        m('label.control-label[for=study-name]', 'Name'),
        m('i.bar')
      ]),
      m('div.game.form-group', [
        m('select#study-visibility', [
          ['public', 'Public'],
          ['private', 'Invite only']
        ].map(function(o) {
          return m('option', {
            value: o[0],
            selected: ctrl.data.visibility === o[0]
          }, o[1]);
        })),
        m('label.control-label[for=study-visibility]', 'Visibility'),
        m('i.bar')
      ]),
      m('div.button-container',
        m('button.submit.button.text[type=submit][data-icon=E]', 'Update study')
      )
    ])
  ]);
}

function contextAction(icon, text, handler) {
  return m('a.action', {
    'data-icon': icon,
    onclick: handler
  }, text);
}

function currentComments(ctrl, includingMine) {
  var path = ctrl.vm.path;
  var node = ctrl.vm.node;
  var chapter = ctrl.study.currentChapter();
  var comments = node.comments || [];
  if (!comments.length) return;
  return m('div.study_comments', comments.map(function(comment) {
    var isMine = ctrl.userId === comment.by;
    if (!includingMine && isMine) return;
    var canDelete = isMine || ctrl.study.members.isOwner();
    return m('div.comment', [
      canDelete ? m('a.edit[data-icon=q][title=Delete]', {
        onclick: function() {
          if (confirm('Delete ' + comment.by + '\'s comment?'))
            ctrl.study.commentForm.delete(chapter.id, path, comment.by);
        }
      }) : null,
      isMine ? m('a.edit[data-icon=m][title=Edit]', {
        onclick: function() {
          ctrl.study.commentForm.open(chapter.id, path, node);
        }
      }) : null,
      m('span.user_link.ulpt', {
        'data-href': '/@/' + comment.by
      }, comment.by),
      ' about ',
      m('span.node', nodeFullName(node)),
      ': ',
      m('span.text', m.trust(comment.text.replace(/\n/g, '
')))
    ]);
  }));
}

module.exports = {

  main: function(ctrl) {

    var activeTab = ctrl.vm.tab();

    var makeTab = function(key, name) {
      return m('a', {
        class: key + (activeTab === key ? ' active' : ''),
        onclick: partial(ctrl.vm.tab, key),
      }, name);
    };

    var tabs = m('div.study_tabs', [
      makeTab('members', 'Members'),
      makeTab('chapters', 'Chapters'),
      ctrl.members.isOwner() ? m('a.more', {
        onclick: function() {
          ctrl.vm.editing = !ctrl.vm.editing;
        }
      }, m('i', {
        'data-icon': '['
      })) : null
    ]);

    var panel;
    if (activeTab === 'members') panel = memberView(ctrl);
    else if (activeTab === 'chapters') panel = chapterView.main(ctrl);

    return [
      tabs,
      panel
    ];
  },

  contextMenu: function(ctrl, path, node) {
    return [
      contextAction('c', 'Comment this move', function() {
        ctrl.commentForm.open(ctrl.currentChapter().id, path, node);
      })
    ];
  },

  overboard: function(ctrl) {
    if (ctrl.chapters.form.vm.open) return chapterFormView(ctrl.chapters.form);
    if (ctrl.vm.editing) return form(ctrl);
  },

  underboard: function(ctrl) {
    var path = ctrl.vm.path;
    var node = ctrl.vm.node;
    var commentForm = commentFormView(ctrl.study.commentForm);
    return [
      currentComments(ctrl, !commentForm),
      commentForm,
      m('div.study_buttons', [
        m('span.sync.hint--top', {
          'data-hint': ctrl.study.vm.behind !== false ? 'Synchronize with other players' : 'Disconnect to play local moves'
        }, m('a.button', (function() {
          var attrs = {
            onclick: ctrl.study.toggleSync
          };
          if (ctrl.study.vm.behind > 0) {
            attrs['data-count'] = ctrl.study.vm.behind;
            attrs.class = 'data-count';
          }
          return attrs;
        })(), m('i', {
          'data-icon': ctrl.study.vm.behind !== false ? 'G' : 'Z'
        }))),
        m('a.button.hint--top', {
          'data-hint': 'Download as PGN',
          href: '/study/' + ctrl.study.data.id + '.pgn'
        }, m('i[data-icon=x]')),
        m('a.button.hint--top', {
          'data-hint': 'Comment this position',
          onclick: function() {
            ctrl.study.commentForm.toggle(ctrl.study.currentChapter().id, path, node)
          }
        }, m('i[data-icon=c]'))
      ])
    ];
  }
};
<|endoftext|>


<commit_before>(function() {
  console.log('puppetmaster')

  // Ping / Ping throttling
  var mouseX = 0
  var mouseY = 0
  var hdeck = model.holodeck
  var mousetrack = function(e) {
    mouseX = e.offsetX
    mouseY = e.offsetY
    hdeck = $(this).data('holodeck')
  }

  var lastPingTime = 0
  var ping = function() {
    lastPingTime = Date.now()
    hdeck.unitCommand('ping', mouseX, mouseY, false)
  }

  var maybePing = function() {
    if (Date.now() - lastPingTime > 500) {
      setTimeout(ping, 0)
    }
  }

  // Spectator Announcement, including drop-pod effect
  var lastHover = {name: '', spec: ''}
  var selectedUnit = lastHover

  handlers.puppetmasterUnitSelected = function(spec) {
    var unit = model.unitSpecs[spec]
    selectedUnit = {spec: spec, name: (unit && unit.name) || 'unknown'}
  }

  var liveGameHover = handlers.hover
  handlers.hover = function(payload) {
    liveGameHover(payload)

    if (payload) {
      lastHover = {spec: payload.spec_id || '', name: payload.name || 'unknown'}
    }
  }

  var announceGift = function(who, count, what) {
    model.send_message("team_chat_message",
      {message: who + ' gets ' + count.toString() + ' ' + what });
  }

  var selectedPlayer = ko.computed(function() {
    var index = model.playerControlFlags().indexOf(true)
    if (index == -1) {
      return 'nobody'
    } else {
      return model.players()[index].name
    }
  })

  var dropPod = function() {
    engineCall("unit.debug.setSpecId", "/pa/puppetmaster/drop_pod_launcher.json")
    engineCall("unit.debug.paste")
    engineCall("unit.debug.setSpecId", selectedUnit.spec)
  }

  // Count tracking
  var pasteCount = ko.observable(0)
  pasteCount.subscribe(function(count) {
    api.panels.devmode && api.panels.devmode.message('pasteCount', count);
  })
  var pasteReset = null
  var resetCount = function() {
    announceGift(selectedPlayer(), pasteCount(), selectedUnit.name)

    pasteCount(0)
    clearTimeout(pasteReset)
    pasteReset = null
  }
  var increment = function(n) {
    pasteCount(pasteCount() + n)
    clearTimeout(pasteReset)
    pasteReset = setTimeout(resetCount, 2000)
  }

  // API Hook
  var engineCall = engine.call
  var puppet = function(method) {
    if (method == 'unit.debug.paste') {
      console.log("Sorry, you're a puppet")
      return undefined;
    } else {
      return engineCall.apply(this, arguments);
    }
  }
  var puppetmaster = function(method) {
    if (method == 'unit.debug.paste') {
      pasteUnits(1)
      return
    } else if (method == 'unit.debug.copy') {
      selectedUnit = lastHover
    }

    return engineCall.apply(this, arguments);
  }

  var pasteUnits = function(n) {
    if (!model.cheatAllowCreateUnit()) return
    if (n < 1) return

    dropPod()
    for (var i = 0;i < n;i++) {
      engineCall("unit.debug.paste")
    }
    increment(n)
    maybePing()
  }

  model.pasteBurst = 10
  action_sets.hacks['paste ten units'] = function() {
    pasteUnits(model.pasteBurst)
  }

  // Power control
  var hasBeenPlayer = !model.isSpectator()

  model.isSpectator.subscribe(function(value) {
    if (value == false) {
      hasBeenPlayer = true
    }
  })

  var enableCheats = function() {
    if (hasBeenPlayer) return

    model.cheatAllowChangeControl(true)
    model.cheatAllowCreateUnit(true)
    model.sandbox(true)
    engine.call = puppetmaster
    $('body').on('mousemove', 'holodeck', mousetrack)
  }

  var disableCheats = function() {
    model.devMode(false)
    model.cheatAllowChangeControl(false)
    model.cheatAllowCreateUnit(false)
    model.sandbox(false)
    engine.call = puppet
    $('body').off('mousemove', 'holodeck', mousetrack)
  }

  var toggleCheats = function() {
    console.log('toggle')
    if (model.cheatAllowCreateUnit()) {
      disableCheats()
    } else {
      enableCheats()
    }
  }

  action_sets.hacks['toggle puppetmaster'] = toggleCheats

  // Enable spectator panel updates while open
  var previousPlayerControl = -1
  handlers.puppetmasterSpectatorPanelStatus = function(status) {
    if (model.cheatAllowChangeControl()) {
      if (status) {
        previousPlayerControl = model.playerControlFlags().indexOf(true)
        model.observerModeCalledOnce(false)
        model.startObserverMode()
      } else if (previousPlayerControl != -1) {
        api.panels.devmode.message('puppetmasterRestoreControl', previousPlayerControl)
      }
    }
  }

  model.pasteUnits = pasteUnits

  api.Panel.message('', 'inputmap.reload');
  disableCheats()
})()
<commit_msg>put puppetmaster in team chat message<commit_after>(function() {
  console.log('puppetmaster')

  // Ping / Ping throttling
  var mouseX = 0
  var mouseY = 0
  var hdeck = model.holodeck
  var mousetrack = function(e) {
    mouseX = e.offsetX
    mouseY = e.offsetY
    hdeck = $(this).data('holodeck')
  }

  var lastPingTime = 0
  var ping = function() {
    lastPingTime = Date.now()
    hdeck.unitCommand('ping', mouseX, mouseY, false)
  }

  var maybePing = function() {
    if (Date.now() - lastPingTime > 500) {
      setTimeout(ping, 0)
    }
  }

  // Spectator Announcement, including drop-pod effect
  var lastHover = {name: '', spec: ''}
  var selectedUnit = lastHover

  handlers.puppetmasterUnitSelected = function(spec) {
    var unit = model.unitSpecs[spec]
    selectedUnit = {spec: spec, name: (unit && unit.name) || 'unknown'}
  }

  var liveGameHover = handlers.hover
  handlers.hover = function(payload) {
    liveGameHover(payload)

    if (payload) {
      lastHover = {spec: payload.spec_id || '', name: payload.name || 'unknown'}
    }
  }

  var announceGift = function(who, count, what) {
    model.send_message("team_chat_message",
      {message: ['Puppetmaster gives', who, count.toString(), what].join(' ')});
  }

  var selectedPlayer = ko.computed(function() {
    var index = model.playerControlFlags().indexOf(true)
    if (index == -1) {
      return 'nobody'
    } else {
      return model.players()[index].name
    }
  })

  var dropPod = function() {
    engineCall("unit.debug.setSpecId", "/pa/puppetmaster/drop_pod_launcher.json")
    engineCall("unit.debug.paste")
    engineCall("unit.debug.setSpecId", selectedUnit.spec)
  }

  // Count tracking
  var pasteCount = ko.observable(0)
  pasteCount.subscribe(function(count) {
    api.panels.devmode && api.panels.devmode.message('pasteCount', count);
  })
  var pasteReset = null
  var resetCount = function() {
    announceGift(selectedPlayer(), pasteCount(), selectedUnit.name)

    pasteCount(0)
    clearTimeout(pasteReset)
    pasteReset = null
  }
  var increment = function(n) {
    pasteCount(pasteCount() + n)
    clearTimeout(pasteReset)
    pasteReset = setTimeout(resetCount, 2000)
  }

  // API Hook
  var engineCall = engine.call
  var puppet = function(method) {
    if (method == 'unit.debug.paste') {
      console.log("Sorry, you're a puppet")
      return undefined;
    } else {
      return engineCall.apply(this, arguments);
    }
  }
  var puppetmaster = function(method) {
    if (method == 'unit.debug.paste') {
      pasteUnits(1)
      return
    } else if (method == 'unit.debug.copy') {
      selectedUnit = lastHover
    }

    return engineCall.apply(this, arguments);
  }

  var pasteUnits = function(n) {
    if (!model.cheatAllowCreateUnit()) return
    if (n < 1) return

    dropPod()
    for (var i = 0;i < n;i++) {
      engineCall("unit.debug.paste")
    }
    increment(n)
    maybePing()
  }

  model.pasteBurst = 10
  action_sets.hacks['paste ten units'] = function() {
    pasteUnits(model.pasteBurst)
  }

  // Power control
  var hasBeenPlayer = !model.isSpectator()

  model.isSpectator.subscribe(function(value) {
    if (value == false) {
      hasBeenPlayer = true
    }
  })

  var enableCheats = function() {
    if (hasBeenPlayer) return

    model.cheatAllowChangeControl(true)
    model.cheatAllowCreateUnit(true)
    model.sandbox(true)
    engine.call = puppetmaster
    $('body').on('mousemove', 'holodeck', mousetrack)
  }

  var disableCheats = function() {
    model.devMode(false)
    model.cheatAllowChangeControl(false)
    model.cheatAllowCreateUnit(false)
    model.sandbox(false)
    engine.call = puppet
    $('body').off('mousemove', 'holodeck', mousetrack)
  }

  var toggleCheats = function() {
    console.log('toggle')
    if (model.cheatAllowCreateUnit()) {
      disableCheats()
    } else {
      enableCheats()
    }
  }

  action_sets.hacks['toggle puppetmaster'] = toggleCheats

  // Enable spectator panel updates while open
  var previousPlayerControl = -1
  handlers.puppetmasterSpectatorPanelStatus = function(status) {
    if (model.cheatAllowChangeControl()) {
      if (status) {
        previousPlayerControl = model.playerControlFlags().indexOf(true)
        model.observerModeCalledOnce(false)
        model.startObserverMode()
      } else if (previousPlayerControl != -1) {
        api.panels.devmode.message('puppetmasterRestoreControl', previousPlayerControl)
      }
    }
  }

  model.pasteUnits = pasteUnits

  api.Panel.message('', 'inputmap.reload');
  disableCheats()
})()
<|endoftext|>


<commit_before>var net = require('net');

var shortid = require('shortid');
var Discord = require('discord.io');

class Bot {
	constructor(key, disnode) {
		this.key = key;
		this.client = {};
		this.disnode = disnode;
		this.bind_onMessage = null;
		this.isRemote = false;
		this.remoteID = "";
	}

	Connect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			console.log(self.disnode.botConfig.balancerEnabled);

			if (self.disnode.botConfig.balancerEnabled == true) {
				if (!self.disnode.botConfig.balancerIP) {
					reject("No Load Balancer IP!");
				}
				if (!self.disnode.botConfig.balancerPORT) {
					reject("No Load Balancer Port!");
				}

				self.client.type == "REMOTE";
				self.client = new net.Socket();
				self.client.connect(self.disnode.botConfig.balancerPORT, self.disnode.botConfig.balancerIP, function () {
					self.remoteID = shortid.generate();
					self.client.write(JSON.stringify({
						type: "REGISTER",
						id: self.remoteID
					}))
					self.client.on('data', (data) => self.ParseRemoteCommand(data));
					self.isRemote = true;
					resolve("Connected to remote Load Balancer!");
					return;
				});
			} else {
				self.client = new Discord.Client({
					autorun: true,
					token: self.key
				});

				self.client.on('ready', function (event) {
					self.SetUpLocalBinds();
					resolve();
				});

			}

		});
	}

	Disconnect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			self.client = {};
			self.client.destroy().then(resolve).catch(reject);
		});
	}

	ParseRemoteCommand(command) {
		var self = this;
		var obj = JSON.parse(command);
		console.log(obj);
		console.log(obj.type);
		switch (obj.type) {
		case "RECV_MESSAGE":
			console.log(obj.data);
			if (self.bind_onMessage) {
				self.bind_onMessage(obj.data);
			} else {
				console.log('NO BIND!');
			}
		}
	}

	SetUpLocalBinds() {
		var self = this;
		self.client.on('message', function (user, userID, channelID, message, event) {
			if (self.bind_onMessage) {
				var _server = "DM";
				if(self.client.channels[channelID]){
						_server = self.client.channels[channelID].guild_id;
				}
				var msgObject = {
					user: user,
					userID: userID,
					channel: channelID,
					message: message,
					server: _server
				}
				self.bind_onMessage(msgObject);
			}
		});
	}

	bindOnMessage(msgFunction) {
		console.log('Binding!');
		this.bind_onMessage = msgFunction;
	}

	SendMessage(channel, msg) {
		var self = this;
		if (self.isRemote) {
			var msgOBJ = {
				channel: channel,
				msg: msg
			};
			var commandObj = {
				type: "SEND_MESSAGE",
				id: self.remoteID,
				data: msgOBJ
			};

			self.client.write(JSON.stringify(commandObj));
		}else{
			self.client.sendMessage({
					 to: channel,
					 message: msg
			 });
		}
	}
	SendEmbed(channel, embed){
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: embed
		});
	}
	SendCompactEmbed(channel, title, body) {
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: {
				color: 3447003,
				author: {},
				fields: [{
					name: title,
					inline: false,
					value: body,
				}],
				footer: {}
			}
		});
	}
}

module.exports = Bot;
<commit_msg>updated some code to d.io added a Restart function to bot.js<commit_after>var net = require('net');

var shortid = require('shortid');
var Discord = require('discord.io');

class Bot {
	constructor(key, disnode) {
		this.key = key;
		this.client = {};
		this.disnode = disnode;
		this.bind_onMessage = null;
		this.isRemote = false;
		this.remoteID = "";
	}

	Connect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			console.log(self.disnode.botConfig.balancerEnabled);

			if (self.disnode.botConfig.balancerEnabled == true) {
				if (!self.disnode.botConfig.balancerIP) {
					reject("No Load Balancer IP!");
				}
				if (!self.disnode.botConfig.balancerPORT) {
					reject("No Load Balancer Port!");
				}

				self.client.type == "REMOTE";
				self.client = new net.Socket();
				self.client.connect(self.disnode.botConfig.balancerPORT, self.disnode.botConfig.balancerIP, function () {
					self.remoteID = shortid.generate();
					self.client.write(JSON.stringify({
						type: "REGISTER",
						id: self.remoteID
					}))
					self.client.on('data', (data) => self.ParseRemoteCommand(data));
					self.isRemote = true;
					resolve("Connected to remote Load Balancer!");
					return;
				});
			} else {
				self.client = new Discord.Client({
					autorun: true,
					token: self.key
				});

				self.client.on('ready', function (event) {
					self.SetUpLocalBinds();
					resolve();
				});

			}

		});
	}

	Disconnect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			self.client.disconnect();
			resolve();
		});
	}

	Restart() {
		var self = this;
		self.Disconnect().then(function (){
			self.client.connect()
		});
	}

	ParseRemoteCommand(command) {
		var self = this;
		var obj = JSON.parse(command);
		console.log(obj);
		console.log(obj.type);
		switch (obj.type) {
		case "RECV_MESSAGE":
			console.log(obj.data);
			if (self.bind_onMessage) {
				self.bind_onMessage(obj.data);
			} else {
				console.log('NO BIND!');
			}
		}
	}

	SetUpLocalBinds() {
		var self = this;
		self.client.on('message', function (user, userID, channelID, message, event) {
			if (self.bind_onMessage) {
				var _server = "DM";
				if(self.client.channels[channelID]){
						_server = self.client.channels[channelID].guild_id;
				}
				var msgObject = {
					user: user,
					userID: userID,
					channel: channelID,
					message: message,
					server: _server
				}
				self.bind_onMessage(msgObject);
			}
		});
	}

	bindOnMessage(msgFunction) {
		console.log('Binding!');
		this.bind_onMessage = msgFunction;
	}

	SendMessage(channel, msg) {
		var self = this;
		if (self.isRemote) {
			var msgOBJ = {
				channel: channel,
				msg: msg
			};
			var commandObj = {
				type: "SEND_MESSAGE",
				id: self.remoteID,
				data: msgOBJ
			};

			self.client.write(JSON.stringify(commandObj));
		}else{
			self.client.sendMessage({
					 to: channel,
					 message: msg
			 });
		}
	}
	SendEmbed(channel, embed){
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: embed
		});
	}
	SendCompactEmbed(channel, title, body) {
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: {
				color: 3447003,
				author: {},
				fields: [{
					name: title,
					inline: false,
					value: body,
				}],
				footer: {}
			}
		});
	}
}

module.exports = Bot;
<|endoftext|>


<commit_before>/*
 * @brief contains functions for bootstrap modal handling
 */
$(document).ready(function(){
	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body, footer').on('hidden.bs.modal', '.modal', function () {
		$(this).removeData('bs.modal');
		$("#modal-title").empty();
		$('#modal-body').empty();
		// remove the file browser from jot (else we would have problems
		// with ajaxupload
		$(".fbrowser").remove();
	});

	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body').on('hidden.bs.modal', '#jot-modal', function () {
		// restore cached jot at its hidden position ("#jot-content")
		$("#jot-content").append(jotcache);
		// clear the jotcache
		jotcache = '';
	});

	// Add Colorbox for viewing Network page images
	//var cBoxClasses = new Array();
	$("body").on("click", ".wall-item-body a img", function(){
		var aElem = $(this).parent();
		var imgHref = aElem.attr("href");

		// We need to make sure we only put a Colorbox on links to Friendica images
		// We'll try to do this by looking for links of the form
		// .../photo/ab803d8eg08daf85023adfec08 (with nothing more following), in hopes
		// that that will be unique enough
		if(imgHref.match(/\/photo\/[a-fA-F0-9]+(-[0-9]\.[\w]+?)?$/)) {

			// Add a unique class to all the images of a certain post, to allow scrolling through
			var cBoxClass = $(this).closest(".wall-item-body").attr("id") + "-lightbox";
			$(this).addClass(cBoxClass);

//			if( $.inArray(cBoxClass, cBoxClasses) < 0 ) {
//				cBoxClasses.push(cBoxClass);
//			}

			aElem.colorbox({
				maxHeight: '90%',
				photo: true, // Colorbox doesn't recognize a URL that don't end in .jpg, etc. as a photo
				rel: cBoxClass //$(this).attr("class").match(/wall-item-body-[\d]+-lightbox/)[0]
			});
		}
	});

	// Navbar login
	$("body").on("click", "#nav-login", function(e){
		e.preventDefault();
		Dialog.show(this.href, this.dataset.originalTitle || this.title);
	});

	// Jot nav menu.
	$("body").on("click", "#jot-modal .jot-nav li a", function(e){
		e.preventDefault();
		toggleJotNav(this);
	});

	// bookmarklet page needs an jot modal which appears automatically
	if(window.location.pathname.indexOf("/bookmarklet") >=0 && $("#jot-modal").length){
		jotShow();
	}

	// Open filebrowser for elements with the class "image-select"
	// The following part handles the filebrowser for field_fileinput.tpl
	$("body").on("click", ".image-select", function(){
		// set a extra attribute to mark the clicked button
		this.setAttribute("image-input", "select");
		Dialog.doImageBrowser("input");
	});

	// Insert filebrowser images into the input field (field_fileinput.tpl)
	$("body").on("fbrowser.image.input", function(e, filename, embedcode, id, img) {
		// select the clicked button by it's attribute
		var elm = $("[image-input='select']");
		// select the input field which belongs to this button
		var input = elm.parent(".input-group").children("input");
		// remove the special indicator attribut from the button
		elm.removeAttr("image-input");
		// inserte the link from the image into the input field
		input.val(img);

	});
});

// overwrite Dialog.show from main js to load the filebrowser into a bs modal
Dialog.show = function(url, title="") {
	var modal = $('#modal').modal();
	modal.find("#modal-header h4").html(title);
	modal
		.find('#modal-body')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				modal.show();

				$(function() {Dialog._load(url);});
			}
		});
};

// overwrite the function _get_url from main.js
Dialog._get_url = function(type, name, id) {
	var hash = name;
	if (id !== undefined) hash = hash + "-" + id;
	return "fbrowser/"+type+"/?mode=none#"+hash;
};

// does load the filebrowser into the jot modal
Dialog.showJot = function() {
	var type = "image";
	var name = "main";

	var url = Dialog._get_url(type, name);
	if(($(".modal-body #jot-fbrowser-wrapper .fbrowser").length) < 1 ) {
		// load new content to fbrowser window
		$("#jot-fbrowser-wrapper").load(url,function(responseText, textStatus){
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				$(function() {Dialog._load(url);});
			}
		});
	}
};

// init the filebrowser after page load
Dialog._load = function(url) {
	// get nickname & filebrowser type from the modal content
	var nickname = $("#fb-nickname").attr("value");
	var type = $("#fb-type").attr("value");

	// try to fetch the hash form the url
	var match = url.match(/fbrowser\/[a-z]+\/\?mode=none(.*)/);
	if (match===null) return; //not fbrowser
	var hash = match[1];

	// initialize the filebrowser
	var jsbrowser = function() {
		FileBrowser.init(nickname, type, hash);
	};
	loadScript("view/theme/frio/js/filebrowser.js", jsbrowser);
};

/**
 * @brief Add first element with the class "heading" as modal title
 *
 * Note: this should be really done in the template
 * and is the solution where we havent done it until this
 * moment or where it isn't possible because of design
 */
function loadModalTitle() {
	// clear the text of the title
	$("#modal-title").empty();

	// hide the first element with the class "heading" of the modal body
	$("#modal-body .heading").first().hide();

	var title = "";

	// get the text of the first element with "heading" class
	title = $("#modal-body .heading").first().text();

	// for event modals we need some speacial handling
	if($("#modal-body .event-wrapper .event-summary").length) {
		title = ' ';
		var eventsum = $("#modal-body .event-wrapper .event-summary").text();
		title = title + eventsum;
	}

	// and append it to modal title
	if (title!=="") {
		$("#modal-title").append(title);
	}
}

// This function loads html content from a friendica page
// into a modal
function addToModal(url) {
	var char = qOrAmp(url);

	url = url + char + 'mode=none';
	var modal = $('#modal').modal();

	modal
		.find('#modal-body')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				modal.show();

				//Get first element with the class "heading"
				//and use it as title
				loadModalTitle();
			}
		});
}

// Add a element (by it's id) to a bootstrap modal
function addElmToModal(id) {
	var elm = $(id).html();
	var modal = $('#modal').modal();

	modal
		.find('#modal-body')
		.append(elm)
		.modal.show;

	loadModalTitle();
}

// function to load the html from the edit post page into
// the jot modal
function editpost(url) {
	// next to normel posts the post can be an event post. The event posts don't
	// use the normal Jot modal. For event posts we will use a normal modal
	// But first we have to test if the url links to an event. So we will split up
	// the url in its parts
	var splitURL = parseUrl(url);
	// Test if in the url path containing "events/event". If the path containing this
	// expression then we will call the addToModal function and exit this function at
	// this point
	if (splitURL.path.indexOf('events/event') > -1) {
		addToModal(splitURL.path);
		return;
	}

	var modal = $('#jot-modal').modal();
	url = url + " #jot-sections";

	//var rand_num = random_digits(12);
	$(".jot-nav .jot-perms-lnk").parent("li").addClass("hidden");

	// For editpost we load the modal html of "jot-sections" of the edit page. So we would have two jot forms in
	// the page html. To avoid js conflicts we store the original jot in the variable jotcache.
	// After closing the modal original jot should be restored at its orginal position in the html structure.
	jotcache = $("#jot-content > #jot-sections");

	// remove the original Jot as long as the edit Jot is open
	jotcache.remove();

	// add the class "edit" to the modal to have some kind of identifier to
	// have the possibility to e.g. put special event-listener
	$("#jot-modal").addClass("edit-jot");

	jotreset();

	modal
		.find('#jot-modal-content')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				// get the item type and hide the input for title and category if it isn't needed
				var type = $(responseText).find("#profile-jot-form input[name='type']").val();
				if(type === "wall-comment" || type === "remote-comment")
				{
					// hide title and category input fields because we don't
					$("#profile-jot-form #jot-title-wrap").hide();
					$("#profile-jot-form #jot-category-wrap").hide();
				}

				modal.show();
				$("#jot-popup").show();
			}
		});
}

// remove content from the jot modal
function jotreset() {
	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body').on('hidden.bs.modal', '#jot-modal.edit-jot', function () {
		$(this).removeData('bs.modal');
		$(".jot-nav .jot-perms-lnk").parent("li").removeClass("hidden");
		$("#profile-jot-form #jot-title-wrap").show();
		$("#profile-jot-form #jot-category-wrap").show();

		// the following was commented out because it is needed anymore
		// because we changed the behavior at an other place
	//		var rand_num = random_digits(12);
	//		$('#jot-title, #jot-category, #profile-jot-text').val("");
	//		$( "#profile-jot-form input[name='type']" ).val("wall");
	//		$( "#profile-jot-form input[name='post_id']" ).val("");
	//		$( "#profile-jot-form input[name='post_id_random']" ).val(rand_num);

		// remove the "edit-jot" class so we can the standard behavior on close
		$("#jot-modal.edit-jot").removeClass("edit-jot");
		$("#jot-modal-content").empty();
	});
}

// Give the active "jot-nav" list element the class "active"
function toggleJotNav (elm) {
	// select all li of jot-nav and remove the active class
	$(".jot-nav li").removeClass("active");
	// add the active class to the parent of the link which was selected
	$(elm).parent("li").addClass("active");
}

// Wall Message needs a special handling because in some cases
// it redirects you to your own server. In such cases we can't
// load it into a modal
function openWallMessage(url) {
	// split the the url in its parts
	var parts = parseUrl(url);

	// If the host isn't the same we can't load it in a modal.
	// So we will go to to the url directly
	if( ("host" in parts) && (parts.host !== window.location.host)) {
		window.location.href = url;
	} else {
		// otherwise load the wall message into a modal
		addToModal(url);
	}
}
<commit_msg>Frio Bugfix: correct wrong use of default arg 'title' in modal.js<commit_after>/*
 * @brief contains functions for bootstrap modal handling
 */
$(document).ready(function(){
	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body, footer').on('hidden.bs.modal', '.modal', function () {
		$(this).removeData('bs.modal');
		$("#modal-title").empty();
		$('#modal-body').empty();
		// remove the file browser from jot (else we would have problems
		// with ajaxupload
		$(".fbrowser").remove();
	});

	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body').on('hidden.bs.modal', '#jot-modal', function () {
		// restore cached jot at its hidden position ("#jot-content")
		$("#jot-content").append(jotcache);
		// clear the jotcache
		jotcache = '';
	});

	// Add Colorbox for viewing Network page images
	//var cBoxClasses = new Array();
	$("body").on("click", ".wall-item-body a img", function(){
		var aElem = $(this).parent();
		var imgHref = aElem.attr("href");

		// We need to make sure we only put a Colorbox on links to Friendica images
		// We'll try to do this by looking for links of the form
		// .../photo/ab803d8eg08daf85023adfec08 (with nothing more following), in hopes
		// that that will be unique enough
		if(imgHref.match(/\/photo\/[a-fA-F0-9]+(-[0-9]\.[\w]+?)?$/)) {

			// Add a unique class to all the images of a certain post, to allow scrolling through
			var cBoxClass = $(this).closest(".wall-item-body").attr("id") + "-lightbox";
			$(this).addClass(cBoxClass);

//			if( $.inArray(cBoxClass, cBoxClasses) < 0 ) {
//				cBoxClasses.push(cBoxClass);
//			}

			aElem.colorbox({
				maxHeight: '90%',
				photo: true, // Colorbox doesn't recognize a URL that don't end in .jpg, etc. as a photo
				rel: cBoxClass //$(this).attr("class").match(/wall-item-body-[\d]+-lightbox/)[0]
			});
		}
	});

	// Navbar login
	$("body").on("click", "#nav-login", function(e){
		e.preventDefault();
		Dialog.show(this.href, this.dataset.originalTitle || this.title);
	});

	// Jot nav menu.
	$("body").on("click", "#jot-modal .jot-nav li a", function(e){
		e.preventDefault();
		toggleJotNav(this);
	});

	// bookmarklet page needs an jot modal which appears automatically
	if(window.location.pathname.indexOf("/bookmarklet") >=0 && $("#jot-modal").length){
		jotShow();
	}

	// Open filebrowser for elements with the class "image-select"
	// The following part handles the filebrowser for field_fileinput.tpl
	$("body").on("click", ".image-select", function(){
		// set a extra attribute to mark the clicked button
		this.setAttribute("image-input", "select");
		Dialog.doImageBrowser("input");
	});

	// Insert filebrowser images into the input field (field_fileinput.tpl)
	$("body").on("fbrowser.image.input", function(e, filename, embedcode, id, img) {
		// select the clicked button by it's attribute
		var elm = $("[image-input='select']");
		// select the input field which belongs to this button
		var input = elm.parent(".input-group").children("input");
		// remove the special indicator attribut from the button
		elm.removeAttr("image-input");
		// inserte the link from the image into the input field
		input.val(img);

	});
});

// overwrite Dialog.show from main js to load the filebrowser into a bs modal
Dialog.show = function(url, title) {
	if (typeof(title)==='undefined') {
		title = "";
	}

	var modal = $('#modal').modal();
	modal.find("#modal-header h4").html(title);
	modal
		.find('#modal-body')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				modal.show();

				$(function() {Dialog._load(url);});
			}
		});
};

// overwrite the function _get_url from main.js
Dialog._get_url = function(type, name, id) {
	var hash = name;
	if (id !== undefined) hash = hash + "-" + id;
	return "fbrowser/"+type+"/?mode=none#"+hash;
};

// does load the filebrowser into the jot modal
Dialog.showJot = function() {
	var type = "image";
	var name = "main";

	var url = Dialog._get_url(type, name);
	if(($(".modal-body #jot-fbrowser-wrapper .fbrowser").length) < 1 ) {
		// load new content to fbrowser window
		$("#jot-fbrowser-wrapper").load(url,function(responseText, textStatus){
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				$(function() {Dialog._load(url);});
			}
		});
	}
};

// init the filebrowser after page load
Dialog._load = function(url) {
	// get nickname & filebrowser type from the modal content
	var nickname = $("#fb-nickname").attr("value");
	var type = $("#fb-type").attr("value");

	// try to fetch the hash form the url
	var match = url.match(/fbrowser\/[a-z]+\/\?mode=none(.*)/);
	if (match===null) return; //not fbrowser
	var hash = match[1];

	// initialize the filebrowser
	var jsbrowser = function() {
		FileBrowser.init(nickname, type, hash);
	};
	loadScript("view/theme/frio/js/filebrowser.js", jsbrowser);
};

/**
 * @brief Add first element with the class "heading" as modal title
 *
 * Note: this should be really done in the template
 * and is the solution where we havent done it until this
 * moment or where it isn't possible because of design
 */
function loadModalTitle() {
	// clear the text of the title
	$("#modal-title").empty();

	// hide the first element with the class "heading" of the modal body
	$("#modal-body .heading").first().hide();

	var title = "";

	// get the text of the first element with "heading" class
	title = $("#modal-body .heading").first().text();

	// for event modals we need some speacial handling
	if($("#modal-body .event-wrapper .event-summary").length) {
		title = ' ';
		var eventsum = $("#modal-body .event-wrapper .event-summary").text();
		title = title + eventsum;
	}

	// and append it to modal title
	if (title!=="") {
		$("#modal-title").append(title);
	}
}

// This function loads html content from a friendica page
// into a modal
function addToModal(url) {
	var char = qOrAmp(url);

	url = url + char + 'mode=none';
	var modal = $('#modal').modal();

	modal
		.find('#modal-body')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				modal.show();

				//Get first element with the class "heading"
				//and use it as title
				loadModalTitle();
			}
		});
}

// Add a element (by it's id) to a bootstrap modal
function addElmToModal(id) {
	var elm = $(id).html();
	var modal = $('#modal').modal();

	modal
		.find('#modal-body')
		.append(elm)
		.modal.show;

	loadModalTitle();
}

// function to load the html from the edit post page into
// the jot modal
function editpost(url) {
	// next to normel posts the post can be an event post. The event posts don't
	// use the normal Jot modal. For event posts we will use a normal modal
	// But first we have to test if the url links to an event. So we will split up
	// the url in its parts
	var splitURL = parseUrl(url);
	// Test if in the url path containing "events/event". If the path containing this
	// expression then we will call the addToModal function and exit this function at
	// this point
	if (splitURL.path.indexOf('events/event') > -1) {
		addToModal(splitURL.path);
		return;
	}

	var modal = $('#jot-modal').modal();
	url = url + " #jot-sections";

	//var rand_num = random_digits(12);
	$(".jot-nav .jot-perms-lnk").parent("li").addClass("hidden");

	// For editpost we load the modal html of "jot-sections" of the edit page. So we would have two jot forms in
	// the page html. To avoid js conflicts we store the original jot in the variable jotcache.
	// After closing the modal original jot should be restored at its orginal position in the html structure.
	jotcache = $("#jot-content > #jot-sections");

	// remove the original Jot as long as the edit Jot is open
	jotcache.remove();

	// add the class "edit" to the modal to have some kind of identifier to
	// have the possibility to e.g. put special event-listener
	$("#jot-modal").addClass("edit-jot");

	jotreset();

	modal
		.find('#jot-modal-content')
		.load(url, function (responseText, textStatus) {
			if ( textStatus === 'success' ||
				textStatus === 'notmodified')
			{
				// get the item type and hide the input for title and category if it isn't needed
				var type = $(responseText).find("#profile-jot-form input[name='type']").val();
				if(type === "wall-comment" || type === "remote-comment")
				{
					// hide title and category input fields because we don't
					$("#profile-jot-form #jot-title-wrap").hide();
					$("#profile-jot-form #jot-category-wrap").hide();
				}

				modal.show();
				$("#jot-popup").show();
			}
		});
}

// remove content from the jot modal
function jotreset() {
	// Clear bs modal on close
	// We need this to prevent that the modal displays old content
	$('body').on('hidden.bs.modal', '#jot-modal.edit-jot', function () {
		$(this).removeData('bs.modal');
		$(".jot-nav .jot-perms-lnk").parent("li").removeClass("hidden");
		$("#profile-jot-form #jot-title-wrap").show();
		$("#profile-jot-form #jot-category-wrap").show();

		// the following was commented out because it is needed anymore
		// because we changed the behavior at an other place
	//		var rand_num = random_digits(12);
	//		$('#jot-title, #jot-category, #profile-jot-text').val("");
	//		$( "#profile-jot-form input[name='type']" ).val("wall");
	//		$( "#profile-jot-form input[name='post_id']" ).val("");
	//		$( "#profile-jot-form input[name='post_id_random']" ).val(rand_num);

		// remove the "edit-jot" class so we can the standard behavior on close
		$("#jot-modal.edit-jot").removeClass("edit-jot");
		$("#jot-modal-content").empty();
	});
}

// Give the active "jot-nav" list element the class "active"
function toggleJotNav (elm) {
	// select all li of jot-nav and remove the active class
	$(".jot-nav li").removeClass("active");
	// add the active class to the parent of the link which was selected
	$(elm).parent("li").addClass("active");
}

// Wall Message needs a special handling because in some cases
// it redirects you to your own server. In such cases we can't
// load it into a modal
function openWallMessage(url) {
	// split the the url in its parts
	var parts = parseUrl(url);

	// If the host isn't the same we can't load it in a modal.
	// So we will go to to the url directly
	if( ("host" in parts) && (parts.host !== window.location.host)) {
		window.location.href = url;
	} else {
		// otherwise load the wall message into a modal
		addToModal(url);
	}
}
<|endoftext|>


<commit_before>var net = require('net');

var shortid = require('shortid');
var Discord = require('discord.io');

class Bot {
	constructor(key, disnode) {
		this.key = key;
		this.client = {};
		this.disnode = disnode;
		this.bind_onMessage = null;
		this.isRemote = false;
		this.remoteID = "";
	}

	Connect() {
		var self = this;
		return new Promise(function (resolve, reject) {

			if (self.disnode.botConfig.balancerEnabled == true) {
				if (!self.disnode.botConfig.balancerIP) {
					reject("No Load Balancer IP!");
				}
				if (!self.disnode.botConfig.balancerPORT) {
					reject("No Load Balancer Port!");
				}

				self.client.type == "REMOTE";
				self.client = new net.Socket();
				self.client.connect(self.disnode.botConfig.balancerPORT, self.disnode.botConfig.balancerIP, function () {
					self.remoteID = shortid.generate();
					self.client.write(JSON.stringify({
						type: "REGISTER",
						id: self.remoteID
					}))
					self.client.on('data', (data) => self.ParseRemoteCommand(data));
					self.isRemote = true;
					resolve("Connected to remote Load Balancer!");
					return;
				});
			} else {
				self.client = new Discord.Client({
					autorun: true,
					token: self.key
				});

				self.client.on('ready', function (event) {
					self.SetUpLocalBinds();
					resolve();
				});

			}

		});
	}

	Disconnect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			self.client.disconnect();
			resolve();
		});
	}

	Restart() {
		var self = this;
		self.Disconnect().then(function (){
			self.client.connect()
		});
	}

	ParseRemoteCommand(command) {
		var self = this;
		var obj = JSON.parse(command);

		switch (obj.type) {
		case "RECV_MESSAGE":

			if (self.bind_onMessage) {
				self.bind_onMessage(obj.data);
			} else {

			}
		}
	}

	SetUpLocalBinds() {
		var self = this;
		self.client.on('message', function (user, userID, channelID, message, event) {
			if (self.bind_onMessage) {
				var _server = "DM";
				if(self.client.channels[channelID]){
						_server = self.client.channels[channelID].guild_id;
				}
				var msgObject = {
					user: user,
					userID: userID,
					channel: channelID,
					message: message,
					server: _server
				}
				self.bind_onMessage(msgObject);
			}
		});
	}

	bindOnMessage(msgFunction) {
		this.bind_onMessage = msgFunction;
	}

	SendMessage(channel, msg, typing=false, tts=false) {
		var self = this;
		if (self.isRemote) {
			var msgOBJ = {
				channel: channel,
				msg: msg,
				typing: typing,
                tts: tts
			};
			var commandObj = {
				type: "SEND_MESSAGE",
				id: self.remoteID,
				data: msgOBJ
			};

			self.client.write(JSON.stringify(commandObj));
		}else{
			return new Promise(function(resolve, reject) {
				self.client.sendMessage({
					to: channel,
					message: msg,
		 			typing: typing,
		      tts: tts
				}, function(err, resp) {
					if(err){
						reject(err);
					}else {
						resolve(resp);
					}
				});
			});
		}
	}
	SendEmbed(channel, embed){
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: embed
		});
	}
	SendCompactEmbed(channel, title, body, color=3447003) {
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: {
				color: color,
				author: {},
				fields: [{
					name: title,
					inline: false,
					value: body,
				}],
				footer: {}
			}
		});
	}
	SetStatus(status){
		var self = this;
		self.client.setPresence({game: {name: status}});
	}

	SetUsername(name){
		var self = this;
		self.client.editUserInfo({username: name});
	}

	JoinVoiceChannel(voiceID){
		var self = this;
		this.client.joinVoiceChannel(voiceID,function(err){
			if(err){
				console.log(err);
			}else{
			}
		})
	}

  LeaveVoiceChannel(voiceID){
		var self = this;
		this.client.leaveVoiceChannel(voiceID,function(err){
			if(err){
				console.log(err);
			}else{
			}
		})
	}
		JoinUsersVoiceChannel(serverID, userID){
		var user = this.GetUserByID(serverID, userID);
		if(!user){
			return;
		}
		this.JoinVoiceChannel(user.voice_channel_id);
	}
    LeaveUsersVoiceChannel(serverID, userID) {
    var user = this.GetUserByID(serverID, userID);
		if(!user){
			return;
		}
			this.LeaveVoiceChannel(user.voice_channel_id);
  }
		GetServerByID(id){
		var servers = this.client.servers;
		return servers[id];
	}
		GetUserByID(serverId, userId){
		var server = this.GetServerByID(serverId);
		if(!server){
			return;
		}
		return server.members[userId];
	}
		GetUserRoles(serverId, userId){
		var user = this.GetUserByID(serverId, userId);
		if(!user){
			return;
		}
		return user.roles;
	}

	GetRoleById(serverId, roleId){
		var server = this.GetServerByID(serverId);
		if(!server){
			return;
		}
		return server.roles[roleId];
	}
	GetBotInfo(){
		var self = this;
		return {
			username: self.client.username,
			id: self.client.id,
			avatar: self.client.avatar,
			bot: self.client.bot,
			discriminator: self.client.discriminator,
		}
	}
	GetUserInfo(UserID){
		var self = this;
		return self.client.users[userID];
	}
}

module.exports = Bot;
<commit_msg>Server name change support.<commit_after>var net = require('net');

var shortid = require('shortid');
var Discord = require('discord.io');

class Bot {
	constructor(key, disnode) {
		this.key = key;
		this.client = {};
		this.disnode = disnode;
		this.bind_onMessage = null;
		this.isRemote = false;
		this.remoteID = "";
	}

	Connect() {
		var self = this;
		return new Promise(function (resolve, reject) {

			if (self.disnode.botConfig.balancerEnabled == true) {
				if (!self.disnode.botConfig.balancerIP) {
					reject("No Load Balancer IP!");
				}
				if (!self.disnode.botConfig.balancerPORT) {
					reject("No Load Balancer Port!");
				}

				self.client.type == "REMOTE";
				self.client = new net.Socket();
				self.client.connect(self.disnode.botConfig.balancerPORT, self.disnode.botConfig.balancerIP, function () {
					self.remoteID = shortid.generate();
					self.client.write(JSON.stringify({
						type: "REGISTER",
						id: self.remoteID
					}))
					self.client.on('data', (data) => self.ParseRemoteCommand(data));
					self.isRemote = true;
					resolve("Connected to remote Load Balancer!");
					return;
				});
			} else {
				self.client = new Discord.Client({
					autorun: true,
					token: self.key
				});

				self.client.on('ready', function (event) {
					self.SetUpLocalBinds();
					resolve();
				});

			}

		});
	}

	Disconnect() {
		var self = this;
		return new Promise(function (resolve, reject) {
			self.client.disconnect();
			resolve();
		});
	}

	Restart() {
		var self = this;
		self.Disconnect().then(function (){
			self.client.connect()
		});
	}

	ParseRemoteCommand(command) {
		var self = this;
		var obj = JSON.parse(command);

		switch (obj.type) {
		case "RECV_MESSAGE":

			if (self.bind_onMessage) {
				self.bind_onMessage(obj.data);
			} else {

			}
		}
	}

	SetUpLocalBinds() {
		var self = this;
		self.client.on('message', function (user, userID, channelID, message, event) {
			if (self.bind_onMessage) {
				var _server = "DM";
				if(self.client.channels[channelID]){
						_server = self.client.channels[channelID].guild_id;
				}
				var msgObject = {
					user: user,
					userID: userID,
					channel: channelID,
					message: message,
					server: _server
				}
				self.bind_onMessage(msgObject);
			}
		});
	}

	bindOnMessage(msgFunction) {
		this.bind_onMessage = msgFunction;
	}

	SendMessage(channel, msg, typing=false, tts=false) {
		var self = this;
		if (self.isRemote) {
			var msgOBJ = {
				channel: channel,
				msg: msg,
				typing: typing,
                tts: tts
			};
			var commandObj = {
				type: "SEND_MESSAGE",
				id: self.remoteID,
				data: msgOBJ
			};

			self.client.write(JSON.stringify(commandObj));
		}else{
			return new Promise(function(resolve, reject) {
				self.client.sendMessage({
					to: channel,
					message: msg,
		 			typing: typing,
		      tts: tts
				}, function(err, resp) {
					if(err){
						reject(err);
					}else {
						resolve(resp);
					}
				});
			});
		}
	}
	SendEmbed(channel, embed){
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: embed
		});
	}
	SendCompactEmbed(channel, title, body, color=3447003) {
		var self = this;
		self.client.sendMessage({
			to: channel,
			embed: {
				color: color,
				author: {},
				fields: [{
					name: title,
					inline: false,
					value: body,
				}],
				footer: {}
			}
		});
	}
	SetStatus(status){
		var self = this;
		self.client.setPresence({game: {name: status}});
	}

	SetUsername(name){
		var self = this;
		self.client.editUserInfo({username: name});
	}

	SetServerName(serverId, servername){
		var self = this;
		self.client.editServer(
        {
            serverID: serverId,
            name: servername
        });
	}

	JoinVoiceChannel(voiceID){
		var self = this;
		this.client.joinVoiceChannel(voiceID,function(err){
			if(err){
				console.log(err);
			}else{
			}
		})
	}

  LeaveVoiceChannel(voiceID){
		var self = this;
		this.client.leaveVoiceChannel(voiceID,function(err){
			if(err){
				console.log(err);
			}else{
			}
		})
	}
		JoinUsersVoiceChannel(serverID, userID){
		var user = this.GetUserByID(serverID, userID);
		if(!user){
			return;
		}
		this.JoinVoiceChannel(user.voice_channel_id);
	}
    LeaveUsersVoiceChannel(serverID, userID) {
    var user = this.GetUserByID(serverID, userID);
		if(!user){
			return;
		}
			this.LeaveVoiceChannel(user.voice_channel_id);
  }
		GetServerByID(id){
		var servers = this.client.servers;
		return servers[id];
	}
		GetUserByID(serverId, userId){
		var server = this.GetServerByID(serverId);
		if(!server){
			return;
		}
		return server.members[userId];
	}
		GetUserRoles(serverId, userId){
		var user = this.GetUserByID(serverId, userId);
		if(!user){
			return;
		}
		return user.roles;
	}

	GetRoleById(serverId, roleId){
		var server = this.GetServerByID(serverId);
		if(!server){
			return;
		}
		return server.roles[roleId];
	}
	GetBotInfo(){
		var self = this;
		return {
			username: self.client.username,
			id: self.client.id,
			avatar: self.client.avatar,
			bot: self.client.bot,
			discriminator: self.client.discriminator,
		}
	}
	GetUserInfo(UserID){
		var self = this;
		return self.client.users[userID];
	}
}

module.exports = Bot;
<|endoftext|>


<commit_before>var EntityListItemView;
(function ($, _, Backbone, Marionette, entityListItemTpl) {
    EntityListItemView = Marionette.EntityListItemView = Backbone.Marionette.View.extend({
        regions: {
            fieldsRegion: {
                el: '.fieldsRegion',
                replaceElement: true
            }
        },
        className: 'list-group-item',
        template: entityListItemTpl,
        constructor: function (options) {
            _.extend(this, options);
            Marionette.View.prototype.constructor.apply(this, arguments);

            this._channel = Backbone.Radio.channel(this.route);

            this.on('render', this.runRenderers, this);
            this.on('dom:refresh', this.runInitializers, this);
        },
        ui: {
            $edit: '.edit',
            $multiAction: '.multi-action',
            $actions: '.actions',
            $delete: '.delete-item-modal-show'
        },
        runInitializers: function () {
            if (this.options.baseClassIds.indexOf(this.model.get('id')) > -1) {
                this.ui.$delete.addClass('not-active');
                this.ui.$edit.addClass('not-active');
                this.ui.$multiAction.addClass('not-active');
            }
        },
        runRenderers: function () {
                var fieldsView =
                    Marionette.View.extend(
                        {
                            template: _.isUndefined(this.fieldsTemplate) ? _.template('
<%= name %>
') : this.fieldsTemplate,
                            model: this.model,
                            templateContext: _.isFunction(this.templateContext) ? this.templateContext() : this.templateContext,
                            onRender: function () {
                                // Get rid of that pesky wrapping-div.
                                // Assumes 1 child element present in template.
                                this.$el = this.$el.children();
                                // Unwrap the element to prevent infinitely
                                // nesting elements during re-render.
                                this.$el.unwrap();
                                this.setElement(this.$el);
                            }
                        });

                this.showChildView('fieldsRegion', new fieldsView());
                this.bindUIElements();

            if (this.baseClassIds.indexOf(this.model.get('id')) === -1) {
                this.$el.attr('data-index', this.collection.indexOf(this.model));
                this.$el.attr('data-id', this.model.get('id'));
            }
        },
        templateContext: function () {
            var route = this.route;

            var allowEdit = this.allowableOperations.indexOf('edit') > -1,
                allowDelete = this.allowableOperations.indexOf('delete') > -1,
                allowDeleteAll = this.allowableOperations.indexOf('delete-all') > -1,
                allowPublishAll = this.allowableOperations.indexOf('clone-all') > -1,
                allowAddAll = this.allowableOperations.indexOf('add-all') > -1,
                allowViewLive = this.allowableOperations.indexOf('view-live') > -1;

            return {
                route: route,
                allowEdit: allowEdit,
                allowDelete: allowDelete,
                allowDeleteAll: allowDeleteAll,
                allowPublishAll: allowPublishAll,
                allowAddAll: allowAddAll,
                allowViewLive: allowViewLive
            };
        },
        getChannel: function () {
            return this._channel;
        }
    });

})(jQuery, _, Backbone, Marionette, this['Templates']['entityListItemTemplate']);
<commit_msg>adding sortable item behavior<commit_after>var EntityListItemView;
(function ($, _, Backbone, Marionette, entityListItemTpl, SortableItemBehavior) {
    EntityListItemView = Marionette.EntityListItemView = Backbone.Marionette.View.extend({
        regions: {
            fieldsRegion: {
                el: '.fieldsRegion',
                replaceElement: true
            }
        },
        className: 'list-group-item',
        template: entityListItemTpl,
        constructor: function (options) {
            _.extend(this, options);
            Marionette.View.prototype.constructor.apply(this, arguments);

            this._channel = Backbone.Radio.channel(this.route);

            this.on('render', this.runRenderers, this);
            this.on('dom:refresh', this.runInitializers, this);
        },
        behaviors: function(){
            var behaviors = {};
          if(this.getOption('sortable'))
          {
              behaviors.Sortable = {
                  behaviorClass: SortableItemBehavior
              };
          }

            return behaviors;
        },
        ui: {
            $edit: '.edit',
            $multiAction: '.multi-action',
            $actions: '.actions',
            $delete: '.delete-item-modal-show'
        },
        runInitializers: function () {
            if (this.options.baseClassIds.indexOf(this.model.get('id')) > -1) {
                this.ui.$delete.addClass('not-active');
                this.ui.$edit.addClass('not-active');
                this.ui.$multiAction.addClass('not-active');
            }
        },
        runRenderers: function () {
                var fieldsView =
                    Marionette.View.extend(
                        {
                            template: _.isUndefined(this.fieldsTemplate) ? _.template('
<%= name %>
') : this.fieldsTemplate,
                            model: this.model,
                            templateContext: _.isFunction(this.templateContext) ? this.templateContext() : this.templateContext,
                            onRender: function () {
                                // Get rid of that pesky wrapping-div.
                                // Assumes 1 child element present in template.
                                this.$el = this.$el.children();
                                // Unwrap the element to prevent infinitely
                                // nesting elements during re-render.
                                this.$el.unwrap();
                                this.setElement(this.$el);
                            }
                        });

                this.showChildView('fieldsRegion', new fieldsView());
                this.bindUIElements();

            if (this.baseClassIds.indexOf(this.model.get('id')) === -1) {
                this.$el.attr('data-index', this.collection.indexOf(this.model));
                this.$el.attr('data-id', this.model.get('id'));
            }
        },
        templateContext: function () {
            var route = this.route;

            var allowEdit = this.allowableOperations.indexOf('edit') > -1,
                allowDelete = this.allowableOperations.indexOf('delete') > -1,
                allowDeleteAll = this.allowableOperations.indexOf('delete-all') > -1,
                allowPublishAll = this.allowableOperations.indexOf('clone-all') > -1,
                allowAddAll = this.allowableOperations.indexOf('add-all') > -1,
                allowViewLive = this.allowableOperations.indexOf('view-live') > -1;

            return {
                route: route,
                allowEdit: allowEdit,
                allowDelete: allowDelete,
                allowDeleteAll: allowDeleteAll,
                allowPublishAll: allowPublishAll,
                allowAddAll: allowAddAll,
                allowViewLive: allowViewLive
            };
        },
        getChannel: function () {
            return this._channel;
        }
    });

})(jQuery, _, Backbone, Marionette, this['Templates']['entityListItemTemplate'], SortableItemBehavior);
<|endoftext|>


<commit_before>var auth = angular.module('beer-tab.auth', []);

auth.controller('AuthCtrl', function ($scope, $rootScope, $window, $location, AuthService) {

  $scope.loginError = false;
  $scope.loginErrorMessage = '';

  $scope.user = {};
  $scope.logIn = function () {
    $window.username = $scope.user.username;
    AuthService.login($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.log(error);
        $scope.loginError = true;
        $scope.loginErrorMessage = error.data;
      });
  };

  $scope.signUp = function () {
    AuthService.signup($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signout = function () {
    AuthService.signout();
  };

  $scope.redirect = function (path) {
    console.log(path);
    $location.path('/' + path);
  };

})
.directive('checkRequired', function () {
  return {
    require: 'ngModel',
    restrict: 'A',
    link: function (scope, element, attrs, ngModel) {
      ngModel.$validators.checkRequired = function (modelValue, viewValue) {
        var value = modelValue || viewValue;
        var match = scope.$eval(attrs.ngTrueValue) || true;
        return value && match === value;
      };
    }
  };
})
.directive('passwordMatch', [function () {
  return {
    restrict: 'A',
    scope: true,
    require: 'ngModel',
    link: function (scope, elem, attrs, control) {
      var checker = function () {

        //get the value of the first password
        var e1 = scope.$eval(attrs.ngModel);

        //get the value of the other password
        var e2 = scope.$eval(attrs.passwordMatch);
        return e1 === e2;
      };
      scope.$watch(checker, function (n) {
      //set the form control to valid if both
      //passwords are the same, else invalid
        control.$setValidity('unique', n);
      });
    }
  };
}]);
<commit_msg>fix syntax<commit_after>var auth = angular.module('beer-tab.auth', []);

auth.controller('AuthCtrl', function ($scope, $rootScope, $window, $location, AuthService) {

  $scope.loginError = false;
  $scope.loginErrorMessage = '';

  $scope.user = {};
  $scope.logIn = function () {
    $window.username = $scope.user.username;
    AuthService.login($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.log(error);
        $scope.loginError = true;
        $scope.loginErrorMessage = error.data;
      });
  };

  $scope.signUp = function () {
    AuthService.signup($scope.user)
      .then(function (token) {
        $window.localStorage.setItem('com.beer-tab', token);
        $location.path('/main');
      })
      .catch(function (error) {
        console.error(error);
      });
  };

  $scope.signout = function () {
    AuthService.signout();
  };

  $scope.redirect = function (path) {
    console.log(path);
    $location.path('/' + path);
  };

})
.directive('checkRequired', function () {
  return {
    require: 'ngModel',
    restrict: 'A',
    link: function (scope, element, attrs, ngModel) {
      ngModel.$validators.checkRequired = function (modelValue, viewValue) {
        var value = modelValue || viewValue;
        var match = scope.$eval(attrs.ngTrueValue) || true;
        return value && match === value;
      };
    }
  };
})
.directive('passwordMatch', function () {
  return {
    restrict: 'A',
    scope: true,
    require: 'ngModel',
    link: function (scope, elem, attrs, control) {
      var checker = function () {

        //get the value of the first password
        var e1 = scope.$eval(attrs.ngModel);

        //get the value of the other password
        var e2 = scope.$eval(attrs.passwordMatch);
        return e1 === e2;
      };
      scope.$watch(checker, function (n) {
      //set the form control to valid if both
      //passwords are the same, else invalid
        control.$setValidity('unique', n);
      });
    }
  };
});
<|endoftext|>


<commit_before>angular.module('swot').controller('EditQuizCtrl', function (quiz, $scope, $timeout, focus, $debounce) {
    $scope.quiz = {
        _id: _quizId || null,
        name: "",
        questions: [{}]
    };
    $scope.alerts = [];
    $scope.saveStatus = "";
    $scope.saveMessage = "";
    $scope.savedSuccessfully = true;
    $scope.addQuestionTooltipsRemaining = 2;    // Stop showing the tooltip after adding a couple questions.

    $scope.isNew = function () {
        return $scope.quiz._id === null;
    }

    $scope.load = function () {
        quiz.load($scope.quiz._id, function (response) {
            $scope.quiz = response.quiz;
        }, function (error) {
            $scope.showError('An error occurred while loading the quiz: \n' + error);
        });
    };

    $scope.save = function (callback) {
        $scope.closeAllAlerts();

        var onSaveFinished = function () {
            $scope.saveStatus = "Saved";
            $scope.savedSuccessfully = true;
            $timeout(function () {
                $scope.saveStatus = "";
            }, 2000);
            if (typeof (callback) === 'function') {
                callback(true);
            };
            $scope.editQuizForm.$setPristine();
        };

        var onError = function (error) {
            $scope.saveStatus = "";
            $scope.saveMessage = 'An error occurred while saving the quiz: ' + error;
            $scope.savedSuccessfully = false;
            if (typeof (callback) === 'function') {
                callback(false);
            };
        };

        if ($scope.isNew()) {
            quiz.create($scope.quiz, function (id) {
                $scope.quiz._id = id;
                onSaveFinished();
            }, onError);
        } else {
            quiz.save($scope.quiz, onSaveFinished, onError);
        }
    };

    $scope.autosave = function () {
        if (!$scope.isNew() && $scope.editQuizForm.$dirty) {
            $scope.save();
        }
    };

    $scope.deleteQuiz = function () {
        $scope.closeAllAlerts();

        quiz.deleteQuiz($scope.quiz._id, function () {
            bootbox.alert("The quiz has been deleted successfully.", function () {
                window.location.href = "/quizzes";
            });
        }, function (error) {
            $scope.showError("An error occurred while deleting the quiz: " + error);
        });
    };

    $scope.addQuestion = function () {
        $scope.quiz.questions.push({});

        // Focus on the first input field for the newly-added question
        focus('newQuestionAdded');

        // Mark the form as dirty
        $scope.editQuizForm.$setDirty();

        // Stop showing the tooltip on the Add Question button after adding a couple questions
        // Note that tooltip is now shown where there are no questions, so adding the first question
        // should not decrement the count.
        if ($scope.quiz.questions.length > 1) {
            $scope.addQuestionTooltipsRemaining--;
        }
    };

    $scope.removeQuestion = function (index) {
        $scope.quiz.questions.splice(index, 1);
        $scope.editQuizForm.$setDirty();
    };

    $scope.exportJson = function () {
        bootbox.confirm('The quiz will be saved before exporting. Continue?', function (confirmed) {
            if (confirmed) {
                $scope.save(function (success) {
                    if (success) {
                        location.href = '/export?id=' + $scope.quiz._id;
                    }
                });
            }
        });
    };

    $scope.sortableOptions = {
        update: function(e, ui) { },
        placeholder: 'sortable-list-placeholder',
        forcePlaceholderSize: true,
        start: function (e, ui) {
            ui.placeholder.height(ui.item.outerHeight());
        }
    };

    $scope.addQuestionTooltip = function () {
        if ($scope.addQuestionTooltipsRemaining <= 0 || $scope.quiz.questions.length == 0) {
            return "";
        }

        return "You can also add a question by hitting the TAB key after typing in the last answer.";
    }

    // Alerts
    // ------

    $scope.showError = function (message) {
        return $scope.alerts.push({ type: 'danger', msg: message }) - 1;
    };

    $scope.showSuccess = function (message) {
        return $scope.alerts.push({ type: 'success', msg: message }) - 1;
    };

    $scope.showInfo = function (message) {
        return $scope.alerts.push({ type: 'info', msg: message }) - 1;
    };

    $scope.showWarning = function (message) {
        return $scope.alerts.push({ type: 'warning', msg: message }) - 1;
    };

    $scope.closeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    $scope.closeAllAlerts = function() {
        $scope.alerts = [];
    };

    // Events
    // ------

    $scope.answerKeypress = function ($index, $event) {
        // Add new question when hitting TAB on the last input
        if ($index === $scope.quiz.questions.length - 1) {
            if ($event.keyCode === 9 &&  !$event.ctrlKey && !$event.metaKey && !$event.altKey && !$event.shiftKey) {
                $scope.addQuestion();
            }
        }
    }

    // Initialization
    // --------------

    if ($scope.quiz._id) { $scope.load(); }

    $scope.$watch('quiz', $debounce($scope.autosave, 1000), true);
});
<commit_msg>Edit Quiz: Trigger auto-save when reordering questions.<commit_after>angular.module('swot').controller('EditQuizCtrl', function (quiz, $scope, $timeout, focus, $debounce) {
    $scope.quiz = {
        _id: _quizId || null,
        name: "",
        questions: [{}]
    };
    $scope.alerts = [];
    $scope.saveStatus = "";
    $scope.saveMessage = "";
    $scope.savedSuccessfully = true;
    $scope.addQuestionTooltipsRemaining = 2;    // Stop showing the tooltip after adding a couple questions.

    $scope.isNew = function () {
        return $scope.quiz._id === null;
    }

    $scope.load = function () {
        quiz.load($scope.quiz._id, function (response) {
            $scope.quiz = response.quiz;
        }, function (error) {
            $scope.showError('An error occurred while loading the quiz: \n' + error);
        });
    };

    $scope.save = function (callback) {
        $scope.closeAllAlerts();

        var onSaveFinished = function () {
            $scope.saveStatus = "Saved";
            $scope.savedSuccessfully = true;
            $timeout(function () {
                $scope.saveStatus = "";
            }, 2000);
            if (typeof (callback) === 'function') {
                callback(true);
            };
            $scope.editQuizForm.$setPristine();
        };

        var onError = function (error) {
            $scope.saveStatus = "";
            $scope.saveMessage = 'An error occurred while saving the quiz: ' + error;
            $scope.savedSuccessfully = false;
            if (typeof (callback) === 'function') {
                callback(false);
            };
        };

        if ($scope.isNew()) {
            quiz.create($scope.quiz, function (id) {
                $scope.quiz._id = id;
                onSaveFinished();
            }, onError);
        } else {
            quiz.save($scope.quiz, onSaveFinished, onError);
        }
    };

    $scope.autosave = function () {
        if (!$scope.isNew() && $scope.editQuizForm.$dirty) {
            $scope.save();
        }
    };

    $scope.deleteQuiz = function () {
        $scope.closeAllAlerts();

        quiz.deleteQuiz($scope.quiz._id, function () {
            bootbox.alert("The quiz has been deleted successfully.", function () {
                window.location.href = "/quizzes";
            });
        }, function (error) {
            $scope.showError("An error occurred while deleting the quiz: " + error);
        });
    };

    $scope.addQuestion = function () {
        $scope.quiz.questions.push({});

        // Focus on the first input field for the newly-added question
        focus('newQuestionAdded');

        // Mark the form as dirty
        $scope.editQuizForm.$setDirty();

        // Stop showing the tooltip on the Add Question button after adding a couple questions
        // Note that tooltip is now shown where there are no questions, so adding the first question
        // should not decrement the count.
        if ($scope.quiz.questions.length > 1) {
            $scope.addQuestionTooltipsRemaining--;
        }
    };

    $scope.removeQuestion = function (index) {
        $scope.quiz.questions.splice(index, 1);
        $scope.editQuizForm.$setDirty();
    };

    $scope.exportJson = function () {
        bootbox.confirm('The quiz will be saved before exporting. Continue?', function (confirmed) {
            if (confirmed) {
                $scope.save(function (success) {
                    if (success) {
                        location.href = '/export?id=' + $scope.quiz._id;
                    }
                });
            }
        });
    };

    $scope.sortableOptions = {
        update: function(e, ui) { },
        change: function(e, ui) { $scope.editQuizForm.$setDirty(); },
        placeholder: 'sortable-list-placeholder',
        forcePlaceholderSize: true,
        start: function (e, ui) {
            ui.placeholder.height(ui.item.outerHeight());
        }
    };

    $scope.addQuestionTooltip = function () {
        if ($scope.addQuestionTooltipsRemaining <= 0 || $scope.quiz.questions.length == 0) {
            return "";
        }

        return "You can also add a question by hitting the TAB key after typing in the last answer.";
    }

    // Alerts
    // ------

    $scope.showError = function (message) {
        return $scope.alerts.push({ type: 'danger', msg: message }) - 1;
    };

    $scope.showSuccess = function (message) {
        return $scope.alerts.push({ type: 'success', msg: message }) - 1;
    };

    $scope.showInfo = function (message) {
        return $scope.alerts.push({ type: 'info', msg: message }) - 1;
    };

    $scope.showWarning = function (message) {
        return $scope.alerts.push({ type: 'warning', msg: message }) - 1;
    };

    $scope.closeAlert = function(index) {
        $scope.alerts.splice(index, 1);
    };

    $scope.closeAllAlerts = function() {
        $scope.alerts = [];
    };

    // Events
    // ------

    $scope.answerKeypress = function ($index, $event) {
        // Add new question when hitting TAB on the last input
        if ($index === $scope.quiz.questions.length - 1) {
            if ($event.keyCode === 9 &&  !$event.ctrlKey && !$event.metaKey && !$event.altKey && !$event.shiftKey) {
                $scope.addQuestion();
            }
        }
    }

    // Initialization
    // --------------

    if ($scope.quiz._id) { $scope.load(); }

    $scope.$watch('quiz', $debounce($scope.autosave, 1000), true);
});
<|endoftext|>