diff --git a/www/protected/components/extendedgitgraph/ExtendedGitGraph.php b/www/protected/components/extendedgitgraph/ExtendedGitGraph.php
new file mode 100644
index 0000000..7615bfb
--- /dev/null
+++ b/www/protected/components/extendedgitgraph/ExtendedGitGraph.php
@@ -0,0 +1,494 @@
+username = $usr_name;
+
+ set_time_limit(300); // 5min
+ }
+
+ public function authenticate($auth_key, $client_id, $client_secret) {
+ $url = sprintf(self::API_TOKEN, $client_id, $client_secret, $auth_key);
+ $result = file_get_contents($url);
+
+ $result = str_replace('access_token=', '', $result);
+ $result = str_replace('&scope=&token_type=bearer', '', $result);
+
+ setToken($result);
+ }
+
+ public function setToken($token) {
+ $this->token = $token;
+ $this->tokenHeader = 'access_token=' . $token . '&token_type=bearer';
+ }
+
+ public function collect() {
+ ob_implicit_flush(true);
+ ob_end_flush();
+
+ //--------------------------------
+
+ $this->listRepositories();
+
+ $this->listAllCommits();
+
+ $this->save();
+
+ //--------------------------------
+
+ $this->output_flushed($this->getRemainingRequests() . ' Requests remaining');
+ }
+
+ private function listRepositories() {
+ $page = 1;
+ $url = sprintf(self::API_REPOSITORIESLIST . '&' . $this->tokenHeader, $this->username, $page);
+
+ $result = $this->getJSON($url);
+
+ $repo_list = array();
+
+ while (! empty($result)) {
+ foreach ($result as $result_repo) {
+ $repo_list[] = $this->parseRepoJSON($result_repo);
+
+ $this->output_flushed("Found Repo: " . $result_repo->{'full_name'});
+ }
+
+ //##########
+
+ $url = sprintf(self::API_REPOSITORIESLIST . '&' . $this->tokenHeader, $this->username, ++$page);
+
+ $result = $this->getJSON($url);
+ }
+
+ $this->repositories = $repo_list;
+ }
+
+ private function getJSON($url) {
+ $options = array('http' => array('user_agent'=> $_SERVER['HTTP_USER_AGENT']));
+// $options = array('http' => array('user_agent'=> 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1944.0 Safari/537.36'));
+ $context = stream_context_create($options);
+ $response = file_get_contents($url, false, $context);
+
+ return json_decode($response);
+ }
+
+ private function getRemainingRequests() {
+ $json = $this->getJSON(self::API_RATELIMIT . '?' . $this->tokenHeader);
+
+ return $json->{'resources'}->{'core'}->{'remaining'};
+ }
+
+ private function listAllCommits() {
+ $this->commits = array();
+
+ foreach($this->repositories as $repo) {
+ $this->listCommits($repo);
+ }
+ }
+
+ private function listCommits($repo) {
+ $page = 1;
+ $url = $repo['commits_url'] . '?per_page=100&page=' . $page . '&author=' .$this->username . '&' .$this->tokenHeader;
+
+ $result = $this->getJSON($url);
+
+ $commit_list = array();
+
+ while (! empty($result)) {
+ foreach ($result as $result_commit) {
+ $commit_list[] = $this->parseCommitJSON($repo, $result_commit);
+ }
+
+ $this->output_flushed("Found 100 Commits from " . $repo['full_name']);
+
+ //##########
+
+ $url = $repo['commits_url'] . '?per_page=100&page=' . ++$page . '&author=' .$this->username . '&' .$this->tokenHeader;
+
+ $result = $this->getJSON($url);
+ }
+
+ $this->commits = array_merge($this->commits, $commit_list);
+ }
+
+ private function parseRepoJSON($json) {
+ return
+ [
+ 'id' => $json->{'id'},
+ 'name' => $json->{'name'},
+ 'full_name' => $json->{'full_name'},
+ 'owner' => $json->{'owner'}->{'login'},
+ 'owner_id' => $json->{'owner'}->{'id'},
+ 'owner_avatar-url' => $json->{'owner'}->{'avatar_url'},
+ 'owner_gravatar-id' => $json->{'owner'}->{'gravatar_id'},
+ 'url' => $json->{'html_url'},
+ 'language' => $json->{'language'},
+ 'url' => $json->{'html_url'},
+ 'creation' => DateTime::createFromFormat(DateTime::ISO8601, $json->{'created_at'}),
+ 'size' => $json->{'size'},
+ 'default_branch' => $json->{'default_branch'},
+ 'commits_url' => str_replace('{/sha}', '', $json->{'commits_url'}),
+ ];
+ }
+
+ private function parseCommitJSON($repo, $json) {
+ return
+ [
+ 'sha' => $json->{'sha'},
+ 'author_name' => $json->{'commit'}->{'author'}->{'name'},
+ 'author_mail' => $json->{'commit'}->{'author'}->{'email'},
+ 'author_login' => $json->{'author'}->{'login'},
+ 'author_id' => $json->{'author'}->{'id'},
+ 'sha' => $json->{'sha'},
+ 'message' => $json->{'commit'}->{'message'},
+ 'repository' => $repo,
+ 'date' => DateTime::createFromFormat(DateTime::ISO8601, $json->{'commit'}->{'author'}->{'date'}),
+ ];
+ }
+
+ private function save() {
+ $this->output_flushed("Start saving data");
+
+ $save = serialize(
+ [
+ 'repositories' => $this->repositories,
+ 'commits' => $this->commits,
+ ]);
+
+ file_put_contents(self::FILE_RAW_DATA, $save);
+
+ $this->output_flushed('Finished saving data');
+ }
+
+ public function output_flushed($txt)
+ {
+ echo '[' . date('H:i.s') . '] ' . $txt . "
";
+ }
+
+ public function loadData() {
+ $data = unserialize(file_get_contents(self::FILE_RAW_DATA));
+
+ $this->repositories = $data['repositories'];
+ $this->commits = $data['commits'];
+ }
+
+ public function generate($year) {
+ $ymap = $this->generateYearMap($year); // unused on purpose (template.php needs it)
+
+ $ymapmax = $this->getMaxCommitCount(); // unused on purpose (template.php needs it)
+
+ ob_start();
+ include('template.php');
+ $returned = ob_get_contents();
+ ob_end_clean();
+
+ return $returned;
+ }
+
+ public function generateAndSave() {
+ $result = '';
+
+ foreach($this->getYears() as $year) {
+ $result.= $this->generate($year);
+ $result.= '
';
+ }
+
+ $this->generateCommitMap();
+
+ file_put_contents(self::FILE_FINISHED_DATA,
+ serialize(
+ [
+ 'creation' => new DateTime(),
+ 'total' => count($this->commits),
+ 'repos' => count($this->repositories),
+ 'streak' => $this->getLongestStreak(),
+ 'streak_start' => $this->getLongestStreakStart(),
+ 'streak_end' => $this->getLongestStreakEnd(),
+ 'max_commits' => $this->getMaxCommits(),
+ 'max_commits_date' => $this->getMaxCommitsDate(),
+ 'avg_commits' => $this->getAvgCommits(),
+ 'start' => $this->startdate,
+ 'end' => $this->enddate,
+ 'content' => $result,
+ ]));
+
+ return $result;
+ }
+
+ private function generateCommitMap() {
+ $this->commitmap = array();
+
+ $this->startdate = $this->getStartDate();
+ $this->enddate = $this->getEndDate();
+
+ $date = clone $this->startdate;
+ while($date < $this->enddate) {
+ $this->commitmap[$date->format('Y-m-d')] = 0;
+
+ $date = $date->modify("+1 day");
+ }
+
+ foreach ($this->commits as $commit) {
+ if(array_key_exists($commit['date']->format('Y-m-d'), $this->commitmap))
+ $this->commitmap[$commit['date']->format('Y-m-d')]++;
+ }
+ }
+
+ private function getStartDate() {
+ $date = $this->commits[0]['date'];
+
+ foreach($this->commits as $commit) {
+ if ($commit['date'] < $date) {
+ $date = clone $commit['date'];
+ }
+ }
+
+ return new DateTime($date->format('Y-m-d'));
+ }
+
+ private function getEndDate() {
+ $date = $this->commits[0]['date'];
+
+ foreach($this->commits as $commit) {
+ if ($commit['date'] > $date) {
+ $date = clone $commit['date'];
+ }
+ }
+
+ return new DateTime($date->format('Y-m-d'));
+ }
+
+ private function getCommitsForDate($d) {
+ $v = $d->format('Y-m-d');
+ if (array_key_exists($v, $this->commitmap))
+ return $this->commitmap[$d->format('Y-m-d')];
+ else
+ return 0;
+ }
+
+ private function getLongestStreak() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $streak_curr = 0;
+ $streak_max = 0;
+
+ while ($curr <= $end) {
+ if ($this->getCommitsForDate($curr) > 0) {
+ $streak_curr++;
+ } else {
+ $streak_max = max($streak_max, $streak_curr);
+ $streak_curr = 0;
+ }
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ $streak_max = max($streak_max, $streak_curr);
+
+ return $streak_max;
+ }
+
+ private function getLongestStreakStart() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $streak_curr_start = clone $curr;
+ $streak_max_start = null;
+
+ $streak_curr = 0;
+ $streak_max = 0;
+
+ while ($curr <= $end) {
+ if ($this->getCommitsForDate($curr) > 0) {
+ $streak_curr++;
+ } else {
+ if ($streak_curr > $streak_max) {
+ $streak_max = $streak_curr;
+ $streak_max_start = clone $streak_curr_start;
+ }
+
+ $streak_curr = 0;
+ $streak_curr_start = clone $curr;
+ }
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ if ($streak_curr > $streak_max) {
+ $streak_max_start = clone $streak_curr_start;
+ }
+
+ return $streak_max_start;
+ }
+
+ private function getLongestStreakEnd() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $streak_max_end = null;
+
+ $streak_curr = 0;
+ $streak_max = 0;
+
+ while ($curr <= $end) {
+ if ($this->getCommitsForDate($curr) > 0) {
+ $streak_curr++;
+ } else {
+ if ($streak_curr > $streak_max) {
+ $streak_max = $streak_curr;
+ $streak_max_end = clone $curr;
+ }
+
+ $streak_curr = 0;
+ }
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ if ($streak_curr > $streak_max) {
+ $streak_max_end = clone $curr;
+ }
+
+ return $streak_max_end;
+ }
+
+ private function getMaxCommits() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $max = 0;
+
+ while ($curr <= $end) {
+ $max = max($max, $this->getCommitsForDate($curr));
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ return $max;
+ }
+
+ private function getMaxCommitsDate() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $max = 0;
+ $max_date = null;
+
+ while ($curr <= $end) {
+ $c = $this->getCommitsForDate($curr);
+ if ($c >= $max) {
+ $max = $c;
+ $max_date = clone $curr;
+ }
+ $max = max($max, $this->getCommitsForDate($curr));
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ return $max_date;
+ }
+
+ private function getAvgCommits() {
+ /* @var $curr DateTime */
+ /* @var $end DateTime */
+ $curr = clone $this->startdate;
+ $end = clone $this->enddate;
+
+ $max = array();
+
+ while ($curr <= $end) {
+ $max[] = $this->getCommitsForDate($curr);
+
+ $curr = $curr->modify('+1 day');
+ }
+
+ $sum = array_sum($max);
+ $count = count($max);
+
+ return $sum / $count;
+ }
+
+ public function loadFinishedContent() {
+ $data = unserialize(file_get_contents(self::FILE_FINISHED_DATA));
+ return $data['content'];
+ }
+
+ public function loadFinishedData() {
+ $data = unserialize(file_get_contents(self::FILE_FINISHED_DATA));
+ return $data;
+ }
+
+ private function getMaxCommitCount() {
+ $max = 0;
+
+ foreach($this->getYears() as $year) {
+ $max = max($max, max($this->generateYearMap($year)));
+ }
+
+ return $max;
+ }
+
+ private function generateYearMap($year) {
+ $ymap = array();
+
+ $date = new DateTime($year . '-01-01');
+ while($date->format('Y') == $year) {
+ $ymap[$date->format('Y-m-d')] = 0;
+
+ $date = $date->modify("+1 day");
+ }
+
+ foreach ($this->commits as $commit) {
+ if(array_key_exists($commit['date']->format('Y-m-d'), $ymap))
+ $ymap[$commit['date']->format('Y-m-d')]++;
+ }
+
+ return $ymap;
+ }
+
+ public function getYears() {
+ $years = array();
+
+ foreach ($this->commits as $commit) {
+ if(! in_array($commit['date']->format('Y'), $years))
+ $years[] = $commit['date']->format('Y');
+ }
+
+ asort($years);
+
+ return $years;
+ }
+}
\ No newline at end of file
diff --git a/www/protected/components/extendedgitgraph/demo.php b/www/protected/components/extendedgitgraph/demo.php
new file mode 100644
index 0000000..fe6ca57
--- /dev/null
+++ b/www/protected/components/extendedgitgraph/demo.php
@@ -0,0 +1,34 @@
+authenticate('7e26c5f1621349c14a7d');
+
+//$v->setToken('7b3f6443cdd4b2f92d75c4c8aa83cfda6c7ca3ce');
+//$v->collect();
+
+$v->loadData();
+
+?>
+
+
+
+