view release on metacpan or search on metacpan
lib/Alice.pm view on Meta::CPAN
if ($limit - $total < 20) {
$step = $limit - $total;
}
$window->buffer->messages($max, $min, $step, sub {
my $msgs = shift;
$stream->send([{
window => $window->serialized,
type => "chunk",
range => (@$msgs ? [$msgs->[0]{msgid}, $msgs->[-1]{msgid}] : []),
html => join "", map {$_->{html}} @$msgs,
}]);
$total += $step;
if (@$msgs == $step and $total < $limit) {
$max = $msgs->[0]->{msgid} - 1;
$self->update_window($stream, $window, $max, $min, $limit, $total, $cb);
}
else {
$cb->() if $cb;
return;
}
});
}
sub handle_message {
lib/Alice/InfoWindow.pm view on Meta::CPAN
my $message = {
type => "message",
event => "say",
nick => $from,
window => $self->serialized,
($options{source} ? (source => $options{source}) : ()),
html => encoded_string($html),
self => $options{self} ? 1 : 0,
hightlight => $options{highlight} ? 1 : 0,
msgid => $self->buffer->next_msgid,
timestamp => time,
monospaced => $options{mono} ? 1 : 0,
consecutive => $from eq $self->buffer->previous_nick ? 1 : 0,
};
$message->{html} = $self->render->("message", $message);
$self->buffer->add($message);
return $message;
}
lib/Alice/MessageBuffer.pm view on Meta::CPAN
has store => (
is => 'ro',
required => 1,
);
has id => (
is => 'ro',
required => 1,
);
sub next_msgid {
my $self = shift;
my $msgid = $self->store->msgid + 1;
$self->store->msgid($msgid);
return $msgid;
}
sub clear {
my $self = shift;
$self->previous_nick("");
$self->store->clear($self->id);
}
sub add {
my ($self, $message) = @_;
$message->{event} eq "say" ? $self->previous_nick($message->{nick})
: $self->previous_nick("");
$self->store->add($self->id, $message);
}
sub messages {
my ($self, $max, $min, $limit, $cb) = @_;
my $msgid = $self->store->msgid;
$max = $msgid if $max > $msgid;
$min = 0 if $min < 0;
$limit = 0 if $limit < 0;
return $self->store->messages($self->id, $max, $min, $limit, $cb);
}
__PACKAGE__->meta->make_immutable;
1;
lib/Alice/MessageStore.pm view on Meta::CPAN
has dbi => (
is => 'ro',
lazy => 1,
default => sub {
my $self = shift;
AnyEvent::DBI->new(@{$self->dsn});
}
);
has msgid => (
is => 'rw',
default => 0,
);
sub BUILD {
my $self = shift;
$self->dbi->exec("SELECT msgid FROM window_buffer ORDER BY msgid DESC LIMIT 1", sub {
my (undef, $row) = @_;
$self->msgid( @$row ? $row->[0][0] : 0);
});
}
sub clear {
my ($self, $id) = @_;
$self->dbi->exec("DELETE FROM window_buffer WHERE window_id = ?", $id, sub {});
}
sub messages {
my ($self, $id, $max, $min, $limit, $cb) = @_;
$self->dbi->exec(
"SELECT message FROM window_buffer WHERE window_id=? " .
"AND msgid <= ? AND msgid >= ? ORDER BY msgid DESC LIMIT ?",
$id, $max, $min, $limit,
sub { $cb->([map {decode_json $_->[0]} reverse @{$_[1]}]) }
);
}
sub add {
my ($self, $id, $message) = @_;
$self->dbi->exec(
"INSERT INTO window_buffer (window_id,msgid,message) VALUES (?,?,?)",
$id, $message->{msgid}, encode_json($message), sub {});
$self->trim->{$id} = 1;
}
sub do_trim {
my $self = shift;
my @trim = keys %{$self->trim};
$self->trim({});
lib/Alice/MessageStore.pm view on Meta::CPAN
}
else {
undef $idle_w;
}
};
}
sub trim_id {
my ($self, $window_id) = @_;
$self->dbi->exec(
"SELECT msgid FROM window_buffer WHERE window_id=? ORDER BY msgid DESC LIMIT ?,1",
$window_id, $self->backlog, sub {
my $rows = $_[1];
if (@$rows) {
my $minid = $rows->[0][0];
$self->dbi->exec(
"DELETE FROM window_buffer WHERE window_id=? AND msgid < ?",
$window_id, $minid, sub{}
);
}
}
);
}
__PACKAGE__->meta->make_immutable;
1;
lib/Alice/Window.pm view on Meta::CPAN
}
sub format_event {
my ($self, $event, $nick, $body) = @_;
my $message = {
type => "message",
event => $event,
nick => $nick,
window => $self->serialized,
body => $body,
msgid => $self->buffer->next_msgid,
timestamp => time,
};
my $html = $self->render->("event", $message);
$message->{html} = $html;
$self->buffer->add($message);
return $message;
}
lib/Alice/Window.pm view on Meta::CPAN
# pass the inverse => italic option if this is NOT monospace
my $html = irc_to_html($body, classes => 1, ($options{monospaced} ? () : (invert => "italic")));
my $message = {
type => "message",
event => "say",
nick => $nick,
window => $self->serialized,
html => encoded_string($html),
msgid => $self->buffer->next_msgid,
timestamp => time,
consecutive => $nick eq $self->buffer->previous_nick,
%options,
};
$message->{html} = $self->render->("message", $message);
$self->buffer->add($message);
return $message;
}
share/buffer.sql view on Meta::CPAN
CREATE TABLE window_buffer (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
msgid INT NOT NULL,
window_id INT NOT NULL,
message BLOB
);
share/static/alice.js view on Meta::CPAN
}
},
growlNotify: function(message) {
if (window.fluid) {
window.fluid.showGrowlNotification({
title: message.subject,
description: message.body,
priority: 1,
sticky: false,
identifier: message.msgid
});
}
else if (window.webkitNotifications) {
if (window.webkitNotifications.checkPermission() == 0) {
var popup = window.webkitNotifications.createNotification(
"http://static.usealice.org/image/alice.png",
message.subject,
message.body
);
share/static/alice.js view on Meta::CPAN
}
}
},
info_window: function(n) {
return this.windows().find(function(win) {
if (win.type == "info") return win;
});
},
openWindow: function(serialized, msgid) {
if (!msgid) msgid = this.msgid();
var win = new Alice.Window(this, serialized, msgid);
this.addWindow(win);
return win;
},
addWindow: function(win) {
this.window_map.set(win.id, win);
if (window.fluid)
window.fluid.addDockMenuItem(win.title, win.focus.bind(win));
},
share/static/alice.js view on Meta::CPAN
if (this.options.debug == "true") {
if (win) {
win.addMessage({
html: '<li class="message monospace"><div class="left">console</div><div class="msg">'+arguments[i].toString()+'</div></li>'
});
}
}
}
},
msgid: function() {
var ids = this.windows().map(function(w){return w.msgid});
return Math.max.apply(Math, ids);
},
setSource: function(id) {
$('source').value = id;
},
showSet: function(name) {
var ids = this.tabsets[name];
if (ids) {
share/static/alice.js view on Meta::CPAN
setTimeout(function() {
this.application.displayMessage(message)
}.bind(this), 1000);
}
}.bind(this)
});
}
});
Alice.Window = Class.create({
initialize: function(application, serialized, msgid) {
this.application = application;
this.element = $(serialized['id']);
this.title = serialized['title'];
this.type = serialized['type'];
this.hashtag = serialized['hashtag'];
this.id = this.element.identify();
this.active = false;
this.topic = serialized['topic'];
this.tab = $(this.id + "_tab");
share/static/alice.js view on Meta::CPAN
this.tabButton = $(this.id + "_tab_button");
this.messages = this.element.down('.messages');
this.visibleNick = "";
this.visibleNickTimeout = "";
this.lasttimestamp = new Date(0);
this.nicks = [];
this.nicks_order = [];
this.statuses = [];
this.messageLimit = this.application.isMobile ? 50 : 100;
this.chunkSize = this.messageLimit / 2;
this.msgid = msgid || 0;
this.visible = true;
this.forceScroll = false;
this.lastScrollPosition = 0;
this.setupEvents();
},
hide: function() {
this.element.hide();
this.tab.addClassName('hidden');
share/static/alice.js view on Meta::CPAN
checkScrollBack: function() {
if (this.active && this.element.scrollTop == 0) {
clearInterval(this.scrollListener);
var first = this.messages.down("li[id]");
if (first) {
first = first.id.replace("msg-", "") - 1;
this.messageLimit += this.chunkSize;
}
else {
first = this.msgid;
}
this.application.log("requesting chunk" + first);
this.tab.addClassName("loading");
this.application.getBacklog(this, first, this.chunkSize);
}
else {
clearTimeout(this.scrollListener);
this.scrollListener = setTimeout(this.checkScrollBack.bind(this), 1000);
}
},
share/static/alice.js view on Meta::CPAN
clearTimeout(this.scrollListener);
if (chunk.range.length == 0) {
this.scrollBackEmpty = true;
this.tab.removeClassName("loading");
return;
}
var position = this.captureScrollPosition();
if (chunk['range'][0] > this.msgid) {
this.messages.insert({"bottom": chunk['html']});
this.trimMessages();
this.msgid = chunk['range'][1];
}
else {
this.bulk_insert = true;
this.messages.insert({"top": chunk['html']});
}
this.messages.select("li:not(.filtered)").each(function (li) {
this.application.applyFilters(li, this);
}.bind(this));
this.bulk_insert = false;
this.scrollToPosition(position);
setTimeout(function(){this.removeClassName("loading")}.bind(this.tab), 1000);
this.scrollListener = setTimeout(this.checkScrollBack.bind(this), 1000);
},
addMessage: function(message) {
if (!message.html || message.msgid <= this.msgid) return;
if (message.msgid) this.msgid = message.msgid;
if (message.nicks) this.updateNicks(message.nicks);
var position = this.captureScrollPosition();
this.messages.insert(message.html);
this.trimMessages();
this.scrollToPosition(position);
var li = this.messages.select("li").last();
share/templates/event.html view on Meta::CPAN
? my ($app, $msg) = @_;
<li class="event <?= $msg->{event} ?>" id="<?= $msg->{msgid} ?>">
<div class="left"><span class="timestamp"><?= $msg->{timestamp} ?></span></div>
<div class="msg">
? if ($msg->{event} eq "topic") {
Topic changed to "<?= Text::MicroTemplate::encoded_string($msg->{body}) ?>"
? if ($msg->{nick}) {
by <a href="javascript:alice.connection.requestWindow('<?= $msg->{nick} ?>','<?= $msg->{window}->{id} ?>')" class="nick"><?= $msg->{nick} ?></a>
? }
? } elsif ($msg->{event} eq "invite") {
share/templates/index_footer.html view on Meta::CPAN
if (alice.isMobile) {
alice.options.images = "hide";
alice.options.avatars = "hide";
}
if (alice.options.avatars == "hide") document.body.addClassName('noavatars');
<? my $tabsets = {map {$_->name => $_->windows } $app->tabsets}; ?>
alice.tabsets = <?= Text::MicroTemplate::encoded_string(JSON::to_json $tabsets, {ascii => 1}) ?>;
<? my $msgid = $app->message_store->msgid; ?>
<? for my $window (@windows) { ?>
alice.openWindow(
<?= Text::MicroTemplate::encoded_string(JSON::to_json $window->serialized, {ascii => 1}) ?>,
<?= $msgid ?>
);
<? } ?>
alice.ready();
<? if ($app->config->first_run or !grep {$_->is_connected or $_->is_connecting} $app->ircs) { ?>
alice.toggleConfig();
<? } ?>
});
</script>
share/templates/message.html view on Meta::CPAN
? my ($app, $msg) = @_;
? my $classes = join " ", (
? grep {$msg->{$_}} qw/highlight self consecutive monospaced avatar/,
? );
<li class="message <?= $classes ?>" id="msg-<?= $msg->{msgid} ?>"<?= $msg->{avatar} ? " avatar=$msg->{avatar}" : "" ?>>
<div class="timehint timestamp"><?= $msg->{timestamp} ?></div>
<div class="left">
<a href="javascript:alice.connection.requestWindow('<?= $msg->{nick} ?>','<?= $msg->{window}{id} ?>')" class="nick"<?= $msg->{source} ? " title=$msg->{source}" : "" ?>>
<span class="nick"><?= $msg->{nick} ?></span>
</a>
</div>
<div class="msg">
<?= $msg->{html} ?>
</div>