Alice

 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>



( run in 1.252 second using v1.01-cache-2.11-cpan-5735350b133 )