parent
0ba0ded728
commit
8028070aaa
@ -0,0 +1,183 @@ |
||||
/* |
||||
* This file contains the weechat models and various |
||||
* helper methods to work with them. |
||||
*/ |
||||
var models = angular.module('weechatModels', []); |
||||
|
||||
models.service('models', ['colors', function(colors) { |
||||
/* |
||||
* Buffer class |
||||
*/ |
||||
this.Buffer = function(message) { |
||||
// weechat properties
|
||||
var fullName = message['full_name'] |
||||
var shortName = message['short_name'] |
||||
var title = message['title'] |
||||
var number = message['number'] |
||||
var pointer = message['pointers'][0] |
||||
var lines = [] |
||||
var active = false; |
||||
var notification = false; |
||||
var unread = ''; |
||||
|
||||
/* |
||||
* Adds a line to this buffer |
||||
*
|
||||
* @param line the BufferLine object |
||||
* @return undefined |
||||
*/ |
||||
var addLine = function(line) { |
||||
lines.push(line); |
||||
} |
||||
|
||||
return { |
||||
id: pointer, |
||||
fullName: fullName, |
||||
shortName: shortName, |
||||
number: number, |
||||
title: title, |
||||
lines: lines, |
||||
addLine: addLine |
||||
} |
||||
|
||||
} |
||||
|
||||
/* |
||||
* BufferLine class |
||||
*/ |
||||
this.BufferLine = function(message) { |
||||
|
||||
/* |
||||
* Parse the text elements from the buffer line added |
||||
* |
||||
* @param message weechat message |
||||
*/ |
||||
function parseLineAddedTextElements(message) { |
||||
var prefix = colors.parse(message['prefix']); |
||||
|
||||
var buffer = message['buffer']; |
||||
text_elements = _.union(prefix, text); |
||||
text_elements =_.map(text_elements, function(text_element) { |
||||
if (text_element && ('fg' in text_element)) { |
||||
text_element['fg'] = colors.prepareCss(text_element['fg']); |
||||
} |
||||
// TODO: parse background as well
|
||||
|
||||
return text_element; |
||||
}); |
||||
return text_elements; |
||||
} |
||||
|
||||
|
||||
var buffer = message['buffer']; |
||||
var date = message['date']; |
||||
var text = colors.parse(message['message']); |
||||
var tags_array = message['tags_array']; |
||||
var displayed = message['displayed']; |
||||
var highlight = message['highlight']; |
||||
var content = parseLineAddedTextElements(message); |
||||
var text = ""; |
||||
if(text[0] != undefined) { |
||||
text = text[0]['text']; |
||||
} |
||||
|
||||
return { |
||||
content: content, |
||||
date: date, |
||||
buffer: buffer, |
||||
tags: tags_array, |
||||
highlight: highlight, |
||||
displayed: displayed, |
||||
text: text, |
||||
} |
||||
|
||||
}
|
||||
|
||||
|
||||
var BufferList = [] |
||||
activeBuffer = null; |
||||
|
||||
this.model = { 'buffers': {} } |
||||
|
||||
/* |
||||
* Adds a buffer to the list |
||||
* |
||||
* @param buffer buffer object |
||||
* @return undefined |
||||
*/ |
||||
this.addBuffer = function(buffer) { |
||||
BufferList[buffer.id] = buffer; |
||||
if (BufferList.length == 1) { |
||||
activeBuffer = buffer.id; |
||||
} |
||||
this.model.buffers[buffer.id] = buffer; |
||||
} |
||||
|
||||
/* |
||||
* Returns the current active buffer |
||||
* |
||||
* @return active buffer object |
||||
*/ |
||||
this.getActiveBuffer = function() { |
||||
return activeBuffer; |
||||
} |
||||
|
||||
/* |
||||
* Sets the buffer specifiee by bufferId as active. |
||||
* Deactivates the previous current buffer. |
||||
* |
||||
* @param bufferId id of the new active buffer |
||||
* @return undefined |
||||
*/ |
||||
this.setActiveBuffer = function(bufferId) { |
||||
|
||||
if (this.getActiveBuffer()) { |
||||
this.getActiveBuffer().active = false; |
||||
} |
||||
|
||||
activeBuffer = _.find(this.model['buffers'], function(buffer) { |
||||
if (buffer['id'] == bufferId) { |
||||
return buffer; |
||||
} |
||||
}); |
||||
activeBuffer.notification = false; |
||||
activeBuffer.active = true; |
||||
activeBuffer.unread = ''; |
||||
|
||||
} |
||||
|
||||
/* |
||||
* Returns the buffer list |
||||
*/ |
||||
this.getBuffers = function() { |
||||
return BufferList; |
||||
} |
||||
|
||||
/* |
||||
* Returns a specific buffer object |
||||
* |
||||
* @param bufferId id of the buffer |
||||
* @return the buffer object |
||||
*/ |
||||
this.getBuffer = function(bufferId) { |
||||
return _.find(this.model['buffers'], function(buffer) { |
||||
if (buffer['id'] == bufferId) { |
||||
return buffer; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/* |
||||
* Closes a weechat buffer. Sets the first buffer |
||||
* as active. |
||||
* |
||||
* @param bufferId id of the buffer to close |
||||
* @return undefined |
||||
*/ |
||||
this.closeBuffer = function(bufferId) { |
||||
|
||||
delete(this.model['buffers'][bufferId.id]); |
||||
var firstBuffer = _.keys(this.model['buffers'])[0]; |
||||
this.setActiveBuffer(firstBuffer); |
||||
} |
||||
}]); |
@ -0,0 +1,132 @@ |
||||
/* |
||||
* This file contains the plugin definitions |
||||
*/ |
||||
|
||||
plugins = angular.module('plugins', []); |
||||
|
||||
/* |
||||
* Definition of a user provided plugin with sensible default values |
||||
* |
||||
* User plugins are created by providing a contentForMessage function |
||||
* that parses a string and return any additional content. |
||||
*/ |
||||
var Plugin = function(contentForMessage) { |
||||
|
||||
return { |
||||
contentForMessage: contentForMessage, |
||||
exclusive: false, |
||||
} |
||||
} |
||||
|
||||
/* |
||||
* This service provides access to the plugin manager |
||||
* |
||||
* The plugin manager is where the various user provided plugins |
||||
* are registered. It is responsible for finding additional content |
||||
* to display when messages are received. |
||||
* |
||||
*/ |
||||
plugins.service('plugins', ['userPlugins', function(userPlugins) { |
||||
|
||||
/* |
||||
* Defines the plugin manager object |
||||
*/ |
||||
var PluginManagerObject = function() { |
||||
|
||||
var plugins = []; |
||||
|
||||
/* |
||||
* Register the user provides plugins |
||||
* |
||||
* @param userPlugins user provided plugins |
||||
*/ |
||||
var registerPlugins = function(userPlugins) { |
||||
for (var i = 0; i < userPlugins.length; i++) { |
||||
plugins.push(userPlugins[i]); |
||||
}; |
||||
} |
||||
|
||||
/* |
||||
* Iterates through all the registered plugins |
||||
* and run their contentForMessage function. |
||||
*/ |
||||
var contentForMessage = function(message) { |
||||
|
||||
var content = []; |
||||
for (var i = 0; i < plugins.length; i++) { |
||||
var pluginContent = plugins[i].contentForMessage(message); |
||||
if (pluginContent) { |
||||
var pluginContent = {'visible': false,
|
||||
'content': pluginContent } |
||||
content.push(pluginContent); |
||||
|
||||
if (plugins[i].exclusive) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
return content; |
||||
} |
||||
|
||||
return { |
||||
registerPlugins: registerPlugins, |
||||
contentForMessage: contentForMessage |
||||
} |
||||
} |
||||
|
||||
// Instanciates and registers the plugin manager.
|
||||
this.PluginManager = new PluginManagerObject(); |
||||
this.PluginManager.registerPlugins(userPlugins.plugins); |
||||
|
||||
}]); |
||||
|
||||
/* |
||||
* This factory exposes the collection of user provided plugins. |
||||
* |
||||
* To create your own plugin, you need to: |
||||
* |
||||
* 1. Define it's contentForMessage function. The contentForMessage |
||||
* function takes a string as a parameter and returns a HTML string. |
||||
* |
||||
* 2. Instanciate a Plugin object with contentForMessage function as it's |
||||
* argument. |
||||
* |
||||
* 3. Add it to the plugins array. |
||||
* |
||||
*/ |
||||
plugins.factory('userPlugins', function() { |
||||
|
||||
var youtubePlugin = new Plugin(function(message) { |
||||
|
||||
if (message.indexOf('youtube.com') != -1) { |
||||
var index = message.indexOf("?v="); |
||||
var token = message.substr(index+3); |
||||
return '<iframe width="560" height="315" src="http://www.youtube.com/embed/' + token + '" frameborder="0" allowfullscreen></iframe>' |
||||
} |
||||
|
||||
return null; |
||||
}); |
||||
|
||||
var urlPlugin = new Plugin(function(message) { |
||||
var urlPattern = /(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/; |
||||
var url = message.match(urlPattern); |
||||
if (url) { |
||||
return '<a href="' + url[0] + '">' + message + '</a>'; |
||||
} |
||||
return null; |
||||
|
||||
}); |
||||
|
||||
var imagePlugin = new Plugin(function(message) { |
||||
var urls = message.match(/https?:\/\/[^\s]*\.(jpg|png|gif)\b/) |
||||
if (urls != null) { |
||||
var url = urls[0]; /* Actually parse one url per message */ |
||||
return '<img src="' + url + '" height="300">'; |
||||
} |
||||
return null; |
||||
}); |
||||
|
||||
return { |
||||
plugins: [youtubePlugin, urlPlugin, imagePlugin] |
||||
} |
||||
}); |
@ -1,180 +0,0 @@ |
||||
var Protocol = function() { |
||||
var self = this; |
||||
|
||||
var getInfo = function() { |
||||
var info = {}; |
||||
info.key = getString(); |
||||
info.value = getString(); |
||||
return info; |
||||
}; |
||||
|
||||
var getHdata = function() { |
||||
var paths; |
||||
var count; |
||||
var objs = []; |
||||
var hpath = getString(); |
||||
|
||||
|
||||
|
||||
keys = getString().split(','); |
||||
paths = hpath.split('/'); |
||||
count = getInt(); |
||||
|
||||
keys = keys.map(function(key) { |
||||
return key.split(':'); |
||||
}); |
||||
var i; |
||||
for (i = 0; i < count; i++) { |
||||
var tmp = {}; |
||||
|
||||
tmp.pointers = paths.map(function(path) { |
||||
return getPointer(); |
||||
}); |
||||
|
||||
keys.forEach(function(key) { |
||||
tmp[key[0]] = runType(key[1]); |
||||
}); |
||||
objs.push(tmp); |
||||
}; |
||||
return objs; |
||||
}; |
||||
|
||||
function getPointer() { |
||||
var l = getChar(); |
||||
|
||||
var pointer = getSlice(l) |
||||
var parsed_data = new Uint8Array(pointer); |
||||
return _uiatos(parsed_data); |
||||
|
||||
}; |
||||
|
||||
var _uiatos =function(uia) { |
||||
var _str = []; |
||||
for (var c = 0; c < uia.length; c++) { |
||||
_str[c] = String.fromCharCode(uia[c]); |
||||
} |
||||
return decodeURIComponent(escape(_str.join(""))); |
||||
}; |
||||
|
||||
var getInt = function() { |
||||
var parsed_data = new Uint8Array(getSlice(4)); |
||||
var i = ((parsed_data[0] & 0xff) << 24) | ((parsed_data[1] & 0xff) << 16) | ((parsed_data[2] & 0xff) << 8) | (parsed_data[3] & 0xff); |
||||
return i; |
||||
|
||||
}; |
||||
|
||||
var getChar = function() { |
||||
var parsed_data = new Uint8Array(getSlice(1)); |
||||
return parsed_data[0]; |
||||
}; |
||||
|
||||
var getString = function() { |
||||
var l = getInt(); |
||||
if (l > 0) { |
||||
var s = getSlice(l); |
||||
var parsed_data = new Uint8Array(s); |
||||
return _uiatos(parsed_data); |
||||
} |
||||
return ""; |
||||
}; |
||||
|
||||
var getSlice = function(length) { |
||||
var slice = self.data.slice(0,length); |
||||
self.data = self.data.slice(length); |
||||
return slice; |
||||
}; |
||||
|
||||
var getType = function() { |
||||
var t = getSlice(3); |
||||
return _uiatos(new Uint8Array(t)); |
||||
}; |
||||
|
||||
var runType = function(type) { |
||||
if (type in types) { |
||||
return types[type](); |
||||
} |
||||
0; |
||||
}; |
||||
|
||||
var getHeader = function() { |
||||
return { |
||||
length: getInt(), |
||||
compression: getChar(), |
||||
} |
||||
}; |
||||
|
||||
var getId = function() { |
||||
return getString(); |
||||
} |
||||
|
||||
var getObject = function() { |
||||
var type = getType(); |
||||
if (type) { |
||||
return object = { |
||||
type: type, |
||||
content: runType(type), |
||||
} |
||||
} |
||||
} |
||||
|
||||
self.parse = function(data) { |
||||
self.setData(data); |
||||
var header = getHeader(); |
||||
var id = getId(); |
||||
var objects = []; |
||||
var object = getObject(); |
||||
while(object) { |
||||
objects.push(object); |
||||
object = getObject(); |
||||
} |
||||
return { |
||||
header: header, |
||||
id: id, |
||||
objects: objects,
|
||||
} |
||||
} |
||||
|
||||
self.setData = function (data) { |
||||
self.data = data; |
||||
}; |
||||
|
||||
function array() { |
||||
var type; |
||||
var count; |
||||
var values; |
||||
|
||||
type = getType(); |
||||
count = getInt(); |
||||
values = []; |
||||
var i; |
||||
for (i = 0; i < count; i++) { |
||||
values.push(runType(type)); |
||||
}; |
||||
return values; |
||||
} |
||||
|
||||
var types = { |
||||
chr: getChar, |
||||
"int": getInt, |
||||
str: getString, |
||||
inf: getInfo, |
||||
hda: getHdata, |
||||
ptr: getPointer, |
||||
lon: getPointer, |
||||
tim: getPointer, |
||||
buf: getString, |
||||
arr: array |
||||
}; |
||||
//TODO: IMPLEMENT THIS STUFF
|
||||
// chr: this.getChar,
|
||||
// 'int': getInt,
|
||||
// hacks
|
||||
|
||||
// hacks
|
||||
// htb: getHashtable,
|
||||
// inf: Protocol.getInfo,
|
||||
// inl: getInfolist,
|
||||
|
||||
// },
|
||||
|
||||
} |
@ -0,0 +1,655 @@ |
||||
/** |
||||
* WeeChat protocol handling. |
||||
* |
||||
* This object parses messages and formats commands for the WeeChat |
||||
* protocol. It's independent from the communication layer and thus |
||||
* may be used with any network mechanism. |
||||
*/ |
||||
var WeeChatProtocol = function() { |
||||
// specific parsing for each message type
|
||||
this._types = { |
||||
'chr': this._getChar, |
||||
'int': this._getInt, |
||||
'str': this._getString, |
||||
'inf': this._getInfo, |
||||
'hda': this._getHdata, |
||||
'ptr': this._getPointer, |
||||
'lon': this._getStrNumber, |
||||
'tim': this._getTime, |
||||
'buf': this._getString, |
||||
'arr': this._getArray, |
||||
'htb': this._getHashTable, |
||||
'inl': function() { |
||||
this._warnUnimplemented('infolist'); |
||||
} |
||||
}; |
||||
|
||||
// string value for some message types
|
||||
this._typesStr = { |
||||
'chr': this._strDirect, |
||||
'str': this._strDirect, |
||||
'int': this._strToString, |
||||
'tim': this._strToString, |
||||
'ptr': this._strDirect |
||||
}; |
||||
}; |
||||
|
||||
/** |
||||
* Unsigned integer array to string. |
||||
* |
||||
* @param uia Unsigned integer array |
||||
* @return Decoded string |
||||
*/ |
||||
WeeChatProtocol._uia2s = function(uia) { |
||||
var str = []; |
||||
|
||||
for (var c = 0; c < uia.length; c++) { |
||||
str.push(String.fromCharCode(uia[c])); |
||||
} |
||||
|
||||
return decodeURIComponent(escape(str.join(''))); |
||||
}; |
||||
|
||||
/** |
||||
* Merges default parameters with overriding parameters. |
||||
* |
||||
* @param defaults Default parameters |
||||
* @param override Overriding parameters |
||||
* @return Merged parameters |
||||
*/ |
||||
WeeChatProtocol._mergeParams = function(defaults, override) { |
||||
for (var v in override) { |
||||
defaults[v] = override[v]; |
||||
} |
||||
|
||||
return defaults; |
||||
} |
||||
|
||||
/** |
||||
* Formats a command. |
||||
* |
||||
* @param id Command ID (null for no ID) |
||||
* @param name Command name |
||||
* @param parts Command parts |
||||
* @return Formatted command string |
||||
*/ |
||||
WeeChatProtocol._formatCmd = function(id, name, parts) { |
||||
var cmdIdName; |
||||
var cmd; |
||||
|
||||
cmdIdName = (id !== null) ? '(' + id + ') ' : ''; |
||||
cmdIdName += name; |
||||
parts.unshift(cmdIdName); |
||||
cmd = parts.join(' '); |
||||
cmd += '\n'; |
||||
|
||||
return cmd; |
||||
}; |
||||
|
||||
/** |
||||
* Formats an init command. |
||||
* |
||||
* @param params Parameters: |
||||
* password: password (optional) |
||||
* compression: compression ('off' or 'zlib') (optional) |
||||
* @return Formatted init command string |
||||
*/ |
||||
WeeChatProtocol.formatInit = function(params) { |
||||
var defaultParams = { |
||||
password: null, |
||||
compression: 'off' |
||||
}; |
||||
var keys = []; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
keys.push('compression=' + params.compression); |
||||
if (params.password !== null) { |
||||
keys.push('password=' + params.password); |
||||
} |
||||
parts.push(keys.join(',')); |
||||
|
||||
return WeeChatProtocol._formatCmd(null, 'init', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats an hdata command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* path: hdata path (mandatory) |
||||
* keys: array of keys (optional) |
||||
* @return Formatted hdata command string |
||||
*/ |
||||
WeeChatProtocol.formatHdata = function(params) { |
||||
var defaultParams = { |
||||
id: null, |
||||
keys: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
parts.push(params.path); |
||||
if (params.keys !== null) { |
||||
parts.push(params.keys.join(',')); |
||||
} |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'hdata', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats an info command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* name: info name (mandatory) |
||||
* @return Formatted info command string |
||||
*/ |
||||
WeeChatProtocol.formatInfo = function(params) { |
||||
var defaultParams = { |
||||
id: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
parts.push(params.name); |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'info', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a nicklist command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* buffer: buffer name (optional) |
||||
* @return Formatted nicklist command string |
||||
*/ |
||||
WeeChatProtocol.formatNicklist = function(params) { |
||||
var defaultParams = { |
||||
id: null, |
||||
buffer: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
if (params.buffer !== null) { |
||||
parts.push(params.buffer); |
||||
} |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'nicklist', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats an input command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* buffer: target buffer (mandatory) |
||||
* data: input data (mandatory) |
||||
* @return Formatted input command string |
||||
*/ |
||||
WeeChatProtocol.formatInput = function(params) { |
||||
var defaultParams = { |
||||
id: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
parts.push(params.buffer); |
||||
parts.push(params.data); |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'input', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a sync or a desync command. |
||||
* |
||||
* @param params Parameters (see _formatSync and _formatDesync) |
||||
* @return Formatted sync/desync command string |
||||
*/ |
||||
WeeChatProtocol._formatSyncDesync = function(cmdName, params) { |
||||
var defaultParams = { |
||||
id: null, |
||||
buffers: null, |
||||
options: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
if (params.buffers !== null) { |
||||
parts.push(params.buffers.join(',')); |
||||
if (params.options !== null) { |
||||
parts.push(params.options.join(',')); |
||||
} |
||||
} |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, cmdName, parts); |
||||
} |
||||
|
||||
/** |
||||
* Formats a sync command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* buffers: array of buffers to sync (optional) |
||||
* options: array of options (optional) |
||||
* @return Formatted sync command string |
||||
*/ |
||||
WeeChatProtocol.formatSync = function(params) { |
||||
return WeeChatProtocol._formatSyncDesync('sync', params); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a desync command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* buffers: array of buffers to desync (optional) |
||||
* options: array of options (optional) |
||||
* @return Formatted desync command string |
||||
*/ |
||||
WeeChatProtocol.formatDesync = function(params) { |
||||
return WeeChatProtocol._formatSyncDesync('desync', params); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a test command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* @return Formatted test command string |
||||
*/ |
||||
WeeChatProtocol.formatTest = function(params) { |
||||
var defaultParams = { |
||||
id: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'test', parts); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a quit command. |
||||
* |
||||
* @return Formatted quit command string |
||||
*/ |
||||
WeeChatProtocol.formatQuit = function() { |
||||
return WeeChatProtocol._formatCmd(null, 'quit', []); |
||||
}; |
||||
|
||||
/** |
||||
* Formats a ping command. |
||||
* |
||||
* @param params Parameters: |
||||
* id: command ID (optional) |
||||
* args: array of custom arguments (optional) |
||||
* @return Formatted ping command string |
||||
*/ |
||||
WeeChatProtocol.formatPing = function(params) { |
||||
var defaultParams = { |
||||
id: null, |
||||
args: null |
||||
}; |
||||
var parts = []; |
||||
|
||||
params = WeeChatProtocol._mergeParams(defaultParams, params); |
||||
if (params.args !== null) { |
||||
parts.push(params.args.join(' ')); |
||||
} |
||||
|
||||
return WeeChatProtocol._formatCmd(params.id, 'ping', parts); |
||||
}; |
||||
|
||||
WeeChatProtocol.prototype = { |
||||
/** |
||||
* Warns that message parsing is not implemented for a |
||||
* specific type. |
||||
* |
||||
* @param type Message type to display |
||||
*/ |
||||
_warnUnimplemented: function(type) { |
||||
console.log('Warning: ' + type + ' message parsing is not implemented'); |
||||
}, |
||||
|
||||
/** |
||||
* Reads a 3-character message type token value from current |
||||
* set data. |
||||
* |
||||
* @return Type |
||||
*/ |
||||
_getType: function() { |
||||
var t = this._getSlice(3); |
||||
|
||||
if (!t) { |
||||
return null; |
||||
} |
||||
|
||||
return WeeChatProtocol._uia2s(new Uint8Array(t)); |
||||
}, |
||||
|
||||
/** |
||||
* Runs the appropriate read routine for the specified message type. |
||||
* |
||||
* @param type Message type |
||||
* @return Data value |
||||
*/ |
||||
_runType: function(type) { |
||||
var cb = this._types[type]; |
||||
var boundCb = cb.bind(this); |
||||
|
||||
return boundCb(); |
||||
}, |
||||
|
||||
/** |
||||
* Reads a "number as a string" token value from current set data. |
||||
* |
||||
* @return Number as a string |
||||
*/ |
||||
_getStrNumber: function() { |
||||
var len = this._getByte(); |
||||
var str = this._getSlice(len); |
||||
|
||||
return WeeChatProtocol._uia2s(new Uint8Array(str)); |
||||
}, |
||||
|
||||
/** |
||||
* Returns the passed object. |
||||
* |
||||
* @param obj Object |
||||
* @return Passed object |
||||
*/ |
||||
_strDirect: function(obj) { |
||||
return obj; |
||||
}, |
||||
|
||||
/** |
||||
* Calls toString() on the passed object and returns the value. |
||||
* |
||||
* @param obj Object to call toString() on |
||||
* @return String value of object |
||||
*/ |
||||
_strToString: function(obj) { |
||||
return obj.toString(); |
||||
}, |
||||
|
||||
/** |
||||
* Gets the string value of an object representing the message |
||||
* value for a specified type. |
||||
* |
||||
* @param obj Object for which to get the string value |
||||
* @param type Message type |
||||
* @return String value of object |
||||
*/ |
||||
_objToString: function(obj, type) { |
||||
var cb = this._typesStr[type]; |
||||
var boundCb = cb.bind(this); |
||||
|
||||
return boundCb(obj); |
||||
}, |
||||
|
||||
/** |
||||
* Reads an info token value from current set data. |
||||
* |
||||
* @return Info object |
||||
*/ |
||||
_getInfo: function() { |
||||
var info = {}; |
||||
info.key = this._getString(); |
||||
info.value = this._getString(); |
||||
|
||||
return info; |
||||
}, |
||||
|
||||
/** |
||||
* Reads an hdata token value from current set data. |
||||
* |
||||
* @return Hdata object |
||||
*/ |
||||
_getHdata: function() { |
||||
var self = this; |
||||
var paths; |
||||
var count; |
||||
var objs = []; |
||||
var hpath = this._getString(); |
||||
|
||||
keys = this._getString().split(','); |
||||
paths = hpath.split('/'); |
||||
count = this._getInt(); |
||||
|
||||
keys = keys.map(function(key) { |
||||
return key.split(':'); |
||||
}); |
||||
|
||||
for (var i = 0; i < count; i++) { |
||||
var tmp = {}; |
||||
|
||||
tmp.pointers = paths.map(function(path) { |
||||
return self._getPointer(); |
||||
}); |
||||
keys.forEach(function(key) { |
||||
tmp[key[0]] = self._runType(key[1]); |
||||
}); |
||||
objs.push(tmp); |
||||
}; |
||||
|
||||
return objs; |
||||
}, |
||||
|
||||
/** |
||||
* Reads a pointer token value from current set data. |
||||
* |
||||
* @return Pointer value |
||||
*/ |
||||
_getPointer: function() { |
||||
return this._getStrNumber(); |
||||
}, |
||||
|
||||
/** |
||||
* Reads a time token value from current set data. |
||||
* |
||||
* @return Time value (Date) |
||||
*/ |
||||
_getTime: function() { |
||||
var str = this._getStrNumber(); |
||||
|
||||
return new Date(parseInt(str) * 1000); |
||||
}, |
||||
|
||||
/** |
||||
* Reads an integer token value from current set data. |
||||
* |
||||
* @return Integer value |
||||
*/ |
||||
_getInt: function() { |
||||
var parsedData = new Uint8Array(this._getSlice(4)); |
||||
|
||||
return ((parsedData[0] & 0xff) << 24) | |
||||
((parsedData[1] & 0xff) << 16) | |
||||
((parsedData[2] & 0xff) << 8) | |
||||
(parsedData[3] & 0xff); |
||||
}, |
||||
|
||||
/** |
||||
* Reads a byte from current set data. |
||||
* |
||||
* @return Byte value (integer) |
||||
*/ |
||||
_getByte: function() { |
||||
var parsedData = new Uint8Array(this._getSlice(1)); |
||||
|
||||
return parsedData[0]; |
||||
}, |
||||
|
||||
/** |
||||
* Reads a character token value from current set data. |
||||
* |
||||
* @return Character (string) |
||||
*/ |
||||
_getChar: function() { |
||||
return this._getByte(); |
||||
}, |
||||
|
||||
/** |
||||
* Reads a string token value from current set data. |
||||
* |
||||
* @return String value |
||||
*/ |
||||
_getString: function() { |
||||
var l = this._getInt(); |
||||
|
||||
if (l > 0) { |
||||
var s = this._getSlice(l); |
||||
var parsedData = new Uint8Array(s); |
||||
|
||||
return WeeChatProtocol._uia2s(parsedData); |
||||
} |
||||
|
||||
return ""; |
||||
}, |
||||
|
||||
/** |
||||
* Reads a message header from current set data. |
||||
* |
||||
* @return Header object |
||||
*/ |
||||
_getHeader: function() { |
||||
var len = this._getInt(); |
||||
var comp = this._getByte(); |
||||
|
||||
return { |
||||
length: len, |
||||
compression: comp, |
||||
}; |
||||
}, |
||||
|
||||
/** |
||||
* Reads a message header ID from current set data. |
||||
* |
||||
* @return Message ID (string) |
||||
*/ |
||||
_getId: function() { |
||||
return this._getString(); |
||||
}, |
||||
|
||||
/** |
||||
* Reads an arbitrary object token from current set data. |
||||
* |
||||
* @return Object value |
||||
*/ |
||||
_getObject: function() { |
||||
var self = this; |
||||
var type = this._getType(); |
||||
|
||||
if (type) { |
||||
return { |
||||
type: type, |
||||
content: self._runType(type), |
||||
}; |
||||
} |
||||
}, |
||||
|
||||
/** |
||||
* Reads an hash table token from current set data. |
||||
* |
||||
* @return Hash table |
||||
*/ |
||||
_getHashTable: function() { |
||||
var self = this; |
||||
var typeKeys, typeValues, count; |
||||
var dict = {}; |
||||
|
||||
typeKeys = this._getType(); |
||||
typeValues = this._getType(); |
||||
count = this._getInt(); |
||||
|
||||
for (var i = 0; i < count; ++i) { |
||||
var key = self._runType(typeKeys); |
||||
var keyStr = self._objToString(key, typeKeys); |
||||
var value = self._runType(typeValues); |
||||
dict[keyStr] = value; |
||||
} |
||||
|
||||
return dict; |
||||
}, |
||||
|
||||
/** |
||||
* Reads an array token from current set data. |
||||
* |
||||
* @return Array |
||||
*/ |
||||
_getArray: function() { |
||||
var self = this; |
||||
var type; |
||||
var count; |
||||
var values; |
||||
|
||||
type = this._getType(); |
||||
count = this._getInt(); |
||||
values = []; |
||||
|
||||
for (var i = 0; i < count; i++) { |
||||
values.push(self._runType(type)); |
||||
}; |
||||
|
||||
return values; |
||||
}, |
||||
|
||||
/** |
||||
* Reads a specified number of bytes from current set data. |
||||
* |
||||
* @param length Number of bytes to read |
||||
* @return Sliced array |
||||
*/ |
||||
_getSlice: function(length) { |
||||
if (this.dataAt + length > this._data.byteLength) { |
||||
return null; |
||||
} |
||||
|
||||
var slice = this._data.slice(this._dataAt, this._dataAt + length); |
||||
|
||||
this._dataAt += length; |
||||
|
||||
return slice; |
||||
}, |
||||
|
||||
/** |
||||
* Sets the current data. |
||||
* |
||||
* @param data Current data |
||||
*/ |
||||
_setData: function (data) { |
||||
this._data = data; |
||||
}, |
||||
|
||||
/** |
||||
* Parses a WeeChat message. |
||||
* |
||||
* @param data Message data (ArrayBuffer) |
||||
* @return Message value |
||||
*/ |
||||
parse: function(data) { |
||||
var self = this; |
||||
|
||||
this._setData(data); |
||||
this._dataAt = 0; |
||||
|
||||
var header = this._getHeader(); |
||||
var id = this._getId(); |
||||
var objects = []; |
||||
var object = this._getObject(); |
||||
|
||||
while (object) { |
||||
objects.push(object); |
||||
object = self._getObject(); |
||||
} |
||||
|
||||
return { |
||||
header: header, |
||||
id: id, |
||||
objects: objects, |
||||
}; |
||||
} |
||||
}; |
Loading…
Reference in new issue