aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2016-02-21 17:27:45 +0100
committerGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2016-02-21 17:27:45 +0100
commitb23cafb9bbaccb62cb8eed4484a1753c2cebb936 (patch)
tree63757ba13a0db17102523e6e74b4abe180c4c878
parent58974f55a4bdfc8aed3454762415c8e8ac9fc6a4 (diff)
parent264d05297c72e87b114a8e930db7eae7affe5690 (diff)
Merge branch 'CSP-no-inline' into dev
-rw-r--r--app/FreshRSS.php58
-rw-r--r--app/Models/StatsDAO.php20
-rw-r--r--app/Models/StatsDAOSQLite.php4
-rw-r--r--app/layout/layout.phtml3
-rw-r--r--app/views/helpers/javascript_vars.phtml54
-rw-r--r--app/views/stats/index.phtml64
-rw-r--r--app/views/stats/repartition.phtml103
-rw-r--r--p/scripts/main.js10
-rw-r--r--p/scripts/repartition.js72
-rw-r--r--p/scripts/stats.js56
10 files changed, 231 insertions, 213 deletions
diff --git a/app/FreshRSS.php b/app/FreshRSS.php
index 8eb862aeb..a6ed2a306 100644
--- a/app/FreshRSS.php
+++ b/app/FreshRSS.php
@@ -110,66 +110,8 @@ class FreshRSS extends Minz_FrontController {
}
}
- private static function setJavascriptCookie() {
- $mark = FreshRSS_Context::$user_conf->mark_when;
- $mail = Minz_Session::param('mail', false);
- $s = FreshRSS_Context::$user_conf->shortcuts;
- $json = json_encode(array(
- 'context' => array(
- 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(),
- 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'),
- 'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order),
- 'auto_mark_article' => !!$mark['article'],
- 'auto_mark_site' => !!$mark['site'],
- 'auto_mark_scroll' => !!$mark['scroll'],
- 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more,
- 'auto_actualize_feeds' => !!Minz_Session::param('actualize_feeds', false),
- 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload ,
- 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(),
- 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout,
- 'auth_type' => FreshRSS_Context::$system_conf->auth_type,
- 'current_user_mail' => $mail ? ('"' . $mail . '"') : null,
- 'current_view' => Minz_Request::actionName(),
- ),
- 'shortcuts' => array(
- 'mark_read' => @$s['mark_read'],
- 'mark_favorite' => @$s['mark_favorite'],
- 'go_website' => @$s['go_website'],
- 'prev_entry' => @$s['prev_entry'],
- 'next_entry' => @$s['next_entry'],
- 'first_entry' => @$s['first_entry'],
- 'last_entry' => @$s['last_entry'],
- 'collapse_entry' => @$s['collapse_entry'],
- 'load_more' => @$s['load_more'],
- 'auto_share' => @$s['auto_share'],
- 'focus_search' => @$s['focus_search'],
- 'user_filter' => @$s['user_filter'],
- 'help' => @$s['help'],
- 'close_dropdown' => @$s['close_dropdown'],
- ),
- 'url' => array(
- 'index' => _url('index', 'index'),
- 'login' => Minz_Url::display(array('c' => 'auth', 'a' => 'login'), 'php'),
- 'logout' => Minz_Url::display(array('c' => 'auth', 'a' => 'logout'), 'php'),
- 'help' => FRESHRSS_WIKI,
- ),
- 'i18n' => array(
- 'confirmation_default' => _t('gen.js.confirm_action'),
- 'notif_title_articles' => _t('gen.js.feedback.title_new_articles'),
- 'notif_body_articles' => _t('gen.js.feedback.body_new_articles'),
- 'notif_request_failed' => _t('gen.js.feedback.request_failed'),
- 'category_empty' => _t('gen.js.category_empty'),
- ),
- 'icons' => array(
- 'close' => _i('close'),
- ),
- ), JSON_UNESCAPED_UNICODE);
- setrawcookie('FreshRSS-vars', rawurlencode($json), 0, Minz_Session::getCookieDir());
- }
-
public static function preLayout() {
header("Content-Security-Policy: default-src 'self'; child-src *; img-src * data:; media-src *; style-src 'self' 'unsafe-inline'");
- self::setJavascriptCookie();
}
private function loadNotifications() {
diff --git a/app/Models/StatsDAO.php b/app/Models/StatsDAO.php
index 80caccc49..5ca333396 100644
--- a/app/Models/StatsDAO.php
+++ b/app/Models/StatsDAO.php
@@ -55,9 +55,9 @@ SQL;
/**
* Calculates entry count per day on a 30 days period.
- * Returns the result as a JSON string.
+ * Returns the result as a JSON object.
*
- * @return string
+ * @return JSON object
*/
public function calculateEntryCount() {
$count = $this->initEntryCountArray();
@@ -257,9 +257,9 @@ SQL;
/**
* Calculates feed count per category.
- * Returns the result as a JSON string.
+ * Returns the result as a JSON object.
*
- * @return string
+ * @return JSON object
*/
public function calculateFeedByCategory() {
$sql = <<<SQL
@@ -282,7 +282,7 @@ SQL;
* Calculates entry count per category.
* Returns the result as a JSON string.
*
- * @return string
+ * @return JSON object
*/
public function calculateEntryByCategory() {
$sql = <<<SQL
@@ -357,7 +357,7 @@ SQL;
$serie[] = array($key, $value);
}
- return json_encode($serie);
+ return $serie;
}
protected function convertToPieSerie($data) {
@@ -368,7 +368,7 @@ SQL;
$serie[] = $value;
}
- return json_encode($serie);
+ return $serie;
}
/**
@@ -411,17 +411,17 @@ SQL;
}
/**
- * Translates array content and encode it as JSON
+ * Translates array content
*
* @param array $data
- * @return string
+ * @return JSON object
*/
private function convertToTranslatedJson($data = array()) {
$translated = array_map(function($a) {
return _t('gen.date.' . $a);
}, $data);
- return json_encode($translated);
+ return $translated;
}
}
diff --git a/app/Models/StatsDAOSQLite.php b/app/Models/StatsDAOSQLite.php
index bb2336532..9bfe8b20a 100644
--- a/app/Models/StatsDAOSQLite.php
+++ b/app/Models/StatsDAOSQLite.php
@@ -4,9 +4,9 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
/**
* Calculates entry count per day on a 30 days period.
- * Returns the result as a JSON string.
+ * Returns the result as a JSON object.
*
- * @return string
+ * @return JSON object
*/
public function calculateEntryCount() {
$count = $this->initEntryCountArray();
diff --git a/app/layout/layout.phtml b/app/layout/layout.phtml
index 7d718efbd..99a3717bc 100644
--- a/app/layout/layout.phtml
+++ b/app/layout/layout.phtml
@@ -8,6 +8,9 @@
<meta name="viewport" content="initial-scale=1.0" />
<?php echo self::headTitle(); ?>
<?php echo self::headStyle(); ?>
+ <script id="jsonVars" type="application/json">
+<?php $this->renderHelper('javascript_vars'); ?>
+ </script>
<?php echo self::headScript(); ?>
<?php
$url_base = Minz_Request::currentRequest();
diff --git a/app/views/helpers/javascript_vars.phtml b/app/views/helpers/javascript_vars.phtml
new file mode 100644
index 000000000..6178cacf2
--- /dev/null
+++ b/app/views/helpers/javascript_vars.phtml
@@ -0,0 +1,54 @@
+<?php
+$mark = FreshRSS_Context::$user_conf->mark_when;
+$mail = Minz_Session::param('mail', false);
+$s = FreshRSS_Context::$user_conf->shortcuts;
+echo htmlspecialchars(json_encode(array(
+ 'context' => array(
+ 'auto_remove_article' => !!FreshRSS_Context::isAutoRemoveAvailable(),
+ 'hide_posts' => !(FreshRSS_Context::$user_conf->display_posts || Minz_Request::actionName() === 'reader'),
+ 'display_order' => Minz_Request::param('order', FreshRSS_Context::$user_conf->sort_order),
+ 'auto_mark_article' => !!$mark['article'],
+ 'auto_mark_site' => !!$mark['site'],
+ 'auto_mark_scroll' => !!$mark['scroll'],
+ 'auto_load_more' => !!FreshRSS_Context::$user_conf->auto_load_more,
+ 'auto_actualize_feeds' => !!Minz_Session::param('actualize_feeds', false),
+ 'does_lazyload' => !!FreshRSS_Context::$user_conf->lazyload ,
+ 'sticky_post' => !!FreshRSS_Context::isStickyPostEnabled(),
+ 'html5_notif_timeout' => FreshRSS_Context::$user_conf->html5_notif_timeout,
+ 'auth_type' => FreshRSS_Context::$system_conf->auth_type,
+ 'current_user_mail' => $mail ? ('"' . $mail . '"') : null,
+ 'current_view' => Minz_Request::actionName(),
+ ),
+ 'shortcuts' => array(
+ 'mark_read' => @$s['mark_read'],
+ 'mark_favorite' => @$s['mark_favorite'],
+ 'go_website' => @$s['go_website'],
+ 'prev_entry' => @$s['prev_entry'],
+ 'next_entry' => @$s['next_entry'],
+ 'first_entry' => @$s['first_entry'],
+ 'last_entry' => @$s['last_entry'],
+ 'collapse_entry' => @$s['collapse_entry'],
+ 'load_more' => @$s['load_more'],
+ 'auto_share' => @$s['auto_share'],
+ 'focus_search' => @$s['focus_search'],
+ 'user_filter' => @$s['user_filter'],
+ 'help' => @$s['help'],
+ 'close_dropdown' => @$s['close_dropdown'],
+ ),
+ 'url' => array(
+ 'index' => _url('index', 'index'),
+ 'login' => Minz_Url::display(array('c' => 'auth', 'a' => 'login'), 'php'),
+ 'logout' => Minz_Url::display(array('c' => 'auth', 'a' => 'logout'), 'php'),
+ 'help' => FRESHRSS_WIKI,
+ ),
+ 'i18n' => array(
+ 'confirmation_default' => _t('gen.js.confirm_action'),
+ 'notif_title_articles' => _t('gen.js.feedback.title_new_articles'),
+ 'notif_body_articles' => _t('gen.js.feedback.body_new_articles'),
+ 'notif_request_failed' => _t('gen.js.feedback.request_failed'),
+ 'category_empty' => _t('gen.js.category_empty'),
+ ),
+ 'icons' => array(
+ 'close' => _i('close'),
+ ),
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
diff --git a/app/views/stats/index.phtml b/app/views/stats/index.phtml
index 18bcd4d99..c11b88999 100644
--- a/app/views/stats/index.phtml
+++ b/app/views/stats/index.phtml
@@ -82,58 +82,12 @@
</div>
</div>
-<script>
-"use strict";
-function initStats() {
- if (!window.Flotr) {
- if (window.console) {
- console.log('FreshRSS waiting for Flotr…');
- }
- window.setTimeout(initStats, 50);
- return;
- }
- // Entry per day
- var avg = [];
- for (var i = -31; i <= 0; i++) {
- avg.push([i, <?php echo $this->average?>]);
- }
- Flotr.draw(document.getElementById('statsEntryPerDay'),
- [{
- data: <?php echo $this->count ?>,
- bars: {horizontal: false, show: true}
- },{
- data: avg,
- lines: {show: true},
- label: "<?php echo $this->average?>"
- }],
- {
- grid: {verticalLines: false},
- xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
- yaxis: {min: 0},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
- });
- // Feed per category
- Flotr.draw(document.getElementById('statsFeedPerCategory'),
- <?php echo $this->feedByCategory ?>,
- {
- grid: {verticalLines: false, horizontalLines: false},
- pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
- xaxis: {showLabels: false},
- yaxis: {showLabels: false},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
- legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
- });
- // Entry per category
- Flotr.draw(document.getElementById('statsEntryPerCategory'),
- <?php echo $this->entryByCategory ?>,
- {
- grid: {verticalLines: false, horizontalLines: false},
- pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
- xaxis: {showLabels: false},
- yaxis: {showLabels: false},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
- legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
- });
-}
-initStats();
-</script>
+<script id="jsonStats" type="application/json"><?php
+echo htmlspecialchars(json_encode(array(
+ 'average' => $this->average,
+ 'dataCount' => $this->count,
+ 'feedByCategory' => $this->feedByCategory,
+ 'entryByCategory' => $this->entryByCategory,
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+?></script>
+<script src="../scripts/stats.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/stats.js'); ?>"></script>
diff --git a/app/views/stats/repartition.phtml b/app/views/stats/repartition.phtml
index b20d9bbd0..980b26a3d 100644
--- a/app/views/stats/repartition.phtml
+++ b/app/views/stats/repartition.phtml
@@ -30,20 +30,20 @@
<?php }?>
<div class="stat">
- <table>
+ <table>
<tr>
- <th><?php echo _t('admin.stats.status_total'); ?></th>
- <th><?php echo _t('admin.stats.status_read'); ?></th>
- <th><?php echo _t('admin.stats.status_unread'); ?></th>
- <th><?php echo _t('admin.stats.status_favorites'); ?></th>
+ <th><?php echo _t('admin.stats.status_total'); ?></th>
+ <th><?php echo _t('admin.stats.status_read'); ?></th>
+ <th><?php echo _t('admin.stats.status_unread'); ?></th>
+ <th><?php echo _t('admin.stats.status_favorites'); ?></th>
</tr>
<tr>
- <td class="numeric"><?php echo $this->repartition['total']; ?></td>
- <td class="numeric"><?php echo $this->repartition['read']; ?></td>
- <td class="numeric"><?php echo $this->repartition['unread']; ?></td>
- <td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
+ <td class="numeric"><?php echo $this->repartition['total']; ?></td>
+ <td class="numeric"><?php echo $this->repartition['read']; ?></td>
+ <td class="numeric"><?php echo $this->repartition['unread']; ?></td>
+ <td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
</tr>
- </table>
+ </table>
</div>
<div class="stat">
@@ -62,76 +62,13 @@
</div>
</div>
-<script>
-"use strict";
-function initStats() {
- if (!window.Flotr) {
- if (window.console) {
- console.log('FreshRSS waiting for Flotr…');
- }
- window.setTimeout(initStats, 50);
- return;
- }
- // Entry per hour
- Flotr.draw(document.getElementById('statsEntryPerHour'),
- [{
- data: <?php echo $this->repartitionHour ?>,
- bars: {horizontal: false, show: true}
- }],
- {
- grid: {verticalLines: false},
- xaxis: {noTicks: 23,
- tickFormatter: function(x) {
- var x = parseInt(x);
- return x + 1;
- },
- min: -0.9,
- max: 23.9,
- tickDecimals: 0},
- yaxis: {min: 0},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
- });
- // Entry per day of week
- Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
- [{
- data: <?php echo $this->repartitionDayOfWeek ?>,
- bars: {horizontal: false, show: true}
- }],
- {
- grid: {verticalLines: false},
- xaxis: {noTicks: 6,
- tickFormatter: function(x) {
- var x = parseInt(x),
- days = <?php echo $this->days?>;
- return days[x];
- },
- min: -0.9,
- max: 6.9,
- tickDecimals: 0},
- yaxis: {min: 0},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
- });
- // Entry per month
- Flotr.draw(document.getElementById('statsEntryPerMonth'),
- [{
- data: <?php echo $this->repartitionMonth ?>,
- bars: {horizontal: false, show: true}
- }],
- {
- grid: {verticalLines: false},
- xaxis: {noTicks: 12,
- tickFormatter: function(x) {
- var x = parseInt(x),
- months = <?php echo $this->months?>;
- return months[(x - 1)];
- },
- min: 0.1,
- max: 12.9,
- tickDecimals: 0},
- yaxis: {min: 0},
- mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
- });
-
-}
-initStats();
-</script>
+<script id="jsonRepartition" type="application/json"><?php
+echo htmlspecialchars(json_encode(array(
+ 'repartitionHour' => $this->repartitionHour,
+ 'repartitionDayOfWeek' => $this->repartitionDayOfWeek,
+ 'days' => $this->days,
+ 'repartitionMonth' => $this->repartitionMonth,
+ 'months' => $this->months,
+), JSON_UNESCAPED_UNICODE), ENT_NOQUOTES);
+?></script>
+<script src="../scripts/repartition.js?<?php echo @filemtime(PUBLIC_PATH . '/scripts/repartition.js'); ?>"></script>
diff --git a/p/scripts/main.js b/p/scripts/main.js
index 31b07721a..f07cdafd7 100644
--- a/p/scripts/main.js
+++ b/p/scripts/main.js
@@ -1245,10 +1245,10 @@ function init_configuration_alert() {
});
}
-function parseJavaScriptCookie() {
- var vars = decodeURIComponent(document.cookie.replace(/(?:(?:^|.*;\s*)FreshRSS-vars\s*\=\s*([^;]*).*$)|^.*$/, "$1"));
- document.cookie = 'FreshRSS-vars=; expires=Thu, 01 Jan 1970 00:00:00 GMT';
- var json = JSON.parse(vars);
+function parseJsonVars() {
+ var jsonVars = document.getElementById('jsonVars'),
+ json = JSON.parse(jsonVars.innerHTML);
+ jsonVars.outerHTML = '';
window.context = json.context;
window.shortcuts = json.shortcuts;
window.url = json.url;
@@ -1264,7 +1264,7 @@ function init_all() {
window.setTimeout(init_all, 50);
return;
}
- parseJavaScriptCookie();
+ parseJsonVars();
init_notifications();
init_confirm_action();
$stream = $('#stream');
diff --git a/p/scripts/repartition.js b/p/scripts/repartition.js
new file mode 100644
index 000000000..a391de2f2
--- /dev/null
+++ b/p/scripts/repartition.js
@@ -0,0 +1,72 @@
+"use strict";
+function initStats() {
+ if (!window.Flotr) {
+ if (window.console) {
+ console.log('FreshRSS waiting for Flotr…');
+ }
+ window.setTimeout(initStats, 50);
+ return;
+ }
+ var jsonRepartition = document.getElementById('jsonRepartition'),
+ stats = JSON.parse(jsonRepartition.innerHTML);
+ jsonRepartition.outerHTML = '';
+ // Entry per hour
+ Flotr.draw(document.getElementById('statsEntryPerHour'),
+ [{
+ data: stats.repartitionHour,
+ bars: {horizontal: false, show: true}
+ }],
+ {
+ grid: {verticalLines: false},
+ xaxis: {noTicks: 23,
+ tickFormatter: function(x) {
+ var x = parseInt(x);
+ return x + 1;
+ },
+ min: -0.9,
+ max: 23.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Entry per day of week
+ Flotr.draw(document.getElementById('statsEntryPerDayOfWeek'),
+ [{
+ data: stats.repartitionDayOfWeek,
+ bars: {horizontal: false, show: true}
+ }],
+ {
+ grid: {verticalLines: false},
+ xaxis: {noTicks: 6,
+ tickFormatter: function(x) {
+ var x = parseInt(x);
+ return stats.days[x];
+ },
+ min: -0.9,
+ max: 6.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Entry per month
+ Flotr.draw(document.getElementById('statsEntryPerMonth'),
+ [{
+ data: stats.repartitionMonth,
+ bars: {horizontal: false, show: true}
+ }],
+ {
+ grid: {verticalLines: false},
+ xaxis: {noTicks: 12,
+ tickFormatter: function(x) {
+ var x = parseInt(x);
+ return stats.months[(x - 1)];
+ },
+ min: 0.1,
+ max: 12.9,
+ tickDecimals: 0},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+
+}
+initStats();
diff --git a/p/scripts/stats.js b/p/scripts/stats.js
new file mode 100644
index 000000000..2e8ab6e27
--- /dev/null
+++ b/p/scripts/stats.js
@@ -0,0 +1,56 @@
+"use strict";
+function initStats() {
+ if (!window.Flotr) {
+ if (window.console) {
+ console.log('FreshRSS waiting for Flotr…');
+ }
+ window.setTimeout(initStats, 50);
+ return;
+ }
+ var jsonStats = document.getElementById('jsonStats'),
+ stats = JSON.parse(jsonStats.innerHTML);
+ jsonStats.outerHTML = '';
+ // Entry per day
+ var avg = [];
+ for (var i = -31; i <= 0; i++) {
+ avg.push([i, stats.average]);
+ }
+ Flotr.draw(document.getElementById('statsEntryPerDay'),
+ [{
+ data: stats.dataCount,
+ bars: {horizontal: false, show: true}
+ },{
+ data: avg,
+ lines: {show: true},
+ label: stats.average,
+ }],
+ {
+ grid: {verticalLines: false},
+ xaxis: {noTicks: 6, showLabels: false, tickDecimals: 0, min: -30.75, max: -0.25},
+ yaxis: {min: 0},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return numberFormat(obj.y);}}
+ });
+ // Feed per category
+ Flotr.draw(document.getElementById('statsFeedPerCategory'),
+ stats.feedByCategory,
+ {
+ grid: {verticalLines: false, horizontalLines: false},
+ pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+ xaxis: {showLabels: false},
+ yaxis: {showLabels: false},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+ legend: {container: document.getElementById('statsFeedPerCategoryLegend'), noColumns: 3}
+ });
+ // Entry per category
+ Flotr.draw(document.getElementById('statsEntryPerCategory'),
+ stats.entryByCategory,
+ {
+ grid: {verticalLines: false, horizontalLines: false},
+ pie: {explode: 10, show: true, labelFormatter: function(){return '';}},
+ xaxis: {showLabels: false},
+ yaxis: {showLabels: false},
+ mouse: {relative: true, track: true, trackDecimals: 0, trackFormatter: function(obj) {return obj.series.label + ' - '+ numberFormat(obj.y) + ' ('+ (obj.fraction * 100).toFixed(1) + '%)';}},
+ legend: {container: document.getElementById('statsEntryPerCategoryLegend'), noColumns: 3}
+ });
+}
+initStats();